From 72cb3d012440c0b20a7df568a7a7c81345d1433a Mon Sep 17 00:00:00 2001 From: Imbris Date: Wed, 7 Apr 2021 23:24:00 -0400 Subject: [PATCH] Clear textures that will be partially written to work around Dx12 bug on AMD --- voxygen/src/render/pipelines/lod_terrain.rs | 2 +- voxygen/src/render/pipelines/shadow.rs | 2 +- voxygen/src/render/renderer.rs | 38 ++++++++++++------- voxygen/src/render/texture.rs | 41 +++++++++++++++++---- 4 files changed, 59 insertions(+), 24 deletions(-) diff --git a/voxygen/src/render/pipelines/lod_terrain.rs b/voxygen/src/render/pipelines/lod_terrain.rs index 957bc09deb..0ed722cf23 100644 --- a/voxygen/src/render/pipelines/lod_terrain.rs +++ b/voxygen/src/render/pipelines/lod_terrain.rs @@ -105,7 +105,7 @@ impl LodData { array_layer_count: None, }; - renderer.create_texture_with_data_raw::<4>( + renderer.create_texture_with_data_raw( &texture_info, &view_info, &sampler_info, diff --git a/voxygen/src/render/pipelines/shadow.rs b/voxygen/src/render/pipelines/shadow.rs index d6e8562c01..7bb52f019b 100644 --- a/voxygen/src/render/pipelines/shadow.rs +++ b/voxygen/src/render/pipelines/shadow.rs @@ -116,7 +116,7 @@ pub fn create_col_lights( array_layer_count: None, }; - renderer.create_texture_with_data_raw::<4>( + renderer.create_texture_with_data_raw( &texture_info, &view_info, &sampler_info, diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index cd192a4014..afa0adcba3 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -1094,7 +1094,10 @@ impl Renderer { } /// Create a new immutable texture from the provided image. - pub fn create_texture_with_data_raw( + /// # Panics + /// If the provided data doesn't completely fill the texture this function + /// will panic. + pub fn create_texture_with_data_raw( &mut self, texture_info: &wgpu::TextureDescriptor, view_info: &wgpu::TextureViewDescriptor, @@ -1103,7 +1106,20 @@ impl Renderer { ) -> Texture { let tex = Texture::new_raw(&self.device, &texture_info, &view_info, &sampler_info); - tex.update::( + let size = texture_info.size; + let block_size = texture_info.format.describe().block_size; + assert_eq!( + size.width as usize + * size.height as usize + * size.depth_or_array_layers as usize + * block_size as usize, + data.len(), + "Provided data length {} does not fill the provided texture size {:?}", + data.len(), + size, + ); + + tex.update( &self.device, &self.queue, [0; 2], @@ -1121,7 +1137,9 @@ impl Renderer { view_info: &wgpu::TextureViewDescriptor, sampler_info: &wgpu::SamplerDescriptor, ) -> Texture { - Texture::new_raw(&self.device, texture_info, view_info, sampler_info) + let texture = Texture::new_raw(&self.device, texture_info, view_info, sampler_info); + texture.clear(&self.device, &self.queue); // Needs to be fully initialized for partial writes to work on Dx12 AMD + texture } /// Create a new texture from the provided image. @@ -1147,7 +1165,7 @@ impl Renderer { /// /// Currently only supports Rgba8Srgb pub fn create_dynamic_texture(&mut self, dims: Vec2) -> Texture { - Texture::new_dynamic(&self.device, dims.x, dims.y) + Texture::new_dynamic(&self.device, &self.queue, dims.x, dims.y) } /// Update a texture with the provided offset, size, and data. @@ -1158,18 +1176,10 @@ impl Renderer { texture: &Texture, /* */ offset: [u32; 2], size: [u32; 2], - // TODO - // data: &[<::Surface as - // gfx::format::SurfaceTyped>::DataType], ) -> Result<(), RenderError> - // where - // ::Surface: gfx::format::TextureSurface, - // ::Channel: gfx::format::TextureChannel, - // <::Surface as gfx::format::SurfaceTyped>::DataType: - // Copy, { - // texture.update(&mut self.encoder, offset, size, data) + // TODO: generic over pixel type data: &[[u8; 4]], ) { - texture.update::<4>( + texture.update( &self.device, &self.queue, offset, diff --git a/voxygen/src/render/texture.rs b/voxygen/src/render/texture.rs index e70fbbd144..fd7b8116b9 100644 --- a/voxygen/src/render/texture.rs +++ b/voxygen/src/render/texture.rs @@ -1,7 +1,7 @@ use super::RenderError; +use core::num::NonZeroU32; use image::{DynamicImage, GenericImageView}; use wgpu::Extent3d; -use core::num::NonZeroU32; /// Represents an image that has been uploaded to the GPU. pub struct Texture { @@ -9,6 +9,8 @@ pub struct Texture { pub view: wgpu::TextureView, pub sampler: wgpu::Sampler, size: Extent3d, + /// TODO: consider making Texture generic over the format + format: wgpu::TextureFormat, } impl Texture { @@ -106,10 +108,16 @@ impl Texture { view, sampler: device.create_sampler(&sampler_info), size, + format, }) } - pub fn new_dynamic(device: &wgpu::Device, width: u32, height: u32) -> Self { + pub fn new_dynamic( + device: &wgpu::Device, + queue: &wgpu::Queue, + width: u32, + height: u32, + ) -> Self { let size = wgpu::Extent3d { width, height, @@ -149,9 +157,13 @@ impl Texture { array_layer_count: None, }; - Self::new_raw(device, &tex_info, &view_info, &sampler_info) + let texture = Self::new_raw(device, &tex_info, &view_info, &sampler_info); + texture.clear(device, queue); // Needs to be fully initialized for partial writes to work on Dx12 AMD + texture } + /// Note: the user is responsible for making sure the texture is fully + /// initialized before doing partial writes on Dx12 AMD: https://github.com/gfx-rs/wgpu/issues/1306 pub fn new_raw( device: &wgpu::Device, texture_info: &wgpu::TextureDescriptor, @@ -166,14 +178,25 @@ impl Texture { view, sampler: device.create_sampler(sampler_info), size: texture_info.size, + format: texture_info.format, } } + /// Clears the texture data to 0 + pub fn clear(&self, device: &wgpu::Device, queue: &wgpu::Queue) { + let size = self.size; + let byte_len = size.width as usize + * size.height as usize + * size.depth_or_array_layers as usize + * self.format.describe().block_size as usize; + let zeros = vec![0; byte_len]; + + self.update(device, queue, [0, 0], [size.width, size.height], &zeros); + } + /// Update a texture with the given data (used for updating the glyph cache /// texture). - /// TODO: using generic here seems a bit hacky, consider storing this info - /// in the texture type or pass in wgpu::TextureFormat - pub fn update( + pub fn update( &self, device: &wgpu::Device, queue: &wgpu::Queue, @@ -181,9 +204,11 @@ impl Texture { size: [u32; 2], data: &[u8], ) { + let bytes_per_pixel = self.format.describe().block_size as u32; + debug_assert_eq!( data.len(), - size[0] as usize * size[1] as usize * BYTES_PER_PIXEL as usize + size[0] as usize * size[1] as usize * bytes_per_pixel as usize ); // TODO: Only works for 2D images queue.write_texture( @@ -199,7 +224,7 @@ impl Texture { data, wgpu::ImageDataLayout { offset: 0, - bytes_per_row: NonZeroU32::new(size[0] * BYTES_PER_PIXEL), + bytes_per_row: NonZeroU32::new(size[0] * bytes_per_pixel), rows_per_image: NonZeroU32::new(size[1]), }, wgpu::Extent3d {