Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test setup for WebAssembly+WebGL #3238

Merged
merged 34 commits into from
Dec 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8b08c41
First wasm test experiment
haraldreingruber Nov 26, 2022
9dc1cf6
Extract render_triangle fn
haraldreingruber Nov 26, 2022
444c457
Add wasm-pack test to GH ci.yml
haraldreingruber Nov 26, 2022
86b4382
Fix broken (non-wasm) test
haraldreingruber Nov 26, 2022
26304b1
Fix fmt
haraldreingruber Nov 26, 2022
051e59d
Install wasm-pack from fork with fix from open PR
haraldreingruber Nov 26, 2022
242a1a7
Fix clippy
haraldreingruber Nov 26, 2022
4fdb8a0
Remove dependency comment
haraldreingruber Nov 27, 2022
f0aa87b
Adapting existing tests to be run with wasm in browser
haraldreingruber Dec 1, 2022
8a3f00f
Also run wasm tests for firefox
haraldreingruber Dec 1, 2022
fd91517
Fix clippy warnings
haraldreingruber Dec 1, 2022
7eee910
Revert "Also run wasm tests for firefox"
haraldreingruber Dec 1, 2022
6616195
Only configure wasm-bindgen-test once
haraldreingruber Dec 2, 2022
5d8065e
Rename GH Actions job
haraldreingruber Dec 2, 2022
c6d6589
Comment indentation
haraldreingruber Dec 2, 2022
9b57230
Unify initalize_adapter()
haraldreingruber Dec 2, 2022
0da5165
Implement SurfaceGuard
haraldreingruber Dec 2, 2022
553cca2
Use log instead of println...
haraldreingruber Dec 2, 2022
81c0480
Add more tests
haraldreingruber Dec 2, 2022
ca41272
Merge branch 'master' into wasm-test-prototype
haraldreingruber Dec 2, 2022
fd9c528
Fixes build because of merged master changes
haraldreingruber Dec 2, 2022
a56cc2d
Cargo fmt
haraldreingruber Dec 2, 2022
e54508c
Fix GH action syntax
haraldreingruber Dec 2, 2022
68b1795
Use log::info to be also visible in wasm test
haraldreingruber Dec 4, 2022
8140608
Update .github/workflows/ci.yml
cwfitzgerald Dec 7, 2022
3b34bcd
Implement PR feedback
haraldreingruber Dec 8, 2022
d79b16d
Revert "Revert "Also run wasm tests for firefox""
haraldreingruber Dec 8, 2022
521ff86
Add setup firefox step to CI script
haraldreingruber Dec 8, 2022
7529eb9
Revert "Add setup firefox step to CI script"
haraldreingruber Dec 8, 2022
cf4a05c
Test gfx.prefer-mesa-llvmpipe config for Firefox CI tests
haraldreingruber Dec 8, 2022
dda910a
Revert "Test gfx.prefer-mesa-llvmpipe config for Firefox CI tests"
haraldreingruber Dec 8, 2022
f4a4e1d
Revert "Also run wasm tests for firefox"
haraldreingruber Dec 8, 2022
da9eec8
Fixes test expected to fail for wasm (texture_bounds.rs)
haraldreingruber Dec 8, 2022
a79a3be
Add changelog
cwfitzgerald Dec 9, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,32 @@ jobs:
# build docs
cargo doc --target ${{ matrix.target }} -p wgpu -p wgpu-core -p wgpu-info -p player --all-features --no-deps

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 @@ -148,6 +148,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 @@ -130,7 +130,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 @@ -287,12 +288,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