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}