diff --git a/CHANGELOG.md b/CHANGELOG.md index 946473f812..a69f551c5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ Bottom level categories: - Add acceleration structure limits. By @Vecvec in [#7845](https://github.com/gfx-rs/wgpu/pull/7845). - Add support for clip-distances feature for Vulkan and GL backends. By @dzamkov in [#7730](https://github.com/gfx-rs/wgpu/pull/7730) - Added `wgpu_types::error::{ErrorType, WebGpuError}` for classification of errors according to WebGPU's [`GPUError`]'s classification scheme, and implement `WebGpuError` for existing errors. This allows users of `wgpu-core` to offload error classification onto the WGPU ecosystem, rather than having to do it themselves without sufficient information. By @ErichDonGubler in [#6547](https://github.com/gfx-rs/wgpu/pull/6547). +- Expose `wgpu::TextureView::descriptor`, `wgpu::TextureView::texture` and `wgpu::TextureView::render_extent`. By @Wumpf in [#7895](https://github.com/gfx-rs/wgpu/pull/7895) [`GPUError`]: https://www.w3.org/TR/webgpu/#gpuerror diff --git a/tests/tests/wgpu-gpu/mem_leaks.rs b/tests/tests/wgpu-gpu/mem_leaks.rs index 0d03f9d374..fcbf5b5427 100644 --- a/tests/tests/wgpu-gpu/mem_leaks.rs +++ b/tests/tests/wgpu-gpu/mem_leaks.rs @@ -169,8 +169,8 @@ async fn draw_test_with_reports( assert_eq!(report.buffers.num_allocated, 1); assert_eq!(report.texture_views.num_allocated, 1); assert_eq!(report.texture_views.num_kept_from_user, 1); - assert_eq!(report.textures.num_allocated, 0); - assert_eq!(report.textures.num_kept_from_user, 0); + assert_eq!(report.textures.num_allocated, 1); // View keeps texture alive. + assert_eq!(report.textures.num_kept_from_user, 1); // View keeps texture alive. let mut encoder = ctx .device @@ -208,7 +208,7 @@ async fn draw_test_with_reports( assert_eq!(report.command_buffers.num_allocated, 1); assert_eq!(report.render_bundles.num_allocated, 0); assert_eq!(report.texture_views.num_allocated, 1); - assert_eq!(report.textures.num_allocated, 0); + assert_eq!(report.textures.num_allocated, 1); // View keeps texture alive. function(&mut rpass); diff --git a/tests/tests/wgpu-gpu/texture_view_creation.rs b/tests/tests/wgpu-gpu/texture_view_creation.rs index d6880fb620..30ffbf03bc 100644 --- a/tests/tests/wgpu-gpu/texture_view_creation.rs +++ b/tests/tests/wgpu-gpu/texture_view_creation.rs @@ -9,7 +9,10 @@ static STENCIL_ONLY_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration:: .limits(wgpu::Limits::downlevel_defaults()), ) .run_async(|ctx| async move { - for format in [TextureFormat::Stencil8, TextureFormat::Depth24PlusStencil8] { + for (format, expected_view_format) in [ + (TextureFormat::Stencil8, TextureFormat::Stencil8), + (TextureFormat::Depth24PlusStencil8, TextureFormat::Stencil8), + ] { let texture = ctx.device.create_texture(&TextureDescriptor { label: None, size: Extent3d { @@ -26,20 +29,39 @@ static STENCIL_ONLY_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration:: | TextureUsages::TEXTURE_BINDING, view_formats: &[], }); - let _view = texture.create_view(&TextureViewDescriptor { + let view = texture.create_view(&TextureViewDescriptor { aspect: TextureAspect::StencilOnly, ..Default::default() }); + + assert!(view.render_extent().is_none()); + + let descriptor = view.descriptor(); + assert_eq!(descriptor.format, Some(expected_view_format)); + assert_eq!(descriptor.dimension, Some(TextureViewDimension::D2)); + assert_eq!( + descriptor.usage, + Some( + TextureUsages::COPY_DST + | TextureUsages::COPY_SRC + | TextureUsages::TEXTURE_BINDING, + ) + ); + assert_eq!(descriptor.mip_level_count, Some(1)); + assert_eq!(descriptor.array_layer_count, Some(1)); } }); #[gpu_test] static DEPTH_ONLY_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration::new().run_async(|ctx| async move { - for format in [ - TextureFormat::Depth16Unorm, - TextureFormat::Depth24Plus, - TextureFormat::Depth24PlusStencil8, + for (format, expected_view_format) in [ + (TextureFormat::Depth16Unorm, TextureFormat::Depth16Unorm), + (TextureFormat::Depth24Plus, TextureFormat::Depth24Plus), + ( + TextureFormat::Depth24PlusStencil8, + TextureFormat::Depth24Plus, + ), ] { let texture = ctx.device.create_texture(&TextureDescriptor { label: None, @@ -57,10 +79,26 @@ static DEPTH_ONLY_VIEW_CREATION: GpuTestConfiguration = | TextureUsages::TEXTURE_BINDING, view_formats: &[], }); - let _view = texture.create_view(&TextureViewDescriptor { + let view = texture.create_view(&TextureViewDescriptor { aspect: TextureAspect::DepthOnly, ..Default::default() }); + + assert!(view.render_extent().is_none()); + + let descriptor = view.descriptor(); + assert_eq!(descriptor.format, Some(expected_view_format)); + assert_eq!(descriptor.dimension, Some(TextureViewDimension::D2)); + assert_eq!( + descriptor.usage, + Some( + TextureUsages::COPY_DST + | TextureUsages::COPY_SRC + | TextureUsages::TEXTURE_BINDING, + ) + ); + assert_eq!(descriptor.mip_level_count, Some(1)); + assert_eq!(descriptor.array_layer_count, Some(1)); } }); @@ -88,7 +126,7 @@ static SHARED_USAGE_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration:: | TextureUsages::RENDER_ATTACHMENT, view_formats: &[TextureFormat::Rgba8UnormSrgb], }); - let _view = texture.create_view(&TextureViewDescriptor { + let view = texture.create_view(&TextureViewDescriptor { aspect: TextureAspect::All, format: Some(view_format), usage: Some( @@ -98,5 +136,28 @@ static SHARED_USAGE_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration:: ), ..Default::default() }); + + assert_eq!( + view.render_extent(), + Some(Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }) + ); + + let descriptor = view.descriptor(); + assert_eq!(descriptor.format, Some(view_format)); + assert_eq!(descriptor.dimension, Some(TextureViewDimension::D2)); + assert_eq!( + descriptor.usage, + Some( + TextureUsages::COPY_DST + | TextureUsages::TEXTURE_BINDING + | TextureUsages::RENDER_ATTACHMENT, + ) + ); + assert_eq!(descriptor.mip_level_count, Some(1)); + assert_eq!(descriptor.array_layer_count, Some(1)); } }); diff --git a/wgpu/src/api/texture.rs b/wgpu/src/api/texture.rs index 6dff7b6a11..4bef0a1cc0 100644 --- a/wgpu/src/api/texture.rs +++ b/wgpu/src/api/texture.rs @@ -51,7 +51,86 @@ impl Texture { pub fn create_view(&self, desc: &TextureViewDescriptor<'_>) -> TextureView { let view = self.inner.create_view(desc); - TextureView { inner: view } + let &TextureViewDescriptor { + label: _, + format, + dimension, + usage, + aspect, + base_mip_level, + mip_level_count, + base_array_layer, + array_layer_count, + } = desc; + + // WebGPU spec requires us to fill in optional fields for later access. + // We could do this by accessing the underlying implementation, but duplicating this + // logic here is a lot simpler than piping this through from a backend. + // See + // See also wgpu-core's `create_texture_view` + + let resolved_format = format.unwrap_or_else(|| { + self.descriptor + .format + .aspect_specific_format(aspect) + .unwrap_or(self.descriptor.format) + }); + + let resolved_dimension = dimension.unwrap_or_else(|| match self.descriptor.dimension { + TextureDimension::D1 => TextureViewDimension::D1, + TextureDimension::D2 => { + if self.descriptor.array_layer_count() == 1 { + TextureViewDimension::D2 + } else { + TextureViewDimension::D2Array + } + } + TextureDimension::D3 => TextureViewDimension::D3, + }); + + let resolved_mip_level_count = mip_level_count.unwrap_or_else(|| { + self.descriptor + .mip_level_count + .saturating_sub(base_mip_level) + }); + + let resolved_array_layer_count = + array_layer_count.unwrap_or_else(|| match resolved_dimension { + TextureViewDimension::D1 | TextureViewDimension::D2 | TextureViewDimension::D3 => 1, + TextureViewDimension::Cube => 6, + TextureViewDimension::D2Array | TextureViewDimension::CubeArray => self + .descriptor + .array_layer_count() + .saturating_sub(base_array_layer), + }); + + let resolved_usage = { + let usage = usage.unwrap_or(wgt::TextureUsages::empty()); + if usage.is_empty() { + self.descriptor.usage + } else { + usage + // If usage is still empty we have an error, but that's handled by the backend. + } + }; + + let filled_descriptor = TextureViewDescriptor { + label: None, + format: Some(resolved_format), + dimension: Some(resolved_dimension), + usage: Some(resolved_usage), + aspect, + base_mip_level, + mip_level_count: Some(resolved_mip_level_count), + base_array_layer, + array_layer_count: Some(resolved_array_layer_count), + }; + + TextureView { + inner: view, + filled_descriptor, + texture: self.clone(), + } } /// Destroy the associated native resources as soon as possible. diff --git a/wgpu/src/api/texture_view.rs b/wgpu/src/api/texture_view.rs index e5af89d62e..c4326ddf23 100644 --- a/wgpu/src/api/texture_view.rs +++ b/wgpu/src/api/texture_view.rs @@ -12,6 +12,8 @@ use crate::*; #[derive(Debug, Clone)] pub struct TextureView { pub(crate) inner: dispatch::DispatchTextureView, + pub(crate) filled_descriptor: TextureViewDescriptor<'static>, + pub(crate) texture: Texture, } #[cfg(send_sync)] static_assertions::assert_impl_all!(TextureView: Send, Sync); @@ -46,6 +48,42 @@ impl TextureView { pub fn as_custom(&self) -> Option<&T> { self.inner.as_custom() } + + /// The [`Texture`] this texture view is a view of. + pub fn texture(&self) -> &Texture { + &self.texture + } + + /// The [`TextureViewDescriptor`] describing this texture view. + /// + /// Fields of [`TextureViewDescriptor`] that were left optional on creation and are + /// derived from the texture are filled out. + /// I.e. this is not the original descriptor used to create this texture view. + /// + /// Does not preserve the original label and leaves it as `None`. + pub fn descriptor(&self) -> &TextureViewDescriptor<'static> { + &self.filled_descriptor + } + + /// The extent of the texture view if this texture view is renderable. + pub fn render_extent(&self) -> Option { + // Computing this on the fly is a lot simpler than piping this through from a backend. + + // The filled_descriptor should have usage always be set. + debug_assert!(self.filled_descriptor.usage.is_some()); + let usage = self + .filled_descriptor + .usage + .unwrap_or(wgt::TextureUsages::empty()); + + usage + .contains(wgt::TextureUsages::RENDER_ATTACHMENT) + .then(|| { + self.texture + .descriptor + .compute_render_extent(self.descriptor().base_mip_level) + }) + } } /// Describes a [`TextureView`].