From 729d10592f4e802f91c555c309ff3ba4b119f843 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Sun, 19 Apr 2026 07:42:52 +0200 Subject: fix: WebGL frame rendering and opencut-wasm 0.2.8 --- rust/wasm/Cargo.toml | 2 +- rust/wasm/src/compositor.rs | 52 ++++++++++++++++++++++++++++++--------------- rust/wasm/src/gpu.rs | 20 ++++++++++++++++- 3 files changed, 55 insertions(+), 19 deletions(-) (limited to 'rust/wasm') diff --git a/rust/wasm/Cargo.toml b/rust/wasm/Cargo.toml index 98e9ed30..d8bda107 100644 --- a/rust/wasm/Cargo.toml +++ b/rust/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "opencut-wasm" -version = "0.2.5" +version = "0.2.8" edition = "2024" description = "Shared video editor logic compiled to WebAssembly" repository = "https://github.com/opencut/opencut" diff --git a/rust/wasm/src/compositor.rs b/rust/wasm/src/compositor.rs index 55756a21..126c7468 100644 --- a/rust/wasm/src/compositor.rs +++ b/rust/wasm/src/compositor.rs @@ -131,22 +131,41 @@ pub fn render_frame(options: JsValue) -> Result<(), JsValue> { )); }; - let surface = gpu_runtime - .context - .instance() - .create_surface(wgpu::SurfaceTarget::Canvas(runtime.canvas.clone())) - .map_err(|error| JsValue::from_str(&error.to_string()))?; - - runtime - .compositor - .render_frame( - &gpu_runtime.context, - RenderFrameOptions { - frame: &frame, - surface: &surface, - }, - ) - .map_err(|error| JsValue::from_str(&error.to_string())) + if gpu_runtime.context.supports_surface_rendering() { + let surface = gpu_runtime + .context + .instance() + .create_surface(wgpu::SurfaceTarget::Canvas(runtime.canvas.clone())) + .map_err(|error| JsValue::from_str(&error.to_string()))?; + + runtime + .compositor + .render_frame( + &gpu_runtime.context, + RenderFrameOptions { + frame: &frame, + surface: &surface, + }, + ) + .map_err(|error| JsValue::from_str(&error.to_string())) + } else { + // WebGL cannot surface-render to an arbitrary canvas element. + // Composite to a texture, then blit to the canvas via the 2D context. + let texture = runtime + .compositor + .render_frame_to_texture(&gpu_runtime.context, &frame) + .map_err(|error| JsValue::from_str(&error.to_string()))?; + + gpu_runtime + .context + .render_texture_via_gl_canvas( + &texture, + &runtime.canvas, + frame.width, + frame.height, + ) + .map_err(|error| JsValue::from_str(&error.to_string())) + } }) }) } @@ -171,4 +190,3 @@ fn parse_upload_texture_options(value: JsValue) -> Result> = const { RefCell::new(None) }; } +fn set_panic_hook() { + static SET_HOOK: std::sync::Once = std::sync::Once::new(); + SET_HOOK.call_once(|| { + std::panic::set_hook(Box::new(|info| { + // Store the full panic message in window.__wasmPanic so the JS catch block + // can surface it instead of the opaque "Unreachable" WASM trap message. + if let Some(window) = web_sys::window() { + let _ = Reflect::set( + &window, + &JsValue::from_str("__wasmPanic"), + &JsValue::from_str(&info.to_string()), + ); + } + console_error_panic_hook::hook(info); + })); + }); +} + #[wasm_bindgen(js_name = initializeGpu)] pub async fn initialize_gpu() -> Result<(), JsValue> { - console_error_panic_hook::set_once(); + set_panic_hook(); if GPU_RUNTIME.with(|runtime| runtime.borrow().is_some()) { return Ok(()); -- cgit v1.3.1