Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions electron/ipc/export/nativeStaticLayoutRoutePlan.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { describe, expect, it } from "vitest";
import {
type NativeStaticLayoutRouteSource,
type NvidiaCudaExportCapabilityProbe,
planNativeStaticLayoutRoutes,
type WindowsD3D11ExportCapabilityProbe,
} from "./nativeStaticLayoutRoutePlan";

const source: NativeStaticLayoutRouteSource = {
inputCodec: "h264",
proxyCodec: "h264",
proxyCreated: false,
};

const cudaProbe: NvidiaCudaExportCapabilityProbe = {
platform: "win32",
appPackaged: true,
explicitEnabled: false,
explicitDisabled: false,
packagedAutoCandidateEnabled: true,
packagedAutoCandidateActive: true,
windowsGpuCompositorEnabled: true,
wrapperPath: "C:\\Recordly\\run-mp4-pipeline.mjs",
hasNvidiaGpu: true,
audioMode: "copy-source",
audioSkipReason: null,
stallTimeoutMs: 120_000,
skipReason: null,
};

const d3d11Probe: WindowsD3D11ExportCapabilityProbe = {
platform: "win32",
windowsGpuCompositorEnabled: true,
helperPath: "C:\\Recordly\\recordly-gpu-export.exe",
adapterIndexOverride: null,
preferHighPerformanceAdapter: true,
nvencSdkRequested: false,
skipReason: null,
};

describe("planNativeStaticLayoutRoutes", () => {
it("selects CUDA first and records D3D11 plus FFmpeg fallbacks", () => {
expect(
planNativeStaticLayoutRoutes({
cuda: cudaProbe,
d3d11: d3d11Probe,
source,
}),
).toEqual({
selectedRoute: "nvidia-cuda-compositor",
decisions: [
{
route: "nvidia-cuda-compositor",
status: "selected",
reasons: ["cuda-wrapper-and-nvidia-gpu-available"],
},
{
route: "windows-d3d11-compositor",
status: "fallback",
reasons: ["documented-fallback-if-cuda-runtime-fails"],
},
{
route: "ffmpeg-static-layout",
status: "fallback",
reasons: ["native-gpu-runtime-fallback"],
},
],
cuda: cudaProbe,
d3d11: d3d11Probe,
source,
});
});

it("selects D3D11 when CUDA is unavailable", () => {
const cuda = {
...cudaProbe,
hasNvidiaGpu: false,
skipReason: "nvidia-gpu-unavailable",
} satisfies NvidiaCudaExportCapabilityProbe;

expect(
planNativeStaticLayoutRoutes({
cuda,
d3d11: d3d11Probe,
source,
}),
).toMatchObject({
selectedRoute: "windows-d3d11-compositor",
decisions: [
{
route: "nvidia-cuda-compositor",
status: "rejected",
reasons: ["nvidia-gpu-unavailable"],
},
{
route: "windows-d3d11-compositor",
status: "selected",
reasons: ["documented-fallback-after-cuda-skip:nvidia-gpu-unavailable"],
},
{
route: "ffmpeg-static-layout",
status: "fallback",
reasons: ["windows-d3d11-runtime-fallback"],
},
],
});
});

it("selects FFmpeg when both native GPU routes are unavailable", () => {
const cuda = {
...cudaProbe,
wrapperPath: null,
skipReason: "cuda-wrapper-unavailable",
} satisfies NvidiaCudaExportCapabilityProbe;
const d3d11 = {
...d3d11Probe,
helperPath: null,
skipReason: "windows-gpu-helper-unavailable",
} satisfies WindowsD3D11ExportCapabilityProbe;

expect(
planNativeStaticLayoutRoutes({
cuda,
d3d11,
source,
}),
).toEqual({
selectedRoute: "ffmpeg-static-layout",
decisions: [
{
route: "nvidia-cuda-compositor",
status: "rejected",
reasons: ["cuda-wrapper-unavailable"],
},
{
route: "windows-d3d11-compositor",
status: "rejected",
reasons: ["windows-gpu-helper-unavailable"],
},
{
route: "ffmpeg-static-layout",
status: "selected",
reasons: ["native-gpu-routes-unavailable"],
},
],
cuda,
d3d11,
source,
});
});

it("preserves proxy source metadata for route diagnostics", () => {
const proxiedSource = {
inputCodec: "vp9",
proxyCodec: "h264",
proxyCreated: true,
};

expect(
planNativeStaticLayoutRoutes({
cuda: cudaProbe,
d3d11: d3d11Probe,
source: proxiedSource,
}).source,
).toEqual(proxiedSource);
});
});
132 changes: 132 additions & 0 deletions electron/ipc/export/nativeStaticLayoutRoutePlan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import type { NativeVideoExportAudioMode } from "../nativeVideoExport";

export type NativeStaticLayoutRoute =
| "nvidia-cuda-compositor"
| "windows-d3d11-compositor"
| "ffmpeg-static-layout";

export interface NativeStaticLayoutRouteDecision {
route: NativeStaticLayoutRoute;
status: "selected" | "fallback" | "rejected";
reasons: string[];
}

export interface NvidiaCudaExportCapabilityProbe {
platform: NodeJS.Platform;
appPackaged: boolean;
explicitEnabled: boolean;
explicitDisabled: boolean;
packagedAutoCandidateEnabled: boolean;
packagedAutoCandidateActive: boolean;
windowsGpuCompositorEnabled: boolean;
wrapperPath: string | null;
hasNvidiaGpu: boolean | null;
audioMode: NativeVideoExportAudioMode;
audioSkipReason: string | null;
stallTimeoutMs: number | null;
skipReason: string | null;
}

export interface WindowsD3D11ExportCapabilityProbe {
platform: NodeJS.Platform;
windowsGpuCompositorEnabled: boolean;
helperPath: string | null;
adapterIndexOverride: number | null;
preferHighPerformanceAdapter: boolean;
nvencSdkRequested: boolean;
skipReason: string | null;
}

export interface NativeStaticLayoutRouteSource {
inputCodec: string;
proxyCodec?: string;
proxyCreated: boolean;
}

export interface NativeStaticLayoutRoutePlan {
selectedRoute: NativeStaticLayoutRoute;
decisions: NativeStaticLayoutRouteDecision[];
cuda: NvidiaCudaExportCapabilityProbe;
d3d11: WindowsD3D11ExportCapabilityProbe;
source: NativeStaticLayoutRouteSource;
}

export function planNativeStaticLayoutRoutes(options: {
cuda: NvidiaCudaExportCapabilityProbe;
d3d11: WindowsD3D11ExportCapabilityProbe;
source: NativeStaticLayoutRouteSource;
}): NativeStaticLayoutRoutePlan {
const { cuda, d3d11, source } = options;
const decisions: NativeStaticLayoutRouteDecision[] = [];

if (!cuda.skipReason) {
decisions.push({
route: "nvidia-cuda-compositor",
status: "selected",
reasons: ["cuda-wrapper-and-nvidia-gpu-available"],
});
decisions.push({
route: "windows-d3d11-compositor",
status: d3d11.skipReason ? "rejected" : "fallback",
reasons: d3d11.skipReason
? [d3d11.skipReason]
: ["documented-fallback-if-cuda-runtime-fails"],
});
decisions.push({
route: "ffmpeg-static-layout",
status: "fallback",
reasons: ["native-gpu-runtime-fallback"],
});
return {
selectedRoute: "nvidia-cuda-compositor",
decisions,
cuda,
d3d11,
source,
};
}

decisions.push({
route: "nvidia-cuda-compositor",
status: "rejected",
reasons: [cuda.skipReason],
});

if (!d3d11.skipReason) {
decisions.push({
route: "windows-d3d11-compositor",
status: "selected",
reasons: [`documented-fallback-after-cuda-skip:${cuda.skipReason}`],
});
decisions.push({
route: "ffmpeg-static-layout",
status: "fallback",
reasons: ["windows-d3d11-runtime-fallback"],
});
return {
selectedRoute: "windows-d3d11-compositor",
decisions,
cuda,
d3d11,
source,
};
}

decisions.push({
route: "windows-d3d11-compositor",
status: "rejected",
reasons: [d3d11.skipReason],
});
decisions.push({
route: "ffmpeg-static-layout",
status: "selected",
reasons: ["native-gpu-routes-unavailable"],
});
return {
selectedRoute: "ffmpeg-static-layout",
decisions,
cuda,
d3d11,
source,
};
}