wgpu/util/
device.rs

1/// Describes a [Buffer](crate::Buffer) when allocating.
2#[derive(Clone, Debug, PartialEq, Eq, Hash)]
3pub struct BufferInitDescriptor<'a> {
4    /// Debug label of a buffer. This will show up in graphics debuggers for easy identification.
5    pub label: crate::Label<'a>,
6    /// Contents of a buffer on creation.
7    pub contents: &'a [u8],
8    /// Usages of a buffer. If the buffer is used in any way that isn't specified here, the operation
9    /// will panic.
10    pub usage: crate::BufferUsages,
11}
12
13/// Order in which TextureData is laid out in memory.
14#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
15pub enum TextureDataOrder {
16    /// The texture is laid out densely in memory as:
17    ///
18    /// ```text
19    /// Layer0Mip0 Layer0Mip1 Layer0Mip2
20    /// Layer1Mip0 Layer1Mip1 Layer1Mip2
21    /// Layer2Mip0 Layer2Mip1 Layer2Mip2
22    /// ````
23    ///
24    /// This is the layout used by dds files.
25    ///
26    /// This was the previous behavior of [`DeviceExt::create_texture_with_data`].
27    #[default]
28    LayerMajor,
29    /// The texture is laid out densely in memory as:
30    ///
31    /// ```text
32    /// Layer0Mip0 Layer1Mip0 Layer2Mip0
33    /// Layer0Mip1 Layer1Mip1 Layer2Mip1
34    /// Layer0Mip2 Layer1Mip2 Layer2Mip2
35    /// ```
36    ///
37    /// This is the layout used by ktx and ktx2 files.
38    MipMajor,
39}
40
41/// Utility methods not meant to be in the main API.
42pub trait DeviceExt {
43    /// Creates a [Buffer](crate::Buffer) with data to initialize it.
44    fn create_buffer_init(&self, desc: &BufferInitDescriptor<'_>) -> crate::Buffer;
45
46    /// Upload an entire texture and its mipmaps from a source buffer.
47    ///
48    /// Expects all mipmaps to be tightly packed in the data buffer.
49    ///
50    /// See [`TextureDataOrder`] for the order in which the data is laid out in memory.
51    ///
52    /// Implicitly adds the `COPY_DST` usage if it is not present in the descriptor,
53    /// as it is required to be able to upload the data to the gpu.
54    fn create_texture_with_data(
55        &self,
56        queue: &crate::Queue,
57        desc: &crate::TextureDescriptor<'_>,
58        order: TextureDataOrder,
59        data: &[u8],
60    ) -> crate::Texture;
61}
62
63impl DeviceExt for crate::Device {
64    fn create_buffer_init(&self, descriptor: &BufferInitDescriptor<'_>) -> crate::Buffer {
65        // Skip mapping if the buffer is zero sized
66        if descriptor.contents.is_empty() {
67            let wgt_descriptor = crate::BufferDescriptor {
68                label: descriptor.label,
69                size: 0,
70                usage: descriptor.usage,
71                mapped_at_creation: false,
72            };
73
74            self.create_buffer(&wgt_descriptor)
75        } else {
76            let unpadded_size = descriptor.contents.len() as crate::BufferAddress;
77            // Valid vulkan usage is
78            // 1. buffer size must be a multiple of COPY_BUFFER_ALIGNMENT.
79            // 2. buffer size must be greater than 0.
80            // Therefore we round the value up to the nearest multiple, and ensure it's at least COPY_BUFFER_ALIGNMENT.
81            let align_mask = crate::COPY_BUFFER_ALIGNMENT - 1;
82            let padded_size =
83                ((unpadded_size + align_mask) & !align_mask).max(crate::COPY_BUFFER_ALIGNMENT);
84
85            let wgt_descriptor = crate::BufferDescriptor {
86                label: descriptor.label,
87                size: padded_size,
88                usage: descriptor.usage,
89                mapped_at_creation: true,
90            };
91
92            let buffer = self.create_buffer(&wgt_descriptor);
93
94            buffer.slice(..).get_mapped_range_mut()[..unpadded_size as usize]
95                .copy_from_slice(descriptor.contents);
96            buffer.unmap();
97
98            buffer
99        }
100    }
101
102    fn create_texture_with_data(
103        &self,
104        queue: &crate::Queue,
105        desc: &crate::TextureDescriptor<'_>,
106        order: TextureDataOrder,
107        data: &[u8],
108    ) -> crate::Texture {
109        // Implicitly add the COPY_DST usage
110        let mut desc = desc.to_owned();
111        desc.usage |= crate::TextureUsages::COPY_DST;
112        let texture = self.create_texture(&desc);
113
114        // Will return None only if it's a combined depth-stencil format
115        // If so, default to 4, validation will fail later anyway since the depth or stencil
116        // aspect needs to be written to individually
117        let block_size = desc.format.block_copy_size(None).unwrap_or(4);
118        let (block_width, block_height) = desc.format.block_dimensions();
119        let layer_iterations = desc.array_layer_count();
120
121        let outer_iteration;
122        let inner_iteration;
123        match order {
124            TextureDataOrder::LayerMajor => {
125                outer_iteration = layer_iterations;
126                inner_iteration = desc.mip_level_count;
127            }
128            TextureDataOrder::MipMajor => {
129                outer_iteration = desc.mip_level_count;
130                inner_iteration = layer_iterations;
131            }
132        }
133
134        let mut binary_offset = 0;
135        for outer in 0..outer_iteration {
136            for inner in 0..inner_iteration {
137                let (layer, mip) = match order {
138                    TextureDataOrder::LayerMajor => (outer, inner),
139                    TextureDataOrder::MipMajor => (inner, outer),
140                };
141
142                let mut mip_size = desc.mip_level_size(mip).unwrap();
143                // copying layers separately
144                if desc.dimension != wgt::TextureDimension::D3 {
145                    mip_size.depth_or_array_layers = 1;
146                }
147
148                // When uploading mips of compressed textures and the mip is supposed to be
149                // a size that isn't a multiple of the block size, the mip needs to be uploaded
150                // as its "physical size" which is the size rounded up to the nearest block size.
151                let mip_physical = mip_size.physical_size(desc.format);
152
153                // All these calculations are performed on the physical size as that's the
154                // data that exists in the buffer.
155                let width_blocks = mip_physical.width / block_width;
156                let height_blocks = mip_physical.height / block_height;
157
158                let bytes_per_row = width_blocks * block_size;
159                let data_size = bytes_per_row * height_blocks * mip_size.depth_or_array_layers;
160
161                let end_offset = binary_offset + data_size as usize;
162
163                queue.write_texture(
164                    crate::ImageCopyTexture {
165                        texture: &texture,
166                        mip_level: mip,
167                        origin: crate::Origin3d {
168                            x: 0,
169                            y: 0,
170                            z: layer,
171                        },
172                        aspect: wgt::TextureAspect::All,
173                    },
174                    &data[binary_offset..end_offset],
175                    crate::ImageDataLayout {
176                        offset: 0,
177                        bytes_per_row: Some(bytes_per_row),
178                        rows_per_image: Some(height_blocks),
179                    },
180                    mip_physical,
181                );
182
183                binary_offset = end_offset;
184            }
185        }
186
187        texture
188    }
189}