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/// Utility methods not meant to be in the main API.
14pub trait DeviceExt {
15    /// Creates a [Buffer](crate::Buffer) with data to initialize it.
16    fn create_buffer_init(&self, desc: &BufferInitDescriptor) -> crate::Buffer;
17
18    /// Upload an entire texture and its mipmaps from a source buffer.
19    ///
20    /// Expects all mipmaps to be tightly packed in the data buffer.
21    ///
22    /// If the texture is a 2DArray texture, uploads each layer in order, expecting
23    /// each layer and its mips to be tightly packed.
24    ///
25    /// Example:
26    /// Layer0Mip0 Layer0Mip1 Layer0Mip2 ... Layer1Mip0 Layer1Mip1 Layer1Mip2 ...
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        data: &[u8],
35    ) -> crate::Texture;
36}
37
38impl DeviceExt for crate::Device {
39    fn create_buffer_init(&self, descriptor: &BufferInitDescriptor<'_>) -> crate::Buffer {
40        // Skip mapping if the buffer is zero sized
41        if descriptor.contents.is_empty() {
42            let wgt_descriptor = crate::BufferDescriptor {
43                label: descriptor.label,
44                size: 0,
45                usage: descriptor.usage,
46                mapped_at_creation: false,
47            };
48
49            self.create_buffer(&wgt_descriptor)
50        } else {
51            let unpadded_size = descriptor.contents.len() as crate::BufferAddress;
52            // Valid vulkan usage is
53            // 1. buffer size must be a multiple of COPY_BUFFER_ALIGNMENT.
54            // 2. buffer size must be greater than 0.
55            // Therefore we round the value up to the nearest multiple, and ensure it's at least COPY_BUFFER_ALIGNMENT.
56            let align_mask = crate::COPY_BUFFER_ALIGNMENT - 1;
57            let padded_size =
58                ((unpadded_size + align_mask) & !align_mask).max(crate::COPY_BUFFER_ALIGNMENT);
59
60            let wgt_descriptor = crate::BufferDescriptor {
61                label: descriptor.label,
62                size: padded_size,
63                usage: descriptor.usage,
64                mapped_at_creation: true,
65            };
66
67            let buffer = self.create_buffer(&wgt_descriptor);
68
69            buffer.slice(..).get_mapped_range_mut()[..unpadded_size as usize]
70                .copy_from_slice(descriptor.contents);
71            buffer.unmap();
72
73            buffer
74        }
75    }
76
77    fn create_texture_with_data(
78        &self,
79        queue: &crate::Queue,
80        desc: &crate::TextureDescriptor,
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_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 mut binary_offset = 0;
96        for layer in 0..layer_iterations {
97            for mip in 0..desc.mip_level_count {
98                let mut mip_size = desc.mip_level_size(mip).unwrap();
99                // copying layers separately
100                if desc.dimension != wgt::TextureDimension::D3 {
101                    mip_size.depth_or_array_layers = 1;
102                }
103
104                // When uploading mips of compressed textures and the mip is supposed to be
105                // a size that isn't a multiple of the block size, the mip needs to be uploaded
106                // as its "physical size" which is the size rounded up to the nearest block size.
107                let mip_physical = mip_size.physical_size(desc.format);
108
109                // All these calculations are performed on the physical size as that's the
110                // data that exists in the buffer.
111                let width_blocks = mip_physical.width / block_width;
112                let height_blocks = mip_physical.height / block_height;
113
114                let bytes_per_row = width_blocks * block_size;
115                let data_size = bytes_per_row * height_blocks * mip_size.depth_or_array_layers;
116
117                let end_offset = binary_offset + data_size as usize;
118
119                queue.write_texture(
120                    crate::ImageCopyTexture {
121                        texture: &texture,
122                        mip_level: mip,
123                        origin: crate::Origin3d {
124                            x: 0,
125                            y: 0,
126                            z: layer,
127                        },
128                        aspect: wgt::TextureAspect::All,
129                    },
130                    &data[binary_offset..end_offset],
131                    crate::ImageDataLayout {
132                        offset: 0,
133                        bytes_per_row: Some(bytes_per_row),
134                        rows_per_image: Some(height_blocks),
135                    },
136                    mip_physical,
137                );
138
139                binary_offset = end_offset;
140            }
141        }
142
143        texture
144    }
145}