Add view_formats in TextureDescriptor (#3237)

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
Co-authored-by: crowlkats <crowlkats@toaxl.com>
Closes https://github.com/gfx-rs/wgpu/issues/3030
This commit is contained in:
Jinlei Li 2023-01-19 05:03:56 +08:00 committed by GitHub
parent fae740df5c
commit 0849e78600
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 383 additions and 28 deletions

View File

@ -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

View File

@ -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",

View File

@ -34,6 +34,7 @@ pub struct CreateTextureArgs {
dimension: wgpu_types::TextureDimension,
format: wgpu_types::TextureFormat,
usage: u32,
view_formats: Vec<wgpu_types::TextureFormat>,
}
#[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(

View File

@ -201,6 +201,7 @@ dictionary GPUTextureDescriptor : GPUObjectDescriptorBase {
GPUTextureDimension dimension = "2d";
required GPUTextureFormat format;
required GPUTextureUsageFlags usage;
sequence<GPUTextureFormat> viewFormats = [];
};
enum GPUTextureDimension {

View File

@ -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(

View File

@ -28,6 +28,7 @@
dimension: r#2d,
format: "rgba8unorm",
usage: 27,
view_formats: [],
)),
CreateTextureView(
id: Id(0, 1, Empty),

View File

@ -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),

View File

@ -21,6 +21,7 @@
dimension: r#2d,
format: "rgba8unorm",
usage: 1, // COPY_SRC
view_formats: [],
)),
CreateBuffer(
Id(0, 1, Empty),

View File

@ -21,6 +21,7 @@
dimension: r#2d,
format: "rgba8unorm",
usage: 17, // RENDER_ATTACHMENT + COPY_SRC
view_formats: [],
)),
CreateTextureView(
id: Id(0, 1, Empty),

View File

@ -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,

View File

@ -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> {

View File

@ -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.
@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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> {
@ -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,
@ -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)]

View File

@ -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;
}
}
@ -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]
@ -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
@ -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,
@ -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()),
}
}
@ -4365,7 +4417,8 @@ 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,
@ -4373,6 +4426,7 @@ impl<L> TextureDescriptor<L> {
/// 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 }));
@ -5090,7 +5144,7 @@ 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,
@ -5098,7 +5152,7 @@ impl ImageSubresourceRange {
}
/// 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 => {

View File

@ -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(),

View File

@ -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

View File

@ -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());

View File

@ -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(

View File

@ -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());

View File

@ -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

View File

@ -109,6 +109,7 @@ impl Example {
format: config.format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
label: None,
view_formats: &[],
};
device

View File

@ -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());

View File

@ -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,
);

View File

@ -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,
});

View File

@ -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
});

View File

@ -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 {

View File

@ -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::<A>(
hal_texture,
device.id,
&desc.map_label(|l| l.map(Borrowed)),
(),
)
};
let (id, error) =
unsafe { global.create_texture_from_hal::<A>(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 {

View File

@ -1583,6 +1583,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::<js_sys::Array>();
mapped_desc.view_formats(&mapped_view_formats);
(create_identified(device.0.create_texture(&mapped_desc)), ())
}

View File

@ -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<Label<'a>>;
pub type TextureDescriptor<'a> = wgt::TextureDescriptor<Label<'a>, &'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()
},
}
@ -4095,6 +4097,7 @@ impl Surface {
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
view_formats: &[],
};
texture_id

View File

@ -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

View File

@ -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
}

View File

@ -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];

View File

@ -44,6 +44,7 @@ fn bad_texture() {
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::all(),
view_formats: &[],
})
});

View File

@ -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;

View File

@ -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());

View File

@ -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<u8> = 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]
)
}
}
}
}

View File

@ -0,0 +1,12 @@
@vertex
fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4<f32> {
let uv: vec2<f32> = vec2<f32>(f32((vertexIndex << 1u) & 2u), f32(vertexIndex & 2u));
return vec4<f32>(uv * 2.0 - 1.0, 0.0, 1.0);
}
@group(0) @binding(0) var tex: texture_2d<f32>;
@fragment
fn fs_main(@builtin(position) coord: vec4<f32>) -> @location(0) vec4<f32> {
return textureLoad(tex, vec2<i32>(coord.xy), 0);
}

View File

@ -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;

View File

@ -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, || {

View File

@ -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],
)

View File

@ -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

View File

@ -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.