mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
This allows `wgpu` to not unconditionally depend on `std::path::Path`. It’s also, in my opinion, more user-friendly, because the feature which most users will not use (and is not currently functional) is now a defaultable struct field instead of a required parameter. The disadvantage is that `wgpu-types` now has to know about tracing.
829 lines
30 KiB
Rust
829 lines
30 KiB
Rust
use std::sync::atomic::AtomicBool;
|
|
|
|
use wgpu_test::{
|
|
fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext,
|
|
};
|
|
|
|
#[gpu_test]
|
|
static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default().expect_fail(FailureCase::always()))
|
|
.run_async(|ctx| async move {
|
|
// Create a bind group using a layout from another device. This should be a validation
|
|
// error but currently crashes.
|
|
let (device2, _) =
|
|
pollster::block_on(ctx.adapter.request_device(&Default::default())).unwrap();
|
|
|
|
{
|
|
let bind_group_layout =
|
|
device2.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
label: None,
|
|
entries: &[],
|
|
});
|
|
|
|
let _bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
label: None,
|
|
layout: &bind_group_layout,
|
|
entries: &[],
|
|
});
|
|
}
|
|
|
|
ctx.async_poll(wgpu::PollType::Poll).await.unwrap();
|
|
});
|
|
|
|
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
|
|
#[gpu_test]
|
|
static DEVICE_LIFETIME_CHECK: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default())
|
|
.run_sync(|ctx| {
|
|
ctx.instance.poll_all(false);
|
|
|
|
let pre_report = ctx.instance.generate_report().unwrap();
|
|
|
|
let TestingContext {
|
|
instance,
|
|
device,
|
|
queue,
|
|
..
|
|
} = ctx;
|
|
|
|
drop(queue);
|
|
drop(device);
|
|
|
|
let post_report = instance.generate_report().unwrap();
|
|
|
|
assert_ne!(
|
|
pre_report, post_report,
|
|
"Queue and Device has not been dropped as expected"
|
|
);
|
|
});
|
|
|
|
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
|
|
#[gpu_test]
|
|
static MULTIPLE_DEVICES: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default())
|
|
.run_sync(|ctx| {
|
|
use pollster::FutureExt as _;
|
|
ctx.adapter
|
|
.request_device(&wgpu::DeviceDescriptor::default())
|
|
.block_on()
|
|
.expect("failed to create device");
|
|
ctx.adapter
|
|
.request_device(&wgpu::DeviceDescriptor::default())
|
|
.block_on()
|
|
.expect("failed to create device");
|
|
});
|
|
|
|
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
|
|
#[gpu_test]
|
|
static REQUEST_DEVICE_ERROR_MESSAGE_NATIVE: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters({
|
|
let default = TestParameters::default();
|
|
|
|
// On CI, we have the vulkan SDK installed and this test initializes the Vulkan backend when all
|
|
// normal tests do not, so we get these weird error messages. This only actually gets hooked up
|
|
// on Windows, because that's the only platform where the actual runtime gets hooked up and there
|
|
// are no drivers.
|
|
if std::env::var("WGPU_CI").is_ok() && cfg!(windows) {
|
|
default.expect_fail(
|
|
FailureCase::always()
|
|
.validation_error("Registry lookup failed to get ICD manifest files. Possibly missing Vulkan driver?")
|
|
.validation_error("vkCreateInstance: Found no drivers!")
|
|
)
|
|
} else {
|
|
default
|
|
}
|
|
})
|
|
.run_async(|_ctx| request_device_error_message());
|
|
|
|
/// Check that `RequestDeviceError`s produced have some diagnostic information.
|
|
///
|
|
/// Note: this is a wasm *and* native test. On wasm it is run directly; on native, indirectly
|
|
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
|
|
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, false).await;
|
|
|
|
let device_error = adapter
|
|
.request_device(&wgpu::DeviceDescriptor {
|
|
// Force a failure by requesting absurd limits.
|
|
required_features: wgpu::Features::all(),
|
|
required_limits: wgpu::Limits {
|
|
max_texture_dimension_1d: u32::MAX,
|
|
max_texture_dimension_2d: u32::MAX,
|
|
max_texture_dimension_3d: u32::MAX,
|
|
max_bind_groups: u32::MAX,
|
|
max_push_constant_size: u32::MAX,
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.await
|
|
.unwrap_err();
|
|
|
|
let device_error = device_error.to_string();
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(all(target_arch = "wasm32", not(feature = "webgl")))] {
|
|
// On WebGPU, so the error we get will be from the browser WebGPU API.
|
|
// Per the WebGPU specification this should be a `TypeError` when features are not
|
|
// available, <https://gpuweb.github.io/gpuweb/#dom-gpuadapter-requestdevice>,
|
|
// and the stringification it goes through for Rust should put that in the message.
|
|
let expected = "TypeError";
|
|
} else {
|
|
// This message appears whenever wgpu-core is used as the implementation.
|
|
let expected = "Unsupported features were requested: Features {";
|
|
}
|
|
}
|
|
assert!(device_error.contains(expected), "{device_error}");
|
|
}
|
|
|
|
// This is a test of device behavior after device.destroy. Specifically, all operations
|
|
// should trigger errors since the device is lost.
|
|
#[gpu_test]
|
|
static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default().features(wgpu::Features::CLEAR_TEXTURE))
|
|
.run_sync(|ctx| {
|
|
// Create some resources on the device that we will attempt to use *after* losing
|
|
// the device.
|
|
|
|
// Create some 512 x 512 2D textures.
|
|
let texture_extent = wgpu::Extent3d {
|
|
width: 512,
|
|
height: 512,
|
|
depth_or_array_layers: 1,
|
|
};
|
|
let texture_for_view = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
|
label: None,
|
|
size: texture_extent,
|
|
mip_level_count: 2,
|
|
sample_count: 1,
|
|
dimension: wgpu::TextureDimension::D2,
|
|
format: wgpu::TextureFormat::Rg8Uint,
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
view_formats: &[],
|
|
});
|
|
let target_view = texture_for_view.create_view(&wgpu::TextureViewDescriptor::default());
|
|
|
|
let texture_for_read = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
|
label: None,
|
|
size: texture_extent,
|
|
mip_level_count: 2,
|
|
sample_count: 1,
|
|
dimension: wgpu::TextureDimension::D2,
|
|
format: wgpu::TextureFormat::Rg8Uint,
|
|
usage: wgpu::TextureUsages::COPY_SRC,
|
|
view_formats: &[],
|
|
});
|
|
|
|
let texture_for_write = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
|
label: None,
|
|
size: texture_extent,
|
|
mip_level_count: 2,
|
|
sample_count: 1,
|
|
dimension: wgpu::TextureDimension::D2,
|
|
format: wgpu::TextureFormat::Rg8Uint,
|
|
usage: wgpu::TextureUsages::COPY_DST,
|
|
view_formats: &[],
|
|
});
|
|
|
|
// Create some buffers.
|
|
let buffer_source = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: None,
|
|
size: 256,
|
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
mapped_at_creation: false,
|
|
});
|
|
let buffer_dest = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: None,
|
|
size: 256,
|
|
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
|
mapped_at_creation: false,
|
|
});
|
|
let buffer_for_map = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: None,
|
|
size: 256,
|
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
mapped_at_creation: false,
|
|
});
|
|
let buffer_for_unmap = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: None,
|
|
size: 256,
|
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
mapped_at_creation: true,
|
|
});
|
|
|
|
// Create a bind group layout.
|
|
let bind_group_layout =
|
|
ctx.device
|
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
label: None,
|
|
entries: &[],
|
|
});
|
|
|
|
// Create a shader module.
|
|
let shader_module = ctx
|
|
.device
|
|
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
label: None,
|
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("")),
|
|
});
|
|
|
|
// Create some command encoders.
|
|
let mut encoder_for_clear = ctx
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
|
|
let mut encoder_for_compute_pass = ctx
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
|
|
let mut encoder_for_render_pass = ctx
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
|
|
let mut encoder_for_buffer_buffer_copy = ctx
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
|
|
let mut encoder_for_buffer_texture_copy = ctx
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
|
|
let mut encoder_for_texture_buffer_copy = ctx
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
|
|
let mut encoder_for_texture_texture_copy = ctx
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
|
|
// Destroy the device. This will cause all other requests to return some variation of
|
|
// a device invalid error.
|
|
ctx.device.destroy();
|
|
|
|
// TODO: verify the following operations will return an invalid device error:
|
|
// * Run a compute or render pass
|
|
// * Finish a render bundle encoder
|
|
// * Create a texture from HAL
|
|
// * Create a buffer from HAL
|
|
// * Create a sampler
|
|
// * Validate a surface configuration
|
|
// * Start or stop capture
|
|
// * Get or set buffer sub data
|
|
|
|
// TODO: figure out how to structure a test around these operations which panic when
|
|
// the device is invalid:
|
|
// * device.features()
|
|
// * device.limits()
|
|
// * device.downlevel_properties()
|
|
// * device.create_query_set()
|
|
|
|
// TODO: change these fail calls to check for the specific errors which indicate that
|
|
// the device is not valid.
|
|
|
|
// Creating a command encoder should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx
|
|
.device
|
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a buffer should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: None,
|
|
size: 256,
|
|
usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
|
|
mapped_at_creation: false,
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a texture should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
|
label: None,
|
|
size: wgpu::Extent3d {
|
|
width: 512,
|
|
height: 512,
|
|
depth_or_array_layers: 1,
|
|
},
|
|
mip_level_count: 2,
|
|
sample_count: 1,
|
|
dimension: wgpu::TextureDimension::D2,
|
|
format: wgpu::TextureFormat::Rg8Uint,
|
|
usage: wgpu::TextureUsages::COPY_SRC,
|
|
view_formats: &[],
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Texture clear should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
encoder_for_clear.clear_texture(
|
|
&texture_for_write,
|
|
&wgpu::ImageSubresourceRange {
|
|
aspect: wgpu::TextureAspect::All,
|
|
base_mip_level: 0,
|
|
mip_level_count: None,
|
|
base_array_layer: 0,
|
|
array_layer_count: None,
|
|
},
|
|
);
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a compute pass should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
encoder_for_compute_pass.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
label: None,
|
|
timestamp_writes: None,
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a render pass should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
encoder_for_render_pass.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
label: None,
|
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
ops: wgpu::Operations::default(),
|
|
resolve_target: None,
|
|
view: &target_view,
|
|
})],
|
|
depth_stencil_attachment: None,
|
|
timestamp_writes: None,
|
|
occlusion_query_set: None,
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Copying a buffer to a buffer should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
encoder_for_buffer_buffer_copy.copy_buffer_to_buffer(
|
|
&buffer_source,
|
|
0,
|
|
&buffer_dest,
|
|
0,
|
|
256,
|
|
);
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Copying a buffer to a texture should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
encoder_for_buffer_texture_copy.copy_buffer_to_texture(
|
|
wgpu::TexelCopyBufferInfo {
|
|
buffer: &buffer_source,
|
|
layout: wgpu::TexelCopyBufferLayout {
|
|
offset: 0,
|
|
bytes_per_row: Some(4),
|
|
rows_per_image: None,
|
|
},
|
|
},
|
|
texture_for_write.as_image_copy(),
|
|
texture_extent,
|
|
);
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Copying a texture to a buffer should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
encoder_for_texture_buffer_copy.copy_texture_to_buffer(
|
|
texture_for_read.as_image_copy(),
|
|
wgpu::TexelCopyBufferInfo {
|
|
buffer: &buffer_source,
|
|
layout: wgpu::TexelCopyBufferLayout {
|
|
offset: 0,
|
|
bytes_per_row: Some(4),
|
|
rows_per_image: None,
|
|
},
|
|
},
|
|
texture_extent,
|
|
);
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Copying a texture to a texture should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
encoder_for_texture_texture_copy.copy_texture_to_texture(
|
|
texture_for_read.as_image_copy(),
|
|
texture_for_write.as_image_copy(),
|
|
texture_extent,
|
|
);
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a bind group layout should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx
|
|
.device
|
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
label: None,
|
|
entries: &[],
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a bind group should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
label: None,
|
|
layout: &bind_group_layout,
|
|
entries: &[wgpu::BindGroupEntry {
|
|
binding: 0,
|
|
resource: wgpu::BindingResource::Buffer(
|
|
buffer_source.as_entire_buffer_binding(),
|
|
),
|
|
}],
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a pipeline layout should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx
|
|
.device
|
|
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
label: None,
|
|
bind_group_layouts: &[],
|
|
push_constant_ranges: &[],
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a shader module should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx
|
|
.device
|
|
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
label: None,
|
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed("")),
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a shader module spirv should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| unsafe {
|
|
let _ = ctx
|
|
.device
|
|
.create_shader_module_spirv(&wgpu::ShaderModuleDescriptorSpirV {
|
|
label: None,
|
|
source: std::borrow::Cow::Borrowed(&[]),
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a render pipeline should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx
|
|
.device
|
|
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
label: None,
|
|
layout: None,
|
|
vertex: wgpu::VertexState {
|
|
module: &shader_module,
|
|
entry_point: Some(""),
|
|
compilation_options: Default::default(),
|
|
buffers: &[],
|
|
},
|
|
primitive: wgpu::PrimitiveState::default(),
|
|
depth_stencil: None,
|
|
multisample: wgpu::MultisampleState::default(),
|
|
fragment: None,
|
|
multiview: None,
|
|
cache: None,
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a compute pipeline should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx
|
|
.device
|
|
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
|
label: None,
|
|
layout: None,
|
|
module: &shader_module,
|
|
entry_point: None,
|
|
compilation_options: Default::default(),
|
|
cache: None,
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Creating a compute pipeline should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
let _ = ctx
|
|
.device
|
|
.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
|
label: None,
|
|
layout: None,
|
|
module: &shader_module,
|
|
entry_point: None,
|
|
compilation_options: Default::default(),
|
|
cache: None,
|
|
});
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Buffer map should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
buffer_for_map
|
|
.slice(..)
|
|
.map_async(wgpu::MapMode::Write, |_| ());
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
|
|
// Buffer unmap should fail.
|
|
fail(
|
|
&ctx.device,
|
|
|| {
|
|
buffer_for_unmap.unmap();
|
|
},
|
|
Some("device with '' label is invalid"),
|
|
);
|
|
});
|
|
|
|
#[gpu_test]
|
|
static DEVICE_DESTROY_THEN_LOST: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default())
|
|
.run_async(|ctx| async move {
|
|
// This test checks that when device.destroy is called, the provided
|
|
// DeviceLostClosure is called with reason DeviceLostReason::Destroyed.
|
|
static WAS_CALLED: AtomicBool = AtomicBool::new(false);
|
|
|
|
// Set a LoseDeviceCallback on the device.
|
|
let callback = Box::new(|reason, _m| {
|
|
WAS_CALLED.store(true, std::sync::atomic::Ordering::SeqCst);
|
|
assert!(
|
|
matches!(reason, wgpu::DeviceLostReason::Destroyed),
|
|
"Device lost info reason should match DeviceLostReason::Destroyed."
|
|
);
|
|
});
|
|
ctx.device.set_device_lost_callback(callback);
|
|
|
|
// Destroy the device.
|
|
ctx.device.destroy();
|
|
|
|
// Make sure the device queues are empty, which ensures that the closure
|
|
// has been called.
|
|
assert!(ctx
|
|
.async_poll(wgpu::PollType::wait())
|
|
.await
|
|
.unwrap()
|
|
.is_queue_empty());
|
|
|
|
assert!(
|
|
WAS_CALLED.load(std::sync::atomic::Ordering::SeqCst),
|
|
"Device lost callback should have been called."
|
|
);
|
|
});
|
|
|
|
#[gpu_test]
|
|
static DIFFERENT_BGL_ORDER_BW_SHADER_AND_API: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default())
|
|
.run_sync(|ctx| {
|
|
// This test addresses a bug found in multiple backends where `wgpu_core` and `wgpu_hal`
|
|
// backends made different assumptions about the element order of vectors of bind group
|
|
// layout entries and bind group resource bindings.
|
|
//
|
|
// Said bug was exposed originally by:
|
|
//
|
|
// 1. Shader-declared bindings having a different order than resource bindings provided to
|
|
// `Device::create_bind_group`.
|
|
// 2. Having more of one type of resource in the bind group than another.
|
|
//
|
|
// …such that internals would accidentally attempt to use an out-of-bounds index (of one
|
|
// resource type) in the wrong list of a different resource type. Let's reproduce that
|
|
// here.
|
|
|
|
let trivial_shaders_with_some_reversed_bindings = concat!(
|
|
"@group(0) @binding(3) var myTexture2: texture_2d<f32>;\n",
|
|
"@group(0) @binding(2) var myTexture1: texture_2d<f32>;\n",
|
|
"@group(0) @binding(1) var mySampler: sampler;\n",
|
|
"\n",
|
|
"@fragment\n",
|
|
"fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4f {\n",
|
|
" return textureSample(myTexture1, mySampler, pos.xy) \n",
|
|
" + textureSample(myTexture2, mySampler, pos.xy);\n",
|
|
"}\n",
|
|
"\n",
|
|
"@vertex\n",
|
|
"fn vs_main() -> @builtin(position) vec4<f32> {\n",
|
|
" return vec4<f32>(0.0, 0.0, 0.0, 1.0);\n",
|
|
"}\n",
|
|
);
|
|
|
|
let trivial_shaders_with_some_reversed_bindings =
|
|
ctx.device
|
|
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
|
label: None,
|
|
source: wgpu::ShaderSource::Wgsl(
|
|
trivial_shaders_with_some_reversed_bindings.into(),
|
|
),
|
|
});
|
|
|
|
let my_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
|
label: None,
|
|
size: wgpu::Extent3d {
|
|
width: 1024,
|
|
height: 512,
|
|
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 | wgpu::TextureUsages::TEXTURE_BINDING,
|
|
view_formats: &[],
|
|
});
|
|
|
|
let my_texture_view = my_texture.create_view(&wgpu::TextureViewDescriptor {
|
|
label: None,
|
|
format: None,
|
|
dimension: None,
|
|
usage: None,
|
|
aspect: wgpu::TextureAspect::All,
|
|
base_mip_level: 0,
|
|
mip_level_count: None,
|
|
base_array_layer: 0,
|
|
array_layer_count: None,
|
|
});
|
|
|
|
let my_sampler = ctx
|
|
.device
|
|
.create_sampler(&wgpu::SamplerDescriptor::default());
|
|
|
|
let render_pipeline = ctx
|
|
.device
|
|
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
fragment: Some(wgpu::FragmentState {
|
|
module: &trivial_shaders_with_some_reversed_bindings,
|
|
entry_point: Some("fs_main"),
|
|
compilation_options: Default::default(),
|
|
targets: &[Some(wgpu::ColorTargetState {
|
|
format: wgpu::TextureFormat::Bgra8Unorm,
|
|
blend: None,
|
|
write_mask: wgpu::ColorWrites::ALL,
|
|
})],
|
|
}),
|
|
layout: None,
|
|
|
|
// Other fields below aren't interesting for this text.
|
|
label: None,
|
|
vertex: wgpu::VertexState {
|
|
module: &trivial_shaders_with_some_reversed_bindings,
|
|
entry_point: Some("vs_main"),
|
|
compilation_options: Default::default(),
|
|
buffers: &[],
|
|
},
|
|
primitive: wgpu::PrimitiveState::default(),
|
|
depth_stencil: None,
|
|
multisample: wgpu::MultisampleState::default(),
|
|
multiview: None,
|
|
cache: None,
|
|
});
|
|
|
|
// fail(&ctx.device, || {
|
|
// }, "");
|
|
let _ = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
label: None,
|
|
layout: &render_pipeline.get_bind_group_layout(0),
|
|
entries: &[
|
|
wgpu::BindGroupEntry {
|
|
binding: 1,
|
|
resource: wgpu::BindingResource::Sampler(&my_sampler),
|
|
},
|
|
wgpu::BindGroupEntry {
|
|
binding: 2,
|
|
resource: wgpu::BindingResource::TextureView(&my_texture_view),
|
|
},
|
|
wgpu::BindGroupEntry {
|
|
binding: 3,
|
|
resource: wgpu::BindingResource::TextureView(&my_texture_view),
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
#[gpu_test]
|
|
static DEVICE_DESTROY_THEN_BUFFER_CLEANUP: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default())
|
|
.run_sync(|ctx| {
|
|
// When a device is destroyed, its resources should be released,
|
|
// without causing a deadlock.
|
|
|
|
// Create a buffer to be left around until the device is destroyed.
|
|
let _buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
|
|
label: None,
|
|
size: 256,
|
|
usage: wgpu::BufferUsages::MAP_READ,
|
|
mapped_at_creation: false,
|
|
});
|
|
|
|
// Create a texture to be left around until the device is destroyed.
|
|
let texture_extent = wgpu::Extent3d {
|
|
width: 512,
|
|
height: 512,
|
|
depth_or_array_layers: 1,
|
|
};
|
|
let _texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
|
|
label: None,
|
|
size: texture_extent,
|
|
mip_level_count: 2,
|
|
sample_count: 1,
|
|
dimension: wgpu::TextureDimension::D2,
|
|
format: wgpu::TextureFormat::Rg8Uint,
|
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
view_formats: &[],
|
|
});
|
|
|
|
// Destroy the device.
|
|
ctx.device.destroy();
|
|
|
|
// Poll the device, which should try to clean up its resources.
|
|
ctx.instance.poll_all(true);
|
|
});
|
|
|
|
#[gpu_test]
|
|
static DEVICE_AND_QUEUE_HAVE_DIFFERENT_IDS: GpuTestConfiguration = GpuTestConfiguration::new()
|
|
.parameters(TestParameters::default())
|
|
.run_async(|ctx| async move {
|
|
let TestingContext {
|
|
adapter,
|
|
device_features,
|
|
device_limits,
|
|
device,
|
|
queue,
|
|
..
|
|
} = ctx;
|
|
|
|
drop(device);
|
|
|
|
let (device2, queue2) =
|
|
wgpu_test::initialize_device(&adapter, device_features, device_limits).await;
|
|
|
|
drop(queue);
|
|
drop(device2);
|
|
drop(queue2); // this would previously panic since we would try to use the Device ID to drop the Queue
|
|
});
|