diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index e6df1be03f2..ea708e8c9c4 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -852,7 +852,16 @@ impl Device { )); } - // TODO: validate missing TextureDescriptor::view_formats. + if let Some(ref view_formats) = desc.view_formats { + for format in view_formats { + if desc.format == *format { + continue; + } + if desc.format.remove_srgb_suffix() != format.remove_srgb_suffix() { + return Err(CreateTextureError::InvalidViewFormat(*format, desc.format)); + } + } + } // Enforce having COPY_DST/DEPTH_STENCIL_WRIT/COLOR_TARGET otherwise we // wouldn't be able to initialize the texture. @@ -1086,10 +1095,16 @@ impl Device { } let format = desc.format.unwrap_or(texture.desc.format); if format != texture.desc.format { - return Err(resource::CreateTextureViewError::FormatReinterpretation { - texture: texture.desc.format, - view: format, - }); + let compatible = match texture.desc.view_formats { + Some(ref view_formats) => view_formats.contains(&format), + None => false, + }; + if !compatible { + return Err(resource::CreateTextureViewError::FormatReinterpretation { + texture: texture.desc.format, + view: format, + }); + } } // filter the usages based on the other criteria diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 41da2fa585d..c1afb5e97c5 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -180,6 +180,7 @@ impl Global { format: config.format, dimension: wgt::TextureDimension::D2, usage: config.usage, + view_formats: None, }, hal_usage: conv::map_texture_usage(config.usage, config.format.into()), format_features: wgt::TextureFormatFeatures { diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 4302df7e61f..3fce8bad5e8 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -507,6 +507,8 @@ pub enum CreateTextureError { if *.2 { " due to downlevel restrictions" } else { "" } )] InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat, bool), + #[error("The view format {0:?} is not compatible with texture format {1:?}.")] + InvalidViewFormat(wgt::TextureFormat, wgt::TextureFormat), #[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")] InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension), #[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")] diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index a83adb9eaad..efcd34c192d 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -2522,6 +2522,29 @@ impl TextureFormat { }, } } + + /// Get texture view format compatible none `Srgb` suffix format. + pub fn remove_srgb_suffix(&self) -> TextureFormat { + match *self { + Self::Rgba8UnormSrgb => Self::Rgba8Unorm, + Self::Bgra8UnormSrgb => Self::Bgra8Unorm, + Self::Bc1RgbaUnormSrgb => Self::Bc1RgbaUnorm, + Self::Bc2RgbaUnormSrgb => Self::Bc2RgbaUnorm, + Self::Bc3RgbaUnormSrgb => Self::Bc3RgbaUnorm, + Self::Bc7RgbaUnormSrgb => Self::Bc7RgbaUnorm, + Self::Etc2Rgb8UnormSrgb => Self::Etc2Rgb8Unorm, + Self::Etc2Rgb8A1UnormSrgb => Self::Etc2Rgb8A1Unorm, + Self::Etc2Rgba8UnormSrgb => Self::Etc2Rgba8Unorm, + Self::Astc { + block, + channel: AstcChannel::UnormSrgb, + } => Self::Astc { + block, + channel: AstcChannel::Unorm, + }, + _ => *self, + } + } } #[test] @@ -4248,7 +4271,10 @@ pub struct TextureDescriptor { pub format: TextureFormat, /// Allowed usages of the texture. If used in other ways, the operation will panic. pub usage: TextureUsages, - // TODO: missing view_formats https://www.w3.org/TR/webgpu/#dom-gputexturedescriptor-viewformats + /// Specifies what view format values will be allowed when calling create_view() on this texture. + /// Note: Adding a format to this list may have a significant performance impact, + /// so it is best to avoid adding formats unnecessarily. + pub view_formats: Option>, } impl TextureDescriptor { @@ -4262,6 +4288,7 @@ impl TextureDescriptor { dimension: self.dimension, format: self.format, usage: self.usage, + view_formats: self.view_formats.clone(), } } @@ -4282,6 +4309,7 @@ impl TextureDescriptor { /// dimension: wgpu::TextureDimension::D3, /// format: wgpu::TextureFormat::Rgba8Sint, /// usage: wgpu::TextureUsages::empty(), + /// view_formats: None /// }; /// /// assert_eq!(desc.mip_level_size(0), Some(wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 })); diff --git a/wgpu/examples/bunnymark/main.rs b/wgpu/examples/bunnymark/main.rs index 1d239b07da9..63702139d0e 100644 --- a/wgpu/examples/bunnymark/main.rs +++ b/wgpu/examples/bunnymark/main.rs @@ -152,6 +152,7 @@ impl framework::Example for Example { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: None, }); queue.write_texture( texture.as_image_copy(), diff --git a/wgpu/examples/capture/main.rs b/wgpu/examples/capture/main.rs index 610a431532f..8e390c3867b 100644 --- a/wgpu/examples/capture/main.rs +++ b/wgpu/examples/capture/main.rs @@ -83,6 +83,7 @@ async fn create_red_image_with_dimensions( format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, label: None, + view_formats: None, }); // Set the background to be red diff --git a/wgpu/examples/conservative-raster/main.rs b/wgpu/examples/conservative-raster/main.rs index 7703c224660..8ec47750805 100644 --- a/wgpu/examples/conservative-raster/main.rs +++ b/wgpu/examples/conservative-raster/main.rs @@ -36,6 +36,7 @@ impl Example { format: RENDER_TARGET_FORMAT, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, + view_formats: None, }) .create_view(&Default::default()); diff --git a/wgpu/examples/cube/main.rs b/wgpu/examples/cube/main.rs index 5f0ffb9dcf8..e89edefc1c0 100644 --- a/wgpu/examples/cube/main.rs +++ b/wgpu/examples/cube/main.rs @@ -202,6 +202,7 @@ impl framework::Example for Example { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::R8Uint, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: None, }); let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); queue.write_texture( diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 5474ff6d5f8..757578948d9 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -520,6 +520,7 @@ pub fn test(mut params: FrameworkRefTest) { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: None, }); let dst_view = dst_texture.create_view(&wgpu::TextureViewDescriptor::default()); diff --git a/wgpu/examples/mipmap/main.rs b/wgpu/examples/mipmap/main.rs index 5e62e35c060..55e67541742 100644 --- a/wgpu/examples/mipmap/main.rs +++ b/wgpu/examples/mipmap/main.rs @@ -232,6 +232,7 @@ impl framework::Example for Example { | wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, label: None, + view_formats: None, }); let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); //Note: we could use queue.write_texture instead, and this is what other diff --git a/wgpu/examples/msaa-line/main.rs b/wgpu/examples/msaa-line/main.rs index b39c144877f..e8f752579f5 100644 --- a/wgpu/examples/msaa-line/main.rs +++ b/wgpu/examples/msaa-line/main.rs @@ -109,6 +109,7 @@ impl Example { format: config.format, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, label: None, + view_formats: None, }; device diff --git a/wgpu/examples/shadow/main.rs b/wgpu/examples/shadow/main.rs index d2eba66c307..577db06fc53 100644 --- a/wgpu/examples/shadow/main.rs +++ b/wgpu/examples/shadow/main.rs @@ -197,6 +197,7 @@ impl Example { format: Self::DEPTH_FORMAT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, label: None, + view_formats: None, }); depth_texture.create_view(&wgpu::TextureViewDescriptor::default()) @@ -385,6 +386,7 @@ impl framework::Example for Example { format: Self::SHADOW_FORMAT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, label: None, + view_formats: None, }); let shadow_view = shadow_texture.create_view(&wgpu::TextureViewDescriptor::default()); diff --git a/wgpu/examples/skybox/main.rs b/wgpu/examples/skybox/main.rs index d5b08634af9..b28ed2307ec 100644 --- a/wgpu/examples/skybox/main.rs +++ b/wgpu/examples/skybox/main.rs @@ -85,6 +85,7 @@ impl Skybox { format: Self::DEPTH_FORMAT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, label: None, + view_formats: None, }); depth_texture.create_view(&wgpu::TextureViewDescriptor::default()) @@ -328,6 +329,7 @@ impl framework::Example for Skybox { format: skybox_format, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, label: None, + view_formats: None, }, &image.data, ); diff --git a/wgpu/examples/texture-arrays/main.rs b/wgpu/examples/texture-arrays/main.rs index 687a0258f7d..7da0c93589c 100644 --- a/wgpu/examples/texture-arrays/main.rs +++ b/wgpu/examples/texture-arrays/main.rs @@ -164,21 +164,26 @@ impl framework::Example for Example { format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, label: None, + view_formats: None, }; let red_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("red"), + view_formats: None, ..texture_descriptor }); let green_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("green"), + view_formats: None, ..texture_descriptor }); let blue_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("blue"), + view_formats: None, ..texture_descriptor }); let white_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("white"), + view_formats: None, ..texture_descriptor }); diff --git a/wgpu/examples/water/main.rs b/wgpu/examples/water/main.rs index aa785c6ec0f..e39b68d3912 100644 --- a/wgpu/examples/water/main.rs +++ b/wgpu/examples/water/main.rs @@ -197,6 +197,7 @@ impl Example { usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::RENDER_ATTACHMENT, + view_formats: None, }); let draw_depth_buffer = device.create_texture(&wgpu::TextureDescriptor { @@ -209,6 +210,7 @@ impl Example { usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::RENDER_ATTACHMENT, + view_formats: None, }); let color_sampler = device.create_sampler(&wgpu::SamplerDescriptor { diff --git a/wgpu/tests/clear_texture.rs b/wgpu/tests/clear_texture.rs index abe86cac437..055361390b6 100644 --- a/wgpu/tests/clear_texture.rs +++ b/wgpu/tests/clear_texture.rs @@ -222,6 +222,7 @@ fn single_texture_clear_test( // Forces internally the required usages to be able to clear it. // This is not visible on the API level. usage: wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: None, }); let mut encoder = ctx .device diff --git a/wgpu/tests/queue_transfer.rs b/wgpu/tests/queue_transfer.rs index 7724c291cdd..ba26e1526d7 100644 --- a/wgpu/tests/queue_transfer.rs +++ b/wgpu/tests/queue_transfer.rs @@ -19,6 +19,7 @@ fn queue_write_texture_overflow() { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba32Float, usage: wgpu::TextureUsages::COPY_DST, + view_formats: None, }); let data = vec![255; 128]; diff --git a/wgpu/tests/resource_error.rs b/wgpu/tests/resource_error.rs index 81d50e5800c..b8e37a1ca8f 100644 --- a/wgpu/tests/resource_error.rs +++ b/wgpu/tests/resource_error.rs @@ -41,6 +41,7 @@ fn bad_texture() { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::all(), + view_formats: None, }) }); diff --git a/wgpu/tests/shader_primitive_index/mod.rs b/wgpu/tests/shader_primitive_index/mod.rs index 5e6c6b1b707..e49a5d890e1 100644 --- a/wgpu/tests/shader_primitive_index/mod.rs +++ b/wgpu/tests/shader_primitive_index/mod.rs @@ -162,6 +162,7 @@ fn pulling_common( dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8Unorm, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: None, }); let color_view = color_texture.create_view(&wgpu::TextureViewDescriptor::default()); diff --git a/wgpu/tests/texture_bounds.rs b/wgpu/tests/texture_bounds.rs index 27968151985..d06674b69f0 100644 --- a/wgpu/tests/texture_bounds.rs +++ b/wgpu/tests/texture_bounds.rs @@ -101,6 +101,7 @@ const TEXTURE_DESCRIPTOR: wgpu::TextureDescriptor = wgpu::TextureDescriptor { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::COPY_DST.union(wgpu::TextureUsages::COPY_SRC), + view_formats: None, }; const BYTES_PER_PIXEL: u32 = 4; diff --git a/wgpu/tests/vertex_indices/mod.rs b/wgpu/tests/vertex_indices/mod.rs index 177b857448a..79ba232001c 100644 --- a/wgpu/tests/vertex_indices/mod.rs +++ b/wgpu/tests/vertex_indices/mod.rs @@ -96,6 +96,7 @@ fn pulling_common( dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8Unorm, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, + view_formats: None, }, &[0, 0, 0, 1], ) diff --git a/wgpu/tests/write_texture.rs b/wgpu/tests/write_texture.rs index 742e97d8187..2eaa81dc36a 100644 --- a/wgpu/tests/write_texture.rs +++ b/wgpu/tests/write_texture.rs @@ -23,6 +23,7 @@ fn write_texture_subset() { | wgpu::TextureUsages::TEXTURE_BINDING, mip_level_count: 1, sample_count: 1, + view_formats: None, }); let data = vec![1u8; size as usize * 2]; // Write the first two rows diff --git a/wgpu/tests/zero_init_texture_after_discard.rs b/wgpu/tests/zero_init_texture_after_discard.rs index 6043d0388f6..0b8c373b91c 100644 --- a/wgpu/tests/zero_init_texture_after_discard.rs +++ b/wgpu/tests/zero_init_texture_after_discard.rs @@ -203,6 +203,7 @@ fn create_white_texture_and_readback_buffer( | wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT }, + view_formats: None, }); // Clear using a write_texture operation. We could also clear using a render_pass clear.