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
169 changes: 169 additions & 0 deletions src/lib/exporter/backendPolicy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { describe, expect, it } from "vitest";
import {
getDefaultLightningRenderBackend,
normalizeLightningRuntimePlatform,
planLightningExportRoutes,
shouldPreferNativeAutoBackend,
shouldPreferNativeStaticLayoutBeforeBreeze,
} from "./backendPolicy";

describe("backendPolicy", () => {
Expand All @@ -24,4 +26,171 @@ describe("backendPolicy", () => {
it("keeps Lightning exports on the stable WebGL renderer by default", () => {
expect(getDefaultLightningRenderBackend()).toBe("webgl");
});

it("puts visually compatible Windows auto exports on native static layout before Breeze", () => {
expect(shouldPreferNativeStaticLayoutBeforeBreeze("win32", "auto")).toBe(true);
expect(shouldPreferNativeStaticLayoutBeforeBreeze("darwin", "auto")).toBe(false);

expect(
planLightningExportRoutes({
backendPreference: "auto",
platform: "win32",
nativeStaticLayoutAvailable: true,
}),
).toMatchObject({
selectedRoute: "native-static-layout",
decisions: [
{ route: "native-static-layout", status: "selected" },
{ route: "breeze-stream", status: "fallback" },
{ route: "webcodecs", status: "fallback" },
],
});
});

it("documents the Breeze fallback when Windows static native is rejected", () => {
expect(
planLightningExportRoutes({
backendPreference: "auto",
platform: "win32",
nativeStaticLayoutAvailable: true,
nativeStaticLayoutSkipReasons: ["unsupported-frame-overlay"],
}),
).toEqual({
selectedRoute: "breeze-stream",
decisions: [
{
route: "native-static-layout",
status: "rejected",
reasons: ["unsupported-frame-overlay"],
},
{
route: "breeze-stream",
status: "selected",
reasons: ["windows-native-static-fallback"],
},
{
route: "webcodecs",
status: "fallback",
reasons: ["breeze-unavailable-fallback"],
},
],
});
});

it("documents the native static layout path when Breeze is selected explicitly", () => {
expect(
planLightningExportRoutes({
backendPreference: "breeze",
platform: "win32",
nativeStaticLayoutAvailable: true,
}),
).toEqual({
selectedRoute: "native-static-layout",
decisions: [
{
route: "native-static-layout",
status: "selected",
reasons: ["visually-compatible"],
},
{
route: "breeze-stream",
status: "fallback",
reasons: ["user-selected-breeze"],
},
{
route: "webcodecs",
status: "fallback",
reasons: ["breeze-unavailable-fallback"],
},
],
});
});

it("keeps explicit Breeze selected when native static layout is unavailable", () => {
expect(
planLightningExportRoutes({
backendPreference: "breeze",
platform: "win32",
nativeStaticLayoutAvailable: false,
}),
).toEqual({
selectedRoute: "breeze-stream",
decisions: [
{
route: "native-static-layout",
status: "rejected",
reasons: ["native-static-unavailable"],
},
{
route: "breeze-stream",
status: "selected",
reasons: ["user-selected-breeze"],
},
{
route: "webcodecs",
status: "fallback",
reasons: ["breeze-unavailable-fallback"],
},
],
});
});

it("records explicit Breeze native static layout skip reasons", () => {
expect(
planLightningExportRoutes({
backendPreference: "breeze",
platform: "win32",
nativeStaticLayoutAvailable: true,
nativeStaticLayoutSkipReasons: ["unsupported-audio-mix"],
}),
).toEqual({
selectedRoute: "breeze-stream",
decisions: [
{
route: "native-static-layout",
status: "rejected",
reasons: ["unsupported-audio-mix"],
},
{
route: "breeze-stream",
status: "selected",
reasons: ["user-selected-breeze"],
},
{
route: "webcodecs",
status: "fallback",
reasons: ["breeze-unavailable-fallback"],
},
],
});
});

it("documents why macOS auto skips native static layout", () => {
expect(
planLightningExportRoutes({
backendPreference: "auto",
platform: "darwin",
nativeStaticLayoutAvailable: true,
}),
).toEqual({
selectedRoute: "breeze-stream",
decisions: [
{
route: "native-static-layout",
status: "rejected",
reasons: ["platform-does-not-use-native-static-layout"],
},
{
route: "breeze-stream",
status: "selected",
reasons: ["platform-prefers-native-streaming"],
},
{
route: "webcodecs",
status: "fallback",
reasons: ["breeze-unavailable-fallback"],
},
],
});
});
});
112 changes: 111 additions & 1 deletion src/lib/exporter/backendPolicy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ExportRenderBackend } from "./types";
import type { ExportBackendPreference, ExportRenderBackend } from "./types";

export type LightningRuntimePlatform = "darwin" | "win32" | "linux" | "unknown";

Expand Down Expand Up @@ -28,6 +28,116 @@ export function shouldPreferNativeAutoBackend(_platform: LightningRuntimePlatfor
return _platform === "darwin" || _platform === "win32";
}

export type LightningExportRoute = "native-static-layout" | "breeze-stream" | "webcodecs";

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

export interface LightningExportRoutePlan {
selectedRoute: LightningExportRoute;
decisions: LightningExportRouteDecision[];
}

export function shouldPreferNativeStaticLayoutBeforeBreeze(
platform: LightningRuntimePlatform,
backendPreference: ExportBackendPreference,
): boolean {
return backendPreference === "auto" && platform === "win32";
}

export function planLightningExportRoutes(options: {
backendPreference: ExportBackendPreference;
platform: LightningRuntimePlatform;
nativeStaticLayoutAvailable: boolean;
nativeStaticLayoutSkipReasons?: string[];
}): LightningExportRoutePlan {
const decisions: LightningExportRouteDecision[] = [];
const nativeStaticLayoutSkipReasons = options.nativeStaticLayoutSkipReasons ?? [];
const canUseNativeStaticLayout =
options.nativeStaticLayoutAvailable && nativeStaticLayoutSkipReasons.length === 0;

const addNativeStaticLayoutDecision = (status: LightningExportRouteDecision["status"]) => {
decisions.push({
route: "native-static-layout",
status,
reasons: canUseNativeStaticLayout
? ["visually-compatible"]
: nativeStaticLayoutSkipReasons.length > 0
? nativeStaticLayoutSkipReasons
: ["native-static-unavailable"],
});
};

if (options.backendPreference === "webcodecs") {
decisions.push({
route: "webcodecs",
status: "selected",
reasons: ["user-selected-webcodecs"],
});
return { selectedRoute: "webcodecs", decisions };
}

const preferStaticFirst =
options.backendPreference === "breeze" ||
shouldPreferNativeStaticLayoutBeforeBreeze(options.platform, options.backendPreference);

if (preferStaticFirst) {
addNativeStaticLayoutDecision(canUseNativeStaticLayout ? "selected" : "rejected");
decisions.push({
route: "breeze-stream",
status: canUseNativeStaticLayout ? "fallback" : "selected",
reasons: [
options.backendPreference === "breeze"
? "user-selected-breeze"
: "windows-native-static-fallback",
],
});
decisions.push({
route: "webcodecs",
status: "fallback",
reasons: ["breeze-unavailable-fallback"],
});
return {
selectedRoute: canUseNativeStaticLayout ? "native-static-layout" : "breeze-stream",
decisions,
};
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (options.backendPreference === "auto" && shouldPreferNativeAutoBackend(options.platform)) {
decisions.push({
route: "native-static-layout",
status: "rejected",
reasons: ["platform-does-not-use-native-static-layout"],
});
decisions.push({
route: "breeze-stream",
status: "selected",
reasons: ["platform-prefers-native-streaming"],
});
decisions.push({
route: "webcodecs",
status: "fallback",
reasons: ["breeze-unavailable-fallback"],
});
return { selectedRoute: "breeze-stream", decisions };
}

decisions.push({
route: "webcodecs",
status: "selected",
reasons: ["default-webcodecs-first"],
});
decisions.push({
route: "breeze-stream",
status: "fallback",
reasons: ["webcodecs-software-or-unavailable-fallback"],
});
return { selectedRoute: "webcodecs", decisions };
}

export function getDefaultLightningRenderBackend(): ExportRenderBackend {
return "webgl";
}