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