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 --- apps/web/package.json | 2 +- .../src/components/providers/editor-provider.tsx | 18 ++++-- apps/web/src/core/managers/project-manager.ts | 10 +++- apps/web/src/services/renderer/effect-preview.ts | 68 ++++++++++++---------- 4 files changed, 60 insertions(+), 38 deletions(-) (limited to 'apps/web') diff --git a/apps/web/package.json b/apps/web/package.json index 69aa7931..065d4ffc 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -52,7 +52,7 @@ "nanoid": "^5.1.5", "next": "16.1.3", "next-themes": "^0.4.4", - "opencut-wasm": "^0.2.6", + "opencut-wasm": "^0.2.8", "pg": "^8.16.2", "postgres": "^3.4.5", "radix-ui": "^1.4.3", diff --git a/apps/web/src/components/providers/editor-provider.tsx b/apps/web/src/components/providers/editor-provider.tsx index cac954ba..98e6d4c6 100644 --- a/apps/web/src/components/providers/editor-provider.tsx +++ b/apps/web/src/components/providers/editor-provider.tsx @@ -10,7 +10,10 @@ import { useKeybindingsStore } from "@/stores/keybindings-store"; import { useTimelineStore } from "@/stores/timeline-store"; import { useEditorActions } from "@/hooks/actions/use-editor-actions"; import { loadFontAtlas } from "@/lib/fonts/google-fonts"; -import { initializeGpuRenderer, isGpuAvailable } from "@/services/renderer/gpu-renderer"; +import { + initializeGpuRenderer, + isGpuAvailable, +} from "@/services/renderer/gpu-renderer"; interface EditorProviderProps { projectId: string; @@ -62,9 +65,16 @@ export function EditorProvider({ projectId, children }: EditorProviderProps) { setIsLoading(false); } } else { - setError( - err instanceof Error ? err.message : "Failed to load project", - ); + const wasmPanic = (window as Window & { __wasmPanic?: string }) + .__wasmPanic; + if (wasmPanic) { + delete (window as Window & { __wasmPanic?: string }).__wasmPanic; + setError(wasmPanic); + } else { + setError( + err instanceof Error ? err.message : "Failed to load project", + ); + } setIsLoading(false); } } diff --git a/apps/web/src/core/managers/project-manager.ts b/apps/web/src/core/managers/project-manager.ts index 81b237da..8555786d 100644 --- a/apps/web/src/core/managers/project-manager.ts +++ b/apps/web/src/core/managers/project-manager.ts @@ -167,9 +167,13 @@ export class ProjectManager { }); if (!project.metadata.thumbnail) { - const didUpdateThumbnail = await this.updateThumbnailFromTimeline(); - if (didUpdateThumbnail) { - await this.saveCurrentProject(); + try { + const didUpdateThumbnail = await this.updateThumbnailFromTimeline(); + if (didUpdateThumbnail) { + await this.saveCurrentProject(); + } + } catch (error) { + console.error("Failed to generate project thumbnail:", error); } } } catch (error) { diff --git a/apps/web/src/services/renderer/effect-preview.ts b/apps/web/src/services/renderer/effect-preview.ts index 8e081039..06e64d0e 100644 --- a/apps/web/src/services/renderer/effect-preview.ts +++ b/apps/web/src/services/renderer/effect-preview.ts @@ -18,11 +18,7 @@ class EffectPreviewService { this.loadPreviewImage(); } - onPreviewImageReady({ - callback, - }: { - callback: () => void; - }): () => void { + onPreviewImageReady({ callback }: { callback: () => void }): () => void { this.onReadyCallbacks.add(callback); return () => this.onReadyCallbacks.delete(callback); } @@ -39,35 +35,47 @@ class EffectPreviewService { uniformDimensions?: { width: number; height: number }; }): void { const size = PREVIEW_SIZE; - const source = this.getTestSource({ width: size, height: size }); - if (!source) return; - - const definition = effectsRegistry.get(effectType); - const resolvedParams = - Object.keys(params).length > 0 - ? params - : buildDefaultParamValues(definition.params); - - const passes = resolveEffectPasses({ - definition, - effectParams: resolvedParams, - width: uniformDimensions?.width ?? size, - height: uniformDimensions?.height ?? size, - }); - const result = this.applyGpuEffect({ - source, - width: size, - height: size, - passes, - }); - const targetCtx = targetCanvas.getContext( "2d", ) as CanvasRenderingContext2D | null; - if (targetCtx) { - targetCanvas.width = size; - targetCanvas.height = size; + if (!targetCtx) { + return; + } + + targetCanvas.width = size; + targetCanvas.height = size; + + const source = this.getTestSource({ width: size, height: size }); + if (!source) { + targetCtx.clearRect(0, 0, size, size); + return; + } + + try { + const definition = effectsRegistry.get(effectType); + const resolvedParams = + Object.keys(params).length > 0 + ? params + : buildDefaultParamValues(definition.params); + + const passes = resolveEffectPasses({ + definition, + effectParams: resolvedParams, + width: uniformDimensions?.width ?? size, + height: uniformDimensions?.height ?? size, + }); + const result = this.applyGpuEffect({ + source, + width: size, + height: size, + passes, + }); + targetCtx.drawImage(result, 0, 0, size, size); + } catch (error) { + console.warn("Failed to render effect preview", { effectType, error }); + targetCtx.clearRect(0, 0, size, size); + targetCtx.drawImage(source, 0, 0, size, size); } } -- cgit v1.3.1