diff --git a/CHANGELOG.md b/CHANGELOG.md index 90f7335877..e7080812e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -107,6 +107,18 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non `wgpu`'s DX12 backend can now suballocate buffers and textures when the `windows_rs` feature is enabled, which can give a significant increase in performance (in testing I've seen a 10000%+ improvement in a simple scene with 200 `write_buffer` calls per frame, and a 40%+ improvement in [Bistro using Bevy](https://github.com/vleue/bevy_bistro_playground)). Previously `wgpu-hal`'s DX12 backend created a new heap on the GPU every time you called write_buffer (by calling `CreateCommittedResource`), whereas now with the `windows_rs` feature enabled it uses [`gpu_allocator`](https://crates.io/crates/gpu-allocator) to manage GPU memory (and calls `CreatePlacedResource` with a suballocated heap). By @Elabajaba in [#3163](https://github.com/gfx-rs/wgpu/pull/3163) +#### Texture Format Reinterpretation + +The `view_formats` field is used to specify formats that are compatible with the texture format to allow the creation of views with different formats, currently, only changing srgb-ness is allowed. + +```diff +let texture = device.create_texture(&wgpu::TextureDescriptor { + // ... + format: TextureFormat::Rgba8UnormSrgb, ++ view_formats: &[TextureFormat::Rgba8Unorm], +}); +``` + ### Changes #### General @@ -125,6 +137,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non - Make `ObjectId` structure and invariants idiomatic. By @teoxoy in [#3347](https://github.com/gfx-rs/wgpu/pull/3347) - Add validation in accordance with WebGPU `GPUSamplerDescriptor` valid usage for `lodMinClamp` and `lodMaxClamp`. By @James2022-rgb in [#3353](https://github.com/gfx-rs/wgpu/pull/3353) - Remove panics in `Deref` implementations for `QueueWriteBufferView` and `BufferViewMut`. Instead, warnings are logged, since reading from these types is not recommended. By @botahamec in [#3336] +- Implement `view_formats` in TextureDescriptor to match the WebGPU spec. By @jinleili in [#3237](https://github.com/gfx-rs/wgpu/pull/3237) #### WebGPU diff --git a/deno_webgpu/src/02_idl_types.js b/deno_webgpu/src/02_idl_types.js index 1308834b19..9a6e66c627 100644 --- a/deno_webgpu/src/02_idl_types.js +++ b/deno_webgpu/src/02_idl_types.js @@ -427,6 +427,15 @@ converter: webidl.converters["GPUTextureUsageFlags"], required: true, }, + { + key: "viewFormats", + converter: webidl.createSequenceConverter( + webidl.converters["GPUTextureFormat"], + ), + get defaultValue() { + return []; + }, + }, ]; webidl.converters["GPUTextureDescriptor"] = webidl.createDictionaryConverter( "GPUTextureDescriptor", diff --git a/deno_webgpu/src/texture.rs b/deno_webgpu/src/texture.rs index db3c1143fe..8a0a323609 100644 --- a/deno_webgpu/src/texture.rs +++ b/deno_webgpu/src/texture.rs @@ -34,6 +34,7 @@ pub struct CreateTextureArgs { dimension: wgpu_types::TextureDimension, format: wgpu_types::TextureFormat, usage: u32, + view_formats: Vec, } #[op] @@ -55,6 +56,7 @@ pub fn op_webgpu_create_texture( dimension: args.dimension, format: args.format, usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage), + view_formats: args.view_formats, }; gfx_put!(device => instance.device_create_texture( diff --git a/deno_webgpu/webgpu.idl b/deno_webgpu/webgpu.idl index 7d5da0a878..7d23c29c34 100644 --- a/deno_webgpu/webgpu.idl +++ b/deno_webgpu/webgpu.idl @@ -200,6 +200,7 @@ dictionary GPUTextureDescriptor : GPUObjectDescriptorBase { GPUTextureDimension dimension = "2d"; required GPUTextureFormat format; required GPUTextureUsageFlags usage; + sequence viewFormats = []; }; enum GPUTextureDimension { diff --git a/player/tests/data/clear-buffer-texture.ron b/player/tests/data/clear-buffer-texture.ron index 7d32a688e7..c6879e31da 100644 --- a/player/tests/data/clear-buffer-texture.ron +++ b/player/tests/data/clear-buffer-texture.ron @@ -31,6 +31,7 @@ dimension: r#2d, format: "rgba8unorm", usage: 27, + view_formats: [], )), // First fill the texture to ensure it wasn't just zero initialized or "happened" to be zero. WriteTexture( diff --git a/player/tests/data/quad.ron b/player/tests/data/quad.ron index 1f7cb266f4..563ba24b84 100644 --- a/player/tests/data/quad.ron +++ b/player/tests/data/quad.ron @@ -28,6 +28,7 @@ dimension: r#2d, format: "rgba8unorm", usage: 27, + view_formats: [], )), CreateTextureView( id: Id(0, 1, Empty), diff --git a/player/tests/data/zero-init-texture-binding.ron b/player/tests/data/zero-init-texture-binding.ron index c6f8c40a53..e94255cfc3 100644 --- a/player/tests/data/zero-init-texture-binding.ron +++ b/player/tests/data/zero-init-texture-binding.ron @@ -28,6 +28,7 @@ dimension: r#2d, format: "rgba8unorm", usage: 5, // SAMPLED + COPY_SRC + view_formats: [], )), CreateTextureView( id: Id(0, 1, Empty), @@ -54,6 +55,7 @@ dimension: r#2d, format: "rgba8unorm", usage: 9, // STORAGE + COPY_SRC + view_formats: [], )), CreateTextureView( id: Id(1, 1, Empty), diff --git a/player/tests/data/zero-init-texture-copytobuffer.ron b/player/tests/data/zero-init-texture-copytobuffer.ron index e3f72f1a08..0bb16ccebb 100644 --- a/player/tests/data/zero-init-texture-copytobuffer.ron +++ b/player/tests/data/zero-init-texture-copytobuffer.ron @@ -21,6 +21,7 @@ dimension: r#2d, format: "rgba8unorm", usage: 1, // COPY_SRC + view_formats: [], )), CreateBuffer( Id(0, 1, Empty), diff --git a/player/tests/data/zero-init-texture-rendertarget.ron b/player/tests/data/zero-init-texture-rendertarget.ron index 5741c7c33c..831af942a2 100644 --- a/player/tests/data/zero-init-texture-rendertarget.ron +++ b/player/tests/data/zero-init-texture-rendertarget.ron @@ -21,6 +21,7 @@ dimension: r#2d, format: "rgba8unorm", usage: 17, // RENDER_ATTACHMENT + COPY_SRC + view_formats: [], )), CreateTextureView( id: Id(0, 1, Empty), diff --git a/wgpu-core/src/command/clear.rs b/wgpu-core/src/command/clear.rs index aa9d60638e..b3f436482b 100644 --- a/wgpu-core/src/command/clear.rs +++ b/wgpu-core/src/command/clear.rs @@ -308,7 +308,7 @@ pub(crate) fn clear_texture( } fn clear_texture_via_buffer_copies( - texture_desc: &wgt::TextureDescriptor<()>, + texture_desc: &wgt::TextureDescriptor<(), Vec>, alignments: &hal::Alignments, zero_buffer: &A::Buffer, // Buffer of size device::ZERO_BUFFER_SIZE range: TextureInitRange, diff --git a/wgpu-core/src/command/transfer.rs b/wgpu-core/src/command/transfer.rs index 70c01a9c46..073872f264 100644 --- a/wgpu-core/src/command/transfer.rs +++ b/wgpu-core/src/command/transfer.rs @@ -313,7 +313,7 @@ pub(crate) fn validate_linear_texture_data( /// [vtcr]: https://gpuweb.github.io/gpuweb/#valid-texture-copy-range pub(crate) fn validate_texture_copy_range( texture_copy_view: &ImageCopyTexture, - desc: &wgt::TextureDescriptor<()>, + desc: &wgt::TextureDescriptor<(), Vec>, texture_side: CopySide, copy_size: &Extent3d, ) -> Result<(hal::CopyExtent, u32), TransferError> { diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index db691f2e55..d3ed405273 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -852,7 +852,14 @@ impl Device { )); } - // TODO: validate missing TextureDescriptor::view_formats. + for format in desc.view_formats.iter() { + 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 +1093,13 @@ 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, - }); + if !texture.desc.view_formats.contains(&format) { + return Err(resource::CreateTextureViewError::FormatReinterpretation { + texture: texture.desc.format, + view: format, + }); + } + self.require_downlevel_flags(wgt::DownlevelFlags::VIEW_FORMATS)?; } // filter the usages based on the other criteria diff --git a/wgpu-core/src/init_tracker/texture.rs b/wgpu-core/src/init_tracker/texture.rs index 90167e6fa6..17368e1014 100644 --- a/wgpu-core/src/init_tracker/texture.rs +++ b/wgpu-core/src/init_tracker/texture.rs @@ -16,7 +16,7 @@ pub(crate) struct TextureInitRange { pub(crate) fn has_copy_partial_init_tracker_coverage( copy_size: &wgt::Extent3d, mip_level: u32, - desc: &wgt::TextureDescriptor<()>, + desc: &wgt::TextureDescriptor<(), Vec>, ) -> bool { let target_size = desc.mip_level_size(mip_level).unwrap(); copy_size.width != target_size.width diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 41da2fa585..7519dc5615 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: vec![], }, 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 38a50a62c7..8d8433107c 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -297,7 +297,7 @@ impl Resource for StagingBuffer { } } -pub type TextureDescriptor<'a> = wgt::TextureDescriptor>; +pub type TextureDescriptor<'a> = wgt::TextureDescriptor, Vec>; #[derive(Debug)] pub(crate) enum TextureInner { @@ -338,7 +338,7 @@ pub enum TextureClearMode { pub struct Texture { pub(crate) inner: TextureInner, pub(crate) device_id: Stored, - pub(crate) desc: wgt::TextureDescriptor<()>, + pub(crate) desc: wgt::TextureDescriptor<(), Vec>, pub(crate) hal_usage: hal::TextureUses, pub(crate) format_features: wgt::TextureFormatFeatures, pub(crate) initialization_status: TextureInitTracker, @@ -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:?}, only changing srgb-ness is allowed.")] + 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")] @@ -626,6 +628,8 @@ pub enum CreateTextureViewError { texture: wgt::TextureFormat, view: wgt::TextureFormat, }, + #[error(transparent)] + MissingDownlevelFlags(#[from] MissingDownlevelFlags), } #[derive(Clone, Debug, Error)] diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 681256e938..5337fcd254 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -1139,6 +1139,11 @@ bitflags::bitflags! { /// /// Corresponds to Vulkan's `VkPhysicalDeviceFeatures.depthBiasClamp` const DEPTH_BIAS_CLAMP = 1 << 18; + + /// Supports specifying which view format values are allowed when create_view() is called on a texture. + /// + /// The WebGL and GLES backends doesn't support this. + const VIEW_FORMATS = 1 << 19; } } @@ -2537,6 +2542,29 @@ impl TextureFormat { }, } } + + /// Strips the `Srgb` suffix from the given texture 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] @@ -4319,7 +4347,7 @@ fn test_max_mips() { #[derive(Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "trace", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct TextureDescriptor { +pub struct TextureDescriptor { /// Debug label of the texture. This will show up in graphics debuggers for easy identification. pub label: L, /// Size of the texture. All components must be greater than zero. For a @@ -4336,12 +4364,17 @@ 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 formats will be allowed when calling create_view() on this texture. + /// + /// View formats of the same format as the texture are always allowed. + /// + /// Note: currently, only the srgb-ness is allowed to change. (ex: Rgba8Unorm texture + Rgba8UnormSrgb view) + pub view_formats: V, } -impl TextureDescriptor { +impl TextureDescriptor { /// Takes a closure and maps the label of the texture descriptor into another. - pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> TextureDescriptor { + pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> TextureDescriptor { TextureDescriptor { label: fun(&self.label), size: self.size, @@ -4350,6 +4383,25 @@ impl TextureDescriptor { dimension: self.dimension, format: self.format, usage: self.usage, + view_formats: self.view_formats.clone(), + } + } + + /// Maps the label and view_formats of the texture descriptor into another. + pub fn map_label_and_view_formats( + &self, + l_fun: impl FnOnce(&L) -> K, + v_fun: impl FnOnce(V) -> M, + ) -> TextureDescriptor { + TextureDescriptor { + label: l_fun(&self.label), + size: self.size, + mip_level_count: self.mip_level_count, + sample_count: self.sample_count, + dimension: self.dimension, + format: self.format, + usage: self.usage, + view_formats: v_fun(self.view_formats.clone()), } } @@ -4362,7 +4414,8 @@ impl TextureDescriptor { /// /// ```rust /// # use wgpu_types as wgpu; - /// let desc = wgpu::TextureDescriptor { + /// # type TextureDescriptor<'a> = wgpu::TextureDescriptor<(), &'a [wgpu::TextureFormat]>; + /// let desc = TextureDescriptor { /// label: (), /// size: wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 }, /// mip_level_count: 7, @@ -4370,6 +4423,7 @@ impl TextureDescriptor { /// dimension: wgpu::TextureDimension::D3, /// format: wgpu::TextureFormat::Rgba8Sint, /// usage: wgpu::TextureUsages::empty(), + /// view_formats: &[], /// }; /// /// assert_eq!(desc.mip_level_size(0), Some(wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 })); @@ -5087,7 +5141,7 @@ impl ImageSubresourceRange { } /// Returns the mip level range of a subresource range describes for a specific texture. - pub fn mip_range(&self, texture_desc: &TextureDescriptor) -> Range { + pub fn mip_range(&self, texture_desc: &TextureDescriptor) -> Range { self.base_mip_level..match self.mip_level_count { Some(mip_level_count) => self.base_mip_level + mip_level_count.get(), None => texture_desc.mip_level_count, @@ -5095,7 +5149,7 @@ impl ImageSubresourceRange { } /// Returns the layer range of a subresource range describes for a specific texture. - pub fn layer_range(&self, texture_desc: &TextureDescriptor) -> Range { + pub fn layer_range(&self, texture_desc: &TextureDescriptor) -> Range { self.base_array_layer..match self.array_layer_count { Some(array_layer_count) => self.base_array_layer + array_layer_count.get(), None => { diff --git a/wgpu/examples/bunnymark/main.rs b/wgpu/examples/bunnymark/main.rs index c7383e0296..4c82aa2041 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: &[], }); queue.write_texture( texture.as_image_copy(), diff --git a/wgpu/examples/capture/main.rs b/wgpu/examples/capture/main.rs index 747b361de2..04a73f93ca 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: &[], }); // Set the background to be red diff --git a/wgpu/examples/conservative-raster/main.rs b/wgpu/examples/conservative-raster/main.rs index 2fc4e98676..b82feab2f6 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: &[], }) .create_view(&Default::default()); diff --git a/wgpu/examples/cube/main.rs b/wgpu/examples/cube/main.rs index d029f7035e..3d646186f3 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: &[], }); 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 9af044bf66..797b3c9ecd 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -523,6 +523,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: &[], }); 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 f1cac82e9c..d5afa2e2f7 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: &[], }); 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 5817b13ced..a5777d71f7 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: &[], }; device diff --git a/wgpu/examples/shadow/main.rs b/wgpu/examples/shadow/main.rs index 85cff2af47..3b1f10d404 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: &[], }); 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: &[], }); 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 987a4ebaa9..5533cf77da 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: &[], }); 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: &[], }, &image.data, ); diff --git a/wgpu/examples/stencil-triangles/main.rs b/wgpu/examples/stencil-triangles/main.rs index 1de2ae8eb9..87c8ae143a 100644 --- a/wgpu/examples/stencil-triangles/main.rs +++ b/wgpu/examples/stencil-triangles/main.rs @@ -153,6 +153,7 @@ impl framework::Example for Triangles { sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Stencil8, + view_formats: &[], usage: wgpu::TextureUsages::RENDER_ATTACHMENT, }); diff --git a/wgpu/examples/texture-arrays/main.rs b/wgpu/examples/texture-arrays/main.rs index 89093b6400..20aec8137c 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: &[], }; let red_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("red"), + view_formats: &[], ..texture_descriptor }); let green_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("green"), + view_formats: &[], ..texture_descriptor }); let blue_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("blue"), + view_formats: &[], ..texture_descriptor }); let white_texture = device.create_texture(&wgpu::TextureDescriptor { label: Some("white"), + view_formats: &[], ..texture_descriptor }); diff --git a/wgpu/examples/water/main.rs b/wgpu/examples/water/main.rs index 3eb08940b9..dfcd4ec1db 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: &[], }); 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: &[], }); let color_sampler = device.create_sampler(&wgpu::SamplerDescriptor { diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 82613084b3..6d5a30ecfe 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -135,15 +135,10 @@ impl Context { device: &Device, desc: &TextureDescriptor, ) -> Texture { + let descriptor = desc.map_label_and_view_formats(|l| l.map(Borrowed), |v| v.to_vec()); let global = &self.0; - let (id, error) = unsafe { - global.create_texture_from_hal::( - hal_texture, - device.id, - &desc.map_label(|l| l.map(Borrowed)), - (), - ) - }; + let (id, error) = + unsafe { global.create_texture_from_hal::(hal_texture, device.id, &descriptor, ()) }; if let Some(cause) = error { self.handle_error( &device.error_sink, @@ -1249,10 +1244,11 @@ impl crate::Context for Context { device_data: &Self::DeviceData, desc: &TextureDescriptor, ) -> (Self::TextureId, Self::TextureData) { + let wgt_desc = desc.map_label_and_view_formats(|l| l.map(Borrowed), |v| v.to_vec()); let global = &self.0; let (id, error) = wgc::gfx_select!(device => global.device_create_texture( *device, - &desc.map_label(|l| l.map(Borrowed)), + &wgt_desc, () )); if let Some(cause) = error { diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index 9a3725d4c4..3014950aa5 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -1546,6 +1546,12 @@ impl crate::context::Context for Context { mapped_desc.dimension(map_texture_dimension(desc.dimension)); mapped_desc.mip_level_count(desc.mip_level_count); mapped_desc.sample_count(desc.sample_count); + let mapped_view_formats = desc + .view_formats + .iter() + .map(|format| JsValue::from(map_texture_format(*format))) + .collect::(); + mapped_desc.view_formats(&mapped_view_formats); (create_identified(device.0.create_texture(&mapped_desc)), ()) } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index b5a12cdb71..4fe3dcdf38 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -896,7 +896,7 @@ static_assertions::assert_impl_all!(RenderBundleDescriptor: Send, Sync); /// /// Corresponds to [WebGPU `GPUTextureDescriptor`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gputexturedescriptor). -pub type TextureDescriptor<'a> = wgt::TextureDescriptor>; +pub type TextureDescriptor<'a> = wgt::TextureDescriptor, &'a [TextureFormat]>; static_assertions::assert_impl_all!(TextureDescriptor: Send, Sync); /// Describes a [`QuerySet`]. /// @@ -2054,6 +2054,7 @@ impl Device { owned: true, descriptor: TextureDescriptor { label: None, + view_formats: &[], ..desc.clone() }, } @@ -2090,6 +2091,7 @@ impl Device { owned: true, descriptor: TextureDescriptor { label: None, + view_formats: &[], ..desc.clone() }, } @@ -4094,6 +4096,7 @@ impl Surface { mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, + view_formats: &[], }; texture_id diff --git a/wgpu/tests/clear_texture.rs b/wgpu/tests/clear_texture.rs index 3afdd98009..556bd8d55d 100644 --- a/wgpu/tests/clear_texture.rs +++ b/wgpu/tests/clear_texture.rs @@ -225,6 +225,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: &[], }); let mut encoder = ctx .device diff --git a/wgpu/tests/common/image.rs b/wgpu/tests/common/image.rs index fc80ac8eac..8911d9a0e7 100644 --- a/wgpu/tests/common/image.rs +++ b/wgpu/tests/common/image.rs @@ -64,7 +64,7 @@ fn write_png( } } -fn calc_difference(lhs: u8, rhs: u8) -> u8 { +pub fn calc_difference(lhs: u8, rhs: u8) -> u8 { (lhs as i16 - rhs as i16).unsigned_abs() as u8 } diff --git a/wgpu/tests/queue_transfer.rs b/wgpu/tests/queue_transfer.rs index 90310ea6e7..3ab1cfa98e 100644 --- a/wgpu/tests/queue_transfer.rs +++ b/wgpu/tests/queue_transfer.rs @@ -21,6 +21,7 @@ fn queue_write_texture_overflow() { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba32Float, usage: wgpu::TextureUsages::COPY_DST, + view_formats: &[], }); let data = vec![255; 128]; diff --git a/wgpu/tests/resource_error.rs b/wgpu/tests/resource_error.rs index 716c646a7d..eb88779100 100644 --- a/wgpu/tests/resource_error.rs +++ b/wgpu/tests/resource_error.rs @@ -44,6 +44,7 @@ fn bad_texture() { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsages::all(), + view_formats: &[], }) }); diff --git a/wgpu/tests/root.rs b/wgpu/tests/root.rs index a3132eda81..122bf790a7 100644 --- a/wgpu/tests/root.rs +++ b/wgpu/tests/root.rs @@ -17,6 +17,7 @@ mod resource_descriptor_accessor; mod resource_error; mod shader; mod shader_primitive_index; +mod shader_view_format; mod texture_bounds; mod transfer; mod vertex_indices; diff --git a/wgpu/tests/shader_primitive_index/mod.rs b/wgpu/tests/shader_primitive_index/mod.rs index a7f68936f2..086bc181d9 100644 --- a/wgpu/tests/shader_primitive_index/mod.rs +++ b/wgpu/tests/shader_primitive_index/mod.rs @@ -165,6 +165,7 @@ fn pulling_common( dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8Unorm, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], }); let color_view = color_texture.create_view(&wgpu::TextureViewDescriptor::default()); diff --git a/wgpu/tests/shader_view_format/mod.rs b/wgpu/tests/shader_view_format/mod.rs new file mode 100644 index 0000000000..f82f8851cf --- /dev/null +++ b/wgpu/tests/shader_view_format/mod.rs @@ -0,0 +1,208 @@ +use crate::common::{image::calc_difference, initialize_test, TestParameters, TestingContext}; +use std::num::NonZeroU32; +use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat}; + +#[test] +fn reinterpret_srgb_ness() { + let parameters = TestParameters::default() + .downlevel_flags(DownlevelFlags::VIEW_FORMATS) + .limits(Limits::downlevel_defaults()) + .specific_failure(Some(wgpu::Backends::GL), None, None, true); + initialize_test(parameters, |ctx| { + let unorm_data: [[u8; 4]; 4] = [ + [180, 0, 0, 255], + [0, 84, 0, 127], + [0, 0, 62, 100], + [62, 180, 84, 90], + ]; + let srgb_data: [[u8; 4]; 4] = [ + [116, 0, 0, 255], + [0, 23, 0, 127], + [0, 0, 12, 100], + [12, 116, 23, 90], + ]; + + let size = wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }; + + let shader = ctx + .device + .create_shader_module(wgpu::include_wgsl!("view_format.wgsl")); + + // Reinterpret Rgba8Unorm as Rgba8UnormSrgb + reinterpret( + &ctx, + &shader, + size, + TextureFormat::Rgba8Unorm, + TextureFormat::Rgba8UnormSrgb, + &unorm_data, + &srgb_data, + ); + + // Reinterpret Rgba8UnormSrgb back to Rgba8Unorm + reinterpret( + &ctx, + &shader, + size, + TextureFormat::Rgba8UnormSrgb, + TextureFormat::Rgba8Unorm, + &srgb_data, + &unorm_data, + ); + }); +} + +fn reinterpret( + ctx: &TestingContext, + shader: &wgpu::ShaderModule, + size: wgpu::Extent3d, + src_format: wgpu::TextureFormat, + reinterpret_to: wgpu::TextureFormat, + src_data: &[[u8; 4]], + expect_data: &[[u8; 4]], +) { + let tex = ctx.device.create_texture_with_data( + &ctx.queue, + &wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size, + format: src_format, + usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[reinterpret_to], + }, + bytemuck::cast_slice(src_data), + ); + let tv = tex.create_view(&wgpu::TextureViewDescriptor { + format: Some(reinterpret_to), + ..Default::default() + }); + let pipeline = ctx + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("reinterpret pipeline"), + layout: None, + vertex: wgpu::VertexState { + module: shader, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: shader, + entry_point: "fs_main", + targets: &[Some(src_format.into())], + }), + primitive: wgpu::PrimitiveState { + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &pipeline.get_bind_group_layout(0), + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&tv), + }], + label: None, + }); + + let target_tex = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: src_format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + ops: wgpu::Operations::default(), + resolve_target: None, + view: &target_view, + })], + depth_stencil_attachment: None, + label: None, + }); + rpass.set_pipeline(&pipeline); + rpass.set_bind_group(0, &bind_group, &[]); + rpass.draw(0..3, 0..1); + drop(rpass); + ctx.queue.submit(Some(encoder.finish())); + + let read_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: wgpu::COPY_BYTES_PER_ROW_ALIGNMENT as u64 * 2, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + encoder.copy_texture_to_buffer( + wgpu::ImageCopyTexture { + texture: &target_tex, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::ImageCopyBuffer { + buffer: &read_buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: NonZeroU32::new(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT), + rows_per_image: None, + }, + }, + size, + ); + ctx.queue.submit(Some(encoder.finish())); + + let slice = read_buffer.slice(..); + slice.map_async(wgpu::MapMode::Read, |_| ()); + ctx.device.poll(wgpu::Maintain::Wait); + + let data: Vec = slice.get_mapped_range().to_vec(); + let tolerance_data: [[u8; 4]; 4] = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 1, 1, 0]]; + + for h in 0..size.height { + let offset = h * wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; + for w in 0..size.width { + let expect = expect_data[(h * size.width + w) as usize]; + let tolerance = tolerance_data[(h * size.width + w) as usize]; + let index = (w * 4 + offset) as usize; + if calc_difference(expect[0], data[index]) > tolerance[0] + || calc_difference(expect[1], data[index + 1]) > tolerance[1] + || calc_difference(expect[2], data[index + 2]) > tolerance[2] + || calc_difference(expect[3], data[index + 3]) > tolerance[3] + { + panic!( + "Reinterpret {:?} as {:?} mismatch! expect {:?} get [{}, {}, {}, {}]", + src_format, + reinterpret_to, + expect, + data[index], + data[index + 1], + data[index + 2], + data[index + 3] + ) + } + } + } +} diff --git a/wgpu/tests/shader_view_format/view_format.wgsl b/wgpu/tests/shader_view_format/view_format.wgsl new file mode 100644 index 0000000000..98d60460a6 --- /dev/null +++ b/wgpu/tests/shader_view_format/view_format.wgsl @@ -0,0 +1,12 @@ +@vertex +fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4 { + let uv: vec2 = vec2(f32((vertexIndex << 1u) & 2u), f32(vertexIndex & 2u)); + return vec4(uv * 2.0 - 1.0, 0.0, 1.0); +} + +@group(0) @binding(0) var tex: texture_2d; + +@fragment +fn fs_main(@builtin(position) coord: vec4) -> @location(0) vec4 { + return textureLoad(tex, vec2(coord.xy), 0); +} \ No newline at end of file diff --git a/wgpu/tests/texture_bounds.rs b/wgpu/tests/texture_bounds.rs index 635dac8480..3bdb9f23e7 100644 --- a/wgpu/tests/texture_bounds.rs +++ b/wgpu/tests/texture_bounds.rs @@ -103,6 +103,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: &[], }; const BYTES_PER_PIXEL: u32 = 4; diff --git a/wgpu/tests/transfer.rs b/wgpu/tests/transfer.rs index 1e94a31ff1..97d53149c0 100644 --- a/wgpu/tests/transfer.rs +++ b/wgpu/tests/transfer.rs @@ -21,6 +21,7 @@ fn copy_overflow_z() { usage: wgpu::TextureUsages::COPY_DST, mip_level_count: 1, sample_count: 1, + view_formats: &[], }); let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, @@ -34,6 +35,7 @@ fn copy_overflow_z() { usage: wgpu::TextureUsages::COPY_DST, mip_level_count: 1, sample_count: 1, + view_formats: &[], }); fail(&ctx.device, || { diff --git a/wgpu/tests/vertex_indices/mod.rs b/wgpu/tests/vertex_indices/mod.rs index 2c739e8b3d..055e10a40b 100644 --- a/wgpu/tests/vertex_indices/mod.rs +++ b/wgpu/tests/vertex_indices/mod.rs @@ -97,6 +97,7 @@ fn pulling_common( dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8Unorm, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, + view_formats: &[], }, &[0, 0, 0, 1], ) diff --git a/wgpu/tests/write_texture.rs b/wgpu/tests/write_texture.rs index 9d351ed70c..7e757606ea 100644 --- a/wgpu/tests/write_texture.rs +++ b/wgpu/tests/write_texture.rs @@ -25,6 +25,7 @@ fn write_texture_subset() { | wgpu::TextureUsages::TEXTURE_BINDING, mip_level_count: 1, sample_count: 1, + view_formats: &[], }); 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 751ae2aa77..6a9bc0a99c 100644 --- a/wgpu/tests/zero_init_texture_after_discard.rs +++ b/wgpu/tests/zero_init_texture_after_discard.rs @@ -208,6 +208,7 @@ fn create_white_texture_and_readback_buffer( | wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT }, + view_formats: &[], }); // Clear using a write_texture operation. We could also clear using a render_pass clear.