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 7 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
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,33 @@ 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 Wasm
haraldreingruber marked this conversation as resolved.
Show resolved Hide resolved
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
# 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
#
# install from fork until this is merged: https://github.com/rustwasm/wasm-pack/pull/1185
haraldreingruber marked this conversation as resolved.
Show resolved Hide resolved
- name: install wasm-pack
run: |
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
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" # dev-dependency
haraldreingruber marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -126,7 +126,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 @@ -283,12 +284,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"
] }
1 change: 1 addition & 0 deletions wgpu/tests/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ mod shader;
mod shader_primitive_index;
mod texture_bounds;
mod vertex_indices;
mod wasm;
mod write_texture;
mod zero_init_texture_after_discard;
11 changes: 11 additions & 0 deletions wgpu/tests/shader.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@vertex
fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
let x = f32(i32(in_vertex_index) - 1);
let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
return vec4<f32>(x, y, 0.0, 1.0);
}

@fragment
fn fs_main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
}
210 changes: 210 additions & 0 deletions wgpu/tests/wasm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#![cfg(target_arch = "wasm32")]

use std::borrow::Cow;

use wasm_bindgen_test::*;
use winit::{
event::Event,
event_loop::{ControlFlow, EventLoop},
platform::web::{EventLoopExtWebSys, WindowExtWebSys},
window::Window,
};

use wasm_bindgen::JsCast;
use web_sys::HtmlCanvasElement;
use wgpu::{Adapter, Device, Queue, Surface};

wasm_bindgen_test_configure!(run_in_browser);

#[wasm_bindgen_test]
async fn test_triangle_rendering() {
render_triangle(|window| {
let size = window.inner_size();

// fetch triangle pixel
let result = read_pixel(
window.canvas(),
(size.width as f32 * 0.5) as i32,
(size.height as f32 * 0.5) as i32,
);
let red = [255, 0, 0, 255_u8];
assert_eq!(result, red);

// fetch background pixel
let result = read_pixel(
window.canvas(),
(size.width as f32 * 0.1) as i32,
(size.height as f32 * 0.9) as i32,
);
let green = [0, 255, 0, 255_u8];
assert_eq!(result, green);
})
.await;
}

async fn render_triangle<F>(assert_rendering_result: F)
where
F: Fn(&Window) + 'static,
{
let (window, event_loop, surface, adapter, device, queue): (
Window,
EventLoop<()>,
Surface,
Adapter,
Device,
Queue,
) = init().await;

let size = window.inner_size();

// Load the shaders from disk
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))),
});

let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[],
push_constant_ranges: &[],
});

let swapchain_format = surface.get_supported_formats(&adapter)[0];

let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
buffers: &[],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
targets: &[Some(swapchain_format.into())],
}),
primitive: wgpu::PrimitiveState::default(),
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
});

let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: swapchain_format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Fifo,
alpha_mode: surface.get_supported_alpha_modes(&adapter)[0],
};

surface.configure(&device, &config);

event_loop.spawn(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
if let Event::RedrawRequested(_) = event {
let frame = surface
.get_current_texture()
.expect("Failed to acquire next swap chain texture");
let view = frame
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder =
device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::GREEN),
store: true,
},
})],
depth_stencil_attachment: None,
});
rpass.set_pipeline(&render_pipeline);
rpass.draw(0..3, 0..1);
}

queue.submit(Some(encoder.finish()));
frame.present();

assert_rendering_result(&window);

*control_flow = ControlFlow::Exit;
}
});
}

fn read_pixel(canvas: HtmlCanvasElement, x: i32, y: i32) -> [u8; 4] {
let mut result = [0_u8; 4];
let context = canvas
.get_context("webgl2")
.unwrap()
.unwrap()
.dyn_into::<web_sys::WebGl2RenderingContext>()
.unwrap();

context
.read_pixels_with_u8_array_and_dst_offset(
x,
y,
1,
1,
web_sys::WebGl2RenderingContext::RGBA,
web_sys::WebGl2RenderingContext::UNSIGNED_BYTE,
&mut result,
0,
)
.unwrap();
result
}

async fn init() -> (Window, EventLoop<()>, Surface, Adapter, Device, Queue) {
let event_loop = EventLoop::new();
haraldreingruber marked this conversation as resolved.
Show resolved Hide resolved
let window = winit::window::Window::new(&event_loop).unwrap();

std::panic::set_hook(Box::new(console_error_panic_hook::hook));
console_log::init().expect("could not initialize logger");
// On wasm, append the canvas to the document body
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| doc.body())
.and_then(|body| {
body.append_child(&web_sys::Element::from(window.canvas()))
.ok()
})
.expect("couldn't append canvas to document body");
haraldreingruber marked this conversation as resolved.
Show resolved Hide resolved

let instance = wgpu::Instance::new(wgpu::Backends::GL);
let surface = unsafe { instance.create_surface(&window) };
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
force_fallback_adapter: false,
// Request an adapter which can render to our surface
compatible_surface: Some(&surface),
})
.await
.expect("Failed to find an appropriate adapter");

// Create the logical device and command queue
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
limits: wgpu::Limits::downlevel_webgl2_defaults()
.using_resolution(adapter.limits()),
},
None,
)
.await
.expect("Failed to create device");

(window, event_loop, surface, adapter, device, queue)
}