Upgrade WGPU and Linebender dependencies (#4154)

* Upgrade WGPU and Linebender dependencies

* Fix CI

* Code review fixes

* Fix again
This commit is contained in:
Keavon Chambers 2026-05-16 19:36:26 -07:00
parent c4a978009a
commit 822b9009a6
23 changed files with 418 additions and 358 deletions

View File

@ -1,3 +1,7 @@
[build]
# Keep `--cfg=web_sys_unstable_apis` here so the wasm wrapper crates build with the same web-sys API signatures (e.g. `put_image_data`/`get_image_data` taking `i32` rather than `f64`) on both native test builds and the wasm target. Cargo applies `[build]` rustflags only when no target-specific rustflags table matches, so the wasm-specific list below must continue to include this cfg.
rustflags = ["--cfg=web_sys_unstable_apis"]
[target.wasm32-unknown-unknown]
rustflags = [
# Currently disabled because of https://github.com/GraphiteEditor/Graphite/issues/1262

View File

@ -6,7 +6,7 @@
},
"ghcr.io/devcontainers/features/node:1": {}
},
"onCreateCommand": "cargo install cargo-watch wasm-pack cargo-about && cargo install -f wasm-bindgen-cli@0.2.100",
"onCreateCommand": "cargo install cargo-watch wasm-pack cargo-about && cargo install -f wasm-bindgen-cli@0.2.121",
"customizations": {
"vscode": {
// NOTE: Keep this in sync with `.vscode/extensions.json`

View File

@ -283,7 +283,7 @@ jobs:
pull-requests: write
env:
WASM_BINDGEN_CLI_VERSION: "0.2.100"
WASM_BINDGEN_CLI_VERSION: "0.2.121"
steps:
- name: 📥 Clone repository
@ -474,7 +474,7 @@ jobs:
pull-requests: write
env:
WASM_BINDGEN_CLI_VERSION: "0.2.100"
WASM_BINDGEN_CLI_VERSION: "0.2.121"
steps:
- name: 📥 Clone repository

View File

@ -57,7 +57,8 @@ jobs:
- name: 🧪 Run Rust tests
env:
RUSTFLAGS: -Dwarnings
# `--cfg=web_sys_unstable_apis` mirrors the `[build]` section of `.cargo/config.toml`
RUSTFLAGS: "-Dwarnings --cfg=web_sys_unstable_apis"
run: mold -run cargo test --all-features
# Rust format check on GitHub runner

View File

@ -16,7 +16,10 @@ let
pkgs = import inputs.nixpkgs {
inherit system;
overlays = [ (import inputs.rust-overlay) ];
overlays = [
(import inputs.rust-overlay)
(import ./overlays/wasm-bindgen-cli.nix)
];
};
info = {

View File

@ -13,6 +13,7 @@ let
pkgs.libGL
pkgs.openssl
pkgs.libraw
pkgs.fontconfig
# X11 Support
pkgs.libxkbcommon
@ -29,7 +30,7 @@ pkgs.mkShell (
pkgs.lld
pkgs.nodejs
pkgs.binaryen
pkgs.wasm-bindgen-cli_0_2_100
pkgs.wasm-bindgen-cli_0_2_121
pkgs.wasm-pack
pkgs.cargo-about

View File

@ -0,0 +1,20 @@
# Backport of nixpkgs's `wasm-bindgen-cli_0_2_121` for the nixpkgs revision
# pinned in flake.lock (which ships only up to `_0_2_117`). Mirrors upstream
# `pkgs/by-name/wa/wasm-bindgen-cli_0_2_121/package.nix` verbatim. Same
# `buildWasmBindgenCli` helper, same hashes. Drop this overlay once flake.lock
# is bumped to a nixpkgs that exposes the attribute directly.
final: _prev: {
wasm-bindgen-cli_0_2_121 = final.buildWasmBindgenCli rec {
src = final.fetchCrate {
pname = "wasm-bindgen-cli";
version = "0.2.121";
hash = "sha256-ZOMgFNOcGkO66Jz/Z83eoIu+DIzo3Z/vq6Z5g6BDY/w=";
};
cargoDeps = final.rustPlatform.fetchCargoVendor {
inherit src;
inherit (src) pname version;
hash = "sha256-DPdCDPTAPBrbqLUqnCwQu1dePs9lGg85JCJOCIr9qjU=";
};
};
}

View File

@ -20,6 +20,7 @@ let
pkgs.libGL
pkgs.openssl
pkgs.libraw
pkgs.fontconfig
# X11 Support
pkgs.libxkbcommon
pkgs.libXcursor
@ -83,7 +84,7 @@ deps.crane.lib.buildPackage (
pkgs.lld
pkgs.nodejs
pkgs.binaryen
pkgs.wasm-bindgen-cli_0_2_100
pkgs.wasm-bindgen-cli_0_2_121
pkgs.wasm-pack
pkgs.cargo-about
pkgs.removeReferencesTo

636
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -120,17 +120,17 @@ quote = "1.0"
chrono = "0.4"
ron = "0.12"
fastnoise-lite = "1.1"
wgpu = { version = "28.0", features = [
wgpu = { version = "29.0", features = [
# We don't have wgpu on multiple threads (yet) https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md#wgpu-types-now-send-sync-on-wasm
"fragile-send-sync-non-atomic-wasm",
"spirv",
"strict_asserts",
] }
once_cell = "1.13" # Remove and replace with `core::cell::LazyCell` (<https://doc.rust-lang.org/core/cell/struct.LazyCell.html>)
wasm-bindgen = "=0.2.100" # NOTICE: ensure this stays in sync with the `wasm-bindgen-cli` version in `website/content/volunteer/guide/project-setup/_index.md`. We pin this version because wasm-bindgen upgrades may break various things.
wasm-bindgen = "=0.2.121" # NOTICE: keep in sync with the `wasm-bindgen-cli` version pinned across CI workflows, devcontainer, Nix, and the `cargo-run` tool. We pin this version because wasm-bindgen upgrades may break various things.
wasm-bindgen-futures = "0.4"
js-sys = "=0.3.77"
web-sys = { version = "=0.3.77", features = [
js-sys = "=0.3.98"
web-sys = { version = "=0.3.98", features = [
"Document",
"DomRect",
"Element",
@ -154,12 +154,12 @@ url = "2.5"
tokio = { version = "1.29", features = ["fs", "macros", "io-std", "rt", "rt-multi-thread"] }
# Linebender ecosystem (BEGIN)
kurbo = { version = "0.13", features = ["serde"] }
vello = "0.8"
vello_encoding = "0.8"
vello = "0.9"
vello_encoding = "0.9"
resvg = "0.47"
usvg = "0.47"
parley = "0.6"
skrifa = "0.40"
parley = "0.9"
skrifa = "0.42"
polycool = "0.4"
color = "0.3"
# Linebender ecosystem (END)
@ -203,8 +203,8 @@ gungraun = { version = "0.18" }
ndarray = "0.16"
strum = { version = "0.27", features = ["derive"] }
dirs = "6.0"
cef = "147"
cef-dll-sys = "147"
cef = "148"
cef-dll-sys = "148"
include_dir = "0.7"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing = "0.1"

View File

@ -582,14 +582,10 @@ impl ApplicationHandler for App {
Err(RenderError::OutdatedUITextureError) => {
self.cef_context.notify_view_info_changed();
}
Err(RenderError::SurfaceError(wgpu::SurfaceError::Lost)) => {
Err(RenderError::SurfaceLost) => {
tracing::warn!("lost surface");
}
Err(RenderError::SurfaceError(wgpu::SurfaceError::OutOfMemory)) => {
tracing::error!("GPU out of memory");
self.exit(None);
}
Err(RenderError::SurfaceError(e)) => tracing::error!("Render error: {:?}", e),
Err(other) => tracing::error!("Render error: {:?}", other),
}
let _ = self.start_render_sender.try_send(());
}

View File

@ -120,7 +120,7 @@ impl RenderState {
let render_pipeline_layout = context.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render Pipeline Layout"),
bind_group_layouts: &[&texture_bind_group_layout],
bind_group_layouts: &[Some(&texture_bind_group_layout)],
immediate_size: size_of::<Immediates>() as u32,
});
@ -262,7 +262,17 @@ impl RenderState {
self.render_overlays(scene);
}
let output = self.surface.get_current_texture().map_err(RenderError::SurfaceError)?;
let (output, suboptimal) = match self.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(t) => (t, false),
// wgpu reports the swapchain no longer matches the underlying surface; present this frame and reconfigure after present, since `Surface::configure` panics while an acquired `SurfaceTexture` is still alive
wgpu::CurrentSurfaceTexture::Suboptimal(t) => (t, true),
// Window is minimized or behind another window: skip the frame silently and try again once it becomes visible
wgpu::CurrentSurfaceTexture::Occluded => return Ok(()),
wgpu::CurrentSurfaceTexture::Lost => return Err(RenderError::SurfaceLost),
wgpu::CurrentSurfaceTexture::Outdated => return Err(RenderError::SurfaceOutdated),
wgpu::CurrentSurfaceTexture::Timeout => return Err(RenderError::SurfaceTimeout),
wgpu::CurrentSurfaceTexture::Validation => return Err(RenderError::SurfaceValidation),
};
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
@ -308,6 +318,10 @@ impl RenderState {
window.pre_present_notify();
output.present();
if suboptimal {
self.surface.configure(&self.context.device, &self.config);
}
if ui_scale.is_some() {
return Err(RenderError::OutdatedUITextureError);
}
@ -349,9 +363,13 @@ impl RenderState {
}
}
#[derive(Debug)]
pub(crate) enum RenderError {
OutdatedUITextureError,
SurfaceError(wgpu::SurfaceError),
SurfaceLost,
SurfaceOutdated,
SurfaceTimeout,
SurfaceValidation,
}
#[repr(C)]

View File

@ -1040,7 +1040,7 @@ impl OverlayContext {
}
let image_data = web_sys::ImageData::new_with_u8_clamped_array_and_sh(wasm_bindgen::Clamped(&data), PATTERN_WIDTH as u32, PATTERN_HEIGHT as u32).unwrap();
pattern_context.put_image_data(&image_data, 0., 0.).unwrap();
pattern_context.put_image_data(&image_data, 0, 0).unwrap();
let pattern = self.render_context.create_pattern_with_offscreen_canvas(&pattern_canvas, "repeat").unwrap().unwrap();
self.push_path(subpaths, transform);

View File

@ -172,7 +172,7 @@ pub(crate) fn render_image_data_to_canvases(image_data: &[(u64, Image<SRGBA8>)])
let clamped_u8_data = wasm_bindgen::Clamped(u8_data);
match ImageData::new_with_u8_clamped_array_and_sh(clamped_u8_data, image.width, image.height) {
Ok(image_data_obj) => {
if context.put_image_data(&image_data_obj, 0., 0.).is_err() {
if context.put_image_data(&image_data_obj, 0, 0).is_err() {
error!("Failed to put image data on canvas for id: {placeholder_id}");
}
}

View File

@ -82,6 +82,7 @@ pub async fn export_document(
// Encode and write raster image when buffer is already provided
write_raster_image(output_path, file_type, data, width, height, transparent)?;
}
#[cfg(target_family = "wasm")]
other => {
return Err(format!("Unexpected render output type: {:?}. Expected Texture, Buffer for raster export or Svg for SVG export.", other).into());
}

View File

@ -113,7 +113,10 @@ impl CanvasSurface for CanvasSurfaceHandle {
},
);
let surface_texture = surface.get_current_texture().expect("Failed to get surface texture");
let surface_texture = match surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(t) | wgpu::CurrentSurfaceTexture::Suboptimal(t) => t,
other => panic!("Failed to get surface texture: {other:?}"),
};
encoder.copy_texture_to_texture(
wgpu::TexelCopyTextureInfoBase {

View File

@ -33,13 +33,13 @@ impl BackgroundCompositor {
let checker_rect_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("background_checker_rect_pipeline_layout"),
bind_group_layouts: &[&checker_bind_group_layout],
bind_group_layouts: &[Some(&checker_bind_group_layout)],
immediate_size: 0,
});
let checker_viewport_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("background_checker_viewport_pipeline_layout"),
bind_group_layouts: &[&checker_bind_group_layout],
bind_group_layouts: &[Some(&checker_bind_group_layout)],
immediate_size: 0,
});
@ -67,7 +67,7 @@ impl BackgroundCompositor {
let fullscreen_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("background_fullscreen_pipeline_layout"),
bind_group_layouts: &[&fullscreen_bind_group_layout],
bind_group_layouts: &[Some(&fullscreen_bind_group_layout)],
immediate_size: 0,
});

View File

@ -68,9 +68,9 @@ impl ContextBuilder {
}
impl ContextBuilder {
fn build_instance(&self) -> Instance {
Instance::new(&wgpu::InstanceDescriptor {
Instance::new(wgpu::InstanceDescriptor {
backends: self.backends,
..Default::default()
..wgpu::InstanceDescriptor::new_without_display_handle()
})
}
async fn request_adapter(&self, instance: &Instance) -> Option<Adapter> {

View File

@ -38,7 +38,7 @@ impl Resampler {
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("resample_pipeline_layout"),
bind_group_layouts: &[&bind_group_layout],
bind_group_layouts: &[Some(&bind_group_layout)],
..Default::default()
});

View File

@ -113,10 +113,10 @@ impl PerPixelAdjustGraphicsPipeline {
};
let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some(&format!("PerPixelAdjust {name} PipelineLayout")),
bind_group_layouts: &[&device.create_bind_group_layout(&BindGroupLayoutDescriptor {
bind_group_layouts: &[Some(&device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some(&format!("PerPixelAdjust {name} BindGroupLayout 0")),
entries,
})],
}))],
..Default::default()
});

View File

@ -245,7 +245,7 @@ where
.draw_image_with_html_image_element_and_dw_and_dh(&image_data, 0., 0., resolution.x as f64, resolution.y as f64)
.unwrap();
let rasterized = context.get_image_data(0., 0., resolution.x as f64, resolution.y as f64).unwrap();
let rasterized = context.get_image_data(0, 0, resolution.x as i32, resolution.y as i32).unwrap();
let image = Image::from_image_data(&rasterized.data().0, resolution.x as u32, resolution.y as u32);
List::new_from_item(

View File

@ -72,7 +72,9 @@ impl TextContext {
builder.push_default(StyleProperty::FontSize(typesetting.font_size as f32));
builder.push_default(StyleProperty::LetterSpacing(typesetting.character_spacing as f32));
builder.push_default(StyleProperty::FontStack(parley::FontStack::Single(parley::FontFamily::Named(std::borrow::Cow::Owned(font_family)))));
builder.push_default(StyleProperty::FontFamily(parley::FontFamily::Single(parley::FontFamilyName::Named(std::borrow::Cow::Owned(
font_family,
)))));
builder.push_default(StyleProperty::FontWeight(font_info.weight()));
builder.push_default(StyleProperty::FontStyle(font_info.style()));
builder.push_default(StyleProperty::FontWidth(font_info.width()));
@ -81,7 +83,7 @@ impl TextContext {
let mut layout: Layout<()> = builder.build(text);
layout.break_all_lines(typesetting.max_width.map(|mw| mw as f32));
layout.align(typesetting.max_width.map(|max_w| max_w as f32), typesetting.align.into(), AlignmentOptions::default());
layout.align(typesetting.align.into(), AlignmentOptions::default());
Some(layout)
}

View File

@ -50,8 +50,8 @@ fn requirements(task: &Task) -> Vec<Requirement> {
command: "wasm-bindgen",
args: &["--version"],
name: "wasm-bindgen-cli",
version: Some("0.2.100"),
install: Some("cargo install -f wasm-bindgen-cli@0.2.100"),
version: Some("0.2.121"),
install: Some("cargo install -f wasm-bindgen-cli@0.2.121"),
skip: Some(&|task| matches!(task.target, Target::Cli)),
},
Requirement {
@ -173,7 +173,11 @@ pub fn check(task: &Task) -> Result<(), Error> {
eprintln!(" {}", dep.install.unwrap());
}
eprintln!();
eprint!("Install them now? [Y/n] ");
if installable.len() == 1 {
eprint!("Install it now? [Y/n] ");
} else {
eprint!("Install them now? [Y/n] ");
}
let mut input = String::new();
std::io::stdin().read_line(&mut input).map_err(|e| Error::Io(e, "Failed to read from stdin".into()))?;