Skip to content

Commit

Permalink
Convert Tests to Use Async Poll (#5053)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
cwfitzgerald and Wumpf authored Jan 13, 2024
1 parent 5fd7d22 commit ad625f4
Show file tree
Hide file tree
Showing 48 changed files with 458 additions and 304 deletions.
2 changes: 1 addition & 1 deletion deno_webgpu/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion examples/src/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion examples/src/hello_compute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion examples/src/hello_synchronization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion examples/src/hello_workgroups/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion examples/src/mipmap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/src/render_to_texture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
{
Expand Down
7 changes: 5 additions & 2 deletions examples/src/repeated_compute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion examples/src/storage_texture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
{
Expand Down
2 changes: 1 addition & 1 deletion examples/src/timestamp_queries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions player/src/bin/play.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
{
Expand Down Expand Up @@ -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();
}
_ => {}
}
Expand Down
3 changes: 2 additions & 1 deletion player/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
44 changes: 28 additions & 16 deletions tests/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
let is_zero = self
.retrieve_buffer(device, buffer, aspect)
.iter()
.all(|b| *b == 0);
buffer.unmap();
is_zero
};
async fn is_zero(
&self,
ctx: &TestingContext,
buffer: &Buffer,
aspect: Option<TextureAspect>,
) -> bool {
let is_zero = self
.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 ({})",
Expand Down
1 change: 1 addition & 0 deletions tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod init;
mod isolation;
pub mod native;
mod params;
mod poll;
mod report;
mod run;

Expand Down
8 changes: 8 additions & 0 deletions tests/src/poll.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
11 changes: 8 additions & 3 deletions tests/src/run.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::panic::AssertUnwindSafe;
use std::{panic::AssertUnwindSafe, sync::Arc};

use futures_lite::FutureExt;
use wgpu::{Adapter, Device, Instance, Queue};
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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);
}
6 changes: 4 additions & 2 deletions tests/tests/bgra8unorm_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down
8 changes: 5 additions & 3 deletions tests/tests/bind_group_layout_dedup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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.
Expand Down
30 changes: 20 additions & 10 deletions tests/tests/buffer.rs
Original file line number Diff line number Diff line change
@@ -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] {
Expand All @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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();
Expand Down
Loading

0 comments on commit ad625f4

Please sign in to comment.