wgpu/util/
device.rs

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