import { describe, expect, it } from "vitest"; import { toolcraftGpuComputeResourceKinds, toolcraftGpuPassResourceKinds, toolcraftGpuPassStages, toolcraftGpuPassStates, toolcraftGpuPassSurfaceSets, type ToolcraftGpuPassExecution, type ToolcraftGpuTechnique, type ToolcraftGpuTechniqueSurfaces, } from "./index"; import { registerToolcraftRendererPipeline, type ToolcraftRendererPipelinePassContract, } from "./renderer-pipeline-registration"; type PreviewModel = Readonly<{ opacity: number }>; const feedbackCompute = { resources: "storage-textures", stage: "compute", state: "feedback", surfaces: ["preview", "export"], } as const satisfies ToolcraftGpuPassExecution; const vgpuTechnique = { backend: "webgpu", capability: "shader-webgpu-vgpu", provider: "vgpu", versionPolicy: "app-pinned", } as const satisfies ToolcraftGpuTechnique; describe("Toolcraft renderer pipeline GPU registration", () => { it("defines the closed immutable GPU execution and provider vocabulary", () => { const neutralRender = { resources: "uniforms-only", stage: "render", state: "stateless", surfaces: ["preview"], } as const satisfies ToolcraftGpuPassExecution; if (false) { // @ts-expect-error Compute stages require storage-capable resources. const invalidCompute: ToolcraftGpuPassExecution = { resources: "uniforms-only", stage: "compute", state: "stateless", surfaces: ["preview"], }; void invalidCompute; const redundant: ToolcraftGpuPassExecution = { // @ts-expect-error State is the sole temporal discriminator. readsPreviousFrame: true, resources: "storage-buffers", stage: "compute", state: "feedback", surfaces: ["preview"], }; void redundant; // @ts-expect-error VGPU is a WebGPU provider. const invalidVgpu: ToolcraftGpuTechnique = { ...vgpuTechnique, backend: "webgl" }; void invalidVgpu; // @ts-expect-error Native WebGPU must carry closed exception evidence. const unprovedNative: ToolcraftGpuTechnique = { backend: "webgpu", provider: "native" }; void unprovedNative; // @ts-expect-error At least one GPU technique surface is required. const empty: ToolcraftGpuTechniqueSurfaces = {}; void empty; // @ts-expect-error Undefined does not declare a GPU technique surface. const undefinedSurface: ToolcraftGpuTechniqueSurfaces = { preview: undefined }; void undefinedSurface; } expect(feedbackCompute).toEqual({ resources: "storage-textures", stage: "compute", state: "feedback", surfaces: ["preview", "export"] }); expect(feedbackCompute.state).toBe("feedback"); expect(feedbackCompute).not.toHaveProperty("readsPreviousFrame"); expect(neutralRender).not.toHaveProperty("wgsl"); expect(vgpuTechnique).toEqual({ backend: "webgpu", capability: "shader-webgpu-vgpu", provider: "vgpu", versionPolicy: "app-pinned" }); expect({ preview: vgpuTechnique } satisfies ToolcraftGpuTechniqueSurfaces) .toEqual({ preview: vgpuTechnique }); expect({ export: vgpuTechnique } satisfies ToolcraftGpuTechniqueSurfaces) .toEqual({ export: vgpuTechnique }); expect({ export: vgpuTechnique, preview: vgpuTechnique } satisfies ToolcraftGpuTechniqueSurfaces) .toEqual({ export: vgpuTechnique, preview: vgpuTechnique }); expect(toolcraftGpuPassResourceKinds).toEqual([ "uniforms-only", "sampled-textures", "storage-buffers", "storage-textures", "mixed"]); expect(toolcraftGpuComputeResourceKinds).toEqual(["storage-buffers", "storage-textures", "mixed"]); expect(toolcraftGpuPassStages).toEqual(["render", "compute"]); expect(toolcraftGpuPassStates).toEqual(["stateless", "feedback"]); expect(toolcraftGpuPassSurfaceSets).toEqual([["preview"], ["export"], ["preview", "export"]]); for (const vocabulary of [toolcraftGpuPassResourceKinds, toolcraftGpuComputeResourceKinds, toolcraftGpuPassStages, toolcraftGpuPassStates, toolcraftGpuPassSurfaceSets]) { expect(Object.isFrozen(vocabulary)).toBe(true); } expect(toolcraftGpuPassSurfaceSets.every(Object.isFrozen)).toBe(true); }); it("clones and freezes GPU execution semantics on compiled passes", () => { const gpu = { resources: "storage-buffers", stage: "compute", state: "feedback", surfaces: ["preview"] } as const satisfies ToolcraftGpuPassExecution; const registration = registerToolcraftRendererPipeline<{ preview: ToolcraftRendererPipelinePassContract; }>()({ interactionInvalidation: [], passes: [{ gpu, id: "preview", inputs: ["appearance.opacity"], invalidatedBy: ["appearance.opacity"], kind: "composite", output: "preview", quality: "full", runsOn: "gpu" }], runtimeId: "gpu-preview-runtime-v1" }); const compiledGpu = registration.passes[0].gpu; expect(compiledGpu).toEqual(gpu); expect(compiledGpu).not.toBe(gpu); expect(compiledGpu.surfaces).not.toBe(gpu.surfaces); expect(Object.isFrozen(compiledGpu)).toBe(true); expect(Object.isFrozen(compiledGpu.surfaces)).toBe(true); }); it("requires GPU semantics exactly on GPU-capable pass locations", () => { const register = registerToolcraftRendererPipeline<{ preview: ToolcraftRendererPipelinePassContract; }>(); const missing = { interactionInvalidation: [], passes: [{ id: "preview", inputs: ["appearance.opacity"], invalidatedBy: ["appearance.opacity"], kind: "composite", output: "preview", quality: "full", runsOn: "gpu" }], runtimeId: "missing-gpu-semantics-v1" } as const; const forbidden = { ...missing, passes: [{ ...missing.passes[0], gpu: feedbackCompute, runsOn: "main" }], runtimeId: "forbidden-gpu-semantics-v1" } as const; if (false) { // @ts-expect-error GPU passes must declare execution semantics. register(missing); // @ts-expect-error Main passes must omit GPU execution semantics. register(forbidden); } expect(() => register(missing as never)).toThrow( 'rendererPipeline pass "preview" runs on gpu and must declare gpu execution semantics.'); expect(() => register(forbidden as never)).toThrow( 'rendererPipeline pass "preview" runs on main and must omit gpu execution semantics.'); }); it.each([ ["resources", { resources: "vertex-buffers" }, 'Renderer pipeline pass "preview" gpu.resources must be one of "uniforms-only", "sampled-textures", "storage-buffers", "storage-textures", or "mixed".'], ["stage", { stage: "fragment" }, 'Renderer pipeline pass "preview" gpu.stage must be one of "render", or "compute".'], ["state", { state: "persistent" }, 'Renderer pipeline pass "preview" gpu.state must be one of "stateless", or "feedback".'], ["surfaces", { surfaces: ["export", "preview"] }, 'Renderer pipeline pass "preview" gpu.surfaces must be one of ["preview"], ["export"], or ["preview","export"].'], ])("rejects unknown GPU %s values before branding", (_field, override, message) => { const descriptor = { interactionInvalidation: [], passes: [{ gpu: { ...feedbackCompute, ...override }, id: "preview", inputs: ["appearance.opacity"], invalidatedBy: ["appearance.opacity"], kind: "composite", output: "preview", quality: "full", runsOn: "gpu" }], runtimeId: "invalid-gpu-semantics-v1" } as const; expect(() => registerToolcraftRendererPipeline<{ preview: ToolcraftRendererPipelinePassContract; }>()(descriptor as never)).toThrow(message); }); it("rejects impossible compute resource semantics before branding", () => { const descriptor = { interactionInvalidation: [], passes: [{ gpu: { ...feedbackCompute, resources: "uniforms-only" }, id: "preview", inputs: ["appearance.opacity"], invalidatedBy: ["appearance.opacity"], kind: "composite", output: "preview", quality: "full", runsOn: "gpu" }], runtimeId: "impossible-gpu-semantics-v1" } as const; expect(() => registerToolcraftRendererPipeline<{ preview: ToolcraftRendererPipelinePassContract; }>()(descriptor as never)).toThrow( 'Renderer pipeline pass "preview" gpu.resources must be one of "storage-buffers", "storage-textures", or "mixed" when stage is "compute".'); }); });