From 0eed7350337d9ef6b1fd098540fa8748371adf3d Mon Sep 17 00:00:00 2001 From: Hritik-Kumar-dev Date: Sun, 27 Sep 2026 17:17:48 +0530 Subject: [PATCH] Keep Lightning exports on WebGL instead of crashing on Linux Lightning exports failed on Linux with: Lightning (Beta) export failed. Reason: Cannot read properties of undefined (reading '_resourceType') The crash is a PixiJS 8.14 WebGPU bug, not app code. On Linux Chromium `navigator.gpu` exists, so WebGPU initialization succeeds, but Pixi sizes its bind group from a WebGL probe (MAX_TEXTURE_IMAGE_UNITS = 32) that exceeds the WebGPU per-stage sampler limit (maxSamplersPerShaderStage = 16). BindGroupSystem._createBindGroup then throws on the first render, killing the export at 0 frames. Because it fails mid-render rather than during init, the existing backend fallback ladder never engages. `getDefaultLightningRenderBackend()` in backendPolicy.ts already returned "webgl" and was covered by a test, but it was never called from production code. Instead modernVideoExporter hardcoded `preferredRenderBackend: undefined`, which also discarded the caller's own preference (so the `?smokeRenderBackend=webgpu` override was silently dropped). `undefined` resolved to a WebGPU-first backend order. - modernVideoExporter: honor the caller's preferredRenderBackend and fall back to getDefaultLightningRenderBackend(). - modernFrameRenderer: make the unspecified case WebGL-first, matching the legacy frameRenderer. WebGPU stays available via an explicit preference, so the smoke-export override still works. - Add a regression test that stubs navigator.gpu and asserts the default resolves to webgl. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/lib/exporter/modernFrameRenderer.test.ts | 28 ++++++++++++++++++++ src/lib/exporter/modernFrameRenderer.ts | 14 +++++----- src/lib/exporter/modernVideoExporter.ts | 4 ++- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/lib/exporter/modernFrameRenderer.test.ts b/src/lib/exporter/modernFrameRenderer.test.ts index 79c2caa1..04bee35f 100644 --- a/src/lib/exporter/modernFrameRenderer.test.ts +++ b/src/lib/exporter/modernFrameRenderer.test.ts @@ -260,6 +260,34 @@ describe("ModernFrameRenderer Pixi lifecycle", () => { vi.unstubAllGlobals(); } }); + + it("defaults to WebGL when no backend is preferred, even when WebGPU is available", async () => { + pixiApplicationInstancesMock.length = 0; + pixiInitializationErrorsMock.length = 0; + // Linux Chromium exposes navigator.gpu, but Pixi's WebGPU bind-group path crashes + // with "Cannot read properties of undefined (reading '_resourceType')". + vi.stubGlobal("navigator", { gpu: {} }); + + try { + const renderer = createRenderer() as unknown as { + config: { preferredRenderBackend?: "webgl" | "webgpu" }; + createPixiApplication: ( + canvas: HTMLCanvasElement, + ) => Promise<{ backend: "webgl" | "webgpu" }>; + }; + renderer.config.preferredRenderBackend = undefined; + + await expect(renderer.createPixiApplication({} as HTMLCanvasElement)).resolves + .toMatchObject({ + backend: "webgl", + }); + + expect(pixiInitializationErrorsMock).toHaveLength(0); + expect(pixiApplicationInstancesMock).toHaveLength(1); + } finally { + vi.unstubAllGlobals(); + } + }); }); describe("ModernFrameRenderer blur export path", () => { diff --git a/src/lib/exporter/modernFrameRenderer.ts b/src/lib/exporter/modernFrameRenderer.ts index c66c18d7..3d0c50b5 100644 --- a/src/lib/exporter/modernFrameRenderer.ts +++ b/src/lib/exporter/modernFrameRenderer.ts @@ -623,14 +623,14 @@ export class FrameRenderer { }; const preferredRenderBackend = this.config.preferredRenderBackend; + // WebGPU is opt-in. Linux Chromium exposes navigator.gpu, but Pixi's WebGPU + // bind-group path can crash mid-render with + // "Cannot read properties of undefined (reading '_resourceType')", which is only + // recoverable by restarting the whole export on a different backend. const backendOrder: ExportRenderBackend[] = - preferredRenderBackend === "webgl" - ? ["webgl", "webgpu"] - : preferredRenderBackend === "webgpu" - ? ["webgpu", "webgl"] - : typeof navigator !== "undefined" && "gpu" in navigator - ? ["webgpu", "webgl"] - : ["webgl"]; + preferredRenderBackend === "webgpu" + ? ["webgpu", "webgl"] + : ["webgl", "webgpu"]; const failures: PixiRendererAttempt[] = []; for (const backend of backendOrder) { diff --git a/src/lib/exporter/modernVideoExporter.ts b/src/lib/exporter/modernVideoExporter.ts index 81137b26..9b209a1e 100644 --- a/src/lib/exporter/modernVideoExporter.ts +++ b/src/lib/exporter/modernVideoExporter.ts @@ -49,6 +49,7 @@ import { } from "@/lib/wallpapers"; import { AudioProcessor, isAacAudioEncodingSupported } from "./audioEncoder"; import { + getDefaultLightningRenderBackend, normalizeLightningRuntimePlatform, shouldPreferNativeAutoBackend, shouldPreferNativeStaticLayoutBeforeBreeze, @@ -604,7 +605,8 @@ export class ModernVideoExporter { timelineEffects: this.config.clipRegions !== undefined, width: this.config.width, height: this.config.height, - preferredRenderBackend: undefined, + preferredRenderBackend: + this.config.preferredRenderBackend ?? getDefaultLightningRenderBackend(), wallpaper: this.config.wallpaper, zoomRegions: this.config.zoomRegions, showShadow: this.config.showShadow,