Skip to content

Commit

Permalink
Add view_formats in TextureDescriptor (#3237)
Browse files Browse the repository at this point in the history
Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
Co-authored-by: crowlkats <crowlkats@toaxl.com>
Closes #3030
  • Loading branch information
jinleili authored Jan 18, 2023
1 parent fae740d commit 0849e78
Show file tree
Hide file tree
Showing 44 changed files with 383 additions and 28 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
9 changes: 9 additions & 0 deletions deno_webgpu/src/02_idl_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,15 @@
converter: webidl.converters["GPUTextureUsageFlags"],
required: true,
},
{
key: "viewFormats",
converter: webidl.createSequenceConverter(
webidl.converters["GPUTextureFormat"],
),
get defaultValue() {
return [];
},
},
];
webidl.converters["GPUTextureDescriptor"] = webidl.createDictionaryConverter(
"GPUTextureDescriptor",
Expand Down
2 changes: 2 additions & 0 deletions deno_webgpu/src/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub struct CreateTextureArgs {
dimension: wgpu_types::TextureDimension,
format: wgpu_types::TextureFormat,
usage: u32,
view_formats: Vec<wgpu_types::TextureFormat>,
}

#[op]
Expand All @@ -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(
Expand Down
1 change: 1 addition & 0 deletions deno_webgpu/webgpu.idl
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ dictionary GPUTextureDescriptor : GPUObjectDescriptorBase {
GPUTextureDimension dimension = "2d";
required GPUTextureFormat format;
required GPUTextureUsageFlags usage;
sequence<GPUTextureFormat> viewFormats = [];
};

enum GPUTextureDimension {
Expand Down
1 change: 1 addition & 0 deletions player/tests/data/clear-buffer-texture.ron
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions player/tests/data/quad.ron
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
dimension: r#2d,
format: "rgba8unorm",
usage: 27,
view_formats: [],
)),
CreateTextureView(
id: Id(0, 1, Empty),
Expand Down
2 changes: 2 additions & 0 deletions player/tests/data/zero-init-texture-binding.ron
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
dimension: r#2d,
format: "rgba8unorm",
usage: 5, // SAMPLED + COPY_SRC
view_formats: [],
)),
CreateTextureView(
id: Id(0, 1, Empty),
Expand All @@ -54,6 +55,7 @@
dimension: r#2d,
format: "rgba8unorm",
usage: 9, // STORAGE + COPY_SRC
view_formats: [],
)),
CreateTextureView(
id: Id(1, 1, Empty),
Expand Down
1 change: 1 addition & 0 deletions player/tests/data/zero-init-texture-copytobuffer.ron
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
dimension: r#2d,
format: "rgba8unorm",
usage: 1, // COPY_SRC
view_formats: [],
)),
CreateBuffer(
Id(0, 1, Empty),
Expand Down
1 change: 1 addition & 0 deletions player/tests/data/zero-init-texture-rendertarget.ron
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
dimension: r#2d,
format: "rgba8unorm",
usage: 17, // RENDER_ATTACHMENT + COPY_SRC
view_formats: [],
)),
CreateTextureView(
id: Id(0, 1, Empty),
Expand Down
2 changes: 1 addition & 1 deletion wgpu-core/src/command/clear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ pub(crate) fn clear_texture<A: HalApi>(
}

fn clear_texture_via_buffer_copies<A: hal::Api>(
texture_desc: &wgt::TextureDescriptor<()>,
texture_desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
alignments: &hal::Alignments,
zero_buffer: &A::Buffer, // Buffer of size device::ZERO_BUFFER_SIZE
range: TextureInitRange,
Expand Down
2 changes: 1 addition & 1 deletion wgpu-core/src/command/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<wgt::TextureFormat>>,
texture_side: CopySide,
copy_size: &Extent3d,
) -> Result<(hal::CopyExtent, u32), TransferError> {
Expand Down
20 changes: 15 additions & 5 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,14 @@ impl<A: HalApi> Device<A> {
));
}

// 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.
Expand Down Expand Up @@ -1086,10 +1093,13 @@ impl<A: HalApi> Device<A> {
}
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
Expand Down
2 changes: 1 addition & 1 deletion wgpu-core/src/init_tracker/texture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<wgt::TextureFormat>>,
) -> bool {
let target_size = desc.mip_level_size(mip_level).unwrap();
copy_size.width != target_size.width
Expand Down
1 change: 1 addition & 0 deletions wgpu-core/src/present.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
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 {
Expand Down
8 changes: 6 additions & 2 deletions wgpu-core/src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl<A: hal::Api> Resource for StagingBuffer<A> {
}
}

pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>>;
pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, Vec<wgt::TextureFormat>>;

#[derive(Debug)]
pub(crate) enum TextureInner<A: hal::Api> {
Expand Down Expand Up @@ -338,7 +338,7 @@ pub enum TextureClearMode<A: hal::Api> {
pub struct Texture<A: hal::Api> {
pub(crate) inner: TextureInner<A>,
pub(crate) device_id: Stored<DeviceId>,
pub(crate) desc: wgt::TextureDescriptor<()>,
pub(crate) desc: wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
pub(crate) hal_usage: hal::TextureUses,
pub(crate) format_features: wgt::TextureFormatFeatures,
pub(crate) initialization_status: TextureInitTracker,
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -626,6 +628,8 @@ pub enum CreateTextureViewError {
texture: wgt::TextureFormat,
view: wgt::TextureFormat,
},
#[error(transparent)]
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
}

#[derive(Clone, Debug, Error)]
Expand Down
68 changes: 61 additions & 7 deletions wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,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;
}
}

Expand Down Expand Up @@ -2540,6 +2545,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]
Expand Down Expand Up @@ -4322,7 +4350,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<L> {
pub struct TextureDescriptor<L, V> {
/// 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
Expand All @@ -4339,12 +4367,17 @@ pub struct TextureDescriptor<L> {
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<L> TextureDescriptor<L> {
impl<L, V: Clone> TextureDescriptor<L, V> {
/// Takes a closure and maps the label of the texture descriptor into another.
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> TextureDescriptor<K> {
pub fn map_label<K>(&self, fun: impl FnOnce(&L) -> K) -> TextureDescriptor<K, V> {
TextureDescriptor {
label: fun(&self.label),
size: self.size,
Expand All @@ -4353,6 +4386,25 @@ impl<L> TextureDescriptor<L> {
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<K, M>(
&self,
l_fun: impl FnOnce(&L) -> K,
v_fun: impl FnOnce(V) -> M,
) -> TextureDescriptor<K, M> {
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()),
}
}

Expand All @@ -4365,14 +4417,16 @@ impl<L> TextureDescriptor<L> {
///
/// ```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,
/// sample_count: 1,
/// 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 }));
Expand Down Expand Up @@ -5090,15 +5144,15 @@ impl ImageSubresourceRange {
}

/// Returns the mip level range of a subresource range describes for a specific texture.
pub fn mip_range<L>(&self, texture_desc: &TextureDescriptor<L>) -> Range<u32> {
pub fn mip_range<L, V>(&self, texture_desc: &TextureDescriptor<L, V>) -> Range<u32> {
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,
}
}

/// Returns the layer range of a subresource range describes for a specific texture.
pub fn layer_range<L>(&self, texture_desc: &TextureDescriptor<L>) -> Range<u32> {
pub fn layer_range<L, V>(&self, texture_desc: &TextureDescriptor<L, V>) -> Range<u32> {
self.base_array_layer..match self.array_layer_count {
Some(array_layer_count) => self.base_array_layer + array_layer_count.get(),
None => {
Expand Down
1 change: 1 addition & 0 deletions wgpu/examples/bunnymark/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions wgpu/examples/capture/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions wgpu/examples/conservative-raster/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
1 change: 1 addition & 0 deletions wgpu/examples/cube/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions wgpu/examples/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ pub fn test<E: Example>(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());
Expand Down
Loading

0 comments on commit 0849e78

Please sign in to comment.