Skip to content

Commit

Permalink
Test setup for WebAssembly+WebGL (#3238)
Browse files Browse the repository at this point in the history
Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
  • Loading branch information
haraldreingruber and cwfitzgerald authored Dec 9, 2022
1 parent 85fda2d commit f0f700c
Show file tree
Hide file tree
Showing 23 changed files with 211 additions and 38 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions wgpu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
] }
2 changes: 2 additions & 0 deletions wgpu/tests/buffer_usages.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand Down
8 changes: 6 additions & 2 deletions wgpu/tests/clear_texture.rs
Original file line number Diff line number Diff line change
@@ -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] = &[
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -315,6 +318,7 @@ fn clear_texture_2d_uncompressed() {
}

#[test]
#[wasm_bindgen_test]
fn clear_texture_d32_s8() {
initialize_test(
TestParameters::default()
Expand Down
98 changes: 81 additions & 17 deletions wgpu/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand All @@ -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
);
Expand All @@ -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
);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions wgpu/tests/device.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 2 additions & 0 deletions wgpu/tests/encoder.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 3 additions & 0 deletions wgpu/tests/instance.rs
Original file line number Diff line number Diff line change
@@ -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),
Expand Down
6 changes: 6 additions & 0 deletions wgpu/tests/poll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
Loading

0 comments on commit f0f700c

Please sign in to comment.