Test setup for WebAssembly+WebGL (#3238)

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
Harald Reingruber 2022-12-09 02:02:39 +01:00 committed by GitHub
parent 85fda2d18e
commit f0f700c2d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 211 additions and 38 deletions

View File

@ -172,6 +172,32 @@ jobs:
cargo doc --target ${{ matrix.target }} -p wgpu -p wgpu-info -p player --all-features --no-deps
cargo doc --target ${{ matrix.target }} -p wgpu-core --no-deps --features="portable_features"
wasm-test:
name: Test WebAssembly
runs-on: ubuntu-latest
steps:
- name: checkout repo
uses: actions/checkout@v3
- name: install rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: install wasm-pack # install from fork until this is merged: https://github.com/rustwasm/wasm-pack/pull/1185
run: |
# replace with "install wasm-pack action", which doesn't work for this project because of https://github.com/rustwasm/wasm-pack/issues/1180
# - name: install wasm-pack
# uses: jetli/wasm-pack-action@v0.4.0
cargo install --git https://github.com/haraldreingruber/wasm-pack wasm-pack
- name: execute tests
run: |
cd wgpu
wasm-pack test --headless --chrome --features webgl
gpu-test:
strategy:
fail-fast: false

View File

@ -192,6 +192,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non
- Update the `minimum supported rust version` to 1.64
- Use cargo 1.64 workspace inheritance feature. By @jinleili in [#3107](https://github.com/gfx-rs/wgpu/pull/3107)
- Move `ResourceMetadata` into its own module. By @jimblandy in [#3213](https://github.com/gfx-rs/wgpu/pull/3213)
- Add WebAssembly testing infrastructure. By @haraldreingruber in [#3238](https://github.com/gfx-rs/wgpu/pull/3238)
#### Vulkan

25
Cargo.lock generated
View File

@ -2638,6 +2638,30 @@ version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d2fff962180c3fadf677438054b1db62bee4aa32af26a45388af07d1287e1d"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4683da3dfc016f704c9f82cf401520c4f1cb3ee440f7f52b3d6ac29506a49ca7"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "wasm-bindgen-threads-xform"
version = "0.2.83"
@ -2815,6 +2839,7 @@ dependencies = [
"static_assertions",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test",
"web-sys",
"wgpu-core",
"wgpu-hal",

View File

@ -112,6 +112,7 @@ console_log = "0.2"
js-sys = "0.3.60"
wasm-bindgen = "0.2.83"
wasm-bindgen-futures = "0.4.33"
wasm-bindgen-test = "0.3"
web-sys = "0.3.60"
# deno dependencies

View File

@ -163,7 +163,8 @@ obj.workspace = true
pollster.workspace = true
png.workspace = true
nanorand = { workspace = true, features = ["wyrand"] }
winit.workspace = true # for "halmark" example # for "halmark" example
wasm-bindgen-test.workspace = true
winit.workspace = true # for "halmark" example
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
async-executor.workspace = true
@ -320,12 +321,13 @@ parking_lot.workspace = true
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
console_error_panic_hook.workspace = true
console_log.workspace = true
# We need these features in the framework examples
# We need these features in the framework examples and tests
web-sys = { workspace = true, features = [
"Location",
"Blob",
"RequestInit",
"RequestMode",
"Request",
"Response"
"Response",
"WebGl2RenderingContext"
] }

View File

@ -1,11 +1,13 @@
//! Tests for buffer usages validation.
use crate::common::{fail_if, initialize_test, TestParameters};
use wasm_bindgen_test::*;
use wgt::BufferAddress;
const BUFFER_SIZE: BufferAddress = 1234;
#[test]
#[wasm_bindgen_test]
fn buffer_usage() {
fn try_create(enable_mappable_primary_buffers: bool, usages: &[(bool, &[wgpu::BufferUsages])]) {
let mut parameters = TestParameters::default();

View File

@ -1,4 +1,5 @@
use crate::common::{initialize_test, TestParameters, TestingContext};
use wasm_bindgen_test::*;
use wgpu::util::align_to;
static TEXTURE_FORMATS_UNCOMPRESSED: &[wgpu::TextureFormat] = &[
@ -202,9 +203,11 @@ fn single_texture_clear_test(
size: wgpu::Extent3d,
dimension: wgpu::TextureDimension,
) {
println!(
log::info!(
"clearing texture with {:?}, dimension {:?}, size {:?}",
format, dimension, size
format,
dimension,
size
);
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {
@ -315,6 +318,7 @@ fn clear_texture_2d_uncompressed() {
}
#[test]
#[wasm_bindgen_test]
fn clear_texture_d32_s8() {
initialize_test(
TestParameters::default()

View File

@ -3,12 +3,13 @@
use std::panic::{catch_unwind, AssertUnwindSafe};
use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue, Surface};
use wgt::{Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits};
use wgpu::{util, Adapter, Device, DownlevelFlags, Instance, Queue};
pub mod image;
const CANVAS_ID: &str = "test-canvas";
async fn initialize_device(
adapter: &Adapter,
features: Features,
@ -168,16 +169,12 @@ impl TestParameters {
}
pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(TestingContext)) {
// We don't actually care if it fails
#[cfg(not(target_arch = "wasm32"))]
let _ = env_logger::try_init();
#[cfg(target_arch = "wasm32")]
let _ = console_log::init_with_level(log::Level::Info);
let backend_bits = util::backend_bits_from_env().unwrap_or_else(Backends::all);
let instance = Instance::new(backend_bits);
let adapter = pollster::block_on(util::initialize_adapter_from_env_or_default(
&instance,
backend_bits,
None,
))
.expect("could not find suitable adapter on the system");
let (adapter, _) = initialize_adapter();
let adapter_info = adapter.get_info();
let adapter_lowercase_name = adapter_info.name.to_lowercase();
@ -187,19 +184,19 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
let missing_features = parameters.required_features - adapter_features;
if !missing_features.is_empty() {
println!("TEST SKIPPED: MISSING FEATURES {:?}", missing_features);
log::info!("TEST SKIPPED: MISSING FEATURES {:?}", missing_features);
return;
}
if !parameters.required_limits.check_limits(&adapter_limits) {
println!("TEST SKIPPED: LIMIT TOO LOW");
log::info!("TEST SKIPPED: LIMIT TOO LOW");
return;
}
let missing_downlevel_flags =
parameters.required_downlevel_properties.flags - adapter_downlevel_capabilities.flags;
if !missing_downlevel_flags.is_empty() {
println!(
log::info!(
"TEST SKIPPED: MISSING DOWNLEVEL FLAGS {:?}",
missing_downlevel_flags
);
@ -209,7 +206,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
if adapter_downlevel_capabilities.shader_model
< parameters.required_downlevel_properties.shader_model
{
println!(
log::info!(
"TEST SKIPPED: LOW SHADER MODEL {:?}",
adapter_downlevel_capabilities.shader_model
);
@ -273,7 +270,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
});
if let Some((reason, true)) = expected_failure_reason {
println!("EXPECTED TEST FAILURE SKIPPED: {:?}", reason);
log::info!("EXPECTED TEST FAILURE SKIPPED: {:?}", reason);
return;
}
@ -301,9 +298,10 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
// We got the conditions we expected
if let Some((expected_reason, _)) = expected_failure_reason {
// Print out reason for the failure
println!(
log::info!(
"GOT EXPECTED TEST FAILURE DUE TO {}: {:?}",
failure_cause, expected_reason
failure_cause,
expected_reason
);
}
} else if let Some((reason, _)) = expected_failure_reason {
@ -314,6 +312,72 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te
}
}
fn initialize_adapter() -> (Adapter, SurfaceGuard) {
let backend_bits = wgpu::util::backend_bits_from_env().unwrap_or_else(Backends::all);
let instance = Instance::new(backend_bits);
let compatible_surface;
#[cfg(not(all(target_arch = "wasm32", feature = "webgl")))]
{
compatible_surface = None;
}
#[cfg(all(target_arch = "wasm32", feature = "webgl"))]
{
// On wasm, append a canvas to the document body for initializing the adapter
let canvas = create_html_canvas();
let surface = instance
.create_surface_from_canvas(&canvas)
.expect("could not create surface from canvas");
compatible_surface = Some(surface);
}
let compatible_surface: Option<&Surface> = compatible_surface.as_ref();
let adapter = pollster::block_on(wgpu::util::initialize_adapter_from_env_or_default(
&instance,
backend_bits,
compatible_surface,
))
.expect("could not find suitable adapter on the system");
(adapter, SurfaceGuard)
}
struct SurfaceGuard;
#[cfg(all(target_arch = "wasm32", feature = "webgl"))]
impl Drop for SurfaceGuard {
fn drop(&mut self) {
delete_html_canvas();
}
}
#[cfg(all(target_arch = "wasm32", feature = "webgl"))]
fn create_html_canvas() -> web_sys::HtmlCanvasElement {
use wasm_bindgen::JsCast;
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| {
let body = doc.body().unwrap();
let canvas = doc.create_element("Canvas").unwrap();
canvas.set_id(CANVAS_ID);
body.append_child(&canvas).unwrap();
canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
})
.expect("couldn't append canvas to document body")
}
#[cfg(all(target_arch = "wasm32", feature = "webgl"))]
fn delete_html_canvas() {
if let Some(document) = web_sys::window().and_then(|win| win.document()) {
if let Some(element) = document.get_element_by_id(CANVAS_ID) {
element.remove();
}
};
}
// Run some code in an error scope and assert that validation fails.
pub fn fail<T>(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T {
device.push_error_scope(wgpu::ErrorFilter::Validation);

View File

@ -1,6 +1,9 @@
use wasm_bindgen_test::*;
use crate::common::{initialize_test, TestParameters};
#[test]
#[wasm_bindgen_test]
fn device_initialization() {
initialize_test(TestParameters::default(), |_ctx| {
// intentionally empty

View File

@ -1,6 +1,8 @@
use crate::common::{initialize_test, TestParameters};
use wasm_bindgen_test::*;
#[test]
#[wasm_bindgen_test]
fn drop_encoder() {
initialize_test(TestParameters::default(), |ctx| {
let encoder = ctx

View File

@ -1,4 +1,7 @@
use wasm_bindgen_test::*;
#[test]
#[wasm_bindgen_test]
fn initialize() {
let _ = wgpu::Instance::new(
wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all),

View File

@ -7,6 +7,7 @@ use wgpu::{
};
use crate::common::{initialize_test, TestParameters, TestingContext};
use wasm_bindgen_test::*;
fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer {
let buffer = ctx.device.create_buffer(&BufferDescriptor {
@ -53,6 +54,7 @@ fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer {
}
#[test]
#[wasm_bindgen_test]
fn wait() {
initialize_test(TestParameters::default().skip(), |ctx| {
let cmd_buf = generate_dummy_work(&ctx);
@ -63,6 +65,7 @@ fn wait() {
}
#[test]
#[wasm_bindgen_test]
fn double_wait() {
initialize_test(TestParameters::default().skip(), |ctx| {
let cmd_buf = generate_dummy_work(&ctx);
@ -74,6 +77,7 @@ fn double_wait() {
}
#[test]
#[wasm_bindgen_test]
fn wait_on_submission() {
initialize_test(TestParameters::default().skip(), |ctx| {
let cmd_buf = generate_dummy_work(&ctx);
@ -84,6 +88,7 @@ fn wait_on_submission() {
}
#[test]
#[wasm_bindgen_test]
fn double_wait_on_submission() {
initialize_test(TestParameters::default().skip(), |ctx| {
let cmd_buf = generate_dummy_work(&ctx);
@ -95,6 +100,7 @@ fn double_wait_on_submission() {
}
#[test]
#[wasm_bindgen_test]
fn wait_out_of_order() {
initialize_test(TestParameters::default().skip(), |ctx| {
let cmd_buf1 = generate_dummy_work(&ctx);

View File

@ -3,8 +3,10 @@
use std::num::NonZeroU32;
use crate::common::{fail, initialize_test, TestParameters};
use wasm_bindgen_test::*;
#[test]
#[wasm_bindgen_test]
fn queue_write_texture_overflow() {
initialize_test(TestParameters::default(), |ctx| {
let texture = ctx.device.create_texture(&wgpu::TextureDescriptor {

View File

@ -1,7 +1,9 @@
use crate::common::{initialize_test, TestParameters};
use wasm_bindgen_test::*;
/// Buffer's size and usage can be read back.
#[test]
#[wasm_bindgen_test]
fn buffer_size_and_usage() {
initialize_test(TestParameters::default(), |ctx| {
let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {

View File

@ -1,6 +1,8 @@
use crate::common::{fail, initialize_test, valid, TestParameters};
use wasm_bindgen_test::*;
#[test]
#[wasm_bindgen_test]
fn bad_buffer() {
// Create a buffer with bad parameters and call a few methods.
// Validation should fail but there should be not panic.
@ -24,6 +26,7 @@ fn bad_buffer() {
}
#[test]
#[wasm_bindgen_test]
fn bad_texture() {
// Create a texture with bad parameters and call a few methods.
// Validation should fail but there should be not panic.

View File

@ -1,3 +1,5 @@
use wasm_bindgen_test::wasm_bindgen_test_configure;
// All files containing tests
mod common;
@ -20,3 +22,5 @@ mod transfer;
mod vertex_indices;
mod write_texture;
mod zero_init_texture_after_discard;
wasm_bindgen_test_configure!(run_in_browser);

View File

@ -1,3 +1,4 @@
use wasm_bindgen_test::*;
use wgpu::{DownlevelFlags, Limits};
use crate::{
@ -40,6 +41,7 @@ fn create_numeric_builtin_test() -> Vec<ShaderTest> {
}
#[test]
#[wasm_bindgen_test]
fn numeric_builtins() {
initialize_test(
TestParameters::default()

View File

@ -1,5 +1,6 @@
use std::fmt::Write;
use wasm_bindgen_test::*;
use wgpu::{Backends, DownlevelFlags, Features, Limits};
use crate::{
@ -177,6 +178,7 @@ fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec<ShaderTest>
}
#[test]
#[wasm_bindgen_test]
fn uniform_input() {
initialize_test(
TestParameters::default()
@ -193,6 +195,7 @@ fn uniform_input() {
}
#[test]
#[wasm_bindgen_test]
fn storage_input() {
initialize_test(
TestParameters::default()
@ -209,6 +212,7 @@ fn storage_input() {
}
#[test]
#[wasm_bindgen_test]
fn push_constant_input() {
initialize_test(
TestParameters::default()

View File

@ -1,5 +1,6 @@
use crate::common::{initialize_test, TestParameters, TestingContext};
use std::num::NonZeroU32;
use wasm_bindgen_test::*;
use wgpu::util::{align_to, DeviceExt};
//
@ -37,6 +38,7 @@ use wgpu::util::{align_to, DeviceExt};
// buffer [3, 4, 5, 0, 1, 2]. This also swaps the resulting pixel colors.
//
#[test]
#[wasm_bindgen_test]
fn draw() {
//
// +-----+-----+
@ -61,6 +63,7 @@ fn draw() {
}
#[test]
#[wasm_bindgen_test]
fn draw_indexed() {
//
// +-----+-----+

View File

@ -1,30 +1,32 @@
//! Tests for texture copy bounds checks.
use crate::common::{initialize_test, TestParameters};
use crate::common::{fail_if, initialize_test, TestParameters};
use std::num::NonZeroU32;
use wasm_bindgen_test::*;
#[test]
#[wasm_bindgen_test]
fn bad_copy_origin() {
fn try_origin(origin: wgpu::Origin3d, size: wgpu::Extent3d, should_panic: bool) {
let mut parameters = TestParameters::default();
if should_panic {
parameters = parameters.failure();
}
let parameters = TestParameters::default();
initialize_test(parameters, |ctx| {
let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR);
let data = vec![255; BUFFER_SIZE as usize];
ctx.queue.write_texture(
wgpu::ImageCopyTexture {
texture: &texture,
mip_level: 0,
origin,
aspect: wgpu::TextureAspect::All,
},
&data,
BUFFER_COPY_LAYOUT,
size,
);
fail_if(&ctx.device, should_panic, || {
ctx.queue.write_texture(
wgpu::ImageCopyTexture {
texture: &texture,
mip_level: 0,
origin,
aspect: wgpu::TextureAspect::All,
},
&data,
BUFFER_COPY_LAYOUT,
size,
)
});
});
}

View File

@ -1,5 +1,6 @@
use std::num::NonZeroU64;
use wasm_bindgen_test::*;
use wgpu::util::DeviceExt;
use crate::common::{initialize_test, TestParameters, TestingContext};
@ -131,6 +132,7 @@ fn pulling_common(
}
#[test]
#[wasm_bindgen_test]
fn draw() {
initialize_test(TestParameters::default().test_features_limits(), |ctx| {
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
@ -140,6 +142,7 @@ fn draw() {
}
#[test]
#[wasm_bindgen_test]
fn draw_vertex_offset() {
initialize_test(
TestParameters::default()
@ -155,6 +158,7 @@ fn draw_vertex_offset() {
}
#[test]
#[wasm_bindgen_test]
fn draw_instanced() {
initialize_test(TestParameters::default().test_features_limits(), |ctx| {
pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| {
@ -164,6 +168,7 @@ fn draw_instanced() {
}
#[test]
#[wasm_bindgen_test]
fn draw_instanced_offset() {
initialize_test(
TestParameters::default()

View File

@ -3,8 +3,10 @@
use crate::common::{initialize_test, TestParameters};
use std::num::NonZeroU32;
use wasm_bindgen_test::*;
#[test]
#[wasm_bindgen_test]
fn write_texture_subset() {
let size = 256;
let parameters = TestParameters::default().backend_failure(wgpu::Backends::DX12);

View File

@ -1,9 +1,11 @@
use std::num::NonZeroU32;
use crate::common::{initialize_test, TestParameters};
use wasm_bindgen_test::*;
// Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in after submit of the encoder.
#[test]
#[wasm_bindgen_test]
fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_after_submit() {
initialize_test(TestParameters::default(), |ctx| {
let (texture, readback_buffer) =
@ -39,6 +41,7 @@ fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_after
// Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in the same encoder to a buffer.
#[test]
#[wasm_bindgen_test]
fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_in_same_encoder() {
initialize_test(TestParameters::default(), |ctx| {
let (texture, readback_buffer) =
@ -67,6 +70,7 @@ fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_in_sa
}
#[test]
#[wasm_bindgen_test]
#[allow(clippy::single_element_loop)]
fn discarding_depth_target_resets_texture_init_state_check_visible_on_copy_in_same_encoder() {
initialize_test(
@ -109,6 +113,7 @@ fn discarding_depth_target_resets_texture_init_state_check_visible_on_copy_in_sa
}
#[test]
#[wasm_bindgen_test]
fn discarding_either_depth_or_stencil_aspect() {
initialize_test(TestParameters::default(), |ctx| {
let (texture, _) = create_white_texture_and_readback_buffer(