wgpu/util/
device.rs

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