Advance M8-M11 parity workflows
This commit is contained in:
105
web/protocol/render-routing.ts
Normal file
105
web/protocol/render-routing.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import type { ErrorCode } from "./error";
|
||||
|
||||
export const RENDER_ROUTING_SCHEMA_VERSION = 1 as const;
|
||||
|
||||
export type RenderRoutingBackend =
|
||||
| "WEBGL2"
|
||||
| "WEBGPU"
|
||||
| "CYCLES"
|
||||
| "EEVEE_COMPLEX"
|
||||
| "CUDA"
|
||||
| "OPTIX"
|
||||
| "HIP"
|
||||
| "METAL"
|
||||
| "ONEAPI";
|
||||
export type RenderRoutingEngine = "BLENDER_EEVEE" | "BLENDER_EEVEE_NEXT" | "BLENDER_CYCLES" | "BLENDER_WORKBENCH";
|
||||
export type RenderRoutingTarget = "WEB_LOCAL_BOUNDED" | "SERVER_JOB";
|
||||
|
||||
export interface RenderRoutingRequestIR {
|
||||
schemaVersion: typeof RENDER_ROUTING_SCHEMA_VERSION;
|
||||
renderEngine: string;
|
||||
backend: RenderRoutingBackend;
|
||||
complexity: "BOUNDED" | "COMPLEX";
|
||||
hardwareBackend?: "NONE" | "CUDA" | "OPTIX" | "HIP" | "METAL" | "ONEAPI";
|
||||
}
|
||||
|
||||
export interface RenderRoutingContextIR {
|
||||
webgpuAvailable?: boolean;
|
||||
webgpuRendererBundled?: boolean;
|
||||
serverRenderAvailable?: boolean;
|
||||
}
|
||||
|
||||
export interface RenderRoutingResultIR {
|
||||
schemaVersion: typeof RENDER_ROUTING_SCHEMA_VERSION;
|
||||
target: RenderRoutingTarget;
|
||||
status: "READY" | "BLOCKED";
|
||||
capability: "WEB_REALTIME_BOUNDED" | "CYCLES_SERVER_RENDER" | "COMPLEX_EEVEE_SERVER_RENDER" |
|
||||
"HARDWARE_SERVER_RENDER" | "WORKBENCH_SERVER_RENDER" | "UNSUPPORTED_RENDER_ENGINE";
|
||||
reason: "BOUNDED_EEVEE" | "WEBGPU_UNAVAILABLE" | "CYCLES_REQUIRES_SERVER" |
|
||||
"COMPLEX_EEVEE_REQUIRES_SERVER" | "HARDWARE_BACKEND_REQUIRES_SERVER" |
|
||||
"WORKBENCH_REQUIRES_SERVER" | "SERVER_ENDPOINT_UNAVAILABLE" | "UNSUPPORTED_ENGINE";
|
||||
issues: Array<{ code: ErrorCode; message: string; recoverable: boolean }>;
|
||||
}
|
||||
|
||||
function blocked(
|
||||
target: RenderRoutingTarget,
|
||||
capability: RenderRoutingResultIR["capability"],
|
||||
reason: RenderRoutingResultIR["reason"],
|
||||
code: ErrorCode,
|
||||
message: string,
|
||||
): RenderRoutingResultIR {
|
||||
return {
|
||||
schemaVersion: RENDER_ROUTING_SCHEMA_VERSION,
|
||||
target,
|
||||
status: "BLOCKED",
|
||||
capability,
|
||||
reason,
|
||||
issues: [{ code, message, recoverable: true }],
|
||||
};
|
||||
}
|
||||
|
||||
function ready(target: RenderRoutingTarget, capability: RenderRoutingResultIR["capability"], reason: RenderRoutingResultIR["reason"]): RenderRoutingResultIR {
|
||||
return { schemaVersion: RENDER_ROUTING_SCHEMA_VERSION, target, status: "READY", capability, reason, issues: [] };
|
||||
}
|
||||
|
||||
function validateRequest(request: RenderRoutingRequestIR): void {
|
||||
if (!request || request.schemaVersion !== RENDER_ROUTING_SCHEMA_VERSION) throw new Error("INVALID_ARGUMENT: render routing schema is unsupported");
|
||||
if (!Object.hasOwn({ WEBGL2: true, WEBGPU: true, CYCLES: true, EEVEE_COMPLEX: true, CUDA: true, OPTIX: true, HIP: true, METAL: true, ONEAPI: true }, request.backend)) {
|
||||
throw new Error("INVALID_ARGUMENT: render routing backend is unsupported");
|
||||
}
|
||||
if (typeof request.renderEngine !== "string" || !/^[A-Z0-9_]{1,64}$/.test(request.renderEngine)) throw new Error("INVALID_ARGUMENT: render routing engine identity is invalid");
|
||||
if (request.complexity !== "BOUNDED" && request.complexity !== "COMPLEX") throw new Error("INVALID_ARGUMENT: render routing complexity is unsupported");
|
||||
if (request.hardwareBackend !== undefined && !Object.hasOwn({ NONE: true, CUDA: true, OPTIX: true, HIP: true, METAL: true, ONEAPI: true }, request.hardwareBackend)) {
|
||||
throw new Error("INVALID_ARGUMENT: render routing hardware backend is unsupported");
|
||||
}
|
||||
}
|
||||
|
||||
/** Routes final-render-only capabilities without claiming a local approximation is equivalent. */
|
||||
export function routeRenderExecution(request: RenderRoutingRequestIR, context: RenderRoutingContextIR = {}): RenderRoutingResultIR {
|
||||
validateRequest(request);
|
||||
const hardware = request.hardwareBackend && request.hardwareBackend !== "NONE" ? request.hardwareBackend : undefined;
|
||||
if (hardware || ["CUDA", "OPTIX", "HIP", "METAL", "ONEAPI"].includes(request.backend)) {
|
||||
if (!context.serverRenderAvailable) return blocked("SERVER_JOB", "HARDWARE_SERVER_RENDER", "HARDWARE_BACKEND_REQUIRES_SERVER", "SERVER_JOB_UNAVAILABLE", `Hardware backend ${hardware ?? request.backend} requires a configured server render job`);
|
||||
return ready("SERVER_JOB", "HARDWARE_SERVER_RENDER", "HARDWARE_BACKEND_REQUIRES_SERVER");
|
||||
}
|
||||
if (request.renderEngine === "BLENDER_CYCLES" || request.backend === "CYCLES") {
|
||||
if (!context.serverRenderAvailable) return blocked("SERVER_JOB", "CYCLES_SERVER_RENDER", "CYCLES_REQUIRES_SERVER", "SERVER_JOB_UNAVAILABLE", "Cycles final rendering requires a configured server render job");
|
||||
return ready("SERVER_JOB", "CYCLES_SERVER_RENDER", "CYCLES_REQUIRES_SERVER");
|
||||
}
|
||||
if (request.renderEngine === "BLENDER_WORKBENCH") {
|
||||
if (!context.serverRenderAvailable) return blocked("SERVER_JOB", "WORKBENCH_SERVER_RENDER", "WORKBENCH_REQUIRES_SERVER", "SERVER_JOB_UNAVAILABLE", "Workbench final rendering is not a Web realtime equivalent and requires a configured server render job");
|
||||
return ready("SERVER_JOB", "WORKBENCH_SERVER_RENDER", "WORKBENCH_REQUIRES_SERVER");
|
||||
}
|
||||
if (request.renderEngine === "BLENDER_EEVEE" || request.renderEngine === "BLENDER_EEVEE_NEXT") {
|
||||
if (request.complexity === "COMPLEX" || request.backend === "EEVEE_COMPLEX") {
|
||||
if (!context.serverRenderAvailable) return blocked("SERVER_JOB", "COMPLEX_EEVEE_SERVER_RENDER", "COMPLEX_EEVEE_REQUIRES_SERVER", "SERVER_JOB_UNAVAILABLE", "Complex Eevee final rendering requires a configured server render job");
|
||||
return ready("SERVER_JOB", "COMPLEX_EEVEE_SERVER_RENDER", "COMPLEX_EEVEE_REQUIRES_SERVER");
|
||||
}
|
||||
if (request.backend === "WEBGPU" && !(context.webgpuAvailable && context.webgpuRendererBundled)) {
|
||||
return blocked("WEB_LOCAL_BOUNDED", "WEB_REALTIME_BOUNDED", "WEBGPU_UNAVAILABLE", "WEBGPU_RENDERER_UNAVAILABLE", "The requested WebGPU realtime renderer is unavailable");
|
||||
}
|
||||
if (request.backend !== "WEBGL2" && request.backend !== "WEBGPU") throw new Error("INVALID_ARGUMENT: bounded Eevee requires WEBGL2 or WEBGPU");
|
||||
return ready("WEB_LOCAL_BOUNDED", "WEB_REALTIME_BOUNDED", "BOUNDED_EEVEE");
|
||||
}
|
||||
return blocked("SERVER_JOB", "UNSUPPORTED_RENDER_ENGINE", "UNSUPPORTED_ENGINE", "PLATFORM_CAPABILITY_UNAVAILABLE", "The requested render engine has no declared Web or server route");
|
||||
}
|
||||
Reference in New Issue
Block a user