agpu/graphics/
texture.rs

1mod view;
2pub use view::*;
3
4mod sampler;
5pub use sampler::*;
6
7mod builder;
8pub use builder::*;
9
10use crate::Gpu;
11
12// Re-export TextureFormat
13pub use wgpu::TextureFormat;
14
15pub trait TextureFormatExt {
16    fn target(&self) -> wgpu::ColorTargetState;
17}
18impl TextureFormatExt for TextureFormat {
19    fn target(&self) -> wgpu::ColorTargetState {
20        wgpu::ColorTargetState {
21            format: *self,
22            blend: None,
23            write_mask: wgpu::ColorWrites::ALL,
24        }
25    }
26}
27
28pub struct Texture<D>
29where
30    D: TextureDimensions,
31{
32    pub(crate) gpu: Gpu,
33    inner: wgpu::Texture,
34    pub view: wgpu::TextureView,
35    pub format: wgpu::TextureFormat,
36    pub size: D,
37    pub usage: wgpu::TextureUsages,
38}
39impl<D> std::ops::Deref for Texture<D>
40where
41    D: TextureDimensions,
42{
43    type Target = wgpu::Texture;
44
45    fn deref(&self) -> &Self::Target {
46        &self.inner
47    }
48}
49impl<D> std::ops::DerefMut for Texture<D>
50where
51    D: TextureDimensions,
52{
53    fn deref_mut(&mut self) -> &mut Self::Target {
54        &mut self.inner
55    }
56}
57
58impl<D> Texture<D>
59where
60    D: crate::TextureDimensions,
61{
62    // FIXME
63    // pub fn new(gpu: GpuHandle, desc: &wgpu::TextureDescriptor) -> Self {
64    //     let inner = gpu.create_texture(desc);
65    //     let view = inner.create_view(&Default::default());
66    //     Self {
67    //         gpu,
68    //         inner,
69    //         view,
70    //         format: desc.format,
71    //         size: desc.size,
72    //         usage: desc.usage,
73    //     }
74    // }
75
76    /// Resize the texture. Old contents are discarded (see resize_and_copy)
77    pub fn resize(&mut self, size: D) {
78        let new_texture = self.gpu.create_texture(&wgpu::TextureDescriptor {
79            // TODO: update label for texture on resize
80            label: None,
81            size: size.as_extent(),
82            // TODO: mip level count on resize??
83            mip_level_count: 1,
84            sample_count: 1,
85            dimension: size.dim(),
86            format: self.format,
87            usage: self.usage,
88        });
89
90        // Create a new view
91        let view = new_texture.create_view(&Default::default());
92
93        self.inner = new_texture;
94        self.size = size;
95        self.view = view;
96    }
97
98    /// Resize the texture and copy the contents
99    pub fn resize_with_copy(&mut self, size: D) {
100        let new_usage = self.usage | wgpu::TextureUsages::COPY_DST;
101
102        let new_texture = self.gpu.create_texture(&wgpu::TextureDescriptor {
103            // TODO: update label for texture on resize
104            label: None,
105            size: size.as_extent(),
106            // TODO: mip level count on resize??
107            mip_level_count: 1,
108            sample_count: 1,
109            dimension: size.dim(),
110            format: self.format,
111            usage: new_usage,
112        });
113
114        // Don't copy depth buffer! (InvalidDepthTextureExtent)
115        match self.format {
116            wgpu::TextureFormat::Depth32Float
117            | wgpu::TextureFormat::Depth24Plus
118            | wgpu::TextureFormat::Depth24PlusStencil8 => {}
119            _ => {
120                let mut enc = self.gpu.create_command_encoder("Texture resize encoder");
121                enc.copy_texture_to_texture(
122                    self.inner.as_image_copy(),
123                    new_texture.as_image_copy(),
124                    self.size.as_extent(),
125                );
126                self.gpu.queue.submit([enc.finish()]);
127            }
128        }
129
130        // Create a new view
131        let view = new_texture.create_view(&Default::default());
132
133        self.inner = new_texture;
134        self.size = size;
135        self.usage = new_usage;
136        self.view = view;
137    }
138
139    pub fn write<T>(&self, size: D, data: &[T])
140    where
141        T: bytemuck::Pod,
142    {
143        self.write_block(D::ZEROED, size, data)
144    }
145
146    pub fn write_block<T>(&self, texel: D, size: D, data: &[T])
147    where
148        T: bytemuck::Pod,
149    {
150        let data_bytes = bytemuck::cast_slice::<_, u8>(data);
151
152        self.gpu.queue.write_texture(
153            wgpu::ImageCopyTextureBase {
154                texture: &self.inner,
155                mip_level: 0,
156                origin: texel.as_origin(),
157                aspect: wgpu::TextureAspect::All,
158            },
159            data_bytes,
160            wgpu::ImageDataLayout {
161                // This is 0 because our source should not be offset
162                offset: 0,
163                bytes_per_row: std::num::NonZeroU32::new(
164                    size.width() * self.format.describe().block_size as u32,
165                ),
166                rows_per_image: None,
167            },
168            size.as_extent(),
169        )
170    }
171
172    // TODO
173    #[allow(unreachable_code)]
174    pub fn read_immediately(&self) -> Result<wgpu::util::DownloadBuffer, wgpu::BufferAsyncError> {
175        let format = self.format.describe();
176        let texel_count = self.size.width() * self.size.height() * self.size.depth();
177        let read_size = texel_count * format.block_size as u32
178            / (format.block_dimensions.0 as u32 * format.block_dimensions.1 as u32);
179        println!("texel_count: {}, read_size: {}", texel_count, read_size);
180        let staging_buf = self
181            .gpu
182            .new_buffer("texture read staging buffer")
183            .allow_copy_to()
184            .allow_map_read()
185            .create_uninit(read_size as _);
186
187        let buffer_dimensions = BufferDimensions::new(self.size.width(), self.size.height());
188
189        let staging_copy = wgpu::ImageCopyBuffer {
190            buffer: &staging_buf,
191            layout: wgpu::ImageDataLayout {
192                offset: 0,
193                bytes_per_row: std::num::NonZeroU32::new(buffer_dimensions.padded_bytes_per_row),
194                rows_per_image: None,
195            },
196        };
197
198        let mut enc = self
199            .gpu
200            .create_command_encoder("texture read immediately enc");
201
202        enc.copy_texture_to_buffer(
203            self.inner.as_image_copy(),
204            staging_copy,
205            self.size.as_extent(),
206        );
207
208        self.gpu.queue.submit([enc.finish()]);
209
210        staging_buf.download_immediately()
211    }
212}
213
214pub type D1 = (u32,);
215pub type D2 = (u32, u32);
216pub type D3 = (u32, u32, u32);
217
218pub trait TextureDimensions: Copy {
219    const ZEROED: Self;
220    fn dim(&self) -> wgpu::TextureDimension;
221    fn as_extent(&self) -> wgpu::Extent3d;
222    fn as_origin(&self) -> wgpu::Origin3d;
223    fn width(&self) -> u32;
224    fn height(&self) -> u32;
225    fn depth(&self) -> u32;
226}
227
228impl TextureDimensions for (u32, u32, u32) {
229    const ZEROED: Self = (0, 0, 0);
230    fn dim(&self) -> wgpu::TextureDimension {
231        wgpu::TextureDimension::D3
232    }
233    fn as_extent(&self) -> wgpu::Extent3d {
234        wgpu::Extent3d {
235            width: self.0,
236            height: self.1,
237            depth_or_array_layers: self.2,
238        }
239    }
240    fn as_origin(&self) -> wgpu::Origin3d {
241        wgpu::Origin3d {
242            x: self.0,
243            y: self.1,
244            z: self.2,
245        }
246    }
247    fn width(&self) -> u32 {
248        self.0
249    }
250    fn height(&self) -> u32 {
251        self.1
252    }
253    fn depth(&self) -> u32 {
254        self.2
255    }
256}
257
258impl TextureDimensions for (u32, u32) {
259    const ZEROED: Self = (0, 0);
260    fn dim(&self) -> wgpu::TextureDimension {
261        wgpu::TextureDimension::D2
262    }
263    fn as_extent(&self) -> wgpu::Extent3d {
264        wgpu::Extent3d {
265            width: self.0,
266            height: self.1,
267            depth_or_array_layers: 1,
268        }
269    }
270    fn as_origin(&self) -> wgpu::Origin3d {
271        wgpu::Origin3d {
272            x: self.0,
273            y: self.1,
274            z: 0,
275        }
276    }
277    fn width(&self) -> u32 {
278        self.0
279    }
280    fn height(&self) -> u32 {
281        self.1
282    }
283    fn depth(&self) -> u32 {
284        1
285    }
286}
287
288impl TextureDimensions for (u32,) {
289    const ZEROED: Self = (0,);
290    fn dim(&self) -> wgpu::TextureDimension {
291        wgpu::TextureDimension::D1
292    }
293    fn as_extent(&self) -> wgpu::Extent3d {
294        wgpu::Extent3d {
295            width: self.0,
296            height: 1,
297            depth_or_array_layers: 1,
298        }
299    }
300    fn as_origin(&self) -> wgpu::Origin3d {
301        wgpu::Origin3d {
302            x: self.0,
303            y: 0,
304            z: 0,
305        }
306    }
307    fn width(&self) -> u32 {
308        self.0
309    }
310    fn height(&self) -> u32 {
311        1
312    }
313    fn depth(&self) -> u32 {
314        1
315    }
316}
317
318#[allow(unused)]
319struct BufferDimensions {
320    width: u32,
321    height: u32,
322    unpadded_bytes_per_row: u32,
323    padded_bytes_per_row: u32,
324}
325
326impl BufferDimensions {
327    fn new(width: u32, height: u32) -> Self {
328        let bytes_per_pixel = std::mem::size_of::<u32>();
329        let unpadded_bytes_per_row = width * bytes_per_pixel as u32;
330        let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
331        let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
332        let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;
333        Self {
334            width,
335            height,
336            unpadded_bytes_per_row,
337            padded_bytes_per_row,
338        }
339    }
340}
341
342// TODO
343// #[cfg(test)]
344// mod tests {
345//     #[test]
346//     fn texture_write() {
347//         let data = [10_u32; 212 * 13];
348
349//         let gpu = crate::Gpu::builder().build_headless().unwrap();
350//         let texture = gpu
351//             .new_texture("resize test")
352//             .allow_copy_from()
353//             .create_empty((1024, 1024));
354//         texture.write((212, 13), &data);
355
356//         // let texture_read = texture.read_immediately().unwrap();
357//         // let texture_read = bytemuck::cast_slice::<_, u8>(&texture_read);
358
359//         // assert_eq!(data, texture_read[..data.len()]);
360//     }
361
362//     #[test]
363//     fn texture_write_u8() {
364//         let data = [10_u8; 212 * 13];
365
366//         let gpu = crate::Gpu::builder().build_headless().unwrap();
367//         let texture = gpu
368//             .new_texture("resize test")
369//             .allow_copy_from()
370//             .with_format(crate::TextureFormat::R8Unorm)
371//             .create_empty((1024, 1024));
372//         texture.write((212, 13), &data);
373
374//         // let texture_read = texture.read_immediately().unwrap();
375//         // let texture_read = bytemuck::cast_slice::<_, u8>(&texture_read);
376
377//         // assert_eq!(data, texture_read[..data.len()]);
378//     }
379// }