From 7e05bba6c448fcf3e5ee4fa464a5fdd21f755d7d Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Fri, 11 Dec 2020 16:42:36 -0500 Subject: [PATCH] [rs] Add create_texture_with_data helper --- wgpu/examples/framework.rs | 2 +- wgpu/examples/skybox/main.rs | 77 ++++++------------------------- wgpu/src/util/mod.rs | 88 ++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 65 deletions(-) diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 412e946cd4..cf6fb47621 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -168,7 +168,7 @@ async fn setup(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), ) diff --git a/wgpu/examples/skybox/main.rs b/wgpu/examples/skybox/main.rs index bbc519fdbb..333d060818 100644 --- a/wgpu/examples/skybox/main.rs +++ b/wgpu/examples/skybox/main.rs @@ -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, diff --git a/wgpu/src/util/mod.rs b/wgpu/src/util/mod.rs index 850fd65385..49a1139262 100644 --- a/wgpu/src/util/mod.rs +++ b/wgpu/src/util/mod.rs @@ -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.