GPU Engine
WebGPU rendering, texture management, preview output, export capture, and GPU-backed analysis.
Linux/Mesa: GPU-accelerated canvas, worker
OffscreenCanvas, and WebGPU paths fail silently on open-source Mesa drivers. Before changing any canvas/GPU code, read Linux / Mesa GPU.
Overview
Section titled “Overview”WebGPUEngine is a thin facade over the rendering subsystems. It owns WebGPU context setup, texture and cache managers, the render loop, the render dispatcher, output windows, and export canvas state.
The engine supports:
- Main preview rendering
- Independent preview targets
- Popup output windows
- Sliced output / corner-pin rendering
- RAM preview and scrubbing caches
- Zero-copy export frames when the export canvas path is available
- CPU readback as a fallback path
- Waveform, histogram, and vectorscope GPU panels
- Native 3D scene and Gaussian-splat composition when their enabled runtime flags apply
Architecture
Section titled “Architecture”Core Pieces
Section titled “Core Pieces”WebGPUContexthandles adapter/device setup and device-loss recovery.RenderLoopruns the RAF loop, idle detection, scrub limiting, and watchdog recovery.RenderDispatcherhandles per-frame texture import, compositing, output, and preview fallbacks.RenderTargetManagerowns ping/pong compositing buffers plus independent preview buffers.TextureManagerandMaskTextureManagermanage image/video/mask textures.CacheManagerowns scrubbing cache, composite RAM preview cache, and GPU frame cache.ExportCanvasManagerowns the export OffscreenCanvas and stacked-alpha mode.
Runtime State
Section titled “Runtime State”- The engine keeps a unified
Mapof target canvases. - Preview canvases and output windows are reconfigured after device restore.
- HMR reuses the engine singleton when possible.
Boot Health And GPU Hazard Recovery
Section titled “Boot Health And GPU Hazard Recovery”Risky GPU startup records a release-scoped boot attempt in bootHealth.ts.
Three interrupted attempts inside 24 hours activate a GPU hazard for that
release and route canvas policy through the existing software fallback. An
attempt becomes healthy only after the active render mode proves a readable
frame; worker submission alone is not treated as pixel proof.
Device loss records the same hazard signal. The visible warning offers a
manual Retry GPU action that clears the safety state and reloads the app.
Clean pagehide completion and a three-second settled startup prevent normal
reloads from being counted as crashes. Runtime owners and the engine singleton
remain HMR-safe.
Texture Paths
Section titled “Texture Paths”HTMLVideoElementis imported as an external texture when the browser supports it.VideoFramecan also be imported as an external texture.- Firefox preview uses copied textures instead of external video import because imported frames can intermittently go black.
- Android Chromium copies HTML-video preview frames into persistent GPU textures during playback, pause, and seeking because its external video textures can otherwise present intermittent black frames.
Images And Canvas
Section titled “Images And Canvas”- Images and canvases are copied into
rgba8unormGPU textures. - Cached image views are reused when possible.
Motion Shapes
Section titled “Motion Shapes”motion-shapeclips render throughsrc/engine/motion/MotionRenderer.ts.- Rectangle, ellipse, polygon, and star primitives are drawn with analytic WGSL SDFs into transparent
rgba8unormtextures. - A bounded uniform layout composites up to 8 ordered color-fill, stroke, linear-gradient, radial-gradient, or texture-fill appearances, with up to 8 stable stops per gradient and six shader blend modes.
- Replicated motion shapes use a per-shape instance buffer and instanced draws in the same shader path. The shader ceiling is 100,000 instances; effective counts are also constrained by the device, render target, and configured user limit.
- The resulting texture view is composited through the normal
CompositorPipeline, so masks, effects, blend mode, nested comps, preview targets, and export share the same downstream path.
- Mask textures are uploaded per layer.
- A 1x1 white fallback texture is used when no mask is present.
Limitation
Section titled “Limitation”- The zero-copy path only applies when the browser and source type support it. Preview and export still have explicit fallback paths.
Render Loop
Section titled “Render Loop”Idle And Scrub Control
Section titled “Idle And Scrub Control”- Idle mode starts after about 1 second of no activity.
- Idle detection is initially suppressed so browser video surfaces can warm up; it is lifted by the first play event or after about 3 seconds.
- Playback is limited to about 60 fps.
- Scrubbing is limited to about 60 fps unless a fresh video frame arrives, which bypasses the limiter.
Watchdog
Section titled “Watchdog”- A watchdog checks for stalls every 2 seconds.
- If the render loop stops producing frames for about 3 seconds while it should be active, it wakes the loop or restarts it.
Export And RAM Preview
Section titled “Export And RAM Preview”- The render loop skips preview rendering while export is active.
- Export and RAM preview both set flags through
ExportCanvasManager. - These modes are separate from the normal preview path, but they share the same engine.
Preview Fallback Chain
Section titled “Preview Fallback Chain”RenderDispatcher uses a best-effort chain for video preview:
- Cached scrubbing frame
- Copied fallback frame
- Live external texture import
- Last known frame or same-clip hold frame
This is why preview can stay stable during seeks while a fresh frame is pending.
Firefox Special Case
Section titled “Firefox Special Case”- Firefox uses
getCopiedHtmlVideoPreviewFrame()for HTML video preview stability. - That path copies the current video frame into a persistent GPU texture and falls back to the previous stable frame when needed.
Output And Export
Section titled “Output And Export”OutputPipeline
Section titled “OutputPipeline”- The output pipeline uses separate uniform buffers for transparency grid off/on and stacked-alpha export.
- Full-frame output and compositor passes draw one oversized triangle, avoiding a diagonal seam or missing half-frame when a mobile WebGPU driver drops one primitive from a two-triangle quad.
renderToCanvas()catches canvas-context loss and simply skips that target for the frame.- Bind-group caches are separate per output mode.
Stacked Alpha
Section titled “Stacked Alpha”- Stacked alpha doubles the encoded export height.
- RGB is rendered in the top half and alpha grayscale in the bottom half.
ExportCanvasManagercreates the doubled-height OffscreenCanvas when stacked alpha is enabled.
Readback
Section titled “Readback”readPixels()is the CPU fallback path.- It is used by export fallback paths and preview capture utilities.
- It is slower than the zero-copy export path and should be treated as a fallback, not the normal route.
Export Canvas
Section titled “Export Canvas”ExportCanvasManager is responsible for export-state flags and the export canvas lifecycle.
Current Behavior
Section titled “Current Behavior”initExportCanvas()creates an OffscreenCanvas with WebGPU context.createVideoFrameFromExport()waits fordevice.queue.onSubmittedWorkDone()before constructing aVideoFrame.- If the zero-copy path cannot be created, export falls back to
readPixels(). cleanupExportCanvas()clears the canvas and stacked-alpha state after export.
Limitation
Section titled “Limitation”- Zero-copy export only works when OffscreenCanvas + WebGPU +
VideoFramecreation are available and the export canvas can be configured.
Feature Flags
Section titled “Feature Flags”Runtime flags are exposed on window.__ENGINE_FLAGS__.
useFullWebCodecsPlaybackanddisableHtmlPreviewFallbackare synced with the persisted settings toggle.useLiveSlotTriggerswaps slot-grid clicks from editor-open behavior to direct live triggering.useWarmSlotDecksprepares reusable slot-owned live decks for faster layer adoption.use3DLayersanduseGaussianSplatare enabled in this branch.workerFirstRenderHostexists for worker-host presentation, but defaults tofalse; the main-thread render host remains the default.timelineCanvasWorkeris enabled for eligible timeline rows; it is separate from the main preview render host.- Motion shape/appearance rendering and the Grid Replicator MVP are always part of the native layer path.
Performance Notes
Section titled “Performance Notes”ScrubbingCachekeeps up to 192 video scrub frames at 30 fps quantization, with a 192 MiB texture budget and a 960-pixel maximum source dimension.- Composite RAM preview caches are capped at 900 frames and 512 MB.
- GPU RAM preview cache is capped at 60 frames.
- These are hard cache limits from code.
Preview Quality
Section titled “Preview Quality”useEngine()scales the active-composition base resolution by the persisted preview-quality multiplier before callingengine.setResolution(...).Full,Half, andQuartertherefore change the GPU render size for engine-backed preview targets.- Export output resolution still comes from the composition/export settings, not from preview quality.
Troubleshooting
Section titled “Troubleshooting”| Problem | What To Check |
|---|---|
| Black preview after reload | Video GPU surfaces may not be warm yet. The engine suppresses idle until first play, but browser readiness still matters. |
| Firefox preview instability | HTML video fallback should be using copied textures, not external import. |
| Export canvas missing | Verify WebGPU context creation on OffscreenCanvas and device validity. |
| Device lost | The engine attempts recovery and reconfigures canvases, but a manual reload may still be needed. |
Sources
Section titled “Sources”Key implementation files:
src/engine/WebGPUEngine.tssrc/engine/render/RenderDispatcher.tssrc/engine/render/RenderLoop.tssrc/engine/render/htmlVideoPreviewFallback.tssrc/engine/motion/MotionRenderer.tssrc/engine/motion/shaders/motionShapes.wgslsrc/engine/analysis/ScopeRenderer.tssrc/engine/native3d/NativeSceneRenderer.tssrc/engine/pipeline/OutputPipeline.tssrc/engine/pipeline/SlicePipeline.tssrc/engine/managers/ExportCanvasManager.tssrc/engine/texture/ScrubbingCache.tssrc/engine/core/RenderTargetManager.tssrc/engine/featureFlags.tssrc/engine/video/VideoFrameManager.tssrc/services/render/renderHostPort.ts