1use alloc::borrow::ToOwned as _;
23use wgt::TextureDataOrder;
45/// 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.
9pub label: crate::Label<'a>,
10/// Contents of a buffer on creation.
11pub 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.
14pub usage: wgt::BufferUsages,
15}
1617/// Utility methods not meant to be in the main API.
18pub trait DeviceExt {
19/// Creates a [Buffer](crate::Buffer) with data to initialize it.
20fn create_buffer_init(&self, desc: &BufferInitDescriptor<'_>) -> crate::Buffer;
2122/// 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.
30fn create_texture_with_data(
31&self,
32 queue: &crate::Queue,
33 desc: &crate::TextureDescriptor<'_>,
34 order: TextureDataOrder,
35 data: &[u8],
36 ) -> crate::Texture;
37}
3839impl DeviceExt for crate::Device {
40fn create_buffer_init(&self, descriptor: &BufferInitDescriptor<'_>) -> crate::Buffer {
41// Skip mapping if the buffer is zero sized
42if descriptor.contents.is_empty() {
43let wgt_descriptor = crate::BufferDescriptor {
44 label: descriptor.label,
45 size: 0,
46 usage: descriptor.usage,
47 mapped_at_creation: false,
48 };
4950self.create_buffer(&wgt_descriptor)
51 } else {
52let 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.
57let align_mask = crate::COPY_BUFFER_ALIGNMENT - 1;
58let padded_size =
59 ((unpadded_size + align_mask) & !align_mask).max(crate::COPY_BUFFER_ALIGNMENT);
6061let wgt_descriptor = crate::BufferDescriptor {
62 label: descriptor.label,
63 size: padded_size,
64 usage: descriptor.usage,
65 mapped_at_creation: true,
66 };
6768let buffer = self.create_buffer(&wgt_descriptor);
6970 buffer.slice(..).get_mapped_range_mut()[..unpadded_size as usize]
71 .copy_from_slice(descriptor.contents);
72 buffer.unmap();
7374 buffer
75 }
76 }
7778fn create_texture_with_data(
79&self,
80 queue: &crate::Queue,
81 desc: &crate::TextureDescriptor<'_>,
82 order: TextureDataOrder,
83 data: &[u8],
84 ) -> crate::Texture {
85// Implicitly add the COPY_DST usage
86let mut desc = desc.to_owned();
87 desc.usage |= crate::TextureUsages::COPY_DST;
88let texture = self.create_texture(&desc);
8990// Will return None only if it's a combined depth-stencil format
91 // If so, default to 4, validation will fail later anyway since the depth or stencil
92 // aspect needs to be written to individually
93let block_size = desc.format.block_copy_size(None).unwrap_or(4);
94let (block_width, block_height) = desc.format.block_dimensions();
95let layer_iterations = desc.array_layer_count();
9697let outer_iteration;
98let inner_iteration;
99match order {
100 TextureDataOrder::LayerMajor => {
101 outer_iteration = layer_iterations;
102 inner_iteration = desc.mip_level_count;
103 }
104 TextureDataOrder::MipMajor => {
105 outer_iteration = desc.mip_level_count;
106 inner_iteration = layer_iterations;
107 }
108 }
109110let mut binary_offset = 0;
111for outer in 0..outer_iteration {
112for inner in 0..inner_iteration {
113let (layer, mip) = match order {
114 TextureDataOrder::LayerMajor => (outer, inner),
115 TextureDataOrder::MipMajor => (inner, outer),
116 };
117118let mut mip_size = desc.mip_level_size(mip).unwrap();
119// copying layers separately
120if desc.dimension != wgt::TextureDimension::D3 {
121 mip_size.depth_or_array_layers = 1;
122 }
123124// When uploading mips of compressed textures and the mip is supposed to be
125 // a size that isn't a multiple of the block size, the mip needs to be uploaded
126 // as its "physical size" which is the size rounded up to the nearest block size.
127let mip_physical = mip_size.physical_size(desc.format);
128129// All these calculations are performed on the physical size as that's the
130 // data that exists in the buffer.
131let width_blocks = mip_physical.width / block_width;
132let height_blocks = mip_physical.height / block_height;
133134let bytes_per_row = width_blocks * block_size;
135let data_size = bytes_per_row * height_blocks * mip_size.depth_or_array_layers;
136137let end_offset = binary_offset + data_size as usize;
138139 queue.write_texture(
140crate::TexelCopyTextureInfo {
141 texture: &texture,
142 mip_level: mip,
143 origin: crate::Origin3d {
144 x: 0,
145 y: 0,
146 z: layer,
147 },
148 aspect: wgt::TextureAspect::All,
149 },
150&data[binary_offset..end_offset],
151crate::TexelCopyBufferLayout {
152 offset: 0,
153 bytes_per_row: Some(bytes_per_row),
154 rows_per_image: Some(height_blocks),
155 },
156 mip_physical,
157 );
158159 binary_offset = end_offset;
160 }
161 }
162163 texture
164 }
165}