Convert Tests to Use Async Poll (#5053)

* Convert Tests to Use Async Poll

* Update examples/src/repeated_compute/mod.rs

Co-authored-by: Andreas Reich <r_andreas2@web.de>

---------

Co-authored-by: Andreas Reich <r_andreas2@web.de>
This commit is contained in:
Connor Fitzgerald 2024-01-13 12:34:51 -05:00 committed by GitHub
parent 5fd7d228ad
commit ad625f433f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 479 additions and 325 deletions

View File

@ -123,7 +123,7 @@ pub async fn op_webgpu_buffer_get_map_async(
{
let state = state.borrow();
let instance = state.borrow::<super::Instance>();
gfx_select!(device => instance.device_poll(device, wgpu_types::Maintain::Wait))
gfx_select!(device => instance.device_poll(device, wgpu_types::Maintain::wait()))
.unwrap();
}
tokio::time::sleep(Duration::from_millis(10)).await;

View File

@ -612,7 +612,9 @@ impl<E: Example + wgpu::WasmNotSendSync> From<ExampleTestParams<E>>
let dst_buffer_slice = dst_buffer.slice(..);
dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let bytes = dst_buffer_slice.get_mapped_range().to_vec();
wgpu_test::image::compare_image_output(

View File

@ -152,7 +152,7 @@ async fn execute_gpu_inner(
// Poll the device in a blocking manner so that our future resolves.
// In an actual application, `device.poll(...)` should
// be called in an event loop or on another thread.
device.poll(wgpu::Maintain::Wait);
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
// Awaits until `buffer_future` can be read from
if let Ok(Ok(())) = receiver.recv_async().await {

View File

@ -183,7 +183,7 @@ async fn get_data<T: bytemuck::Pod>(
let buffer_slice = staging_buffer.slice(..);
let (sender, receiver) = flume::bounded(1);
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
device.poll(wgpu::Maintain::Wait);
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
receiver.recv_async().await.unwrap().unwrap();
output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..]));
staging_buffer.unmap();

View File

@ -172,7 +172,7 @@ async fn get_data<T: bytemuck::Pod>(
let buffer_slice = staging_buffer.slice(..);
let (sender, receiver) = flume::bounded(1);
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
device.poll(wgpu::Maintain::Wait);
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
receiver.recv_async().await.unwrap().unwrap();
output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..]));
staging_buffer.unmap();

View File

@ -410,7 +410,7 @@ impl crate::framework::Example for Example {
.slice(..)
.map_async(wgpu::MapMode::Read, |_| ());
// Wait for device to be done rendering mipmaps
device.poll(wgpu::Maintain::Wait);
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
// This is guaranteed to be ready.
let timestamp_view = query_sets
.mapping_buffer

View File

@ -131,7 +131,7 @@ async fn run(_path: Option<String>) {
let buffer_slice = output_staging_buffer.slice(..);
let (sender, receiver) = flume::bounded(1);
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
device.poll(wgpu::Maintain::Wait);
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
receiver.recv_async().await.unwrap().unwrap();
log::info!("Output buffer mapped.");
{

View File

@ -108,8 +108,11 @@ async fn compute(local_buffer: &mut [u32], context: &WgpuContext) {
// In order for the mapping to be completed, one of three things must happen.
// One of those can be calling `Device::poll`. This isn't necessary on the web as devices
// are polled automatically but natively, we need to make sure this happens manually.
// `Maintain::Wait` will cause the thread to wait on native but not the web.
context.device.poll(wgpu::Maintain::Wait);
// `Maintain::Wait` will cause the thread to wait on native but not on WebGpu.
context
.device
.poll(wgpu::Maintain::wait())
.panic_on_timeout();
log::info!("Device polled.");
// Now we await the receiving and panic if anything went wrong because we're lazy.
receiver.recv_async().await.unwrap().unwrap();

View File

@ -143,7 +143,7 @@ async fn run(_path: Option<String>) {
let buffer_slice = output_staging_buffer.slice(..);
let (sender, receiver) = flume::bounded(1);
buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap());
device.poll(wgpu::Maintain::Wait);
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
receiver.recv_async().await.unwrap().unwrap();
log::info!("Output buffer mapped");
{

View File

@ -159,7 +159,7 @@ impl Queries {
self.destination_buffer
.slice(..)
.map_async(wgpu::MapMode::Read, |_| ());
device.poll(wgpu::Maintain::Wait);
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
let timestamps = {
let timestamp_view = self

View File

@ -114,7 +114,7 @@ fn main() {
}
gfx_select!(device => global.device_stop_capture(device));
gfx_select!(device => global.device_poll(device, wgt::Maintain::Wait)).unwrap();
gfx_select!(device => global.device_poll(device, wgt::Maintain::wait())).unwrap();
}
#[cfg(feature = "winit")]
{
@ -196,7 +196,7 @@ fn main() {
},
Event::LoopExiting => {
log::info!("Closing");
gfx_select!(device => global.device_poll(device, wgt::Maintain::Wait)).unwrap();
gfx_select!(device => global.device_poll(device, wgt::Maintain::wait())).unwrap();
}
_ => {}
}

View File

@ -121,7 +121,8 @@ impl Test<'_> {
}
println!("\t\t\tWaiting...");
wgc::gfx_select!(device_id => global.device_poll(device_id, wgt::Maintain::Wait)).unwrap();
wgc::gfx_select!(device_id => global.device_poll(device_id, wgt::Maintain::wait()))
.unwrap();
for expect in self.expectations {
println!("\t\t\tChecking {}", expect.name);

View File

@ -5,6 +5,8 @@ use std::{borrow::Cow, ffi::OsStr, path::Path};
use wgpu::util::{align_to, DeviceExt};
use wgpu::*;
use crate::TestingContext;
#[cfg(not(target_arch = "wasm32"))]
async fn read_png(path: impl AsRef<Path>, width: u32, height: u32) -> Option<Vec<u8>> {
let data = match std::fs::read(&path) {
@ -563,15 +565,15 @@ impl ReadbackBuffers {
copy_texture_to_buffer(device, encoder, texture, &self.buffer, &self.buffer_stencil);
}
fn retrieve_buffer(
async fn retrieve_buffer(
&self,
device: &Device,
ctx: &TestingContext,
buffer: &Buffer,
aspect: Option<TextureAspect>,
) -> Vec<u8> {
let buffer_slice = buffer.slice(..);
buffer_slice.map_async(MapMode::Read, |_| ());
device.poll(Maintain::Wait);
ctx.async_poll(Maintain::wait()).await.panic_on_timeout();
let (block_width, block_height) = self.texture_format.block_dimensions();
let expected_bytes_per_row = (self.texture_width / block_width)
* self.texture_format.block_copy_size(aspect).unwrap_or(4);
@ -600,26 +602,36 @@ impl ReadbackBuffers {
}
}
pub fn are_zero(&self, device: &Device) -> bool {
let is_zero = |device: &Device, buffer: &Buffer, aspect: Option<TextureAspect>| -> bool {
async fn is_zero(
&self,
ctx: &TestingContext,
buffer: &Buffer,
aspect: Option<TextureAspect>,
) -> bool {
let is_zero = self
.retrieve_buffer(device, buffer, aspect)
.retrieve_buffer(ctx, buffer, aspect)
.await
.iter()
.all(|b| *b == 0);
buffer.unmap();
is_zero
};
}
let buffer_zero = is_zero(device, &self.buffer, self.buffer_aspect());
pub async fn are_zero(&self, ctx: &TestingContext) -> bool {
let buffer_zero = self.is_zero(ctx, &self.buffer, self.buffer_aspect()).await;
let mut stencil_buffer_zero = true;
if let Some(buffer) = &self.buffer_stencil {
stencil_buffer_zero = is_zero(device, buffer, Some(TextureAspect::StencilOnly));
stencil_buffer_zero = self
.is_zero(ctx, buffer, Some(TextureAspect::StencilOnly))
.await;
};
buffer_zero && stencil_buffer_zero
}
pub fn assert_buffer_contents(&self, device: &Device, expected_data: &[u8]) {
let result_buffer = self.retrieve_buffer(device, &self.buffer, self.buffer_aspect());
pub async fn assert_buffer_contents(&self, ctx: &TestingContext, expected_data: &[u8]) {
let result_buffer = self
.retrieve_buffer(ctx, &self.buffer, self.buffer_aspect())
.await;
assert!(
result_buffer.len() >= expected_data.len(),
"Result buffer ({}) smaller than expected buffer ({})",

View File

@ -7,6 +7,7 @@ mod init;
mod isolation;
pub mod native;
mod params;
mod poll;
mod report;
mod run;

8
tests/src/poll.rs Normal file
View File

@ -0,0 +1,8 @@
use crate::TestingContext;
impl TestingContext {
/// Utility to allow future asynchronous polling.
pub async fn async_poll(&self, maintain: wgpu::Maintain) -> wgpu::MaintainResult {
self.device.poll(maintain)
}
}

View File

@ -1,4 +1,4 @@
use std::panic::AssertUnwindSafe;
use std::{panic::AssertUnwindSafe, sync::Arc};
use futures_lite::FutureExt;
use wgpu::{Adapter, Device, Instance, Queue};
@ -18,7 +18,7 @@ pub struct TestingContext {
pub adapter: Adapter,
pub adapter_info: wgpu::AdapterInfo,
pub adapter_downlevel_capabilities: wgpu::DownlevelCapabilities,
pub device: Device,
pub device: Arc<Device>,
pub device_features: wgpu::Features,
pub device_limits: wgpu::Limits,
pub queue: Queue,
@ -58,6 +58,9 @@ pub async fn execute_test(
return;
}
// Print the name of the test.
log::info!("TEST: {}", config.name);
let (device, queue) = pollster::block_on(initialize_device(
&adapter,
config.params.required_features,
@ -69,7 +72,7 @@ pub async fn execute_test(
adapter,
adapter_info,
adapter_downlevel_capabilities,
device,
device: Arc::new(device),
device_features: config.params.required_features,
device_limits: config.params.required_limits.clone(),
queue,
@ -109,4 +112,6 @@ pub async fn execute_test(
if expectations_match_failures(&test_info.failures, failures) == ExpectationMatchResult::Panic {
panic!();
}
// Print the name of the test.
log::info!("TEST FINISHED: {}", config.name);
}

View File

@ -23,7 +23,7 @@ static BGRA8_UNORM_STORAGE: GpuTestConfiguration = GpuTestConfiguration::new()
})
.features(wgpu::Features::BGRA8UNORM_STORAGE),
)
.run_sync(|ctx| {
.run_async(|ctx| async move {
let device = &ctx.device;
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
@ -139,7 +139,9 @@ static BGRA8_UNORM_STORAGE: GpuTestConfiguration = GpuTestConfiguration::new()
let buffer_slice = readback_buffer.slice(..);
buffer_slice.map_async(wgpu::MapMode::Read, Result::unwrap);
device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
{
let texels = buffer_slice.get_mapped_range();

View File

@ -31,9 +31,9 @@ const ENTRY: wgpu::BindGroupLayoutEntry = wgpu::BindGroupLayoutEntry {
#[gpu_test]
static BIND_GROUP_LAYOUT_DEDUPLICATION: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().test_features_limits())
.run_sync(bgl_dedupe);
.run_async(bgl_dedupe);
fn bgl_dedupe(ctx: TestingContext) {
async fn bgl_dedupe(ctx: TestingContext) {
let entries_1 = &[];
let entries_2 = &[ENTRY];
@ -126,7 +126,9 @@ fn bgl_dedupe(ctx: TestingContext) {
}
}
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
if ctx.adapter_info.backend != wgt::Backend::BrowserWebGpu {
// Now all of the BGL ids should be dead, so we should get the same ids again.

View File

@ -1,6 +1,6 @@
use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext};
fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) {
async fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) {
let r = wgpu::BufferUsages::MAP_READ;
let rw = wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::MAP_WRITE;
for usage in [r, rw] {
@ -14,7 +14,9 @@ fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str)
b0.slice(0..0)
.map_async(wgpu::MapMode::Read, Result::unwrap);
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
{
let view = b0.slice(0..0).get_mapped_range();
@ -48,7 +50,9 @@ fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str)
b0.slice(0..0)
.map_async(wgpu::MapMode::Write, Result::unwrap);
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
//{
// let view = b0.slice(0..0).get_mapped_range_mut();
@ -77,19 +81,21 @@ fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str)
b1.unmap();
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
}
#[gpu_test]
static EMPTY_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().expect_fail(FailureCase::always()))
.run_sync(|ctx| {
test_empty_buffer_range(&ctx, 2048, "regular buffer");
test_empty_buffer_range(&ctx, 0, "zero-sized buffer");
.run_async(|ctx| async move {
test_empty_buffer_range(&ctx, 2048, "regular buffer").await;
test_empty_buffer_range(&ctx, 0, "zero-sized buffer").await;
});
#[gpu_test]
static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new().run_async(|ctx| async move {
// This test writes 16 bytes at the beginning of buffer mapped mapped with
// an offset of 32 bytes. Then the buffer is copied into another buffer that
// is read back and we check that the written bytes are correctly placed at
@ -116,7 +122,9 @@ static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|
result.unwrap();
});
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
{
let slice = write_buf.slice(32..48);
@ -140,7 +148,9 @@ static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|
.slice(..)
.map_async(wgpu::MapMode::Read, Result::unwrap);
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let slice = read_buf.slice(..);
let view = slice.get_mapped_range();

View File

@ -1,7 +1,7 @@
//! Tests for buffer usages validation.
use wgpu::{BufferUsages as Bu, MapMode as Ma};
use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters};
use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters, TestingContext};
use wgt::BufferAddress;
const BUFFER_SIZE: BufferAddress = 1234;
@ -26,7 +26,7 @@ const NEEDS_MAPPABLE_PRIMARY_BUFFERS: &[Bu; 7] = &[
const INVALID_BITS: Bu = Bu::from_bits_retain(0b1111111111111);
const ALWAYS_FAIL: &[Bu; 2] = &[Bu::empty(), INVALID_BITS];
fn try_create(ctx: wgpu_test::TestingContext, usages: &[(bool, &[wgpu::BufferUsages])]) {
fn try_create(ctx: TestingContext, usages: &[(bool, &[wgpu::BufferUsages])]) {
for (expect_validation_error, usage) in usages
.iter()
.flat_map(|&(expect_error, usages)| usages.iter().copied().map(move |u| (expect_error, u)))
@ -69,7 +69,7 @@ static BUFFER_USAGE_MAPPABLE_PRIMARY_BUFFERS: GpuTestConfiguration = GpuTestConf
});
async fn map_test(
device: &wgpu::Device,
ctx: &TestingContext,
usage_type: &str,
map_mode_type: Ma,
before_unmap: bool,
@ -89,8 +89,8 @@ async fn map_test(
let mut buffer = None;
fail_if(device, buffer_creation_validation_error, || {
buffer = Some(device.create_buffer(&wgpu::BufferDescriptor {
fail_if(&ctx.device, buffer_creation_validation_error, || {
buffer = Some(ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size,
usage,
@ -107,7 +107,7 @@ async fn map_test(
|| (map_mode_type == Ma::Read && !usage.contains(Bu::MAP_READ))
|| (map_mode_type == Ma::Write && !usage.contains(Bu::MAP_WRITE));
fail_if(device, map_async_validation_error, || {
fail_if(&ctx.device, map_async_validation_error, || {
buffer.slice(0..size).map_async(map_mode_type, |_| {});
});
@ -123,7 +123,9 @@ async fn map_test(
buffer.destroy();
}
device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
if !before_unmap && !before_destroy {
{
@ -152,7 +154,7 @@ static BUFFER_MAP_ASYNC_MAP_STATE: GpuTestConfiguration = GpuTestConfiguration::
for after_unmap in [false, true] {
for after_destroy in [false, true] {
map_test(
&ctx.device,
&ctx,
usage_type,
map_mode_type,
before_unmap,

View File

@ -203,7 +203,7 @@ static TEXTURE_FORMATS_ASTC: &[wgpu::TextureFormat] = &[
},
];
fn single_texture_clear_test(
async fn single_texture_clear_test(
ctx: &TestingContext,
format: wgpu::TextureFormat,
size: wgpu::Extent3d,
@ -259,12 +259,12 @@ fn single_texture_clear_test(
ctx.queue.submit([encoder.finish()]);
assert!(
readback_buffers.are_zero(&ctx.device),
readback_buffers.are_zero(ctx).await,
"texture with format {format:?} was not fully cleared"
);
}
fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) {
async fn clear_texture_tests(ctx: TestingContext, formats: &'static [wgpu::TextureFormat]) {
for &format in formats {
let (block_width, block_height) = format.block_dimensions();
let rounded_width = block_width * wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
@ -278,7 +278,7 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) {
// 1D texture
if supports_1d {
single_texture_clear_test(
ctx,
&ctx,
format,
wgpu::Extent3d {
width: rounded_width,
@ -286,11 +286,12 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) {
depth_or_array_layers: 1,
},
wgpu::TextureDimension::D1,
);
)
.await;
}
// 2D texture
single_texture_clear_test(
ctx,
&ctx,
format,
wgpu::Extent3d {
width: rounded_width,
@ -298,10 +299,11 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) {
depth_or_array_layers: 1,
},
wgpu::TextureDimension::D2,
);
)
.await;
// 2D array texture
single_texture_clear_test(
ctx,
&ctx,
format,
wgpu::Extent3d {
width: rounded_width,
@ -309,11 +311,12 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) {
depth_or_array_layers: 4,
},
wgpu::TextureDimension::D2,
);
)
.await;
if supports_3d {
// volume texture
single_texture_clear_test(
ctx,
&ctx,
format,
wgpu::Extent3d {
width: rounded_width,
@ -321,7 +324,8 @@ fn clear_texture_tests(ctx: &TestingContext, formats: &[wgpu::TextureFormat]) {
depth_or_array_layers: 16,
},
wgpu::TextureDimension::D3,
);
)
.await;
}
}
}
@ -333,9 +337,7 @@ static CLEAR_TEXTURE_UNCOMPRESSED_GLES: GpuTestConfiguration = GpuTestConfigurat
.features(wgpu::Features::CLEAR_TEXTURE)
.skip(FailureCase::webgl2()),
)
.run_sync(|ctx| {
clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT);
});
.run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT));
#[gpu_test]
static CLEAR_TEXTURE_UNCOMPRESSED: GpuTestConfiguration = GpuTestConfiguration::new()
@ -350,9 +352,7 @@ static CLEAR_TEXTURE_UNCOMPRESSED: GpuTestConfiguration = GpuTestConfiguration::
)
.features(wgpu::Features::CLEAR_TEXTURE),
)
.run_sync(|ctx| {
clear_texture_tests(&ctx, TEXTURE_FORMATS_UNCOMPRESSED);
});
.run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_UNCOMPRESSED));
#[gpu_test]
static CLEAR_TEXTURE_DEPTH: GpuTestConfiguration = GpuTestConfiguration::new()
@ -368,9 +368,7 @@ static CLEAR_TEXTURE_DEPTH: GpuTestConfiguration = GpuTestConfiguration::new()
.limits(wgpu::Limits::downlevel_defaults())
.features(wgpu::Features::CLEAR_TEXTURE),
)
.run_sync(|ctx| {
clear_texture_tests(&ctx, TEXTURE_FORMATS_DEPTH);
});
.run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_DEPTH));
#[gpu_test]
static CLEAR_TEXTURE_DEPTH32_STENCIL8: GpuTestConfiguration = GpuTestConfiguration::new()
@ -380,9 +378,7 @@ static CLEAR_TEXTURE_DEPTH32_STENCIL8: GpuTestConfiguration = GpuTestConfigurati
// https://github.com/gfx-rs/wgpu/issues/5016
.skip(FailureCase::adapter("Apple Paravirtual device")),
)
.run_sync(|ctx| {
clear_texture_tests(&ctx, &[wgpu::TextureFormat::Depth32FloatStencil8]);
});
.run_async(|ctx| clear_texture_tests(ctx, &[wgpu::TextureFormat::Depth32FloatStencil8]));
#[gpu_test]
static CLEAR_TEXTURE_COMPRESSED_BCN: GpuTestConfiguration = GpuTestConfiguration::new()
@ -394,9 +390,7 @@ static CLEAR_TEXTURE_COMPRESSED_BCN: GpuTestConfiguration = GpuTestConfiguration
// compressed texture copy to buffer not yet implemented
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
)
.run_sync(|ctx| {
clear_texture_tests(&ctx, TEXTURE_FORMATS_BC);
});
.run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_BC));
#[gpu_test]
static CLEAR_TEXTURE_COMPRESSED_ASTC: GpuTestConfiguration = GpuTestConfiguration::new()
@ -412,9 +406,7 @@ static CLEAR_TEXTURE_COMPRESSED_ASTC: GpuTestConfiguration = GpuTestConfiguratio
// compressed texture copy to buffer not yet implemented
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
)
.run_sync(|ctx| {
clear_texture_tests(&ctx, TEXTURE_FORMATS_ASTC);
});
.run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_ASTC));
#[gpu_test]
static CLEAR_TEXTURE_COMPRESSED_ETC2: GpuTestConfiguration = GpuTestConfiguration::new()
@ -426,6 +418,4 @@ static CLEAR_TEXTURE_COMPRESSED_ETC2: GpuTestConfiguration = GpuTestConfiguratio
// compressed texture copy to buffer not yet implemented
.expect_fail(FailureCase::backend(wgpu::Backends::GL)),
)
.run_sync(|ctx| {
clear_texture_tests(&ctx, TEXTURE_FORMATS_ETC2);
});
.run_async(|ctx| clear_texture_tests(ctx, TEXTURE_FORMATS_ETC2));

View File

@ -3,7 +3,7 @@ use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameter
#[gpu_test]
static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().expect_fail(FailureCase::always()))
.run_sync(|ctx| {
.run_async(|ctx| async move {
// Create a bind group uisng a layout from another device. This should be a validation
// error but currently crashes.
let (device2, _) =
@ -23,7 +23,9 @@ static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguratio
});
}
ctx.device.poll(wgpu::Maintain::Poll);
ctx.async_poll(wgpu::Maintain::Poll)
.await
.panic_on_timeout();
});
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
@ -483,7 +485,7 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne
#[gpu_test]
static DEVICE_DESTROY_THEN_LOST: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_sync(|ctx| {
.run_async(|ctx| async move {
// This test checks that when device.destroy is called, the provided
// DeviceLostClosure is called with reason DeviceLostReason::Destroyed.
let was_called = std::sync::Arc::<std::sync::atomic::AtomicBool>::new(false.into());
@ -504,7 +506,10 @@ static DEVICE_DESTROY_THEN_LOST: GpuTestConfiguration = GpuTestConfiguration::ne
// Make sure the device queues are empty, which ensures that the closure
// has been called.
assert!(ctx.device.poll(wgpu::Maintain::Wait));
assert!(ctx
.async_poll(wgpu::Maintain::wait())
.await
.is_queue_empty());
assert!(
was_called.load(std::sync::atomic::Ordering::SeqCst),

View File

@ -323,7 +323,9 @@ static IMAGE_BITMAP_IMPORT: GpuTestConfiguration =
readback_buffer
.slice(..)
.map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let buffer = readback_buffer.slice(..).get_mapped_range();

View File

@ -1,7 +1,8 @@
use wgpu_test::{fail, gpu_test, GpuTestConfiguration};
#[gpu_test]
static BUFFER_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
static BUFFER_DESTROY: GpuTestConfiguration =
GpuTestConfiguration::new().run_async(|ctx| async move {
let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
@ -13,7 +14,9 @@ static BUFFER_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new().run_sy
buffer.destroy();
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
fail(&ctx.device, || {
buffer
@ -23,7 +26,9 @@ static BUFFER_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new().run_sy
buffer.destroy();
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
buffer.destroy();
@ -45,7 +50,9 @@ static BUFFER_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new().run_sy
}
let buffer = ctx.device.create_buffer(&descriptor);
buffer.destroy();
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let buffer = ctx.device.create_buffer(&descriptor);
buffer.destroy();
{
@ -54,16 +61,21 @@ static BUFFER_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new().run_sy
let buffer = ctx.device.create_buffer(&descriptor);
buffer.destroy();
let buffer = ctx.device.create_buffer(&descriptor);
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
buffer.destroy();
}
let buffer = ctx.device.create_buffer(&descriptor);
buffer.destroy();
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
});
#[gpu_test]
static TEXTURE_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
static TEXTURE_DESTROY: GpuTestConfiguration =
GpuTestConfiguration::new().run_async(|ctx| async move {
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
@ -83,11 +95,15 @@ static TEXTURE_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new().run_s
texture.destroy();
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
texture.destroy();
ctx.device.poll(wgpu::MaintainBase::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
texture.destroy();

View File

@ -3,7 +3,7 @@
target_os = "emscripten",
feature = "webgl"
))]
fn draw_test_with_reports(
async fn draw_test_with_reports(
ctx: wgpu_test::TestingContext,
expected: &[u32],
function: impl FnOnce(&mut wgpu::RenderPass<'_>),
@ -241,8 +241,9 @@ fn draw_test_with_reports(
let report = global_report.hub_report(ctx.adapter_info.backend);
assert_eq!(report.command_buffers.num_allocated, 0);
ctx.device
.poll(wgpu::Maintain::WaitForSubmissionIndex(submit_index));
ctx.async_poll(wgpu::Maintain::wait_for(submit_index))
.await
.panic_on_timeout();
let global_report = ctx.instance.generate_report();
let report = global_report.hub_report(ctx.adapter_info.backend);
@ -285,7 +286,7 @@ static SIMPLE_DRAW_CHECK_MEM_LEAKS: wgpu_test::GpuTestConfiguration =
.test_features_limits()
.features(wgpu::Features::VERTEX_WRITABLE_STORAGE),
)
.run_sync(|ctx| {
.run_async(|ctx| {
draw_test_with_reports(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
cmb.draw(0..6, 0..1);
})

View File

@ -4,7 +4,7 @@ use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters};
#[gpu_test]
static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))
.run_sync(|ctx| {
.run_async(|ctx| async move {
// Create depth texture
let depth_texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: Some("Depth texture"),
@ -117,7 +117,9 @@ static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new()
mapping_buffer
.slice(..)
.map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let query_buffer_view = mapping_buffer.slice(..).get_mapped_range();
let query_data: &[u64; 3] = bytemuck::from_bytes(&query_buffer_view);

View File

@ -14,7 +14,7 @@ static PARTIALLY_BOUNDED_ARRAY: GpuTestConfiguration = GpuTestConfiguration::new
)
.limits(wgpu::Limits::downlevel_defaults()),
)
.run_sync(|ctx| {
.run_async(|ctx| async move {
let device = &ctx.device;
let texture_extent = wgpu::Extent3d {
@ -98,5 +98,6 @@ static PARTIALLY_BOUNDED_ARRAY: GpuTestConfiguration = GpuTestConfiguration::new
ctx.queue.submit(Some(encoder.finish()));
readback_buffers
.assert_buffer_contents(device, bytemuck::bytes_of(&[4.0f32, 3.0, 2.0, 1.0]));
.assert_buffer_contents(&ctx, bytemuck::bytes_of(&[4.0f32, 3.0, 2.0, 1.0]))
.await;
});

View File

@ -68,48 +68,60 @@ impl DummyWorkData {
}
#[gpu_test]
static WAIT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
static WAIT: GpuTestConfiguration = GpuTestConfiguration::new().run_async(|ctx| async move {
let data = DummyWorkData::new(&ctx);
ctx.queue.submit(Some(data.cmd_buf));
ctx.device.poll(Maintain::Wait);
ctx.async_poll(Maintain::wait()).await.panic_on_timeout();
});
#[gpu_test]
static DOUBLE_WAIT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
static DOUBLE_WAIT: GpuTestConfiguration =
GpuTestConfiguration::new().run_async(|ctx| async move {
let data = DummyWorkData::new(&ctx);
ctx.queue.submit(Some(data.cmd_buf));
ctx.device.poll(Maintain::Wait);
ctx.device.poll(Maintain::Wait);
ctx.async_poll(Maintain::wait()).await.panic_on_timeout();
ctx.async_poll(Maintain::wait()).await.panic_on_timeout();
});
#[gpu_test]
static WAIT_ON_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
static WAIT_ON_SUBMISSION: GpuTestConfiguration =
GpuTestConfiguration::new().run_async(|ctx| async move {
let data = DummyWorkData::new(&ctx);
let index = ctx.queue.submit(Some(data.cmd_buf));
ctx.device.poll(Maintain::WaitForSubmissionIndex(index));
ctx.async_poll(Maintain::wait_for(index))
.await
.panic_on_timeout();
});
#[gpu_test]
static DOUBLE_WAIT_ON_SUBMISSION: GpuTestConfiguration =
GpuTestConfiguration::new().run_sync(|ctx| {
GpuTestConfiguration::new().run_async(|ctx| async move {
let data = DummyWorkData::new(&ctx);
let index = ctx.queue.submit(Some(data.cmd_buf));
ctx.device
.poll(Maintain::WaitForSubmissionIndex(index.clone()));
ctx.device.poll(Maintain::WaitForSubmissionIndex(index));
ctx.async_poll(Maintain::wait_for(index.clone()))
.await
.panic_on_timeout();
ctx.async_poll(Maintain::wait_for(index))
.await
.panic_on_timeout();
});
#[gpu_test]
static WAIT_OUT_OF_ORDER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
static WAIT_OUT_OF_ORDER: GpuTestConfiguration =
GpuTestConfiguration::new().run_async(|ctx| async move {
let data1 = DummyWorkData::new(&ctx);
let data2 = DummyWorkData::new(&ctx);
let index1 = ctx.queue.submit(Some(data1.cmd_buf));
let index2 = ctx.queue.submit(Some(data2.cmd_buf));
ctx.device.poll(Maintain::WaitForSubmissionIndex(index2));
ctx.device.poll(Maintain::WaitForSubmissionIndex(index1));
ctx.async_poll(Maintain::wait_for(index2))
.await
.panic_on_timeout();
ctx.async_poll(Maintain::wait_for(index1))
.await
.panic_on_timeout();
});

View File

@ -19,7 +19,7 @@ static PARTIAL_UPDATE: GpuTestConfiguration = GpuTestConfiguration::new()
..Default::default()
}),
)
.run_sync(partial_update_test);
.run_async(partial_update_test);
const SHADER: &str = r#"
struct Pc {
@ -38,7 +38,7 @@ const SHADER: &str = r#"
}
"#;
fn partial_update_test(ctx: TestingContext) {
async fn partial_update_test(ctx: TestingContext) {
let sm = ctx
.device
.create_shader_module(wgpu::ShaderModuleDescriptor {
@ -139,7 +139,9 @@ fn partial_update_test(ctx: TestingContext) {
encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, 32);
ctx.queue.submit([encoder.finish()]);
cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let data = cpu_buffer.slice(..).get_mapped_range();

View File

@ -32,9 +32,9 @@ static MULTI_STAGE_DATA_BINDING: GpuTestConfiguration = GpuTestConfiguration::ne
..Default::default()
}),
)
.run_sync(multi_stage_data_binding_test);
.run_async(multi_stage_data_binding_test);
fn multi_stage_data_binding_test(ctx: TestingContext) {
async fn multi_stage_data_binding_test(ctx: TestingContext) {
// We use different shader modules to allow us to use different
// types for the uniform and push constant blocks between stages.
let vs_sm = ctx
@ -174,5 +174,5 @@ fn multi_stage_data_binding_test(ctx: TestingContext) {
ctx.queue.submit([encoder.finish()]);
let result = input_as_unorm.repeat(4);
buffers.assert_buffer_contents(&ctx.device, &result);
buffers.assert_buffer_contents(&ctx, &result).await;
}

View File

@ -16,7 +16,7 @@ use wgpu::*;
/// that we unset the correct locations (see PR #3706).
#[gpu_test]
static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration =
GpuTestConfiguration::new().run_sync(|ctx| {
GpuTestConfiguration::new().run_async(|ctx| async move {
let module = ctx
.device
.create_shader_module(include_wgsl!("issue_3457.wgsl"));
@ -160,7 +160,7 @@ static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration =
drop(vertex_buffer2);
// Make sure the buffers are actually deleted.
ctx.device.poll(Maintain::Wait);
ctx.async_poll(Maintain::wait()).await.panic_on_timeout();
let mut encoder2 = ctx
.device

View File

@ -14,7 +14,7 @@ use wgpu::*;
/// to add them to. This is incorrect, as we do not immediatley invoke map_async callbacks.
#[gpu_test]
static QUEUE_SUBMITTED_CALLBACK_ORDERING: GpuTestConfiguration = GpuTestConfiguration::new()
.run_sync(|ctx| {
.run_async(|ctx| async move {
// Create a mappable buffer
let buffer = ctx.device.create_buffer(&BufferDescriptor {
label: Some("mappable buffer"),
@ -36,7 +36,7 @@ static QUEUE_SUBMITTED_CALLBACK_ORDERING: GpuTestConfiguration = GpuTestConfigur
// Submit the work.
ctx.queue.submit(Some(encoder.finish()));
// Ensure the work is finished.
ctx.device.poll(MaintainBase::Wait);
ctx.async_poll(Maintain::wait()).await.panic_on_timeout();
#[derive(Debug)]
struct OrderingContext {
@ -74,7 +74,7 @@ static QUEUE_SUBMITTED_CALLBACK_ORDERING: GpuTestConfiguration = GpuTestConfigur
});
// No GPU work is happening at this point, but we want to process callbacks.
ctx.device.poll(MaintainBase::Poll);
ctx.async_poll(MaintainBase::Poll).await.panic_on_timeout();
// Extract the ordering out of the arc.
let ordering = Arc::into_inner(ordering).unwrap().into_inner();

View File

@ -1,8 +1,8 @@
use std::ops::Range;
use wgpu_test::{gpu_test, GpuTestConfiguration, TestingContext};
use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext};
fn fill_test(ctx: &TestingContext, range: Range<u64>, size: u64) -> bool {
async fn fill_test(ctx: &TestingContext, range: Range<u64>, size: u64) -> bool {
let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("gpu_buffer"),
size,
@ -32,7 +32,9 @@ fn fill_test(ctx: &TestingContext, range: Range<u64>, size: u64) -> bool {
ctx.queue.submit(Some(encoder.finish()));
cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let buffer_slice = cpu_buffer.slice(..);
let buffer_data = buffer_slice.get_mapped_range();
@ -84,8 +86,9 @@ fn fill_test(ctx: &TestingContext, range: Range<u64>, size: u64) -> bool {
///
/// This test will fail on nvidia if the bug is not properly worked around.
#[gpu_test]
static CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration =
GpuTestConfiguration::new().run_sync(|ctx| {
static CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default())
.run_async(|ctx| async move {
// This hits most of the cases in nvidia's clear buffer bug
let mut succeeded = true;
for power in 4..14 {
@ -93,7 +96,7 @@ static CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration =
for start_offset in (0..=36).step_by(4) {
for size_offset in (0..=36).step_by(4) {
let range = start_offset..size + size_offset + start_offset;
let result = fill_test(&ctx, range, 1 << 16);
let result = fill_test(&ctx, range, 1 << 16).await;
succeeded &= result;
}

View File

@ -11,7 +11,11 @@ const TEXTURE_HEIGHT: u32 = 2;
const TEXTURE_WIDTH: u32 = 2;
const BUFFER_SIZE: usize = (TEXTURE_WIDTH * TEXTURE_HEIGHT * 4) as usize;
fn scissor_test_impl(ctx: &TestingContext, scissor_rect: Rect, expected_data: [u8; BUFFER_SIZE]) {
async fn scissor_test_impl(
ctx: &TestingContext,
scissor_rect: Rect,
expected_data: [u8; BUFFER_SIZE],
) {
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
label: Some("Offscreen texture"),
size: wgpu::Extent3d {
@ -94,11 +98,14 @@ fn scissor_test_impl(ctx: &TestingContext, scissor_rect: Rect, expected_data: [u
readback_buffer.copy_from(&ctx.device, &mut encoder, &texture);
ctx.queue.submit(Some(encoder.finish()));
}
readback_buffer.assert_buffer_contents(&ctx.device, &expected_data);
readback_buffer
.assert_buffer_contents(ctx, &expected_data)
.await;
}
#[gpu_test]
static SCISSOR_TEST_FULL_RECT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| {
static SCISSOR_TEST_FULL_RECT: GpuTestConfiguration =
GpuTestConfiguration::new().run_async(|ctx| async move {
scissor_test_impl(
&ctx,
Rect {
@ -108,12 +115,13 @@ static SCISSOR_TEST_FULL_RECT: GpuTestConfiguration = GpuTestConfiguration::new(
height: TEXTURE_HEIGHT,
},
[255; BUFFER_SIZE],
);
)
.await
});
#[gpu_test]
static SCISSOR_TEST_EMPTY_RECT: GpuTestConfiguration =
GpuTestConfiguration::new().run_sync(|ctx| {
GpuTestConfiguration::new().run_async(|ctx| async move {
scissor_test_impl(
&ctx,
Rect {
@ -123,12 +131,13 @@ static SCISSOR_TEST_EMPTY_RECT: GpuTestConfiguration =
height: 0,
},
[0; BUFFER_SIZE],
);
)
.await;
});
#[gpu_test]
static SCISSOR_TEST_EMPTY_RECT_WITH_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new()
.run_sync(|ctx| {
.run_async(|ctx| async move {
scissor_test_impl(
&ctx,
Rect {
@ -138,12 +147,13 @@ static SCISSOR_TEST_EMPTY_RECT_WITH_OFFSET: GpuTestConfiguration = GpuTestConfig
height: 0,
},
[0; BUFFER_SIZE],
);
)
.await
});
#[gpu_test]
static SCISSOR_TEST_CUSTOM_RECT: GpuTestConfiguration =
GpuTestConfiguration::new().run_sync(|ctx| {
GpuTestConfiguration::new().run_async(|ctx| async move {
let mut expected_result = [0; BUFFER_SIZE];
expected_result[((3 * BUFFER_SIZE) / 4)..][..BUFFER_SIZE / 4]
.copy_from_slice(&[255; BUFFER_SIZE / 4]);
@ -157,5 +167,6 @@ static SCISSOR_TEST_CUSTOM_RECT: GpuTestConfiguration =
height: TEXTURE_HEIGHT / 2,
},
expected_result,
);
)
.await;
});

View File

@ -177,7 +177,7 @@ impl ShaderTest {
const MAX_BUFFER_SIZE: u64 = 128;
/// Runs the given shader tests with the given storage_type for the input_buffer.
fn shader_input_output_test(
async fn shader_input_output_test(
ctx: TestingContext,
storage_type: InputStorageType,
tests: Vec<ShaderTest>,
@ -355,7 +355,7 @@ fn shader_input_output_test(
ctx.queue.submit(Some(encoder.finish()));
mapping_buffer.slice(..).map_async(MapMode::Read, |_| ());
ctx.device.poll(Maintain::Wait);
ctx.async_poll(Maintain::wait()).await.panic_on_timeout();
let mapped = mapping_buffer.slice(..).get_mapped_range();

View File

@ -44,10 +44,10 @@ static NUMERIC_BUILTINS: GpuTestConfiguration = GpuTestConfiguration::new()
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
.limits(Limits::downlevel_defaults()),
)
.run_sync(|ctx| {
.run_async(|ctx| {
shader_input_output_test(
ctx,
InputStorageType::Storage,
create_numeric_builtin_test(),
);
)
});

View File

@ -231,12 +231,12 @@ static UNIFORM_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()
)
.limits(Limits::downlevel_defaults()),
)
.run_sync(|ctx| {
.run_async(|ctx| {
shader_input_output_test(
ctx,
InputStorageType::Uniform,
create_struct_layout_tests(InputStorageType::Uniform),
);
)
});
#[gpu_test]
@ -246,12 +246,12 @@ static STORAGE_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()
.downlevel_flags(DownlevelFlags::COMPUTE_SHADERS)
.limits(Limits::downlevel_defaults()),
)
.run_sync(|ctx| {
.run_async(|ctx| {
shader_input_output_test(
ctx,
InputStorageType::Storage,
create_struct_layout_tests(InputStorageType::Storage),
);
)
});
#[gpu_test]
@ -265,10 +265,10 @@ static PUSH_CONSTANT_INPUT: GpuTestConfiguration = GpuTestConfiguration::new()
..Limits::downlevel_defaults()
}),
)
.run_sync(|ctx| {
.run_async(|ctx| {
shader_input_output_test(
ctx,
InputStorageType::PushConstant,
create_struct_layout_tests(InputStorageType::PushConstant),
);
)
});

View File

@ -24,7 +24,7 @@ static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::
..FailureCase::default()
}),
)
.run_sync(|ctx| {
.run_async(|ctx| async move {
let bgl = ctx
.device
.create_bind_group_layout(&BindGroupLayoutDescriptor {
@ -134,7 +134,7 @@ static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::
ctx.queue.submit(Some(encoder.finish()));
mapping_buffer.slice(..).map_async(MapMode::Read, |_| ());
ctx.device.poll(Maintain::Wait);
ctx.async_poll(Maintain::wait()).await.panic_on_timeout();
let mapped = mapping_buffer.slice(..).get_mapped_range();

View File

@ -43,7 +43,7 @@ static DRAW: GpuTestConfiguration = GpuTestConfiguration::new()
.test_features_limits()
.features(wgpu::Features::SHADER_PRIMITIVE_INDEX),
)
.run_sync(|ctx| {
.run_async(|ctx| async move {
//
// +-----+-----+
// |white|blue |
@ -57,6 +57,7 @@ static DRAW: GpuTestConfiguration = GpuTestConfiguration::new()
pulling_common(ctx, &expected, |rpass| {
rpass.draw(0..6, 0..1);
})
.await;
});
#[gpu_test]
@ -66,7 +67,7 @@ static DRAW_INDEXED: GpuTestConfiguration = GpuTestConfiguration::new()
.test_features_limits()
.features(wgpu::Features::SHADER_PRIMITIVE_INDEX),
)
.run_sync(|ctx| {
.run_async(|ctx| async move {
//
// +-----+-----+
// |white| red |
@ -80,9 +81,10 @@ static DRAW_INDEXED: GpuTestConfiguration = GpuTestConfiguration::new()
pulling_common(ctx, &expected, |rpass| {
rpass.draw_indexed(0..6, 0, 0..1);
})
.await;
});
fn pulling_common(
async fn pulling_common(
ctx: TestingContext,
expected: &[u8],
draw_command: impl FnOnce(&mut wgpu::RenderPass<'_>),
@ -192,5 +194,5 @@ fn pulling_common(
}
readback_buffer.copy_from(&ctx.device, &mut encoder, &color_texture);
ctx.queue.submit(Some(encoder.finish()));
readback_buffer.assert_buffer_contents(&ctx.device, expected);
readback_buffer.assert_buffer_contents(&ctx, expected).await;
}

View File

@ -8,7 +8,7 @@ static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new()
.downlevel_flags(DownlevelFlags::VIEW_FORMATS)
.limits(Limits::downlevel_defaults()),
)
.run_sync(|ctx| {
.run_async(|ctx| async move {
let unorm_data: [[u8; 4]; 4] = [
[180, 0, 0, 255],
[0, 84, 0, 127],
@ -41,7 +41,8 @@ static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new()
TextureFormat::Rgba8UnormSrgb,
&unorm_data,
&srgb_data,
);
)
.await;
// Reinterpret Rgba8UnormSrgb back to Rgba8Unorm
reinterpret(
@ -52,10 +53,11 @@ static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new()
TextureFormat::Rgba8Unorm,
&srgb_data,
&unorm_data,
);
)
.await;
});
fn reinterpret(
async fn reinterpret(
ctx: &TestingContext,
shader: &wgpu::ShaderModule,
size: wgpu::Extent3d,
@ -178,7 +180,9 @@ fn reinterpret(
let slice = read_buffer.slice(..);
slice.map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
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]];

View File

@ -225,7 +225,7 @@ impl Test {
}
}
fn vertex_index_common(ctx: TestingContext) {
async fn vertex_index_common(ctx: TestingContext) {
let identity_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor {
label: Some("identity buffer"),
contents: bytemuck::cast_slice(&[0u32, 1, 2, 3, 4, 5, 6, 7, 8]),
@ -428,11 +428,15 @@ fn vertex_index_common(ctx: TestingContext) {
// See https://github.com/gfx-rs/wgpu/issues/4732 for why this is split between two submissions
// with a hard wait in between.
ctx.queue.submit([encoder1.finish()]);
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
ctx.queue.submit([encoder2.finish()]);
let slice = cpu_buffer.slice(..);
slice.map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let data: Vec<u32> = bytemuck::cast_slice(&slice.get_mapped_range()).to_vec();
if data != expected {
@ -463,4 +467,4 @@ static VERTEX_INDICES: GpuTestConfiguration = GpuTestConfiguration::new()
.test_features_limits()
.features(wgpu::Features::VERTEX_WRITABLE_STORAGE),
)
.run_sync(vertex_index_common);
.run_async(vertex_index_common);

View File

@ -4,7 +4,7 @@ use wgpu_test::{gpu_test, GpuTestConfiguration};
#[gpu_test]
static WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration =
GpuTestConfiguration::new().run_sync(|ctx| {
GpuTestConfiguration::new().run_async(|ctx| async move {
let size = 256;
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
@ -84,7 +84,9 @@ static WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration =
let slice = read_buffer.slice(..);
slice.map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let data: Vec<u8> = slice.get_mapped_range().to_vec();
for byte in &data[..(size as usize * 2)] {
@ -97,7 +99,7 @@ static WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration =
#[gpu_test]
static WRITE_TEXTURE_SUBSET_3D: GpuTestConfiguration =
GpuTestConfiguration::new().run_sync(|ctx| {
GpuTestConfiguration::new().run_async(|ctx| async move {
let size = 256;
let depth = 4;
let tex = ctx.device.create_texture(&wgpu::TextureDescriptor {
@ -177,7 +179,9 @@ static WRITE_TEXTURE_SUBSET_3D: GpuTestConfiguration =
let slice = read_buffer.slice(..);
slice.map_async(wgpu::MapMode::Read, |_| ());
ctx.device.poll(wgpu::Maintain::Wait);
ctx.async_poll(wgpu::Maintain::wait())
.await
.panic_on_timeout();
let data: Vec<u8> = slice.get_mapped_range().to_vec();
for byte in &data[..((size * size) as usize * 2)] {

View File

@ -9,7 +9,7 @@ use wgpu_test::{
static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT:
GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))
.run_sync(|mut ctx| {
.run_async(|mut ctx| async move {
let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);
case.create_command_encoder();
case.discard();
@ -19,21 +19,21 @@ static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_A
case.copy_texture_to_buffer();
case.submit_command_encoder();
case.assert_buffers_are_zero();
case.assert_buffers_are_zero().await;
});
#[gpu_test]
static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER:
GpuTestConfiguration = GpuTestConfiguration::new()
.parameters(TestParameters::default().expect_fail(FailureCase::webgl2()))
.run_sync(|mut ctx| {
.run_async(|mut ctx| async move {
let mut case = TestCase::new(&mut ctx, TextureFormat::Rgba8UnormSrgb);
case.create_command_encoder();
case.discard();
case.copy_texture_to_buffer();
case.submit_command_encoder();
case.assert_buffers_are_zero();
case.assert_buffers_are_zero().await;
});
#[gpu_test]
@ -46,7 +46,7 @@ static DISCARDING_DEPTH_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_I
)
.limits(Limits::downlevel_defaults()),
)
.run_sync(|mut ctx| {
.run_async(|mut ctx| async move {
for format in [
TextureFormat::Stencil8,
TextureFormat::Depth16Unorm,
@ -60,7 +60,7 @@ static DISCARDING_DEPTH_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_I
case.copy_texture_to_buffer();
case.submit_command_encoder();
case.assert_buffers_are_zero();
case.assert_buffers_are_zero().await;
}
});
@ -75,7 +75,7 @@ static DISCARDING_EITHER_DEPTH_OR_STENCIL_ASPECT_TEST: GpuTestConfiguration =
)
.limits(Limits::downlevel_defaults()),
)
.run_sync(|mut ctx| {
.run_async(|mut ctx| async move {
for format in [
TextureFormat::Stencil8,
TextureFormat::Depth16Unorm,
@ -96,7 +96,7 @@ static DISCARDING_EITHER_DEPTH_OR_STENCIL_ASPECT_TEST: GpuTestConfiguration =
case.copy_texture_to_buffer();
case.submit_command_encoder();
case.assert_buffers_are_zero();
case.assert_buffers_are_zero().await;
}
});
@ -310,9 +310,9 @@ impl<'ctx> TestCase<'ctx> {
);
}
pub fn assert_buffers_are_zero(&mut self) {
pub async fn assert_buffers_are_zero(&mut self) {
assert!(
self.readback_buffers.are_zero(&self.ctx.device),
self.readback_buffers.are_zero(self.ctx).await,
"texture was not fully cleared"
);
}

View File

@ -4280,10 +4280,10 @@ impl Default for ColorWrites {
/// Passed to `Device::poll` to control how and if it should block.
#[derive(Clone)]
pub enum Maintain<T> {
/// On native backends, block until the given submission has
/// On wgpu-core based backends, block until the given submission has
/// completed execution, and any callbacks have been invoked.
///
/// On the web, this has no effect. Callbacks are invoked from the
/// On WebGPU, this has no effect. Callbacks are invoked from the
/// window event loop.
WaitForSubmissionIndex(T),
/// Same as WaitForSubmissionIndex but waits for the most recent submission.
@ -4293,6 +4293,22 @@ pub enum Maintain<T> {
}
impl<T> Maintain<T> {
/// Construct a wait variant
pub fn wait() -> Self {
// This function seems a little silly, but it is useful to allow
// <https://github.com/gfx-rs/wgpu/pull/5012> to be split up, as
// it has meaning in that PR.
Self::Wait
}
/// Construct a WaitForSubmissionIndex variant
pub fn wait_for(submission_index: T) -> Self {
// This function seems a little silly, but it is useful to allow
// <https://github.com/gfx-rs/wgpu/pull/5012> to be split up, as
// it has meaning in that PR.
Self::WaitForSubmissionIndex(submission_index)
}
/// This maintain represents a wait of some kind.
pub fn is_wait(&self) -> bool {
match *self {
@ -4314,6 +4330,29 @@ impl<T> Maintain<T> {
}
}
/// Result of a maintain operation.
pub enum MaintainResult {
/// There are no active submissions in flight as of the beginning of the poll call.
/// Other submissions may have been queued on other threads at the same time.
///
/// This implies that the given poll is complete.
SubmissionQueueEmpty,
/// More information coming soon <https://github.com/gfx-rs/wgpu/pull/5012>
Ok,
}
impl MaintainResult {
/// Returns true if the result is [`Self::SubmissionQueueEmpty`]`.
pub fn is_queue_empty(&self) -> bool {
matches!(self, Self::SubmissionQueueEmpty)
}
/// Panics if the MaintainResult is not Ok.
pub fn panic_on_timeout(self) {
let _ = self;
}
}
/// State of the stencil operation (fixed-pipeline stage).
///
/// For use in [`DepthStencilState`].

View File

@ -1403,8 +1403,8 @@ impl crate::Context for Context {
#[cfg(any(native, emscripten))]
{
let global = &self.0;
match wgc::gfx_select!(device => global.device_poll(*device, wgt::Maintain::Wait)) {
Ok(_) => (),
match wgc::gfx_select!(device => global.device_poll(*device, wgt::Maintain::wait())) {
Ok(_) => {}
Err(err) => self.handle_error_fatal(err, "Device::drop"),
}
wgc::gfx_select!(device => global.device_drop(*device));
@ -1445,14 +1445,17 @@ impl crate::Context for Context {
device: &Self::DeviceId,
_device_data: &Self::DeviceData,
maintain: crate::Maintain,
) -> bool {
) -> wgt::MaintainResult {
let global = &self.0;
let maintain_inner = maintain.map_index(|i| *i.1.as_ref().downcast_ref().unwrap());
match wgc::gfx_select!(device => global.device_poll(
*device,
maintain_inner
)) {
Ok(queue_empty) => queue_empty,
Ok(done) => match done {
true => wgt::MaintainResult::SubmissionQueueEmpty,
false => wgt::MaintainResult::Ok,
},
Err(err) => self.handle_error_fatal(err, "Device::poll"),
}
}

View File

@ -1950,9 +1950,9 @@ impl crate::context::Context for Context {
_device: &Self::DeviceId,
_device_data: &Self::DeviceData,
_maintain: crate::Maintain,
) -> bool {
) -> crate::MaintainResult {
// Device is polled automatically
true
crate::MaintainResult::SubmissionQueueEmpty
}
fn device_on_uncaptured_error(

View File

@ -10,8 +10,8 @@ use wgt::{
use crate::{
AnyWasmNotSendSync, BindGroupDescriptor, BindGroupLayoutDescriptor, Buffer, BufferAsyncError,
BufferDescriptor, CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor,
DeviceDescriptor, Error, ErrorFilter, ImageCopyBuffer, ImageCopyTexture, Maintain, MapMode,
PipelineLayoutDescriptor, QuerySetDescriptor, RenderBundleDescriptor,
DeviceDescriptor, Error, ErrorFilter, ImageCopyBuffer, ImageCopyTexture, Maintain,
MaintainResult, MapMode, PipelineLayoutDescriptor, QuerySetDescriptor, RenderBundleDescriptor,
RenderBundleEncoderDescriptor, RenderPassDescriptor, RenderPipelineDescriptor,
RequestAdapterOptions, RequestDeviceError, SamplerDescriptor, ShaderModuleDescriptor,
ShaderModuleDescriptorSpirV, SurfaceTargetUnsafe, Texture, TextureDescriptor,
@ -287,7 +287,7 @@ pub trait Context: Debug + WasmNotSendSync + Sized {
device: &Self::DeviceId,
device_data: &Self::DeviceData,
maintain: Maintain,
) -> bool;
) -> MaintainResult;
fn device_on_uncaptured_error(
&self,
device: &Self::DeviceId,
@ -1309,8 +1309,12 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync {
fn device_destroy(&self, device: &ObjectId, device_data: &crate::Data);
fn device_mark_lost(&self, device: &ObjectId, device_data: &crate::Data, message: &str);
fn queue_drop(&self, queue: &ObjectId, queue_data: &crate::Data);
fn device_poll(&self, device: &ObjectId, device_data: &crate::Data, maintain: Maintain)
-> bool;
fn device_poll(
&self,
device: &ObjectId,
device_data: &crate::Data,
maintain: Maintain,
) -> MaintainResult;
fn device_on_uncaptured_error(
&self,
device: &ObjectId,
@ -2399,7 +2403,7 @@ where
device: &ObjectId,
device_data: &crate::Data,
maintain: Maintain,
) -> bool {
) -> MaintainResult {
let device = <T::DeviceId>::from(*device);
let device_data = downcast_ref(device_data);
Context::device_poll(self, &device, device_data, maintain)

View File

@ -85,16 +85,17 @@ pub use wgt::{
DepthStencilState, DeviceLostReason, DeviceType, DownlevelCapabilities, DownlevelFlags,
Dx12Compiler, DynamicOffset, Extent3d, Face, Features, FilterMode, FrontFace,
Gles3MinorVersion, ImageDataLayout, ImageSubresourceRange, IndexFormat, InstanceDescriptor,
InstanceFlags, Limits, MultisampleState, Origin2d, Origin3d, PipelineStatisticsTypes,
PolygonMode, PowerPreference, PredefinedColorSpace, PresentMode, PresentationTimestamp,
PrimitiveState, PrimitiveTopology, PushConstantRange, QueryType, RenderBundleDepthStencil,
SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel, ShaderStages,
StencilFaceState, StencilOperation, StencilState, StorageTextureAccess, SurfaceCapabilities,
SurfaceStatus, TextureAspect, TextureDimension, TextureFormat, TextureFormatFeatureFlags,
TextureFormatFeatures, TextureSampleType, TextureUsages, TextureViewDimension, VertexAttribute,
VertexFormat, VertexStepMode, WasmNotSend, WasmNotSendSync, WasmNotSync, COPY_BUFFER_ALIGNMENT,
COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT,
QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_STRIDE_ALIGNMENT,
InstanceFlags, Limits, MaintainResult, MultisampleState, Origin2d, Origin3d,
PipelineStatisticsTypes, PolygonMode, PowerPreference, PredefinedColorSpace, PresentMode,
PresentationTimestamp, PrimitiveState, PrimitiveTopology, PushConstantRange, QueryType,
RenderBundleDepthStencil, SamplerBindingType, SamplerBorderColor, ShaderLocation, ShaderModel,
ShaderStages, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess,
SurfaceCapabilities, SurfaceStatus, TextureAspect, TextureDimension, TextureFormat,
TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType, TextureUsages,
TextureViewDimension, VertexAttribute, VertexFormat, VertexStepMode, WasmNotSend,
WasmNotSendSync, WasmNotSync, COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT,
MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT, QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES,
QUERY_SIZE, VERTEX_STRIDE_ALIGNMENT,
};
#[cfg(not(webgpu))]
@ -2205,7 +2206,7 @@ impl Adapter {
}
impl Device {
/// Check for resource cleanups and mapping callbacks.
/// Check for resource cleanups and mapping callbacks. Will block if [`Maintain::Wait`] is passed.
///
/// Return `true` if the queue is empty, or `false` if there are more queue
/// submissions still in flight. (Note that, unless access to the [`Queue`] is
@ -2213,8 +2214,8 @@ impl Device {
/// the caller receives it. `Queue`s can be shared between threads, so
/// other threads could submit new work at any time.)
///
/// On the web, this is a no-op. `Device`s are automatically polled.
pub fn poll(&self, maintain: Maintain) -> bool {
/// When running on WebGPU, this is a no-op. `Device`s are automatically polled.
pub fn poll(&self, maintain: Maintain) -> MaintainResult {
DynContext::device_poll(&*self.context, &self.id, self.data.as_ref(), maintain)
}