From 622de2a5bb50ab67356985edffd2888d63fb5a87 Mon Sep 17 00:00:00 2001 From: Othunderlight Date: Fri, 25 Sep 2026 06:03:10 +0300 Subject: [PATCH] fix(export): render Lightning frames with WebGL and allow software H.264 Lightning exports failed on Linux with "Cannot read properties of undefined (reading '_resourceType')". The export renderer preferred WebGPU whenever navigator.gpu existed, but the preview renderer always prefers WebGL, so exports ran on a backend that never worked on this machine. PixiJS then read a missing filter resource in BindGroupSystem._createBindGroup and threw. Breeze was separately unreachable on Linux: its Annex B encoder probe only tried prefer-hardware, while electron/gpuSwitches.ts disables VaapiVideoDecoder and VaapiVideoEncoder on Linux, so the probe rejected the software encoder that WebCodecs had already reported as supported. - Default the export renderer to WebGL-first to match VideoPlayback.tsx; keep WebGPU available when explicitly requested. - Probe Breeze with prefer-hardware and fall back to prefer-software, and configure the encoder with whichever config passed. --- src/lib/exporter/modernFrameRenderer.ts | 13 ++++++------ src/lib/exporter/modernVideoExporter.ts | 27 ++++++++++++++++++++----- src/lib/exporter/videoExporter.ts | 26 +++++++++++++++++++----- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/lib/exporter/modernFrameRenderer.ts b/src/lib/exporter/modernFrameRenderer.ts index c66c18d79..5aff72586 100644 --- a/src/lib/exporter/modernFrameRenderer.ts +++ b/src/lib/exporter/modernFrameRenderer.ts @@ -623,14 +623,13 @@ export class FrameRenderer { }; const preferredRenderBackend = this.config.preferredRenderBackend; + // Default to WebGL to match the preview renderer (VideoPlayback.tsx) and + // the legacy export renderer, so exported frames render on the same + // backend users see while editing. WebGPU remains available when it is + // explicitly requested; unproven WebGPU support has failed mid-export + // inside PixiJS bind-group setup on some Linux drivers. 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 81137b267..a736ca7b9 100644 --- a/src/lib/exporter/modernVideoExporter.ts +++ b/src/lib/exporter/modernVideoExporter.ts @@ -2780,21 +2780,38 @@ export class ModernVideoExporter { return false; } - const encoderConfig: VideoEncoderConfig = { + const buildEncoderConfig = ( + hardwareAcceleration: VideoEncoderConfig["hardwareAcceleration"], + ): VideoEncoderConfig => ({ codec: "avc1.640034", width: this.config.width, height: this.config.height, bitrate: this.config.bitrate, framerate: this.config.frameRate, - hardwareAcceleration: "prefer-hardware", + hardwareAcceleration, avc: { format: "annexb" }, - }; + }); + + // Prefer hardware H.264, but fall back to software. Recordly disables + // VA-API on Linux (see electron/gpuSwitches.ts), so hardware encoding is + // frequently unavailable there even though software encoding works. + let encoderConfig = buildEncoderConfig("prefer-hardware"); try { const support = await VideoEncoder.isConfigSupported(encoderConfig); if (!support.supported) { - this.lastNativeExportError = `H.264 Annex B encoding is not supported at ${this.config.width}x${this.config.height}.`; - return false; + const softwareConfig = buildEncoderConfig("prefer-software"); + const softwareSupport = await VideoEncoder.isConfigSupported(softwareConfig); + + if (!softwareSupport.supported) { + this.lastNativeExportError = `H.264 Annex B encoding is not supported at ${this.config.width}x${this.config.height}.`; + return false; + } + + encoderConfig = softwareConfig; + console.warn( + `[VideoExporter] ${NATIVE_EXPORT_ENGINE_NAME} hardware H.264 encoding is unavailable at ${this.config.width}x${this.config.height}; using software encoding instead.`, + ); } } catch (error) { this.lastNativeExportError = error instanceof Error ? error.message : String(error); diff --git a/src/lib/exporter/videoExporter.ts b/src/lib/exporter/videoExporter.ts index d58642c72..27e48bb44 100644 --- a/src/lib/exporter/videoExporter.ts +++ b/src/lib/exporter/videoExporter.ts @@ -686,23 +686,39 @@ export class VideoExporter { return false; } - const encoderConfig: VideoEncoderConfig = { + const buildEncoderConfig = ( + hardwareAcceleration: VideoEncoderConfig["hardwareAcceleration"], + ): VideoEncoderConfig => ({ codec: "avc1.640034", width: this.config.width, height: this.config.height, bitrate: this.config.bitrate, framerate: this.config.frameRate, - hardwareAcceleration: "prefer-hardware", + hardwareAcceleration, avc: { format: "annexb" }, - }; + }); + + // Hardware H.264 is unavailable on many Linux builds because VA-API is + // disabled (see electron/gpuSwitches.ts); software encoding still works. + let encoderConfig = buildEncoderConfig("prefer-hardware"); try { const support = await VideoEncoder.isConfigSupported(encoderConfig); if (!support.supported) { + const softwareConfig = buildEncoderConfig("prefer-software"); + const softwareSupport = await VideoEncoder.isConfigSupported(softwareConfig); + + if (!softwareSupport.supported) { + console.warn( + `[VideoExporter] Native H.264 Annex B encoding is unsupported at ${this.config.width}x${this.config.height}`, + ); + return false; + } + + encoderConfig = softwareConfig; console.warn( - `[VideoExporter] Native H.264 Annex B encoding is unsupported at ${this.config.width}x${this.config.height}`, + `[VideoExporter] Native hardware H.264 encoding is unavailable at ${this.config.width}x${this.config.height}; using software encoding instead.`, ); - return false; } } catch (error) { console.warn("[VideoExporter] Native encoder support check failed:", error);