672: Add write_whole_texture_mips Helper r=kvark a=cwfitzgerald

Adds a helper in QueueExt for uploading an entire texture with all of its mips. Name very bikeshedable.

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
bors[bot]
2020-12-13 05:45:03 +00:00
committed by GitHub
3 changed files with 102 additions and 65 deletions

View File

@@ -168,7 +168,7 @@ async fn setup<E: Example>(title: &str) -> Setup {
label: None,
features: (optional_features & adapter_features) | required_features,
limits: needed_limits,
shader_validation: true,
shader_validation: false,
},
trace_dir.ok().as_ref().map(std::path::Path::new),
)

View File

@@ -165,16 +165,6 @@ impl framework::Example for Skybox {
let layer_size = wgpu::Extent3d { depth: 1, ..size };
let max_mips = layer_size.max_mips();
let texture = device.create_texture(&wgpu::TextureDescriptor {
size,
mip_level_count: max_mips as u32,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: skybox_format,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
label: None,
});
log::debug!(
"Copying {:?} skybox images of size {}, {}, 6 with {} mips to gpu",
skybox_format,
@@ -193,60 +183,19 @@ impl framework::Example for Skybox {
let image = ddsfile::Dds::read(&mut std::io::Cursor::new(&bytes)).unwrap();
let format_info = skybox_format.describe();
let mut binary_offset = 0;
for layer in 0..6 {
for mip in 0..max_mips {
let mip_size = layer_size.at_mip_level(mip).unwrap();
// 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 it's "physical size" which is the size rounded up to the nearest block size.
let mip_physical = mip_size.physical_size(skybox_format);
log::debug!(
"Copying layer {} mip {} of virtual size ({}, {}) and physical size ({}, {})",
layer,
mip,
mip_size.width,
mip_size.height,
mip_physical.width,
mip_physical.height,
);
// 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 / format_info.block_dimensions.0 as u32;
let height_blocks = mip_physical.height / format_info.block_dimensions.1 as u32;
let bytes_per_row = width_blocks * format_info.block_size as u32;
let data_size = bytes_per_row * height_blocks;
let end_offset = binary_offset + data_size as usize;
queue.write_texture(
wgpu::TextureCopyView {
texture: &texture,
mip_level: mip as u32,
origin: wgpu::Origin3d {
x: 0,
y: 0,
z: layer,
},
},
&image.data[binary_offset..end_offset],
wgpu::TextureDataLayout {
offset: 0,
bytes_per_row,
rows_per_image: 0,
},
mip_physical,
);
binary_offset = end_offset;
}
}
let texture = device.create_texture_with_data(
&queue,
&wgpu::TextureDescriptor {
size,
mip_level_count: max_mips as u32,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: skybox_format,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
label: None,
},
&image.data,
);
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor {
label: None,

View File

@@ -4,6 +4,7 @@ mod belt;
use std::{
borrow::Cow,
convert::TryFrom,
mem::{align_of, size_of},
ptr::copy_nonoverlapping,
};
@@ -55,6 +56,22 @@ pub fn make_spirv<'a>(data: &'a [u8]) -> super::ShaderSource<'a> {
pub trait DeviceExt {
/// Creates a [`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.
///
/// If the texture is a 2DArray texture, uploads each layer in order, expecting
/// each layer and its mips to be tightly packed.
///
/// Example:
/// Layer0Mip0 Layer0Mip1 Layer0Mip2 ... Layer1Mip0 Layer1Mip1 Layer1Mip2 ...
fn create_texture_with_data(
&self,
queue: &crate::Queue,
desc: &crate::TextureDescriptor,
data: &[u8],
) -> crate::Texture;
}
impl DeviceExt for crate::Device {
@@ -86,6 +103,77 @@ impl DeviceExt for crate::Device {
buffer.unmap();
buffer
}
fn create_texture_with_data(
&self,
queue: &crate::Queue,
desc: &crate::TextureDescriptor,
data: &[u8],
) -> crate::Texture {
let texture = self.create_texture(desc);
let format_info = desc.format.describe();
let (layer_iterations, mip_extent) = if desc.dimension == crate::TextureDimension::D3 {
(1, desc.size)
} else {
(
desc.size.depth,
crate::Extent3d {
depth: 1,
..desc.size
},
)
};
let mip_level_count =
u8::try_from(desc.mip_level_count).expect("mip level count overflows a u8");
let mut binary_offset = 0;
for layer in 0..layer_iterations {
for mip in 0..mip_level_count {
let mip_size = mip_extent.at_mip_level(mip).unwrap();
// 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 it's "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 / format_info.block_dimensions.0 as u32;
let height_blocks = mip_physical.height / format_info.block_dimensions.1 as u32;
let bytes_per_row = width_blocks * format_info.block_size as u32;
let data_size = bytes_per_row * height_blocks;
let end_offset = binary_offset + data_size as usize;
queue.write_texture(
crate::TextureCopyView {
texture: &texture,
mip_level: mip as u32,
origin: crate::Origin3d {
x: 0,
y: 0,
z: layer,
},
},
&data[binary_offset..end_offset],
crate::TextureDataLayout {
offset: 0,
bytes_per_row,
rows_per_image: 0,
},
mip_physical,
);
binary_offset = end_offset;
}
}
texture
}
}
/// Describes a [`Buffer`] when allocating.