diff --git a/deno_webgpu/texture.rs b/deno_webgpu/texture.rs index 5a31259586..2ad2814996 100644 --- a/deno_webgpu/texture.rs +++ b/deno_webgpu/texture.rs @@ -115,6 +115,7 @@ pub fn op_webgpu_create_texture_view( format: args.format, dimension: args.dimension, range: args.range, + usage: None, // FIXME: Obtain actual value from desc }; gfx_put!(instance.texture_create_view( diff --git a/examples/src/mipmap/mod.rs b/examples/src/mipmap/mod.rs index fa5bedf8cb..9e96fec87a 100644 --- a/examples/src/mipmap/mod.rs +++ b/examples/src/mipmap/mod.rs @@ -127,6 +127,7 @@ impl Example { label: Some("mip"), format: None, dimension: None, + usage: None, aspect: wgpu::TextureAspect::All, base_mip_level: mip, mip_level_count: Some(1), diff --git a/examples/src/multiple_render_targets/mod.rs b/examples/src/multiple_render_targets/mod.rs index 7680ad4385..c7301024b5 100644 --- a/examples/src/multiple_render_targets/mod.rs +++ b/examples/src/multiple_render_targets/mod.rs @@ -69,6 +69,7 @@ impl MultiTargetRenderer { label: Some("view"), format: None, dimension: Some(wgpu::TextureViewDimension::D2), + usage: None, aspect: wgpu::TextureAspect::All, base_mip_level: 0, mip_level_count: None, diff --git a/examples/src/ray_cube_compute/mod.rs b/examples/src/ray_cube_compute/mod.rs index 3f8f31ee0a..62a3e36aab 100644 --- a/examples/src/ray_cube_compute/mod.rs +++ b/examples/src/ray_cube_compute/mod.rs @@ -177,6 +177,7 @@ impl crate::framework::Example for Example { label: None, format: Some(wgpu::TextureFormat::Rgba8Unorm), dimension: Some(wgpu::TextureViewDimension::D2), + usage: None, aspect: wgpu::TextureAspect::All, base_mip_level: 0, mip_level_count: None, diff --git a/examples/src/shadow/mod.rs b/examples/src/shadow/mod.rs index 44ad84a73f..a911bd7108 100644 --- a/examples/src/shadow/mod.rs +++ b/examples/src/shadow/mod.rs @@ -393,6 +393,7 @@ impl crate::framework::Example for Example { label: Some("shadow"), format: None, dimension: Some(wgpu::TextureViewDimension::D2), + usage: None, aspect: wgpu::TextureAspect::All, base_mip_level: 0, mip_level_count: None, diff --git a/tests/tests/bgra8unorm_storage.rs b/tests/tests/bgra8unorm_storage.rs index 30babe0d82..698a4988b7 100644 --- a/tests/tests/bgra8unorm_storage.rs +++ b/tests/tests/bgra8unorm_storage.rs @@ -44,6 +44,7 @@ static BGRA8_UNORM_STORAGE: GpuTestConfiguration = GpuTestConfiguration::new() label: None, format: None, dimension: None, + usage: None, aspect: wgpu::TextureAspect::All, base_mip_level: 0, base_array_layer: 0, diff --git a/tests/tests/device.rs b/tests/tests/device.rs index 896ac49349..41efc9b565 100644 --- a/tests/tests/device.rs +++ b/tests/tests/device.rs @@ -688,6 +688,7 @@ static DIFFERENT_BGL_ORDER_BW_SHADER_AND_API: GpuTestConfiguration = GpuTestConf label: None, format: None, dimension: None, + usage: None, aspect: wgt::TextureAspect::All, base_mip_level: 0, mip_level_count: None, diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index e83e843ea1..a609edefca 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -1086,6 +1086,39 @@ impl Device { .saturating_sub(desc.range.base_array_layer), }); + let resolved_usage = { + let usage = desc.usage.unwrap_or(wgt::TextureUsages::empty()); + if usage.is_empty() { + texture.desc.usage + } else if texture.desc.usage.contains(usage) { + usage + } else { + return Err(resource::CreateTextureViewError::InvalidTextureViewUsage { + view: usage, + texture: texture.desc.usage, + }); + } + }; + + let allowed_format_usages = self + .describe_format_features(resolved_format)? + .allowed_usages; + if resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) + && !allowed_format_usages.contains(wgt::TextureUsages::RENDER_ATTACHMENT) + { + return Err( + resource::CreateTextureViewError::TextureViewFormatNotRenderable(resolved_format), + ); + } + + if resolved_usage.contains(wgt::TextureUsages::STORAGE_BINDING) + && !allowed_format_usages.contains(wgt::TextureUsages::STORAGE_BINDING) + { + return Err( + resource::CreateTextureViewError::TextureViewFormatNotStorage(resolved_format), + ); + } + // validate TextureViewDescriptor let aspects = hal::FormatAspects::new(texture.desc.format, desc.range.aspect); @@ -1207,12 +1240,8 @@ impl Device { // https://gpuweb.github.io/gpuweb/#abstract-opdef-renderable-texture-view let render_extent = 'error: { - if !texture - .desc - .usage - .contains(wgt::TextureUsages::RENDER_ATTACHMENT) - { - break 'error Err(TextureViewNotRenderableReason::Usage(texture.desc.usage)); + if !resolved_usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) { + break 'error Err(TextureViewNotRenderableReason::Usage(resolved_usage)); } if !(resolved_dimension == TextureViewDimension::D2 @@ -1309,6 +1338,7 @@ impl Device { texture_format: texture.desc.format, format: resolved_format, dimension: resolved_dimension, + usage: resolved_usage, range: resolved_range, }, format_features: texture.format_features, @@ -2090,7 +2120,7 @@ impl Device { { view.same_device(self)?; - let (pub_usage, internal_use) = self.texture_use_parameters( + let internal_use = self.texture_use_parameters( binding, decl, view, @@ -2100,7 +2130,6 @@ impl Device { used.views.insert_single(view.clone(), internal_use); let texture = &view.parent; - texture.check_usage(pub_usage)?; used_texture_ranges.push(TextureInitTrackerAction { texture: texture.clone(), @@ -2399,7 +2428,7 @@ impl Device { decl: &wgt::BindGroupLayoutEntry, view: &TextureView, expected: &'static str, - ) -> Result<(wgt::TextureUsages, hal::TextureUses), binding_model::CreateBindGroupError> { + ) -> Result { use crate::binding_model::CreateBindGroupError as Error; if view .desc @@ -2458,10 +2487,8 @@ impl Device { view_dimension: view.desc.dimension, }); } - Ok(( - wgt::TextureUsages::TEXTURE_BINDING, - hal::TextureUses::RESOURCE, - )) + view.check_usage(wgt::TextureUsages::TEXTURE_BINDING)?; + Ok(hal::TextureUses::RESOURCE) } wgt::BindingType::StorageTexture { access, @@ -2524,7 +2551,8 @@ impl Device { hal::TextureUses::STORAGE_READ_WRITE } }; - Ok((wgt::TextureUsages::STORAGE_BINDING, internal_use)) + view.check_usage(wgt::TextureUsages::STORAGE_BINDING)?; + Ok(internal_use) } _ => Err(Error::WrongBindingType { binding, diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 4846c257e2..08c76c6d33 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -1060,6 +1060,7 @@ impl Texture { bind_groups: Mutex::new(rank::TEXTURE_BIND_GROUPS, WeakVec::new()), } } + /// Checks that the given texture usage contains the required texture usage, /// returns an error otherwise. pub(crate) fn check_usage( @@ -1552,6 +1553,9 @@ pub struct TextureViewDescriptor<'a> { /// - For 2D textures it must be one of `D2`, `D2Array`, `Cube`, or `CubeArray`. /// - For 3D textures it must be `D3`. pub dimension: Option, + /// The allowed usage(s) for the texture view. Must be a subset of the usage flags of the texture. + /// If not provided, defaults to the full set of usage flags of the texture. + pub usage: Option, /// Range within the texture that is accessible via this view. pub range: wgt::ImageSubresourceRange, } @@ -1560,6 +1564,7 @@ pub struct TextureViewDescriptor<'a> { pub(crate) struct HalTextureViewDescriptor { pub texture_format: wgt::TextureFormat, pub format: wgt::TextureFormat, + pub usage: wgt::TextureUsages, pub dimension: wgt::TextureViewDimension, pub range: wgt::ImageSubresourceRange, } @@ -1631,6 +1636,23 @@ impl TextureView { .map(|it| it.as_ref()) .ok_or_else(|| DestroyedResourceError(self.error_ident())) } + + /// Checks that the given texture usage contains the required texture usage, + /// returns an error otherwise. + pub(crate) fn check_usage( + &self, + expected: wgt::TextureUsages, + ) -> Result<(), MissingTextureUsageError> { + if self.desc.usage.contains(expected) { + Ok(()) + } else { + Err(MissingTextureUsageError { + res: self.error_ident(), + actual: self.desc.usage, + expected, + }) + } + } } #[derive(Clone, Debug, Error)] @@ -1645,6 +1667,15 @@ pub enum CreateTextureViewError { view: wgt::TextureViewDimension, texture: wgt::TextureDimension, }, + #[error("Texture view format `{0:?}` is not renderable")] + TextureViewFormatNotRenderable(wgt::TextureFormat), + #[error("Texture view format `{0:?}` is not storage bindable")] + TextureViewFormatNotStorage(wgt::TextureFormat), + #[error("Invalid texture view usage `{view:?}` with texture of usage `{texture:?}`")] + InvalidTextureViewUsage { + view: wgt::TextureUsages, + texture: wgt::TextureUsages, + }, #[error("Invalid texture view dimension `{0:?}` of a multisampled texture")] InvalidMultisampledTextureViewDimension(wgt::TextureViewDimension), #[error("Invalid texture depth `{depth}` for texture view of dimension `Cubemap`. Cubemap views must use images of size 6.")] @@ -1680,6 +1711,8 @@ pub enum CreateTextureViewError { }, #[error(transparent)] InvalidResource(#[from] InvalidResourceError), + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), } #[derive(Clone, Debug, Error)] diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index fa7dbe173c..94e6f76650 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -6102,6 +6102,9 @@ pub struct TextureViewDescriptor { /// The dimension of the texture view. For 1D textures, this must be `D1`. For 2D textures it must be one of /// `D2`, `D2Array`, `Cube`, and `CubeArray`. For 3D textures it must be `D3` pub dimension: Option, + /// The allowed usage(s) for the texture view. Must be a subset of the usage flags of the texture. + /// If not provided, defaults to the full set of usage flags of the texture. + pub usage: Option, /// Aspect of the texture. Color textures must be [`TextureAspect::All`]. pub aspect: TextureAspect, /// Base mip level. diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 2e16c603b2..b2f1f19079 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -2683,6 +2683,7 @@ impl dispatch::TextureInterface for WebTexture { if let Some(label) = desc.label { mapped.set_label(label); } + mapped.set_usage(desc.usage.unwrap_or(wgt::TextureUsages::empty()).bits()); let view = self.inner.create_view_with_descriptor(&mapped).unwrap(); diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index 8a7c8e1c31..eacd1fdc64 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -1967,6 +1967,7 @@ impl dispatch::TextureInterface for CoreTexture { label: desc.label.map(Borrowed), format: desc.format, dimension: desc.dimension, + usage: desc.usage, range: wgt::ImageSubresourceRange { aspect: desc.aspect, base_mip_level: desc.base_mip_level,