wisp/texture/
render_texture.rs1use std::sync::Arc;
9
10use crate::application::Application;
11use crate::texture::Texture;
12
13#[derive(Clone)]
17pub struct RenderTexture {
18 inner: Arc<RenderTextureInner>,
19}
20
21struct RenderTextureInner {
22 texture: wgpu::Texture,
23 view: wgpu::TextureView,
24 sampler: wgpu::Sampler,
25 width: u32,
26 height: u32,
27 format: wgpu::TextureFormat,
28}
29
30impl RenderTexture {
31 #[must_use]
35 pub fn new(app: &Application, width: u32, height: u32) -> Self {
36 Self::with_format(app, width, height, wgpu::TextureFormat::Rgba8UnormSrgb)
37 }
38
39 #[must_use]
41 pub fn with_format(
42 app: &Application,
43 width: u32,
44 height: u32,
45 format: wgpu::TextureFormat,
46 ) -> Self {
47 let device = app.device();
48 let texture = device.create_texture(&wgpu::TextureDescriptor {
49 label: Some("wisp::RenderTexture"),
50 size: wgpu::Extent3d {
51 width,
52 height,
53 depth_or_array_layers: 1,
54 },
55 mip_level_count: 1,
56 sample_count: 1,
57 dimension: wgpu::TextureDimension::D2,
58 format,
59 usage: wgpu::TextureUsages::RENDER_ATTACHMENT
60 | wgpu::TextureUsages::COPY_SRC
61 | wgpu::TextureUsages::TEXTURE_BINDING,
62 view_formats: &[],
63 });
64 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
65 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
66 label: Some("wisp::RenderTexture sampler"),
67 mag_filter: wgpu::FilterMode::Linear,
68 min_filter: wgpu::FilterMode::Linear,
69 ..Default::default()
70 });
71 Self {
72 inner: Arc::new(RenderTextureInner {
73 texture,
74 view,
75 sampler,
76 width,
77 height,
78 format,
79 }),
80 }
81 }
82
83 #[must_use]
85 pub fn view(&self) -> &wgpu::TextureView {
86 &self.inner.view
87 }
88
89 #[must_use]
91 pub fn sampler(&self) -> &wgpu::Sampler {
92 &self.inner.sampler
93 }
94
95 #[must_use]
97 pub fn format(&self) -> wgpu::TextureFormat {
98 self.inner.format
99 }
100
101 #[must_use]
103 pub fn width(&self) -> u32 {
104 self.inner.width
105 }
106
107 #[must_use]
109 pub fn height(&self) -> u32 {
110 self.inner.height
111 }
112
113 #[must_use]
124 pub fn as_texture(&self) -> Texture {
125 Texture::from_render_texture_parts(
126 self.inner.texture.clone(),
127 self.inner.view.clone(),
128 self.inner.sampler.clone(),
129 self.inner.width,
130 self.inner.height,
131 )
132 }
133
134 #[must_use]
142 pub fn read_pixels(&self, app: &Application) -> Vec<u8> {
143 let device = app.device();
144 let queue = app.queue();
145 let unpadded_bytes_per_row = self.inner.width * 4;
146 let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
147 let padded_bytes_per_row = unpadded_bytes_per_row.div_ceil(align) * align;
148 let buffer_size = u64::from(padded_bytes_per_row) * u64::from(self.inner.height);
149
150 let staging = device.create_buffer(&wgpu::BufferDescriptor {
151 label: Some("wisp::RenderTexture readback"),
152 size: buffer_size,
153 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
154 mapped_at_creation: false,
155 });
156
157 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
158 label: Some("wisp::RenderTexture::read_pixels encoder"),
159 });
160 encoder.copy_texture_to_buffer(
161 wgpu::TexelCopyTextureInfo {
162 texture: &self.inner.texture,
163 mip_level: 0,
164 origin: wgpu::Origin3d::ZERO,
165 aspect: wgpu::TextureAspect::All,
166 },
167 wgpu::TexelCopyBufferInfo {
168 buffer: &staging,
169 layout: wgpu::TexelCopyBufferLayout {
170 offset: 0,
171 bytes_per_row: Some(padded_bytes_per_row),
172 rows_per_image: Some(self.inner.height),
173 },
174 },
175 wgpu::Extent3d {
176 width: self.inner.width,
177 height: self.inner.height,
178 depth_or_array_layers: 1,
179 },
180 );
181 queue.submit(std::iter::once(encoder.finish()));
182
183 let (sender, receiver) = std::sync::mpsc::channel();
184 staging
185 .slice(..)
186 .map_async(wgpu::MapMode::Read, move |result| {
187 let _ = sender.send(result);
188 });
189 device.poll(wgpu::Maintain::Wait);
190 receiver
191 .recv()
192 .expect("readback channel dropped")
193 .expect("readback map failed");
194
195 let mapped = staging.slice(..).get_mapped_range();
196 let unpadded_size = (unpadded_bytes_per_row as usize) * (self.inner.height as usize);
197 let mut output = Vec::with_capacity(unpadded_size);
198 for row in 0..self.inner.height {
199 let start = (row * padded_bytes_per_row) as usize;
200 let end = start + unpadded_bytes_per_row as usize;
201 output.extend_from_slice(&mapped[start..end]);
202 }
203 drop(mapped);
204 staging.unmap();
205 output
206 }
207}
208
209impl std::fmt::Debug for RenderTexture {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 f.debug_struct("RenderTexture")
212 .field("width", &self.inner.width)
213 .field("height", &self.inner.height)
214 .field("format", &self.inner.format)
215 .finish()
216 }
217}