Skip to main content

Module wasm

Module wasm 

Source
Expand description

WASM target support for browser-based GPU compute via WebGPU.

This module is conditionally compiled on wasm32 targets (or when the wasm feature is enabled for native testing) and provides browser-friendly wrappers around the wgpu WebGPU backend.

§Architecture

+-------------------------------------------+
|         JavaScript / Browser              |
+-------------------+-----------------------+
                    |
+-------------------v-----------------------+
|   WasmGpuDevice / WasmBackend (wasm32)    |
+-------------------+-----------------------+
                    |  delegates to
+-------------------v-----------------------+
|   WebGpuBackend (wgpu web-sys backend)    |
+-------------------------------------------+

§Usage

The WasmBackend wraps the existing WebGpuBackend and adds browser-specific initialisation methods such as init_from_canvas.

The WasmMemoryManager provides async-friendly buffer staging suited to the browser event loop.

§Known limitation: WasmBackend does not actually use WasmMemoryManager

Despite the name, every WasmBackend compute and memory operation forwards to WebGpuBackend, which is backed by WebGpuMemoryManager — not by WasmMemoryManager in this module. WasmMemoryManager (and WasmGpuDevice) exist, are async-safe, and are unit-tested, but nothing in WasmBackend constructs or calls them. This matters because:

  • WebGpuBackend’s compute methods end in a blocking Device::poll(PollType::wait_indefinitely()) (see backend.rs), and WebGpuMemoryManager::copy_from_device’s readback blocks on an mpsc::channel recv() that only resolves once that same poll drives the map_async callback to completion. On the single-threaded browser main thread, Device::poll cannot make progress without yielding back to the event loop that would deliver that callback — so this would deadlock the tab, exactly the failure WasmMemoryManager::copy_dtoh’s own #[cfg(target_arch = "wasm32")] guard (in this file) already exists to prevent, just on the wrong type.
  • Fixing this by swapping WasmBackend’s memory calls (alloc, copy_htod, copy_dtoh) over to WasmMemoryManager while leaving the compute calls (gemm, unary, …) on self.inner: WebGpuBackend is not a valid partial fix: the two memory managers keep independent HashMap<u64, Buffer> handle tables, each with its own next_handle/AtomicU64 counter starting at 1. A buffer allocated through WasmMemoryManager would not exist in WebGpuMemoryManager’s map (or worse, its handle number would collide with an unrelated WebGpuMemoryManager buffer), so every compute call would either fail with “unknown handle” or silently operate on the wrong buffer. Correctly fixing this requires WasmBackend to own one coherent device + buffer table end-to-end and give every compute dispatch (not just readback) an async, non-blocking form — a genuine rework of this module and backend.rs together, out of scope here.

Until that rework lands: WasmBackend is appropriate for native testing of the WASM code paths (via the wasm feature, where blocking is safe) and for non-browser wasm32 hosts (e.g. a WASI runtime driving its own event loop outside a browser tab). On an actual browser main thread, treat every WasmBackend compute/readback call as unsafe to call synchronously; WasmMemoryManager::copy_dtoh_async is the already-implemented pattern a real async rework would extend to the rest of the surface.

§Deeper pre-existing gap: this crate does not compile for wasm32-unknown-unknown at all

cargo check -p oxicuda-webgpu --target wasm32-unknown-unknown fails today (independent of anything in this module): oxicuda_backend:: ComputeBackend requires Send + Sync, but on the real wasm32 target wgpu’s WebGPU backend represents Device/Buffer using Rc/RefCell internally (browser JS handles are not thread-safe), which makes WebGpuDevice/WebGpuBufferInfo — and therefore WebGpuBackend, which every WasmBackend method forwards to — not Send. So WasmBackend (and WebGpuBackend) cannot implement ComputeBackend on that target today at all; this is a compile error, not a runtime one, so nothing in this crate has ever actually run compiled-for-wasm32. Fixing it needs either a ?Send carve-out on ComputeBackend for wasm32 (a change to oxicuda-backend, out of scope for this crate) or a non-ComputeBackend wasm32-native entry point built directly on WasmGpuDevice; both are part of the same async rework noted above.

Structs§

WasmBackend
WebGPU compute backend for WASM (browser) targets.
WasmGpuDevice
A WebGPU device obtained from the browser’s navigator.gpu API.
WasmMemoryManager
Browser-side buffer manager that uses async map_async staging.

Functions§

request_adapter
Request a WebGPU adapter from the browser.