Skip to content
Merged
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 @@ -132,6 +132,7 @@ By @beholdnec in [#8505](https://github.com/gfx-rs/wgpu/pull/8505).
```
By @AdrianEddy in [#9496](https://github.com/gfx-rs/wgpu/pull/9496).
- Extend `copy_texture_to_texture` to allow copying a single plane of a multi-planar source (NV12, P010) into a single-plane destination of the matching format (e.g. NV12 `Plane0` → `R8Unorm`, NV12 `Plane1` → `Rg8Unorm`). `copy_size` is interpreted in plane texels, not luma texels. By @AdrianEddy in [#9551](https://github.com/gfx-rs/wgpu/pull/9551).
- Added `InstanceFlags::STRICT_WEBGPU_COMPLIANCE` flag, which restricts the available feature set to the one defined by the WebGPU specification. By @teoxoy in [#9586](https://github.com/gfx-rs/wgpu/pull/9586).

#### Metal

Expand Down
36 changes: 25 additions & 11 deletions tests/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,12 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) ->
}

/// Initialize a wgpu adapter, using the given adapter report to match the adapter.
///
/// Returns `None` if the adapter from the report is not returned by `enumerate_adapters` due to `InstanceFlags::STRICT_WEBGPU_COMPLIANCE` being set.
pub async fn initialize_adapter(
adapter_report: Option<&AdapterReport>,
params: &TestParameters,
) -> (Instance, Adapter, Option<SurfaceGuard>) {
) -> Option<(Instance, Adapter, Option<SurfaceGuard>)> {
let backends = adapter_report
.map(|report| Backends::from(report.info.backend))
.unwrap_or_default();
Expand Down Expand Up @@ -157,24 +159,36 @@ pub async fn initialize_adapter(
} else {
true
});
let Some(adapter) = adapter else {
panic!(
"Could not find adapter with info {:#?} in {:#?}",
adapter_report.map(|r| &r.info),
instance.enumerate_adapters(backends).await.into_iter().map(|a| a.get_info()).collect::<Vec<_>>(),
);
};
} else {
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
compatible_surface: surface.as_ref(),
..Default::default()
}).await.unwrap();
}).await.ok();
}
}

log::info!("Testing using adapter: {:#?}", adapter.get_info());
let Some(adapter) = adapter else {
if params
.required_instance_flags
.contains(wgpu::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
{
return None;
} else {
panic!(
"Could not find adapter with info {:#?} in {:#?}",
adapter_report.map(|r| &r.info),
instance
.enumerate_adapters(backends)
.await
.into_iter()
.map(|a| a.get_info())
.collect::<Vec<_>>(),
);
}
};

(instance, adapter, surface_guard)
log::info!("Testing using adapter: {:#?}", adapter.get_info());
Some((instance, adapter, surface_guard))
}

/// Initialize a wgpu device from a given adapter.
Expand Down
7 changes: 5 additions & 2 deletions tests/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ pub async fn execute_test(

let _test_guard = isolation::OneTestPerProcessGuard::new();

let (instance, adapter, _surface_guard) =
initialize_adapter(adapter_report, &config.params).await;
let Some((instance, adapter, _surface_guard)) =
initialize_adapter(adapter_report, &config.params).await
else {
return;
};

let adapter_info = adapter.get_info();
let adapter_downlevel_capabilities = adapter.get_downlevel_capabilities();
Expand Down
20 changes: 20 additions & 0 deletions tests/tests/wgpu-gpu/adapter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};

pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
vec.push(STRICT_WEBGPU_COMPLIANCE_ADAPTER);
}

#[gpu_test]
static STRICT_WEBGPU_COMPLIANCE_ADAPTER: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.instance_flags(wgpu::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
.enable_noop(),
)
.run_sync(|ctx| {
assert!(ctx
.adapter
.get_downlevel_capabilities()
.is_webgpu_compliant());
assert!(wgpu::Limits::defaults().check_limits(&ctx.adapter.limits()));
});
4 changes: 3 additions & 1 deletion tests/tests/wgpu-gpu/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ async fn request_device_error_message() {
// Not using initialize_test() because that doesn't let us catch the error
// nor .await anything
let (_instance, adapter, _surface_guard) =
wgpu_test::initialize_adapter(None, &TestParameters::default()).await;
wgpu_test::initialize_adapter(None, &TestParameters::default())
.await
.unwrap();

let device_error = adapter
.request_device(&wgpu::DeviceDescriptor {
Expand Down
2 changes: 2 additions & 0 deletions tests/tests/wgpu-gpu/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod regression {
pub mod issue_9115;
}

mod adapter;
mod bgra8unorm_storage;
mod bind_group_layout_dedup;
mod bind_groups;
Expand Down Expand Up @@ -86,6 +87,7 @@ mod zero_init;
fn all_tests() -> Vec<wgpu_test::GpuTestInitializer> {
let mut tests = Vec::new();

adapter::all_tests(&mut tests);
bgra8unorm_storage::all_tests(&mut tests);
bind_group_layout_dedup::all_tests(&mut tests);
bind_groups::all_tests(&mut tests);
Expand Down
149 changes: 144 additions & 5 deletions tests/tests/wgpu-gpu/pass_ops/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
use wgpu_test::{
gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, TestParameters,
TestingContext,
fail, gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer,
TestParameters, TestingContext,
};

pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
vec.push(DONT_CARE);
vec.extend([
DONT_CARE,
DONT_CARE_COLOR_STRICT_WEBGPU_COMPLIANCE,
DONT_CARE_DEPTH_STRICT_WEBGPU_COMPLIANCE,
DONT_CARE_STENCIL_STRICT_WEBGPU_COMPLIANCE,
]);
}

#[gpu_test]
static DONT_CARE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_async(run_test);
.run_async(dont_care);

async fn run_test(ctx: TestingContext) {
async fn dont_care(ctx: TestingContext) {
let shader_src = "
const triangles = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));

Expand Down Expand Up @@ -109,3 +114,137 @@ async fn run_test(ctx: TestingContext) {
.assert_buffer_contents(&ctx, &[127, 127, 127, 127])
.await;
}

#[gpu_test]
static DONT_CARE_COLOR_STRICT_WEBGPU_COMPLIANCE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.instance_flags(wgpu::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
.enable_noop(),
)
.run_sync(|ctx| {
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::DontCare(unsafe { wgpu::LoadOpDontCare::enabled() }),
store: wgpu::StoreOp::Store,
},
})],
..Default::default()
});
fail(
&ctx.device,
|| encoder.finish(),
Some("STRICT_WEBGPU_COMPLIANCE"),
);
});

#[gpu_test]
static DONT_CARE_DEPTH_STRICT_WEBGPU_COMPLIANCE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.instance_flags(wgpu::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
.enable_noop(),
)
.run_sync(|ctx| {
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Depth16Unorm,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::DontCare(unsafe { wgpu::LoadOpDontCare::enabled() }),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
..Default::default()
});
fail(
&ctx.device,
|| encoder.finish(),
Some("STRICT_WEBGPU_COMPLIANCE"),
);
});

#[gpu_test]
static DONT_CARE_STENCIL_STRICT_WEBGPU_COMPLIANCE: GpuTestConfiguration =
GpuTestConfiguration::new()
.parameters(
TestParameters::default()
.instance_flags(wgpu::InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
.enable_noop(),
)
.run_sync(|ctx| {
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Stencil8,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
let view = tex.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = ctx
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &view,
depth_ops: None,
stencil_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::DontCare(unsafe { wgpu::LoadOpDontCare::enabled() }),
store: wgpu::StoreOp::Store,
}),
}),
..Default::default()
});
fail(
&ctx.device,
|| encoder.finish(),
Some("STRICT_WEBGPU_COMPLIANCE"),
);
});
32 changes: 26 additions & 6 deletions wgpu-core/src/command/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use arrayvec::ArrayVec;
use thiserror::Error;
use wgt::{
error::{ErrorType, WebGpuError},
BufferAddress, BufferSize, BufferUsages, Color, DynamicOffset, IndexFormat, TextureSelector,
TextureUsages, TextureViewDimension, VertexStepMode,
BufferAddress, BufferSize, BufferUsages, Color, DynamicOffset, IndexFormat, InstanceFlags,
TextureSelector, TextureUsages, TextureViewDimension, VertexStepMode,
};

use crate::{
Expand Down Expand Up @@ -109,6 +109,7 @@ pub struct PassChannel<V> {
impl<V: Copy + Default> PassChannel<Option<V>> {
fn resolve(
&self,
instance_flags: InstanceFlags,
handle_clear: impl Fn(Option<V>) -> Result<V, AttachmentError>,
) -> Result<ResolvedPassChannel<V>, AttachmentError> {
if self.read_only {
Expand All @@ -123,7 +124,12 @@ impl<V: Copy + Default> PassChannel<Option<V>> {
Ok(ResolvedPassChannel::Operational(wgt::Operations {
load: match self.load_op.ok_or(AttachmentError::NoLoad)? {
LoadOp::Clear(clear_value) => LoadOp::Clear(handle_clear(clear_value)?),
LoadOp::DontCare(token) => LoadOp::DontCare(token),
LoadOp::DontCare(token) => {
if instance_flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
return Err(AttachmentError::LoadOpDontCareUnderStrictWebgpuCompliance);
}
LoadOp::DontCare(token)
}
LoadOp::Load => LoadOp::Load,
},
store: self.store_op.ok_or(AttachmentError::NoStore)?,
Expand Down Expand Up @@ -803,6 +809,8 @@ pub enum ColorAttachmentError {
},
#[error("Color attachment's usage contains {0:?}. This can only be used with StoreOp::{1:?}, but StoreOp::{2:?} was provided")]
InvalidUsageForStoreOp(TextureUsages, StoreOp, StoreOp),
#[error("Color attachment's load op is `LoadOp::DontCare` but `InstanceFlags::STRICT_WEBGPU_COMPLIANCE` is set")]
LoadOpDontCareUnderStrictWebgpuCompliance,
}

impl WebGpuError for ColorAttachmentError {
Expand Down Expand Up @@ -838,6 +846,8 @@ pub enum AttachmentError {
NoClearValue,
#[error("Clear value ({0}) must be between 0.0 and 1.0, inclusive")]
ClearValueOutOfRange(f32),
#[error("Load op is `DontCare` but `InstanceFlags::STRICT_WEBGPU_COMPLIANCE` is set")]
LoadOpDontCareUnderStrictWebgpuCompliance,
}

impl WebGpuError for AttachmentError {
Expand Down Expand Up @@ -1688,7 +1698,7 @@ impl RenderPassInfo {
raw: &mut dyn hal::DynCommandEncoder,
snatch_guard: &SnatchGuard,
scope: &mut UsageScope<'_>,
instance_flags: wgt::InstanceFlags,
instance_flags: InstanceFlags,
) -> Result<(), RenderPassErrorInner> {
profiling::scope!("RenderPassInfo::finish");
unsafe {
Expand Down Expand Up @@ -1811,6 +1821,16 @@ impl Global {
let view = texture_views.get(*view_id).get()?;
view.same_device(device)?;

if matches!(*load_op, LoadOp::DontCare(..))
&& device
.instance_flags
.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
{
return Err(RenderPassErrorInner::ColorAttachment(
ColorAttachmentError::LoadOpDontCareUnderStrictWebgpuCompliance,
));
}

if view.desc.usage.contains(TextureUsages::TRANSIENT)
&& *store_op != StoreOp::Discard
{
Expand Down Expand Up @@ -1862,7 +1882,7 @@ impl Global {
Some(ResolvedRenderPassDepthStencilAttachment {
view,
depth: if format.has_depth_aspect() {
depth_stencil_attachment.depth.resolve(|clear| if let Some(clear) = clear {
depth_stencil_attachment.depth.resolve(device.instance_flags, |clear| if let Some(clear) = clear {
// If this.depthLoadOp is "clear", this.depthClearValue must be provided and must be between 0.0 and 1.0, inclusive.
if !(0.0..=1.0).contains(&clear) {
Err(AttachmentError::ClearValueOutOfRange(clear))
Expand All @@ -1882,7 +1902,7 @@ impl Global {
ResolvedPassChannel::ReadOnly
},
stencil: if format.has_stencil_aspect() {
depth_stencil_attachment.stencil.resolve(|clear| {
depth_stencil_attachment.stencil.resolve(device.instance_flags, |clear| {
Ok(convert_stencil_value(clear.unwrap_or_default(), Some(format)))
})?
} else {
Expand Down
Loading
Loading