Skip to content

Expose wgpu::TextureView-descriptor/texture/render_extent #7895

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: trunk
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 3 additions & 3 deletions tests/tests/wgpu-gpu/mem_leaks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down
77 changes: 69 additions & 8 deletions tests/tests/wgpu-gpu/texture_view_creation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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));
}
});

Expand Down Expand Up @@ -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(
Expand All @@ -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));
}
});
81 changes: 80 additions & 1 deletion wgpu/src/api/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://www.w3.org/TR/webgpu/#abstract-opdef-resolving-gputextureviewdescriptor-defaults>
// 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.
Expand Down
38 changes: 38 additions & 0 deletions wgpu/src/api/texture_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -46,6 +48,42 @@ impl TextureView {
pub fn as_custom<T: custom::TextureViewInterface>(&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<Extent3d> {
// 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`].
Expand Down