-
Notifications
You must be signed in to change notification settings - Fork 185
Add WebGL browser tests to vello_sparse_tests
#1020
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
Changes from 3 commits
86e9573
7c52996
a45dbe5
ed896f7
1bf65e1
1cb46ba
e8ca89b
6b028c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,5 +28,27 @@ image = { workspace = true, features = ["png"] } | |
| skrifa = { workspace = true } | ||
| smallvec = { workspace = true } | ||
|
|
||
| [target.'cfg(target_arch = "wasm32")'.dependencies] | ||
| wasm-bindgen-test = "0.3.50" | ||
| web-sys = { version = "0.3.77", features = [ | ||
| "HtmlCanvasElement", | ||
| "WebGl2RenderingContext", | ||
| "Document", | ||
| "Window", | ||
| "Element", | ||
| "HtmlElement", | ||
| "HtmlImageElement", | ||
| "Blob", | ||
| "BlobPropertyBag", | ||
| "Url", | ||
| ] } | ||
| wasm-bindgen = "0.2.100" | ||
|
|
||
| [build-dependencies] | ||
| quote = { workspace = true } | ||
|
|
||
| [features] | ||
| webgl = ["vello_hybrid/webgl"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This ensures that we're testing the vello_hybrid WebGL2 rendering backend by activating the |
||
|
|
||
| [lints] | ||
| workspace = true | ||
ajakubowicz-canva marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <div align="center"> | ||
|
|
||
| # Vello Sparse Tests | ||
|
|
||
| [](#license) | ||
| \ | ||
| [](https://xi.zulipchat.com/#narrow/channel/197075-vello) | ||
| [](https://github.com/linebender/vello/actions) | ||
|
|
||
| </div> | ||
|
|
||
| This is a development only crate for testing the sparse_strip renderers across a corpus of reference | ||
| images: | ||
| - cpu | ||
| - wgpu | ||
| - wasm32 WebGL | ||
|
|
||
| ## Testing WebGL on the Browser | ||
|
|
||
| To run the `vello_sparse_tests` suite on WebGL headless: | ||
|
|
||
| ```sh | ||
| wasm-pack test --headless --chrome --features webgl | ||
ajakubowicz-canva marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| To debug the output images in webgl, run the same command without `--headless`. Any tests that fail | ||
| will have their diff image appended to the bottom of the page. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| // Copyright 2025 the Vello Authors | ||
| // SPDX-License-Identifier: Apache-2.0 OR MIT | ||
|
|
||
| //! Build step for `vello_sparse_tests`. | ||
| //! | ||
| //! This build step exists primarily for the wasm32 target testing the browser exclusive "webgl" | ||
| //! feature which cannot read snapshot images from the file system. Instead, the reference images | ||
| //! are inlined into the binary. | ||
| fn main() { | ||
| println!("cargo:rerun-if-changed=snapshots"); | ||
|
|
||
| // Target architecture for compilation must be queried via environment variables. | ||
| // Exit early if not building for `wasm32`. | ||
| if std::env::var("CARGO_CFG_TARGET_ARCH").as_deref() != Ok("wasm32") { | ||
| return; | ||
| } | ||
|
|
||
| use quote::quote; | ||
| use std::fs; | ||
| use std::path::Path; | ||
|
|
||
| let out_dir = std::env::var("OUT_DIR").unwrap(); | ||
| let dest_path = Path::new(&out_dir).join("reference_images_wasm.rs"); | ||
|
|
||
| let snapshot_dir = Path::new("snapshots"); | ||
| let mut match_arms = Vec::new(); | ||
|
|
||
| if snapshot_dir.exists() { | ||
| for entry in fs::read_dir(snapshot_dir).unwrap() { | ||
| let entry = entry.unwrap(); | ||
| let path = entry.path(); | ||
| if path.extension().and_then(|s| s.to_str()) == Some("png") { | ||
| let name = path.file_stem().unwrap().to_str().unwrap(); | ||
| let name_str = name.to_string(); | ||
| let path_str = format!("/snapshots/{}.png", name); | ||
|
|
||
| match_arms.push(quote! { | ||
| #name_str => Some(include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), #path_str))), | ||
| }); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let generated = quote! { | ||
| /// Generated by build.rs: Inlined reference images for WASM tests. | ||
| pub(crate) mod reference_images_wasm { | ||
| pub(crate) fn get_reference_image(name: &str) -> Option<&'static [u8]> { | ||
| match name { | ||
| #(#match_arms)* | ||
| _ => None, | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| fs::write(&dest_path, generated.to_string()).unwrap(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,6 +219,7 @@ impl Renderer for Scene { | |
| // This method creates device resources every time it is called. This does not matter much for | ||
| // testing, but should not be used as a basis for implementing something real. This would be a | ||
| // very bad example for that. | ||
| #[cfg(not(all(target_arch = "wasm32", feature = "webgl")))] | ||
ajakubowicz-canva marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| fn render_to_pixmap(&self, pixmap: &mut Pixmap, _: RenderMode) { | ||
| // On some platforms using `cargo test` triggers segmentation faults in wgpu when the GPU | ||
| // tests are run in parallel (likely related to the number of device resources being | ||
|
|
@@ -362,6 +363,57 @@ impl Renderer for Scene { | |
| texture_copy_buffer.unmap(); | ||
| } | ||
|
|
||
| #[cfg(all(target_arch = "wasm32", feature = "webgl"))] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have we given much thought to the pattern we use for target specific implementations? We're currently using the below approach: #[cfg(TARGET_1)]
pub fn check_ref(...) { ... }
#[cfg(TARGET_2)]
pub fn check_ref(...) { ... }I suspect that, for better organisation and discovering all target implementations, something like the below may be better when we have separate function implementations. It's more code, but I think it might ease the addition of later targets (e.g. cpu webgl) and improves discoverability. pub(crate) fn check_ref(
...
) {
#[cfg(TARGET_1)]
check_ref_wgpu(...)
#[cfg(TARGET_2]
check_ref_webgl(...)
}
#[cfg(TARGET_1)]
fn check_ref_wgpu(...) { ... }
#[cfg(TARGET_2)]
fn check_ref_webgl(...) { ... }I think when we have sufficiently complex scenarios, it might be worth using a module splitting approach. But perhaps for small functions, the above is better? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tracking this great refactor as followup work that I'll include in the PR resolving this issue: #1025 (comment) |
||
| fn render_to_pixmap(&self, pixmap: &mut Pixmap, _: RenderMode) { | ||
| use wasm_bindgen::JsCast; | ||
| use web_sys::{HtmlCanvasElement, WebGl2RenderingContext}; | ||
|
|
||
| let width = self.width(); | ||
| let height = self.height(); | ||
|
|
||
| // Create an offscreen HTMLCanvasElement, render the test image to it, and finally read off | ||
| // the pixmap for diff checking. | ||
| let document = web_sys::window().unwrap().document().unwrap(); | ||
|
|
||
| let canvas = document | ||
| .create_element("canvas") | ||
| .unwrap() | ||
| .dyn_into::<HtmlCanvasElement>() | ||
| .unwrap(); | ||
|
|
||
| canvas.set_width(width.into()); | ||
| canvas.set_height(height.into()); | ||
|
|
||
| let mut renderer = vello_hybrid::WebGlRenderer::new(&canvas); | ||
| let render_size = vello_hybrid::RenderSize { | ||
| width: width.into(), | ||
| height: height.into(), | ||
| }; | ||
|
|
||
| renderer.render(self, &render_size).unwrap(); | ||
|
|
||
| let gl = canvas | ||
| .get_context("webgl2") | ||
| .unwrap() | ||
| .unwrap() | ||
| .dyn_into::<WebGl2RenderingContext>() | ||
| .unwrap(); | ||
| let mut pixels = vec![0_u8; (width as usize) * (height as usize) * 4]; | ||
| gl.read_pixels_with_opt_u8_array( | ||
| 0, | ||
| 0, | ||
| width.into(), | ||
| height.into(), | ||
| WebGl2RenderingContext::RGBA, | ||
| WebGl2RenderingContext::UNSIGNED_BYTE, | ||
| Some(&mut pixels), | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| let pixmap_data = pixmap.data_as_u8_slice_mut(); | ||
| pixmap_data.copy_from_slice(&pixels); | ||
| } | ||
|
|
||
| fn width(&self) -> u16 { | ||
| Self::width(self) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the vello hybrid webgl tests and LFS. Without LFS the build system can't find any images to compile into the WASM test binary.