Clear textures that will be partially written to work around Dx12 bug on AMD

This commit is contained in:
Imbris 2021-04-07 23:24:00 -04:00
parent 76c7161364
commit 72cb3d0124
4 changed files with 59 additions and 24 deletions

View File

@ -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,

View File

@ -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,

View File

@ -1094,7 +1094,10 @@ impl Renderer {
}
/// Create a new immutable texture from the provided image.
pub fn create_texture_with_data_raw<const BYTES_PER_PIXEL: u32>(
/// # 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::<BYTES_PER_PIXEL>(
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<u32>) -> 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, /* <T> */
offset: [u32; 2],
size: [u32; 2],
// TODO
// data: &[<<T as gfx::format::Formatted>::Surface as
// gfx::format::SurfaceTyped>::DataType], ) -> Result<(), RenderError>
// where
// <T as gfx::format::Formatted>::Surface: gfx::format::TextureSurface,
// <T as gfx::format::Formatted>::Channel: gfx::format::TextureChannel,
// <<T as gfx::format::Formatted>::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,

View File

@ -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<const BYTES_PER_PIXEL: u32>(
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 {