fix(wgpu): Raise validation error instead of panicking in get_bind_group_layout. (#6280)

This commit is contained in:
Ben Reeves 2024-09-20 02:32:01 -05:00 committed by GitHub
parent b54fb72d4a
commit 9f85f8aeea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 222 additions and 57 deletions

View File

@ -27,11 +27,12 @@ Top level categories:
Bottom level categories:
- Naga
- General
- DX12
- Vulkan
- Metal
- GLES
- GLES / OpenGL
- WebGPU
- Emscripten
- Hal
@ -82,6 +83,10 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
- Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181).
- Support for more atomic ops in the SPIR-V frontend. By @schell in [#5824](https://github.com/gfx-rs/wgpu/pull/5824).
#### General
- Add `VideoFrame` to `ExternalImageSource` enum. By @jprochazk in [#6170](https://github.com/gfx-rs/wgpu/pull/6170)
#### Vulkan
- Allow using [VK_GOOGLE_display_timing](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html) unsafely with the `VULKAN_GOOGLE_DISPLAY_TIMING` feature. By @DJMcNab in [#6149](https://github.com/gfx-rs/wgpu/pull/6149)
@ -89,7 +94,6 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
### Bug Fixes
- Fix incorrect hlsl image output type conversion. By @atlv24 in [#6123](https://github.com/gfx-rs/wgpu/pull/6123)
- Fix JS `TypeError` exception in `Instance::request_adapter` when browser doesn't support WebGPU but `wgpu` not compiled with `webgl` support. By @bgr360 in [#6197](https://github.com/gfx-rs/wgpu/pull/6197).
#### Naga
@ -107,13 +111,17 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216).
- Deduplicate bind group layouts that are created from pipelines with "auto" layouts. By @teoxoy [#6049](https://github.com/gfx-rs/wgpu/pull/6049)
- Fix crash when dropping the surface after the device. By @wumpf in [#6052](https://github.com/gfx-rs/wgpu/pull/6052)
- Fix error message that is thrown in create_render_pass to no longer say `compute_pass`. By @matthew-wong1 [#6041](https://github.com/gfx-rs/wgpu/pull/6041)
- Add `VideoFrame` to `ExternalImageSource` enum. By @jprochazk in [#6170](https://github.com/gfx-rs/wgpu/pull/6170)
- Document `wgpu_hal` bounds-checking promises, and adapt `wgpu_core`'s lazy initialization logic to the slightly weaker-than-expected guarantees. By @jimblandy in [#6201](https://github.com/gfx-rs/wgpu/pull/6201)
- Raise validation error instead of panicking in `{Render,Compute}Pipeline::get_bind_group_layout` on native / WebGL. By @bgr360 in [#6280](https://github.com/gfx-rs/wgpu/pull/6280).
#### GLES / OpenGL
- Fix GL debug message callbacks not being properly cleaned up (causing UB). By @Imberflur in [#6114](https://github.com/gfx-rs/wgpu/pull/6114)
#### WebGPU
- Fix JS `TypeError` exception in `Instance::request_adapter` when browser doesn't support WebGPU but `wgpu` not compiled with `webgl` support. By @bgr360 in [#6197](https://github.com/gfx-rs/wgpu/pull/6197).
#### Vulkan
- Vulkan debug labels assumed no interior nul byte. By @DJMcNab in [#6257](https://github.com/gfx-rs/wgpu/pull/6257)

View File

@ -1,44 +1,16 @@
use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters};
// Create an invalid shader and a compute pipeline that uses it
// with a default bindgroup layout, and then ask for that layout.
// Validation should fail, but wgpu should not panic.
#[gpu_test]
static PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(
TestParameters::default()
// https://github.com/gfx-rs/wgpu/issues/4167
.expect_fail(FailureCase::always().panic("Error reflecting bind group")),
)
.run_sync(|ctx| {
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
const INVALID_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {
label: Some("invalid shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("not valid wgsl")),
};
fail(
&ctx.device,
|| {
let module = ctx
.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl("not valid wgsl".into()),
});
let pipeline =
ctx.device
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("mandelbrot compute pipeline"),
layout: None,
module: &module,
entry_point: Some("doesn't exist"),
compilation_options: Default::default(),
cache: None,
});
pipeline.get_bind_group_layout(0);
},
None,
);
});
const TRIVIAL_COMPUTE_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {
label: Some("trivial compute shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
"@compute @workgroup_size(1) fn main() {}",
)),
};
const TRIVIAL_VERTEX_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {
label: Some("trivial vertex shader"),
@ -47,6 +19,161 @@ const TRIVIAL_VERTEX_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderMod
)),
};
const TRIVIAL_FRAGMENT_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor {
label: Some("trivial fragment shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
"@fragment fn main() -> @location(0) vec4<f32> { return vec4<f32>(0); }",
)),
};
// Create an invalid shader and a compute pipeline that uses it
// with a default bindgroup layout, and then ask for that layout.
// Validation should fail, but wgpu should not panic.
#[gpu_test]
static COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration =
GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_sync(|ctx| {
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
fail(
&ctx.device,
|| {
let module = ctx.device.create_shader_module(INVALID_SHADER_DESC);
let pipeline =
ctx.device
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("compute pipeline"),
layout: None,
module: &module,
entry_point: Some("doesn't exist"),
compilation_options: Default::default(),
cache: None,
});
// https://github.com/gfx-rs/wgpu/issues/4167 this used to panic
pipeline.get_bind_group_layout(0);
},
Some("Shader 'invalid shader' parsing error"),
);
});
#[gpu_test]
static COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration =
GpuTestConfiguration::new()
.parameters(TestParameters::default().test_features_limits())
.run_sync(|ctx| {
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
fail(
&ctx.device,
|| {
let module = ctx.device.create_shader_module(TRIVIAL_COMPUTE_SHADER_DESC);
let pipeline =
ctx.device
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: Some("compute pipeline"),
layout: None,
module: &module,
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
pipeline.get_bind_group_layout(0);
},
Some("Invalid group index 0"),
);
});
#[gpu_test]
static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration =
GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_sync(|ctx| {
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
fail(
&ctx.device,
|| {
let module = ctx.device.create_shader_module(INVALID_SHADER_DESC);
let pipeline =
ctx.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("render pipeline"),
layout: None,
vertex: wgpu::VertexState {
module: &module,
entry_point: Some("doesn't exist"),
compilation_options: Default::default(),
buffers: &[],
},
primitive: Default::default(),
depth_stencil: None,
multisample: Default::default(),
fragment: None,
multiview: None,
cache: None,
});
pipeline.get_bind_group_layout(0);
},
Some("Shader 'invalid shader' parsing error"),
);
});
#[gpu_test]
static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration =
GpuTestConfiguration::new()
.parameters(TestParameters::default().test_features_limits())
.run_sync(|ctx| {
ctx.device.push_error_scope(wgpu::ErrorFilter::Validation);
fail(
&ctx.device,
|| {
let vs_module = ctx.device.create_shader_module(TRIVIAL_VERTEX_SHADER_DESC);
let fs_module = ctx
.device
.create_shader_module(TRIVIAL_FRAGMENT_SHADER_DESC);
let pipeline =
ctx.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("render pipeline"),
layout: None,
vertex: wgpu::VertexState {
module: &vs_module,
entry_point: Some("main"),
compilation_options: Default::default(),
buffers: &[],
},
primitive: Default::default(),
depth_stencil: None,
multisample: Default::default(),
fragment: Some(wgpu::FragmentState {
module: &fs_module,
entry_point: Some("main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba8Unorm,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
multiview: None,
cache: None,
});
pipeline.get_bind_group_layout(0);
},
Some("Invalid group index 0"),
);
});
#[gpu_test]
static NO_TARGETLESS_RENDER: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())

View File

@ -24,6 +24,8 @@ impl ComputePipeline {
/// If this pipeline was created with a [default layout][ComputePipelineDescriptor::layout],
/// then bind groups created with the returned `BindGroupLayout` can only be used with this
/// pipeline.
///
/// This method will raise a validation error if there is no bind group layout at `index`.
pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout {
let context = Arc::clone(&self.context);
let data = self

View File

@ -31,6 +31,8 @@ impl RenderPipeline {
///
/// If this pipeline was created with a [default layout][RenderPipelineDescriptor::layout], then
/// bind groups created with the returned `BindGroupLayout` can only be used with this pipeline.
///
/// This method will raise a validation error if there is no bind group layout at `index`.
pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout {
let context = Arc::clone(&self.context);
let data = self

View File

@ -473,6 +473,18 @@ pub struct Queue {
error_sink: ErrorSink,
}
#[derive(Debug)]
pub struct ComputePipeline {
id: wgc::id::ComputePipelineId,
error_sink: ErrorSink,
}
#[derive(Debug)]
pub struct RenderPipeline {
id: wgc::id::RenderPipelineId,
error_sink: ErrorSink,
}
#[derive(Debug)]
pub struct ComputePass {
pass: wgc::command::ComputePass,
@ -505,8 +517,8 @@ impl crate::Context for ContextWgpuCore {
type TextureData = Texture;
type QuerySetData = wgc::id::QuerySetId;
type PipelineLayoutData = wgc::id::PipelineLayoutId;
type RenderPipelineData = wgc::id::RenderPipelineId;
type ComputePipelineData = wgc::id::ComputePipelineId;
type RenderPipelineData = RenderPipeline;
type ComputePipelineData = ComputePipeline;
type PipelineCacheData = wgc::id::PipelineCacheId;
type CommandEncoderData = CommandEncoder;
type ComputePassData = ComputePass;
@ -1097,7 +1109,10 @@ impl crate::Context for ContextWgpuCore {
"Device::create_render_pipeline",
);
}
id
RenderPipeline {
id,
error_sink: Arc::clone(&device_data.error_sink),
}
}
fn device_create_compute_pipeline(
&self,
@ -1139,7 +1154,10 @@ impl crate::Context for ContextWgpuCore {
"Device::create_compute_pipeline",
);
}
id
ComputePipeline {
id,
error_sink: Arc::clone(&device_data.error_sink),
}
}
unsafe fn device_create_pipeline_cache(
@ -1531,11 +1549,11 @@ impl crate::Context for ContextWgpuCore {
}
fn compute_pipeline_drop(&self, pipeline_data: &Self::ComputePipelineData) {
self.0.compute_pipeline_drop(*pipeline_data)
self.0.compute_pipeline_drop(pipeline_data.id)
}
fn render_pipeline_drop(&self, pipeline_data: &Self::RenderPipelineData) {
self.0.render_pipeline_drop(*pipeline_data)
self.0.render_pipeline_drop(pipeline_data.id)
}
fn pipeline_cache_drop(&self, cache_data: &Self::PipelineCacheData) {
@ -1549,9 +1567,13 @@ impl crate::Context for ContextWgpuCore {
) -> Self::BindGroupLayoutData {
let (id, error) =
self.0
.compute_pipeline_get_bind_group_layout(*pipeline_data, index, None);
.compute_pipeline_get_bind_group_layout(pipeline_data.id, index, None);
if let Some(err) = error {
panic!("Error reflecting bind group {index}: {err}");
self.handle_error_nolabel(
&pipeline_data.error_sink,
err,
"ComputePipeline::get_bind_group_layout",
)
}
id
}
@ -1561,11 +1583,15 @@ impl crate::Context for ContextWgpuCore {
pipeline_data: &Self::RenderPipelineData,
index: u32,
) -> Self::BindGroupLayoutData {
let (id, error) = self
.0
.render_pipeline_get_bind_group_layout(*pipeline_data, index, None);
let (id, error) =
self.0
.render_pipeline_get_bind_group_layout(pipeline_data.id, index, None);
if let Some(err) = error {
panic!("Error reflecting bind group {index}: {err}");
self.handle_error_nolabel(
&pipeline_data.error_sink,
err,
"RenderPipeline::get_bind_group_layout",
)
}
id
}
@ -2108,7 +2134,7 @@ impl crate::Context for ContextWgpuCore {
) {
if let Err(cause) = self
.0
.compute_pass_set_pipeline(&mut pass_data.pass, *pipeline_data)
.compute_pass_set_pipeline(&mut pass_data.pass, pipeline_data.id)
{
self.handle_error(
&pass_data.error_sink,
@ -2311,7 +2337,7 @@ impl crate::Context for ContextWgpuCore {
encoder_data: &mut Self::RenderBundleEncoderData,
pipeline_data: &Self::RenderPipelineData,
) {
wgpu_render_bundle_set_pipeline(encoder_data, *pipeline_data)
wgpu_render_bundle_set_pipeline(encoder_data, pipeline_data.id)
}
fn render_bundle_encoder_set_bind_group(
@ -2434,7 +2460,7 @@ impl crate::Context for ContextWgpuCore {
) {
if let Err(cause) = self
.0
.render_pass_set_pipeline(&mut pass_data.pass, *pipeline_data)
.render_pass_set_pipeline(&mut pass_data.pass, pipeline_data.id)
{
self.handle_error(
&pass_data.error_sink,