diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-07 01:09:13 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-07 01:09:13 +0200 |
| commit | e4b67094e757917588fb624a30aa046667403add (patch) | |
| tree | 55452692eac69a5a63b58f2a5cdb701dd7023d68 /rust/wasm/src/gpu.rs | |
| parent | 79df73643185c02493364a077402726d1fa7b9ec (diff) | |
| download | opencut-e4b67094e757917588fb624a30aa046667403add.tar.gz | |
refactor: split effects and masks into dedicated rust crates, introduce MediaTime and FrameRate
Diffstat (limited to 'rust/wasm/src/gpu.rs')
| -rw-r--r-- | rust/wasm/src/gpu.rs | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/rust/wasm/src/gpu.rs b/rust/wasm/src/gpu.rs new file mode 100644 index 00000000..70f3a241 --- /dev/null +++ b/rust/wasm/src/gpu.rs @@ -0,0 +1,123 @@ +#![cfg(target_arch = "wasm32")] + +use std::cell::RefCell; + +use effects::EffectPipeline; +use gpu::{GpuContext, wgpu}; +use js_sys::{Object, Reflect}; +use masks::MaskFeatherPipeline; +use serde::Deserialize; +use wasm_bindgen::{JsCast, JsValue, prelude::wasm_bindgen}; + +pub(crate) struct GpuRuntime { + pub(crate) context: GpuContext, + pub(crate) effects: EffectPipeline, + pub(crate) masks: MaskFeatherPipeline, +} + +thread_local! { + static GPU_RUNTIME: RefCell<Option<GpuRuntime>> = const { RefCell::new(None) }; +} + +#[wasm_bindgen(js_name = initializeGpu)] +pub async fn initialize_gpu() -> Result<(), JsValue> { + if GPU_RUNTIME.with(|runtime| runtime.borrow().is_some()) { + return Ok(()); + } + + let context = GpuContext::new() + .await + .map_err(|error| JsValue::from_str(&error.to_string()))?; + let effects = EffectPipeline::new(&context); + let masks = MaskFeatherPipeline::new(&context); + + GPU_RUNTIME.with(|runtime| { + runtime.replace(Some(GpuRuntime { + context, + effects, + masks, + })); + }); + + Ok(()) +} + +pub(crate) fn with_gpu_runtime<T>( + action: impl FnOnce(&GpuRuntime) -> Result<T, JsValue>, +) -> Result<T, JsValue> { + GPU_RUNTIME.with(|runtime| { + let borrow = runtime.borrow(); + let Some(gpu_runtime) = borrow.as_ref() else { + return Err(JsValue::from_str( + "GPU context not initialized. Call initializeGpu() first.", + )); + }; + action(gpu_runtime) + }) +} + +pub(crate) fn import_canvas_texture( + context: &GpuContext, + canvas: &wgpu::web_sys::OffscreenCanvas, + width: u32, + height: u32, + label: &'static str, +) -> wgpu::Texture { + context.import_offscreen_canvas_texture(canvas, width, height, label) +} + +pub(crate) fn render_texture_to_canvas( + context: &GpuContext, + texture: &wgpu::Texture, + width: u32, + height: u32, +) -> Result<wgpu::web_sys::OffscreenCanvas, JsValue> { + let canvas = wgpu::web_sys::OffscreenCanvas::new(width, height)?; + context + .render_texture_to_offscreen_canvas(texture, &canvas, width, height) + .map_err(|error| JsValue::from_str(&error.to_string()))?; + Ok(canvas) +} + +pub(crate) fn read_property(object: &Object, name: &str) -> Result<JsValue, JsValue> { + Reflect::get(object, &JsValue::from_str(name)) + .map_err(|_| JsValue::from_str(&format!("Missing property '{name}'"))) +} + +pub(crate) fn read_offscreen_canvas_property( + object: &Object, + name: &str, +) -> Result<wgpu::web_sys::OffscreenCanvas, JsValue> { + read_property(object, name)? + .dyn_into::<wgpu::web_sys::OffscreenCanvas>() + .map_err(|_| JsValue::from_str(&format!("Property '{name}' must be an OffscreenCanvas"))) +} + +pub(crate) fn read_u32_property(object: &Object, name: &str) -> Result<u32, JsValue> { + let value = read_property(object, name)?; + let Some(number) = value.as_f64() else { + return Err(JsValue::from_str(&format!( + "Property '{name}' must be a number" + ))); + }; + Ok(number as u32) +} + +pub(crate) fn read_f32_property(object: &Object, name: &str) -> Result<f32, JsValue> { + let value = read_property(object, name)?; + let Some(number) = value.as_f64() else { + return Err(JsValue::from_str(&format!( + "Property '{name}' must be a number" + ))); + }; + Ok(number as f32) +} + +pub(crate) fn read_serde_property<T>(object: &Object, name: &str) -> Result<T, JsValue> +where + T: for<'de> Deserialize<'de>, +{ + let value = read_property(object, name)?; + serde_wasm_bindgen::from_value(value) + .map_err(|error| JsValue::from_str(&format!("Invalid property '{name}': {error}"))) +} |
