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}