@motion-core/motion-gpu 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -0
- package/dist/advanced.js +3 -1
- package/dist/core/advanced.js +3 -1
- package/dist/core/compute-shader.d.ts +87 -0
- package/dist/core/compute-shader.d.ts.map +1 -0
- package/dist/core/compute-shader.js +205 -0
- package/dist/core/compute-shader.js.map +1 -0
- package/dist/core/error-report.d.ts +1 -1
- package/dist/core/error-report.d.ts.map +1 -1
- package/dist/core/error-report.js +63 -0
- package/dist/core/error-report.js.map +1 -1
- package/dist/core/frame-registry.d.ts.map +1 -1
- package/dist/core/frame-registry.js +1 -1
- package/dist/core/frame-registry.js.map +1 -1
- package/dist/core/index.d.ts +5 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -1
- package/dist/core/material-preprocess.d.ts.map +1 -1
- package/dist/core/material-preprocess.js +5 -3
- package/dist/core/material-preprocess.js.map +1 -1
- package/dist/core/material.d.ts +22 -6
- package/dist/core/material.d.ts.map +1 -1
- package/dist/core/material.js +30 -3
- package/dist/core/material.js.map +1 -1
- package/dist/core/render-graph.d.ts +7 -3
- package/dist/core/render-graph.d.ts.map +1 -1
- package/dist/core/render-graph.js +22 -6
- package/dist/core/render-graph.js.map +1 -1
- package/dist/core/renderer.d.ts.map +1 -1
- package/dist/core/renderer.js +418 -23
- package/dist/core/renderer.js.map +1 -1
- package/dist/core/runtime-loop.d.ts +2 -2
- package/dist/core/runtime-loop.d.ts.map +1 -1
- package/dist/core/runtime-loop.js +49 -1
- package/dist/core/runtime-loop.js.map +1 -1
- package/dist/core/shader.d.ts +9 -1
- package/dist/core/shader.d.ts.map +1 -1
- package/dist/core/shader.js +21 -2
- package/dist/core/shader.js.map +1 -1
- package/dist/core/storage-buffers.d.ts +37 -0
- package/dist/core/storage-buffers.d.ts.map +1 -0
- package/dist/core/storage-buffers.js +95 -0
- package/dist/core/storage-buffers.js.map +1 -0
- package/dist/core/texture-loader.d.ts.map +1 -1
- package/dist/core/texture-loader.js +4 -0
- package/dist/core/texture-loader.js.map +1 -1
- package/dist/core/textures.d.ts +12 -0
- package/dist/core/textures.d.ts.map +1 -1
- package/dist/core/textures.js +7 -2
- package/dist/core/textures.js.map +1 -1
- package/dist/core/types.d.ts +146 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/passes/ComputePass.d.ts +83 -0
- package/dist/passes/ComputePass.d.ts.map +1 -0
- package/dist/passes/ComputePass.js +92 -0
- package/dist/passes/ComputePass.js.map +1 -0
- package/dist/passes/PingPongComputePass.d.ts +104 -0
- package/dist/passes/PingPongComputePass.d.ts.map +1 -0
- package/dist/passes/PingPongComputePass.js +132 -0
- package/dist/passes/PingPongComputePass.js.map +1 -0
- package/dist/passes/ShaderPass.d.ts.map +1 -1
- package/dist/passes/ShaderPass.js +2 -1
- package/dist/passes/ShaderPass.js.map +1 -1
- package/dist/passes/index.d.ts +2 -0
- package/dist/passes/index.d.ts.map +1 -1
- package/dist/passes/index.js +3 -1
- package/dist/react/FragCanvas.d.ts +2 -2
- package/dist/react/FragCanvas.d.ts.map +1 -1
- package/dist/react/FragCanvas.js.map +1 -1
- package/dist/react/MotionGPUErrorOverlay.d.ts.map +1 -1
- package/dist/react/MotionGPUErrorOverlay.js +123 -20
- package/dist/react/MotionGPUErrorOverlay.js.map +1 -1
- package/dist/react/advanced.js +3 -1
- package/dist/react/index.d.ts +5 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -1
- package/dist/svelte/FragCanvas.svelte +2 -2
- package/dist/svelte/FragCanvas.svelte.d.ts +2 -2
- package/dist/svelte/FragCanvas.svelte.d.ts.map +1 -1
- package/dist/svelte/MotionGPUErrorOverlay.svelte +137 -7
- package/dist/svelte/MotionGPUErrorOverlay.svelte.d.ts.map +1 -1
- package/dist/svelte/advanced.js +3 -1
- package/dist/svelte/index.d.ts +5 -2
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +3 -1
- package/package.json +1 -1
- package/src/lib/core/compute-shader.ts +326 -0
- package/src/lib/core/error-report.ts +129 -0
- package/src/lib/core/frame-registry.ts +2 -1
- package/src/lib/core/index.ts +18 -1
- package/src/lib/core/material-preprocess.ts +17 -6
- package/src/lib/core/material.ts +101 -20
- package/src/lib/core/render-graph.ts +39 -9
- package/src/lib/core/renderer.ts +655 -41
- package/src/lib/core/runtime-loop.ts +82 -3
- package/src/lib/core/shader.ts +45 -2
- package/src/lib/core/storage-buffers.ts +142 -0
- package/src/lib/core/texture-loader.ts +6 -0
- package/src/lib/core/textures.ts +24 -2
- package/src/lib/core/types.ts +165 -4
- package/src/lib/passes/ComputePass.ts +136 -0
- package/src/lib/passes/PingPongComputePass.ts +180 -0
- package/src/lib/passes/ShaderPass.ts +2 -1
- package/src/lib/passes/index.ts +6 -0
- package/src/lib/react/FragCanvas.tsx +3 -3
- package/src/lib/react/MotionGPUErrorOverlay.tsx +137 -5
- package/src/lib/react/index.ts +18 -1
- package/src/lib/svelte/FragCanvas.svelte +2 -2
- package/src/lib/svelte/MotionGPUErrorOverlay.svelte +137 -7
- package/src/lib/svelte/index.ts +18 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-loop.js","names":[],"sources":["../../src/lib/core/runtime-loop.ts"],"sourcesContent":["import type { CurrentReadable, CurrentWritable } from './current-value.js';\nimport { resolveMaterial, type FragMaterial, type ResolvedMaterial } from './material.js';\nimport {\n\ttoMotionGPUErrorReport,\n\ttype MotionGPUErrorPhase,\n\ttype MotionGPUErrorReport\n} from './error-report.js';\nimport { createRenderer } from './renderer.js';\nimport { buildRendererPipelineSignature } from './recompile-policy.js';\nimport { assertUniformValueForType } from './uniforms.js';\nimport type { FrameRegistry } from './frame-registry.js';\nimport type {\n\tFrameInvalidationToken,\n\tOutputColorSpace,\n\tRenderPass,\n\tRenderer,\n\tRenderTargetDefinitionMap,\n\tTextureMap,\n\tTextureValue,\n\tUniformType,\n\tUniformValue\n} from './types.js';\n\nexport interface MotionGPURuntimeLoopOptions {\n\tcanvas: HTMLCanvasElement;\n\tregistry: FrameRegistry;\n\tsize: CurrentWritable<{ width: number; height: number }>;\n\tdpr: CurrentReadable<number>;\n\tmaxDelta: CurrentReadable<number>;\n\tgetMaterial: () => FragMaterial;\n\tgetRenderTargets: () => RenderTargetDefinitionMap;\n\tgetPasses: () => RenderPass[];\n\tgetClearColor: () => [number, number, number, number];\n\tgetOutputColorSpace: () => OutputColorSpace;\n\tgetAdapterOptions: () => GPURequestAdapterOptions | undefined;\n\tgetDeviceDescriptor: () => GPUDeviceDescriptor | undefined;\n\tgetOnError: () => ((report: MotionGPUErrorReport) => void) | undefined;\n\treportError: (report: MotionGPUErrorReport | null) => void;\n\tgetErrorHistoryLimit?: () => number | undefined;\n\tgetOnErrorHistory?: () => ((history: MotionGPUErrorReport[]) => void) | undefined;\n\treportErrorHistory?: (history: MotionGPUErrorReport[]) => void;\n}\n\nexport interface MotionGPURuntimeLoop {\n\trequestFrame: () => void;\n\tinvalidate: (token?: FrameInvalidationToken) => void;\n\tadvance: () => void;\n\tdestroy: () => void;\n}\n\nfunction getRendererRetryDelayMs(attempt: number): number {\n\treturn Math.min(8000, 250 * 2 ** Math.max(0, attempt - 1));\n}\n\nexport function createMotionGPURuntimeLoop(\n\toptions: MotionGPURuntimeLoopOptions\n): MotionGPURuntimeLoop {\n\tconst { canvas: canvasElement, registry, size } = options;\n\tlet frameId: number | null = null;\n\tlet renderer: Renderer | null = null;\n\tlet isDisposed = false;\n\tlet previousTime = performance.now() / 1000;\n\tlet activeRendererSignature = '';\n\tlet failedRendererSignature: string | null = null;\n\tlet failedRendererAttempts = 0;\n\tlet nextRendererRetryAt = 0;\n\tlet rendererRebuildPromise: Promise<void> | null = null;\n\n\tconst runtimeUniforms: Record<string, UniformValue> = {};\n\tconst runtimeTextures: TextureMap = {};\n\tlet activeUniforms: Record<string, UniformValue> = {};\n\tlet activeTextures: Record<string, { source?: TextureValue }> = {};\n\tlet uniformKeys: string[] = [];\n\tlet uniformKeySet = new Set<string>();\n\tlet uniformTypes = new Map<string, UniformType>();\n\tlet textureKeys: string[] = [];\n\tlet textureKeySet = new Set<string>();\n\tlet activeMaterialSignature = '';\n\tlet currentCssWidth = -1;\n\tlet currentCssHeight = -1;\n\tconst renderUniforms: Record<string, UniformValue> = {};\n\tconst renderTextures: TextureMap = {};\n\tconst canvasSize = { width: 0, height: 0 };\n\tlet shouldContinueAfterFrame = false;\n\tlet activeErrorKey: string | null = null;\n\tlet errorHistory: MotionGPUErrorReport[] = [];\n\n\tconst getHistoryLimit = (): number => {\n\t\tconst value = options.getErrorHistoryLimit?.() ?? 0;\n\t\tif (!Number.isFinite(value) || value <= 0) {\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn Math.floor(value);\n\t};\n\n\tconst publishErrorHistory = (): void => {\n\t\toptions.reportErrorHistory?.(errorHistory);\n\t\tconst onErrorHistory = options.getOnErrorHistory?.();\n\t\tif (!onErrorHistory) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tonErrorHistory(errorHistory);\n\t\t} catch {\n\t\t\t// User-provided error history handlers must not break runtime error recovery.\n\t\t}\n\t};\n\n\tconst syncErrorHistory = (): void => {\n\t\tconst limit = getHistoryLimit();\n\t\tif (limit <= 0) {\n\t\t\tif (errorHistory.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\terrorHistory = [];\n\t\t\tpublishErrorHistory();\n\t\t\treturn;\n\t\t}\n\n\t\tif (errorHistory.length <= limit) {\n\t\t\treturn;\n\t\t}\n\n\t\terrorHistory = errorHistory.slice(errorHistory.length - limit);\n\t\tpublishErrorHistory();\n\t};\n\n\tconst setError = (error: unknown, phase: MotionGPUErrorPhase): void => {\n\t\tconst report = toMotionGPUErrorReport(error, phase);\n\t\tconst reportKey = JSON.stringify({\n\t\t\tphase: report.phase,\n\t\t\ttitle: report.title,\n\t\t\tmessage: report.message,\n\t\t\trawMessage: report.rawMessage\n\t\t});\n\t\tif (activeErrorKey === reportKey) {\n\t\t\treturn;\n\t\t}\n\t\tactiveErrorKey = reportKey;\n\t\tconst historyLimit = getHistoryLimit();\n\t\tif (historyLimit > 0) {\n\t\t\terrorHistory = [...errorHistory, report];\n\t\t\tif (errorHistory.length > historyLimit) {\n\t\t\t\terrorHistory = errorHistory.slice(errorHistory.length - historyLimit);\n\t\t\t}\n\t\t\tpublishErrorHistory();\n\t\t}\n\t\toptions.reportError(report);\n\t\tconst onError = options.getOnError();\n\t\tif (!onError) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tonError(report);\n\t\t} catch {\n\t\t\t// User-provided error handlers must not break runtime error recovery.\n\t\t}\n\t};\n\n\tconst clearError = (): void => {\n\t\tif (activeErrorKey === null) {\n\t\t\treturn;\n\t\t}\n\n\t\tactiveErrorKey = null;\n\t\toptions.reportError(null);\n\t};\n\n\tconst scheduleFrame = (): void => {\n\t\tif (isDisposed || frameId !== null) {\n\t\t\treturn;\n\t\t}\n\n\t\tframeId = requestAnimationFrame(renderFrame);\n\t};\n\n\tconst requestFrame = (): void => {\n\t\tscheduleFrame();\n\t};\n\n\tconst invalidate = (token?: FrameInvalidationToken): void => {\n\t\tregistry.invalidate(token);\n\t\trequestFrame();\n\t};\n\n\tconst advance = (): void => {\n\t\tregistry.advance();\n\t\trequestFrame();\n\t};\n\n\tconst resetRuntimeMaps = (): void => {\n\t\tfor (const key of Object.keys(runtimeUniforms)) {\n\t\t\tif (!uniformKeySet.has(key)) {\n\t\t\t\tReflect.deleteProperty(runtimeUniforms, key);\n\t\t\t}\n\t\t}\n\n\t\tfor (const key of Object.keys(runtimeTextures)) {\n\t\t\tif (!textureKeySet.has(key)) {\n\t\t\t\tReflect.deleteProperty(runtimeTextures, key);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst resetRenderPayloadMaps = (): void => {\n\t\tfor (const key of Object.keys(renderUniforms)) {\n\t\t\tif (!uniformKeySet.has(key)) {\n\t\t\t\tReflect.deleteProperty(renderUniforms, key);\n\t\t\t}\n\t\t}\n\n\t\tfor (const key of Object.keys(renderTextures)) {\n\t\t\tif (!textureKeySet.has(key)) {\n\t\t\t\tReflect.deleteProperty(renderTextures, key);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst syncMaterialRuntimeState = (materialState: ResolvedMaterial): void => {\n\t\tconst signatureChanged = activeMaterialSignature !== materialState.signature;\n\t\tconst defaultsChanged =\n\t\t\tactiveUniforms !== materialState.uniforms || activeTextures !== materialState.textures;\n\n\t\tif (!signatureChanged && !defaultsChanged) {\n\t\t\treturn;\n\t\t}\n\n\t\tactiveUniforms = materialState.uniforms;\n\t\tactiveTextures = materialState.textures;\n\t\tif (!signatureChanged) {\n\t\t\treturn;\n\t\t}\n\n\t\tuniformKeys = materialState.uniformLayout.entries.map((entry) => entry.name);\n\t\tuniformTypes = new Map(\n\t\t\tmaterialState.uniformLayout.entries.map((entry) => [entry.name, entry.type])\n\t\t);\n\t\ttextureKeys = materialState.textureKeys;\n\t\tuniformKeySet = new Set(uniformKeys);\n\t\ttextureKeySet = new Set(textureKeys);\n\t\tresetRuntimeMaps();\n\t\tresetRenderPayloadMaps();\n\t\tactiveMaterialSignature = materialState.signature;\n\t};\n\n\tconst resolveActiveMaterial = (): ResolvedMaterial => {\n\t\treturn resolveMaterial(options.getMaterial());\n\t};\n\n\tconst setUniform = (name: string, value: UniformValue): void => {\n\t\tif (!uniformKeySet.has(name)) {\n\t\t\tthrow new Error(`Unknown uniform \"${name}\". Declare it in material.uniforms first.`);\n\t\t}\n\t\tconst expectedType = uniformTypes.get(name);\n\t\tif (!expectedType) {\n\t\t\tthrow new Error(`Unknown uniform type for \"${name}\"`);\n\t\t}\n\t\tassertUniformValueForType(expectedType, value);\n\t\truntimeUniforms[name] = value;\n\t};\n\n\tconst setTexture = (name: string, value: TextureValue): void => {\n\t\tif (!textureKeySet.has(name)) {\n\t\t\tthrow new Error(`Unknown texture \"${name}\". Declare it in material.textures first.`);\n\t\t}\n\t\truntimeTextures[name] = value;\n\t};\n\n\tconst renderFrame = (timestamp: number): void => {\n\t\tframeId = null;\n\t\tif (isDisposed) {\n\t\t\treturn;\n\t\t}\n\t\tsyncErrorHistory();\n\n\t\tlet materialState: ResolvedMaterial;\n\t\ttry {\n\t\t\tmaterialState = resolveActiveMaterial();\n\t\t} catch (error) {\n\t\t\tsetError(error, 'initialization');\n\t\t\tscheduleFrame();\n\t\t\treturn;\n\t\t}\n\n\t\tshouldContinueAfterFrame = false;\n\n\t\tconst outputColorSpace = options.getOutputColorSpace();\n\t\tconst rendererSignature = buildRendererPipelineSignature({\n\t\t\tmaterialSignature: materialState.signature,\n\t\t\toutputColorSpace\n\t\t});\n\t\tsyncMaterialRuntimeState(materialState);\n\n\t\tif (failedRendererSignature && failedRendererSignature !== rendererSignature) {\n\t\t\tfailedRendererSignature = null;\n\t\t\tfailedRendererAttempts = 0;\n\t\t\tnextRendererRetryAt = 0;\n\t\t}\n\n\t\tif (!renderer || activeRendererSignature !== rendererSignature) {\n\t\t\tif (\n\t\t\t\tfailedRendererSignature === rendererSignature &&\n\t\t\t\tperformance.now() < nextRendererRetryAt\n\t\t\t) {\n\t\t\t\tscheduleFrame();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!rendererRebuildPromise) {\n\t\t\t\trendererRebuildPromise = (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst nextRenderer = await createRenderer({\n\t\t\t\t\t\t\tcanvas: canvasElement,\n\t\t\t\t\t\t\tfragmentWgsl: materialState.fragmentWgsl,\n\t\t\t\t\t\t\tfragmentLineMap: materialState.fragmentLineMap,\n\t\t\t\t\t\t\tfragmentSource: materialState.fragmentSource,\n\t\t\t\t\t\t\tincludeSources: materialState.includeSources,\n\t\t\t\t\t\t\tdefineBlockSource: materialState.defineBlockSource,\n\t\t\t\t\t\t\tmaterialSource: materialState.source,\n\t\t\t\t\t\t\tmaterialSignature: materialState.signature,\n\t\t\t\t\t\t\tuniformLayout: materialState.uniformLayout,\n\t\t\t\t\t\t\ttextureKeys: materialState.textureKeys,\n\t\t\t\t\t\t\ttextureDefinitions: materialState.textures,\n\t\t\t\t\t\t\tgetRenderTargets: options.getRenderTargets,\n\t\t\t\t\t\t\tgetPasses: options.getPasses,\n\t\t\t\t\t\t\toutputColorSpace,\n\t\t\t\t\t\t\tgetClearColor: options.getClearColor,\n\t\t\t\t\t\t\tgetDpr: () => options.dpr.current,\n\t\t\t\t\t\t\tadapterOptions: options.getAdapterOptions(),\n\t\t\t\t\t\t\tdeviceDescriptor: options.getDeviceDescriptor()\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (isDisposed) {\n\t\t\t\t\t\t\tnextRenderer.destroy();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\trenderer?.destroy();\n\t\t\t\t\t\trenderer = nextRenderer;\n\t\t\t\t\t\tactiveRendererSignature = rendererSignature;\n\t\t\t\t\t\tfailedRendererSignature = null;\n\t\t\t\t\t\tfailedRendererAttempts = 0;\n\t\t\t\t\t\tnextRendererRetryAt = 0;\n\t\t\t\t\t\tclearError();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tfailedRendererSignature = rendererSignature;\n\t\t\t\t\t\tfailedRendererAttempts += 1;\n\t\t\t\t\t\tconst retryDelayMs = getRendererRetryDelayMs(failedRendererAttempts);\n\t\t\t\t\t\tnextRendererRetryAt = performance.now() + retryDelayMs;\n\t\t\t\t\t\tsetError(error, 'initialization');\n\t\t\t\t\t} finally {\n\t\t\t\t\t\trendererRebuildPromise = null;\n\t\t\t\t\t\tscheduleFrame();\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst time = timestamp / 1000;\n\t\tconst rawDelta = Math.max(0, time - previousTime);\n\t\tconst delta = Math.min(rawDelta, options.maxDelta.current);\n\t\tpreviousTime = time;\n\t\tconst rect = canvasElement.getBoundingClientRect();\n\t\tconst width = Math.max(0, Math.floor(rect.width));\n\t\tconst height = Math.max(0, Math.floor(rect.height));\n\t\tif (width !== currentCssWidth || height !== currentCssHeight) {\n\t\t\tcurrentCssWidth = width;\n\t\t\tcurrentCssHeight = height;\n\t\t\tsize.set({ width, height });\n\t\t}\n\n\t\ttry {\n\t\t\tregistry.run({\n\t\t\t\ttime,\n\t\t\t\tdelta,\n\t\t\t\tsetUniform,\n\t\t\t\tsetTexture,\n\t\t\t\tinvalidate,\n\t\t\t\tadvance,\n\t\t\t\trenderMode: registry.getRenderMode(),\n\t\t\t\tautoRender: registry.getAutoRender(),\n\t\t\t\tcanvas: canvasElement\n\t\t\t});\n\n\t\t\tconst shouldRenderFrame = registry.shouldRender();\n\t\t\tshouldContinueAfterFrame =\n\t\t\t\tregistry.getRenderMode() === 'always' ||\n\t\t\t\t(registry.getRenderMode() === 'on-demand' && shouldRenderFrame);\n\n\t\t\tif (shouldRenderFrame) {\n\t\t\t\tfor (const key of uniformKeys) {\n\t\t\t\t\tconst runtimeValue = runtimeUniforms[key];\n\t\t\t\t\trenderUniforms[key] =\n\t\t\t\t\t\truntimeValue === undefined ? (activeUniforms[key] as UniformValue) : runtimeValue;\n\t\t\t\t}\n\n\t\t\t\tfor (const key of textureKeys) {\n\t\t\t\t\tconst runtimeValue = runtimeTextures[key];\n\t\t\t\t\trenderTextures[key] =\n\t\t\t\t\t\truntimeValue === undefined ? (activeTextures[key]?.source ?? null) : runtimeValue;\n\t\t\t\t}\n\n\t\t\t\tcanvasSize.width = width;\n\t\t\t\tcanvasSize.height = height;\n\t\t\t\trenderer.render({\n\t\t\t\t\ttime,\n\t\t\t\t\tdelta,\n\t\t\t\t\trenderMode: registry.getRenderMode(),\n\t\t\t\t\tuniforms: renderUniforms,\n\t\t\t\t\ttextures: renderTextures,\n\t\t\t\t\tcanvasSize\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tclearError();\n\t\t} catch (error) {\n\t\t\tsetError(error, 'render');\n\t\t} finally {\n\t\t\tregistry.endFrame();\n\t\t}\n\n\t\tif (shouldContinueAfterFrame) {\n\t\t\tscheduleFrame();\n\t\t}\n\t};\n\n\t(async () => {\n\t\ttry {\n\t\t\tconst initialMaterial = resolveActiveMaterial();\n\t\t\tsyncMaterialRuntimeState(initialMaterial);\n\t\t\tactiveRendererSignature = '';\n\t\t\tscheduleFrame();\n\t\t} catch (error) {\n\t\t\tsetError(error, 'initialization');\n\t\t\tscheduleFrame();\n\t\t}\n\t})();\n\n\treturn {\n\t\trequestFrame,\n\t\tinvalidate,\n\t\tadvance,\n\t\tdestroy: () => {\n\t\t\tisDisposed = true;\n\t\t\tif (frameId !== null) {\n\t\t\t\tcancelAnimationFrame(frameId);\n\t\t\t\tframeId = null;\n\t\t\t}\n\t\t\trenderer?.destroy();\n\t\t\tregistry.clear();\n\t\t}\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime-loop.js","names":[],"sources":["../../src/lib/core/runtime-loop.ts"],"sourcesContent":["import type { CurrentReadable, CurrentWritable } from './current-value.js';\nimport { resolveMaterial, type FragMaterial, type ResolvedMaterial } from './material.js';\nimport {\n\ttoMotionGPUErrorReport,\n\ttype MotionGPUErrorPhase,\n\ttype MotionGPUErrorReport\n} from './error-report.js';\nimport { createRenderer } from './renderer.js';\nimport { buildRendererPipelineSignature } from './recompile-policy.js';\nimport { assertUniformValueForType } from './uniforms.js';\nimport type { FrameRegistry } from './frame-registry.js';\nimport type {\n\tAnyPass,\n\tFrameInvalidationToken,\n\tOutputColorSpace,\n\tPendingStorageWrite,\n\tRenderer,\n\tRenderTargetDefinitionMap,\n\tStorageBufferDefinitionMap,\n\tTextureMap,\n\tTextureValue,\n\tUniformType,\n\tUniformValue\n} from './types.js';\n\nexport interface MotionGPURuntimeLoopOptions {\n\tcanvas: HTMLCanvasElement;\n\tregistry: FrameRegistry;\n\tsize: CurrentWritable<{ width: number; height: number }>;\n\tdpr: CurrentReadable<number>;\n\tmaxDelta: CurrentReadable<number>;\n\tgetMaterial: () => FragMaterial;\n\tgetRenderTargets: () => RenderTargetDefinitionMap;\n\tgetPasses: () => AnyPass[];\n\tgetClearColor: () => [number, number, number, number];\n\tgetOutputColorSpace: () => OutputColorSpace;\n\tgetAdapterOptions: () => GPURequestAdapterOptions | undefined;\n\tgetDeviceDescriptor: () => GPUDeviceDescriptor | undefined;\n\tgetOnError: () => ((report: MotionGPUErrorReport) => void) | undefined;\n\treportError: (report: MotionGPUErrorReport | null) => void;\n\tgetErrorHistoryLimit?: () => number | undefined;\n\tgetOnErrorHistory?: () => ((history: MotionGPUErrorReport[]) => void) | undefined;\n\treportErrorHistory?: (history: MotionGPUErrorReport[]) => void;\n}\n\nexport interface MotionGPURuntimeLoop {\n\trequestFrame: () => void;\n\tinvalidate: (token?: FrameInvalidationToken) => void;\n\tadvance: () => void;\n\tdestroy: () => void;\n}\n\nfunction getRendererRetryDelayMs(attempt: number): number {\n\treturn Math.min(8000, 250 * 2 ** Math.max(0, attempt - 1));\n}\n\nexport function createMotionGPURuntimeLoop(\n\toptions: MotionGPURuntimeLoopOptions\n): MotionGPURuntimeLoop {\n\tconst { canvas: canvasElement, registry, size } = options;\n\tlet frameId: number | null = null;\n\tlet renderer: Renderer | null = null;\n\tlet isDisposed = false;\n\tlet previousTime = performance.now() / 1000;\n\tlet activeRendererSignature = '';\n\tlet failedRendererSignature: string | null = null;\n\tlet failedRendererAttempts = 0;\n\tlet nextRendererRetryAt = 0;\n\tlet rendererRebuildPromise: Promise<void> | null = null;\n\n\tconst runtimeUniforms: Record<string, UniformValue> = {};\n\tconst runtimeTextures: TextureMap = {};\n\tlet activeUniforms: Record<string, UniformValue> = {};\n\tlet activeTextures: Record<string, { source?: TextureValue }> = {};\n\tlet uniformKeys: string[] = [];\n\tlet uniformKeySet = new Set<string>();\n\tlet uniformTypes = new Map<string, UniformType>();\n\tlet textureKeys: string[] = [];\n\tlet textureKeySet = new Set<string>();\n\tlet activeMaterialSignature = '';\n\tlet currentCssWidth = -1;\n\tlet currentCssHeight = -1;\n\tconst renderUniforms: Record<string, UniformValue> = {};\n\tconst renderTextures: TextureMap = {};\n\tconst canvasSize = { width: 0, height: 0 };\n\tlet storageBufferKeys: string[] = [];\n\tlet storageBufferKeySet = new Set<string>();\n\tlet storageBufferDefinitions: StorageBufferDefinitionMap = {};\n\tconst pendingStorageWrites: PendingStorageWrite[] = [];\n\tlet shouldContinueAfterFrame = false;\n\tlet activeErrorKey: string | null = null;\n\tlet errorHistory: MotionGPUErrorReport[] = [];\n\n\tconst getHistoryLimit = (): number => {\n\t\tconst value = options.getErrorHistoryLimit?.() ?? 0;\n\t\tif (!Number.isFinite(value) || value <= 0) {\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn Math.floor(value);\n\t};\n\n\tconst publishErrorHistory = (): void => {\n\t\toptions.reportErrorHistory?.(errorHistory);\n\t\tconst onErrorHistory = options.getOnErrorHistory?.();\n\t\tif (!onErrorHistory) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tonErrorHistory(errorHistory);\n\t\t} catch {\n\t\t\t// User-provided error history handlers must not break runtime error recovery.\n\t\t}\n\t};\n\n\tconst syncErrorHistory = (): void => {\n\t\tconst limit = getHistoryLimit();\n\t\tif (limit <= 0) {\n\t\t\tif (errorHistory.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\terrorHistory = [];\n\t\t\tpublishErrorHistory();\n\t\t\treturn;\n\t\t}\n\n\t\tif (errorHistory.length <= limit) {\n\t\t\treturn;\n\t\t}\n\n\t\terrorHistory = errorHistory.slice(errorHistory.length - limit);\n\t\tpublishErrorHistory();\n\t};\n\n\tconst setError = (error: unknown, phase: MotionGPUErrorPhase): void => {\n\t\tconst report = toMotionGPUErrorReport(error, phase);\n\t\tconst reportKey = JSON.stringify({\n\t\t\tphase: report.phase,\n\t\t\ttitle: report.title,\n\t\t\tmessage: report.message,\n\t\t\trawMessage: report.rawMessage\n\t\t});\n\t\tif (activeErrorKey === reportKey) {\n\t\t\treturn;\n\t\t}\n\t\tactiveErrorKey = reportKey;\n\t\tconst historyLimit = getHistoryLimit();\n\t\tif (historyLimit > 0) {\n\t\t\terrorHistory = [...errorHistory, report];\n\t\t\tif (errorHistory.length > historyLimit) {\n\t\t\t\terrorHistory = errorHistory.slice(errorHistory.length - historyLimit);\n\t\t\t}\n\t\t\tpublishErrorHistory();\n\t\t}\n\t\toptions.reportError(report);\n\t\tconst onError = options.getOnError();\n\t\tif (!onError) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tonError(report);\n\t\t} catch {\n\t\t\t// User-provided error handlers must not break runtime error recovery.\n\t\t}\n\t};\n\n\tconst clearError = (): void => {\n\t\tif (activeErrorKey === null) {\n\t\t\treturn;\n\t\t}\n\n\t\tactiveErrorKey = null;\n\t\toptions.reportError(null);\n\t};\n\n\tconst scheduleFrame = (): void => {\n\t\tif (isDisposed || frameId !== null) {\n\t\t\treturn;\n\t\t}\n\n\t\tframeId = requestAnimationFrame(renderFrame);\n\t};\n\n\tconst requestFrame = (): void => {\n\t\tscheduleFrame();\n\t};\n\n\tconst invalidate = (token?: FrameInvalidationToken): void => {\n\t\tregistry.invalidate(token);\n\t\trequestFrame();\n\t};\n\n\tconst advance = (): void => {\n\t\tregistry.advance();\n\t\trequestFrame();\n\t};\n\n\tconst resetRuntimeMaps = (): void => {\n\t\tfor (const key of Object.keys(runtimeUniforms)) {\n\t\t\tif (!uniformKeySet.has(key)) {\n\t\t\t\tReflect.deleteProperty(runtimeUniforms, key);\n\t\t\t}\n\t\t}\n\n\t\tfor (const key of Object.keys(runtimeTextures)) {\n\t\t\tif (!textureKeySet.has(key)) {\n\t\t\t\tReflect.deleteProperty(runtimeTextures, key);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst resetRenderPayloadMaps = (): void => {\n\t\tfor (const key of Object.keys(renderUniforms)) {\n\t\t\tif (!uniformKeySet.has(key)) {\n\t\t\t\tReflect.deleteProperty(renderUniforms, key);\n\t\t\t}\n\t\t}\n\n\t\tfor (const key of Object.keys(renderTextures)) {\n\t\t\tif (!textureKeySet.has(key)) {\n\t\t\t\tReflect.deleteProperty(renderTextures, key);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst syncMaterialRuntimeState = (materialState: ResolvedMaterial): void => {\n\t\tconst signatureChanged = activeMaterialSignature !== materialState.signature;\n\t\tconst defaultsChanged =\n\t\t\tactiveUniforms !== materialState.uniforms || activeTextures !== materialState.textures;\n\n\t\tif (!signatureChanged && !defaultsChanged) {\n\t\t\treturn;\n\t\t}\n\n\t\tactiveUniforms = materialState.uniforms;\n\t\tactiveTextures = materialState.textures;\n\t\tif (!signatureChanged) {\n\t\t\treturn;\n\t\t}\n\n\t\tuniformKeys = materialState.uniformLayout.entries.map((entry) => entry.name);\n\t\tuniformTypes = new Map(\n\t\t\tmaterialState.uniformLayout.entries.map((entry) => [entry.name, entry.type])\n\t\t);\n\t\ttextureKeys = materialState.textureKeys;\n\t\tuniformKeySet = new Set(uniformKeys);\n\t\ttextureKeySet = new Set(textureKeys);\n\t\tstorageBufferKeys = materialState.storageBufferKeys;\n\t\tstorageBufferKeySet = new Set(storageBufferKeys);\n\t\tstorageBufferDefinitions = (options.getMaterial().storageBuffers ??\n\t\t\t{}) as StorageBufferDefinitionMap;\n\t\tresetRuntimeMaps();\n\t\tresetRenderPayloadMaps();\n\t\tactiveMaterialSignature = materialState.signature;\n\t};\n\n\tconst resolveActiveMaterial = (): ResolvedMaterial => {\n\t\treturn resolveMaterial(options.getMaterial());\n\t};\n\n\tconst setUniform = (name: string, value: UniformValue): void => {\n\t\tif (!uniformKeySet.has(name)) {\n\t\t\tthrow new Error(`Unknown uniform \"${name}\". Declare it in material.uniforms first.`);\n\t\t}\n\t\tconst expectedType = uniformTypes.get(name);\n\t\tif (!expectedType) {\n\t\t\tthrow new Error(`Unknown uniform type for \"${name}\"`);\n\t\t}\n\t\tassertUniformValueForType(expectedType, value);\n\t\truntimeUniforms[name] = value;\n\t};\n\n\tconst setTexture = (name: string, value: TextureValue): void => {\n\t\tif (!textureKeySet.has(name)) {\n\t\t\tthrow new Error(`Unknown texture \"${name}\". Declare it in material.textures first.`);\n\t\t}\n\t\truntimeTextures[name] = value;\n\t};\n\n\tconst writeStorageBuffer = (\n\t\tname: string,\n\t\tdata: ArrayBufferView,\n\t\twriteOptions?: { offset?: number }\n\t): void => {\n\t\tif (!storageBufferKeySet.has(name)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Unknown storage buffer \"${name}\". Declare it in material.storageBuffers first.`\n\t\t\t);\n\t\t}\n\t\tconst definition = storageBufferDefinitions[name];\n\t\tif (!definition) {\n\t\t\tthrow new Error(`Missing definition for storage buffer \"${name}\".`);\n\t\t}\n\t\tconst offset = writeOptions?.offset ?? 0;\n\t\tif (offset < 0 || offset + data.byteLength > definition.size) {\n\t\t\tthrow new Error(\n\t\t\t\t`Storage buffer \"${name}\" write out of bounds: offset=${offset}, dataSize=${data.byteLength}, bufferSize=${definition.size}.`\n\t\t\t);\n\t\t}\n\t\tpendingStorageWrites.push({ name, data, offset });\n\t};\n\n\tconst readStorageBuffer = (name: string): Promise<ArrayBuffer> => {\n\t\tif (!storageBufferKeySet.has(name)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Unknown storage buffer \"${name}\". Declare it in material.storageBuffers first.`\n\t\t\t);\n\t\t}\n\t\tif (!renderer) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew Error(`Cannot read storage buffer \"${name}\": renderer not initialized.`)\n\t\t\t);\n\t\t}\n\t\tconst gpuBuffer = renderer.getStorageBuffer?.(name);\n\t\tif (!gpuBuffer) {\n\t\t\treturn Promise.reject(new Error(`Storage buffer \"${name}\" not allocated on GPU.`));\n\t\t}\n\t\tconst device = renderer.getDevice?.();\n\t\tif (!device) {\n\t\t\treturn Promise.reject(new Error('Cannot read storage buffer: GPU device unavailable.'));\n\t\t}\n\t\tconst definition = storageBufferDefinitions[name];\n\t\tif (!definition) {\n\t\t\treturn Promise.reject(new Error(`Missing definition for storage buffer \"${name}\".`));\n\t\t}\n\t\tconst stagingBuffer = device.createBuffer({\n\t\t\tsize: definition.size,\n\t\t\tusage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST\n\t\t});\n\t\tconst commandEncoder = device.createCommandEncoder();\n\t\tcommandEncoder.copyBufferToBuffer(gpuBuffer, 0, stagingBuffer, 0, definition.size);\n\t\tdevice.queue.submit([commandEncoder.finish()]);\n\t\treturn stagingBuffer.mapAsync(GPUMapMode.READ).then(() => {\n\t\t\tconst result = stagingBuffer.getMappedRange().slice(0);\n\t\t\tstagingBuffer.unmap();\n\t\t\tstagingBuffer.destroy();\n\t\t\treturn result;\n\t\t});\n\t};\n\n\tconst renderFrame = (timestamp: number): void => {\n\t\tframeId = null;\n\t\tif (isDisposed) {\n\t\t\treturn;\n\t\t}\n\t\tsyncErrorHistory();\n\n\t\tlet materialState: ResolvedMaterial;\n\t\ttry {\n\t\t\tmaterialState = resolveActiveMaterial();\n\t\t} catch (error) {\n\t\t\tsetError(error, 'initialization');\n\t\t\tscheduleFrame();\n\t\t\treturn;\n\t\t}\n\n\t\tshouldContinueAfterFrame = false;\n\n\t\tconst outputColorSpace = options.getOutputColorSpace();\n\t\tconst rendererSignature = buildRendererPipelineSignature({\n\t\t\tmaterialSignature: materialState.signature,\n\t\t\toutputColorSpace\n\t\t});\n\t\tsyncMaterialRuntimeState(materialState);\n\n\t\tif (failedRendererSignature && failedRendererSignature !== rendererSignature) {\n\t\t\tfailedRendererSignature = null;\n\t\t\tfailedRendererAttempts = 0;\n\t\t\tnextRendererRetryAt = 0;\n\t\t}\n\n\t\tif (!renderer || activeRendererSignature !== rendererSignature) {\n\t\t\tif (\n\t\t\t\tfailedRendererSignature === rendererSignature &&\n\t\t\t\tperformance.now() < nextRendererRetryAt\n\t\t\t) {\n\t\t\t\tscheduleFrame();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!rendererRebuildPromise) {\n\t\t\t\trendererRebuildPromise = (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst nextRenderer = await createRenderer({\n\t\t\t\t\t\t\tcanvas: canvasElement,\n\t\t\t\t\t\t\tfragmentWgsl: materialState.fragmentWgsl,\n\t\t\t\t\t\t\tfragmentLineMap: materialState.fragmentLineMap,\n\t\t\t\t\t\t\tfragmentSource: materialState.fragmentSource,\n\t\t\t\t\t\t\tincludeSources: materialState.includeSources,\n\t\t\t\t\t\t\tdefineBlockSource: materialState.defineBlockSource,\n\t\t\t\t\t\t\tmaterialSource: materialState.source,\n\t\t\t\t\t\t\tmaterialSignature: materialState.signature,\n\t\t\t\t\t\t\tuniformLayout: materialState.uniformLayout,\n\t\t\t\t\t\t\ttextureKeys: materialState.textureKeys,\n\t\t\t\t\t\t\ttextureDefinitions: materialState.textures,\n\t\t\t\t\t\t\tstorageBufferKeys: materialState.storageBufferKeys,\n\t\t\t\t\t\t\tstorageBufferDefinitions,\n\t\t\t\t\t\t\tstorageTextureKeys: materialState.storageTextureKeys,\n\t\t\t\t\t\t\tgetRenderTargets: options.getRenderTargets,\n\t\t\t\t\t\t\tgetPasses: options.getPasses,\n\t\t\t\t\t\t\toutputColorSpace,\n\t\t\t\t\t\t\tgetClearColor: options.getClearColor,\n\t\t\t\t\t\t\tgetDpr: () => options.dpr.current,\n\t\t\t\t\t\t\tadapterOptions: options.getAdapterOptions(),\n\t\t\t\t\t\t\tdeviceDescriptor: options.getDeviceDescriptor()\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (isDisposed) {\n\t\t\t\t\t\t\tnextRenderer.destroy();\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\trenderer?.destroy();\n\t\t\t\t\t\trenderer = nextRenderer;\n\t\t\t\t\t\tactiveRendererSignature = rendererSignature;\n\t\t\t\t\t\tfailedRendererSignature = null;\n\t\t\t\t\t\tfailedRendererAttempts = 0;\n\t\t\t\t\t\tnextRendererRetryAt = 0;\n\t\t\t\t\t\tclearError();\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tfailedRendererSignature = rendererSignature;\n\t\t\t\t\t\tfailedRendererAttempts += 1;\n\t\t\t\t\t\tconst retryDelayMs = getRendererRetryDelayMs(failedRendererAttempts);\n\t\t\t\t\t\tnextRendererRetryAt = performance.now() + retryDelayMs;\n\t\t\t\t\t\tsetError(error, 'initialization');\n\t\t\t\t\t} finally {\n\t\t\t\t\t\trendererRebuildPromise = null;\n\t\t\t\t\t\tscheduleFrame();\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\tconst time = timestamp / 1000;\n\t\tconst rawDelta = Math.max(0, time - previousTime);\n\t\tconst delta = Math.min(rawDelta, options.maxDelta.current);\n\t\tpreviousTime = time;\n\t\tconst rect = canvasElement.getBoundingClientRect();\n\t\tconst width = Math.max(0, Math.floor(rect.width));\n\t\tconst height = Math.max(0, Math.floor(rect.height));\n\t\tif (width !== currentCssWidth || height !== currentCssHeight) {\n\t\t\tcurrentCssWidth = width;\n\t\t\tcurrentCssHeight = height;\n\t\t\tsize.set({ width, height });\n\t\t}\n\n\t\ttry {\n\t\t\tregistry.run({\n\t\t\t\ttime,\n\t\t\t\tdelta,\n\t\t\t\tsetUniform,\n\t\t\t\tsetTexture,\n\t\t\t\twriteStorageBuffer,\n\t\t\t\treadStorageBuffer,\n\t\t\t\tinvalidate,\n\t\t\t\tadvance,\n\t\t\t\trenderMode: registry.getRenderMode(),\n\t\t\t\tautoRender: registry.getAutoRender(),\n\t\t\t\tcanvas: canvasElement\n\t\t\t});\n\n\t\t\tconst shouldRenderFrame = registry.shouldRender();\n\t\t\tshouldContinueAfterFrame =\n\t\t\t\tregistry.getRenderMode() === 'always' ||\n\t\t\t\t(registry.getRenderMode() === 'on-demand' && shouldRenderFrame);\n\n\t\t\tif (shouldRenderFrame) {\n\t\t\t\tfor (const key of uniformKeys) {\n\t\t\t\t\tconst runtimeValue = runtimeUniforms[key];\n\t\t\t\t\trenderUniforms[key] =\n\t\t\t\t\t\truntimeValue === undefined ? (activeUniforms[key] as UniformValue) : runtimeValue;\n\t\t\t\t}\n\n\t\t\t\tfor (const key of textureKeys) {\n\t\t\t\t\tconst runtimeValue = runtimeTextures[key];\n\t\t\t\t\trenderTextures[key] =\n\t\t\t\t\t\truntimeValue === undefined ? (activeTextures[key]?.source ?? null) : runtimeValue;\n\t\t\t\t}\n\n\t\t\t\tcanvasSize.width = width;\n\t\t\t\tcanvasSize.height = height;\n\t\t\t\trenderer.render({\n\t\t\t\t\ttime,\n\t\t\t\t\tdelta,\n\t\t\t\t\trenderMode: registry.getRenderMode(),\n\t\t\t\t\tuniforms: renderUniforms,\n\t\t\t\t\ttextures: renderTextures,\n\t\t\t\t\tcanvasSize,\n\t\t\t\t\t...(pendingStorageWrites.length > 0\n\t\t\t\t\t\t? { pendingStorageWrites: pendingStorageWrites.splice(0) }\n\t\t\t\t\t\t: {})\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tclearError();\n\t\t} catch (error) {\n\t\t\tsetError(error, 'render');\n\t\t} finally {\n\t\t\tregistry.endFrame();\n\t\t}\n\n\t\tif (shouldContinueAfterFrame) {\n\t\t\tscheduleFrame();\n\t\t}\n\t};\n\n\t(async () => {\n\t\ttry {\n\t\t\tconst initialMaterial = resolveActiveMaterial();\n\t\t\tsyncMaterialRuntimeState(initialMaterial);\n\t\t\tactiveRendererSignature = '';\n\t\t\tscheduleFrame();\n\t\t} catch (error) {\n\t\t\tsetError(error, 'initialization');\n\t\t\tscheduleFrame();\n\t\t}\n\t})();\n\n\treturn {\n\t\trequestFrame,\n\t\tinvalidate,\n\t\tadvance,\n\t\tdestroy: () => {\n\t\t\tisDisposed = true;\n\t\t\tif (frameId !== null) {\n\t\t\t\tcancelAnimationFrame(frameId);\n\t\t\t\tframeId = null;\n\t\t\t}\n\t\t\trenderer?.destroy();\n\t\t\tregistry.clear();\n\t\t}\n\t};\n}\n"],"mappings":"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"}
|
package/dist/core/shader.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MaterialLineMap, MaterialSourceLocation } from './material-preprocess.js';
|
|
2
|
-
import type { UniformLayout } from './types.js';
|
|
2
|
+
import type { StorageBufferType, UniformLayout } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* 1-based map from generated WGSL lines to original material source lines.
|
|
5
5
|
*/
|
|
@@ -28,6 +28,10 @@ export interface BuiltShaderSource {
|
|
|
28
28
|
*/
|
|
29
29
|
export declare function buildShaderSource(fragmentWgsl: string, uniformLayout: UniformLayout, textureKeys?: string[], options?: {
|
|
30
30
|
convertLinearToSrgb?: boolean;
|
|
31
|
+
storageBufferKeys?: string[];
|
|
32
|
+
storageBufferDefinitions?: Record<string, {
|
|
33
|
+
type: StorageBufferType;
|
|
34
|
+
}>;
|
|
31
35
|
}): string;
|
|
32
36
|
/**
|
|
33
37
|
* Assembles complete WGSL shader source with material-source line mapping metadata.
|
|
@@ -35,6 +39,10 @@ export declare function buildShaderSource(fragmentWgsl: string, uniformLayout: U
|
|
|
35
39
|
export declare function buildShaderSourceWithMap(fragmentWgsl: string, uniformLayout: UniformLayout, textureKeys?: string[], options?: {
|
|
36
40
|
convertLinearToSrgb?: boolean;
|
|
37
41
|
fragmentLineMap?: MaterialLineMap;
|
|
42
|
+
storageBufferKeys?: string[];
|
|
43
|
+
storageBufferDefinitions?: Record<string, {
|
|
44
|
+
type: StorageBufferType;
|
|
45
|
+
}>;
|
|
38
46
|
}): BuiltShaderSource;
|
|
39
47
|
/**
|
|
40
48
|
* Converts source location metadata to user-facing diagnostics label.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader.d.ts","sourceRoot":"","sources":["../../src/lib/core/shader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACxF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"shader.d.ts","sourceRoot":"","sources":["../../src/lib/core/shader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACxF,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA+InE;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,OAAO,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAChC,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,aAAa,EAC5B,WAAW,GAAE,MAAM,EAAO,EAC1B,OAAO,CAAC,EAAE;IACT,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE,CAAC,CAAC;CACvE,GACC,MAAM,CAwDR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACvC,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,aAAa,EAC5B,WAAW,GAAE,MAAM,EAAO,EAC1B,OAAO,CAAC,EAAE;IACT,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE,CAAC,CAAC;CACvE,GACC,iBAAiB,CA4BnB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAcjG"}
|
package/dist/core/shader.js
CHANGED
|
@@ -42,6 +42,21 @@ function buildTextureBindings(textureKeys) {
|
|
|
42
42
|
return declarations.join("\n");
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
|
+
* Builds read-only storage buffer bindings for fragment shader.
|
|
46
|
+
*/
|
|
47
|
+
function buildFragmentStorageBufferBindings(storageBufferKeys, definitions) {
|
|
48
|
+
if (storageBufferKeys.length === 0) return "";
|
|
49
|
+
const declarations = [];
|
|
50
|
+
for (let index = 0; index < storageBufferKeys.length; index += 1) {
|
|
51
|
+
const key = storageBufferKeys[index];
|
|
52
|
+
if (key === void 0) continue;
|
|
53
|
+
const definition = definitions[key];
|
|
54
|
+
if (!definition) continue;
|
|
55
|
+
declarations.push(`@group(1) @binding(${index}) var<storage, read> ${key}: ${definition.type};`);
|
|
56
|
+
}
|
|
57
|
+
return declarations.join("\n");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
45
60
|
* Optionally returns helper WGSL for linear-to-sRGB conversion.
|
|
46
61
|
*/
|
|
47
62
|
function buildColorTransformHelpers(enableSrgbTransform) {
|
|
@@ -86,6 +101,9 @@ function buildShaderSource(fragmentWgsl, uniformLayout, textureKeys = [], option
|
|
|
86
101
|
const keepAliveExpression = getKeepAliveExpression(uniformLayout);
|
|
87
102
|
const textureBindings = buildTextureBindings(textureKeys);
|
|
88
103
|
const enableSrgbTransform = options?.convertLinearToSrgb ?? false;
|
|
104
|
+
const colorTransformHelpers = buildColorTransformHelpers(enableSrgbTransform);
|
|
105
|
+
const fragmentOutput = buildFragmentOutput(keepAliveExpression, enableSrgbTransform);
|
|
106
|
+
const storageBufferBindings = buildFragmentStorageBufferBindings(options?.storageBufferKeys ?? [], options?.storageBufferDefinitions ?? {});
|
|
89
107
|
return `
|
|
90
108
|
struct MotionGPUFrame {
|
|
91
109
|
time: f32,
|
|
@@ -100,7 +118,8 @@ struct MotionGPUUniforms {
|
|
|
100
118
|
@group(0) @binding(0) var<uniform> motiongpuFrame: MotionGPUFrame;
|
|
101
119
|
@group(0) @binding(1) var<uniform> motiongpuUniforms: MotionGPUUniforms;
|
|
102
120
|
${textureBindings}
|
|
103
|
-
${
|
|
121
|
+
${storageBufferBindings ? "\n" + storageBufferBindings : ""}
|
|
122
|
+
${colorTransformHelpers}
|
|
104
123
|
|
|
105
124
|
struct MotionGPUVertexOut {
|
|
106
125
|
@builtin(position) position: vec4f,
|
|
@@ -126,7 +145,7 @@ ${fragmentWgsl}
|
|
|
126
145
|
|
|
127
146
|
@fragment
|
|
128
147
|
fn motiongpuFragment(in: MotionGPUVertexOut) -> @location(0) vec4f {
|
|
129
|
-
${
|
|
148
|
+
${fragmentOutput}
|
|
130
149
|
}
|
|
131
150
|
`;
|
|
132
151
|
}
|
package/dist/core/shader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader.js","names":[],"sources":["../../src/lib/core/shader.ts"],"sourcesContent":["import { assertUniformName } from './uniforms.js';\nimport type { MaterialLineMap, MaterialSourceLocation } from './material-preprocess.js';\nimport type { UniformLayout } from './types.js';\n\n/**\n * Fallback uniform field used when no custom uniforms are provided.\n */\nconst DEFAULT_UNIFORM_FIELD = 'motiongpu_unused: vec4f,';\n\n/**\n * Builds WGSL struct fields for user uniforms.\n */\nfunction buildUniformStruct(layout: UniformLayout): string {\n\tif (layout.entries.length === 0) {\n\t\treturn DEFAULT_UNIFORM_FIELD;\n\t}\n\n\treturn layout.entries\n\t\t.map((entry) => {\n\t\t\tassertUniformName(entry.name);\n\t\t\treturn `${entry.name}: ${entry.type},`;\n\t\t})\n\t\t.join('\\n\\t');\n}\n\n/**\n * Builds a numeric expression that references one uniform value to keep bindings alive.\n */\nfunction getKeepAliveExpression(layout: UniformLayout): string {\n\tif (layout.entries.length === 0) {\n\t\treturn 'motiongpuUniforms.motiongpu_unused.x';\n\t}\n\n\tconst [firstEntry] = layout.entries;\n\tif (!firstEntry) {\n\t\treturn 'motiongpuUniforms.motiongpu_unused.x';\n\t}\n\n\tif (firstEntry.type === 'f32') {\n\t\treturn `motiongpuUniforms.${firstEntry.name}`;\n\t}\n\n\tif (firstEntry.type === 'mat4x4f') {\n\t\treturn `motiongpuUniforms.${firstEntry.name}[0].x`;\n\t}\n\n\treturn `motiongpuUniforms.${firstEntry.name}.x`;\n}\n\n/**\n * Builds texture sampler/texture binding declarations.\n */\nfunction buildTextureBindings(textureKeys: string[]): string {\n\tif (textureKeys.length === 0) {\n\t\treturn '';\n\t}\n\n\tconst declarations: string[] = [];\n\n\tfor (let index = 0; index < textureKeys.length; index += 1) {\n\t\tconst key = textureKeys[index];\n\t\tif (key === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tassertUniformName(key);\n\t\tconst binding = 2 + index * 2;\n\t\tdeclarations.push(`@group(0) @binding(${binding}) var ${key}Sampler: sampler;`);\n\t\tdeclarations.push(`@group(0) @binding(${binding + 1}) var ${key}: texture_2d<f32>;`);\n\t}\n\n\treturn declarations.join('\\n');\n}\n\n/**\n * Optionally returns helper WGSL for linear-to-sRGB conversion.\n */\nfunction buildColorTransformHelpers(enableSrgbTransform: boolean): string {\n\tif (!enableSrgbTransform) {\n\t\treturn '';\n\t}\n\n\treturn `\nfn motiongpuLinearToSrgb(linearColor: vec3f) -> vec3f {\n\tlet cutoff = vec3f(0.0031308);\n\tlet lower = linearColor * 12.92;\n\tlet higher = vec3f(1.055) * pow(linearColor, vec3f(1.0 / 2.4)) - vec3f(0.055);\n\treturn select(lower, higher, linearColor > cutoff);\n}\n`;\n}\n\n/**\n * Builds fragment output code with optional color-space conversion.\n */\nfunction buildFragmentOutput(keepAliveExpression: string, enableSrgbTransform: boolean): string {\n\tif (enableSrgbTransform) {\n\t\treturn `\n\tlet fragColor = frag(in.uv);\n\tlet motiongpuKeepAlive = ${keepAliveExpression};\n\tlet motiongpuLinear = vec4f(fragColor.rgb + motiongpuKeepAlive * 0.0, fragColor.a);\n\tlet motiongpuSrgb = motiongpuLinearToSrgb(max(motiongpuLinear.rgb, vec3f(0.0)));\n\treturn vec4f(motiongpuSrgb, motiongpuLinear.a);\n`;\n\t}\n\n\treturn `\n\tlet fragColor = frag(in.uv);\n\tlet motiongpuKeepAlive = ${keepAliveExpression};\n\treturn vec4f(fragColor.rgb + motiongpuKeepAlive * 0.0, fragColor.a);\n`;\n}\n\n/**\n * 1-based map from generated WGSL lines to original material source lines.\n */\nexport type ShaderLineMap = Array<MaterialSourceLocation | null>;\n\n/**\n * Result of shader source generation with line mapping metadata.\n */\nexport interface BuiltShaderSource {\n\t/**\n\t * Full WGSL source code.\n\t */\n\tcode: string;\n\t/**\n\t * 1-based generated-line map to material source locations.\n\t */\n\tlineMap: ShaderLineMap;\n}\n\n/**\n * Assembles complete WGSL shader source used by the fullscreen renderer pipeline.\n *\n * @param fragmentWgsl - User fragment shader code containing `frag(uv: vec2f) -> vec4f`.\n * @param uniformLayout - Resolved uniform layout.\n * @param textureKeys - Sorted texture keys.\n * @param options - Shader build options.\n * @returns Complete WGSL source for vertex + fragment stages.\n */\nexport function buildShaderSource(\n\tfragmentWgsl: string,\n\tuniformLayout: UniformLayout,\n\ttextureKeys: string[] = [],\n\toptions?: { convertLinearToSrgb?: boolean }\n): string {\n\tconst uniformFields = buildUniformStruct(uniformLayout);\n\tconst keepAliveExpression = getKeepAliveExpression(uniformLayout);\n\tconst textureBindings = buildTextureBindings(textureKeys);\n\tconst enableSrgbTransform = options?.convertLinearToSrgb ?? false;\n\tconst colorTransformHelpers = buildColorTransformHelpers(enableSrgbTransform);\n\tconst fragmentOutput = buildFragmentOutput(keepAliveExpression, enableSrgbTransform);\n\n\treturn `\nstruct MotionGPUFrame {\n\ttime: f32,\n\tdelta: f32,\n\tresolution: vec2f,\n};\n\nstruct MotionGPUUniforms {\n\t${uniformFields}\n};\n\n@group(0) @binding(0) var<uniform> motiongpuFrame: MotionGPUFrame;\n@group(0) @binding(1) var<uniform> motiongpuUniforms: MotionGPUUniforms;\n${textureBindings}\n${colorTransformHelpers}\n\nstruct MotionGPUVertexOut {\n\t@builtin(position) position: vec4f,\n\t@location(0) uv: vec2f,\n};\n\n@vertex\nfn motiongpuVertex(@builtin(vertex_index) index: u32) -> MotionGPUVertexOut {\n\tvar positions = array<vec2f, 3>(\n\t\tvec2f(-1.0, -3.0),\n\t\tvec2f(-1.0, 1.0),\n\t\tvec2f(3.0, 1.0)\n\t);\n\n\tlet position = positions[index];\n\tvar out: MotionGPUVertexOut;\n\tout.position = vec4f(position, 0.0, 1.0);\n\tout.uv = (position + vec2f(1.0, 1.0)) * 0.5;\n\treturn out;\n}\n\n${fragmentWgsl}\n\n@fragment\nfn motiongpuFragment(in: MotionGPUVertexOut) -> @location(0) vec4f {\n\t${fragmentOutput}\n}\n`;\n}\n\n/**\n * Assembles complete WGSL shader source with material-source line mapping metadata.\n */\nexport function buildShaderSourceWithMap(\n\tfragmentWgsl: string,\n\tuniformLayout: UniformLayout,\n\ttextureKeys: string[] = [],\n\toptions?: {\n\t\tconvertLinearToSrgb?: boolean;\n\t\tfragmentLineMap?: MaterialLineMap;\n\t}\n): BuiltShaderSource {\n\tconst code = buildShaderSource(fragmentWgsl, uniformLayout, textureKeys, options);\n\tconst fragmentStartIndex = code.indexOf(fragmentWgsl);\n\tconst lineCount = code.split('\\n').length;\n\tconst lineMap: ShaderLineMap = new Array(lineCount + 1).fill(null);\n\n\tif (fragmentStartIndex === -1) {\n\t\treturn {\n\t\t\tcode,\n\t\t\tlineMap\n\t\t};\n\t}\n\n\tconst fragmentStartLine = code.slice(0, fragmentStartIndex).split('\\n').length;\n\tconst fragmentLineCount = fragmentWgsl.split('\\n').length;\n\n\tfor (let line = 0; line < fragmentLineCount; line += 1) {\n\t\tconst generatedLine = fragmentStartLine + line;\n\t\tlineMap[generatedLine] = options?.fragmentLineMap?.[line + 1] ?? {\n\t\t\tkind: 'fragment',\n\t\t\tline: line + 1\n\t\t};\n\t}\n\n\treturn {\n\t\tcode,\n\t\tlineMap\n\t};\n}\n\n/**\n * Converts source location metadata to user-facing diagnostics label.\n */\nexport function formatShaderSourceLocation(location: MaterialSourceLocation | null): string | null {\n\tif (!location) {\n\t\treturn null;\n\t}\n\n\tif (location.kind === 'fragment') {\n\t\treturn `fragment line ${location.line}`;\n\t}\n\n\tif (location.kind === 'include') {\n\t\treturn `include <${location.include}> line ${location.line}`;\n\t}\n\n\treturn `define \"${location.define}\" line ${location.line}`;\n}\n"],"mappings":";;;;;AAOA,IAAM,wBAAwB;;;;AAK9B,SAAS,mBAAmB,QAA+B;AAC1D,KAAI,OAAO,QAAQ,WAAW,EAC7B,QAAO;AAGR,QAAO,OAAO,QACZ,KAAK,UAAU;AACf,oBAAkB,MAAM,KAAK;AAC7B,SAAO,GAAG,MAAM,KAAK,IAAI,MAAM,KAAK;GACnC,CACD,KAAK,MAAO;;;;;AAMf,SAAS,uBAAuB,QAA+B;AAC9D,KAAI,OAAO,QAAQ,WAAW,EAC7B,QAAO;CAGR,MAAM,CAAC,cAAc,OAAO;AAC5B,KAAI,CAAC,WACJ,QAAO;AAGR,KAAI,WAAW,SAAS,MACvB,QAAO,qBAAqB,WAAW;AAGxC,KAAI,WAAW,SAAS,UACvB,QAAO,qBAAqB,WAAW,KAAK;AAG7C,QAAO,qBAAqB,WAAW,KAAK;;;;;AAM7C,SAAS,qBAAqB,aAA+B;AAC5D,KAAI,YAAY,WAAW,EAC1B,QAAO;CAGR,MAAM,eAAyB,EAAE;AAEjC,MAAK,IAAI,QAAQ,GAAG,QAAQ,YAAY,QAAQ,SAAS,GAAG;EAC3D,MAAM,MAAM,YAAY;AACxB,MAAI,QAAQ,OACX;AAGD,oBAAkB,IAAI;EACtB,MAAM,UAAU,IAAI,QAAQ;AAC5B,eAAa,KAAK,sBAAsB,QAAQ,QAAQ,IAAI,mBAAmB;AAC/E,eAAa,KAAK,sBAAsB,UAAU,EAAE,QAAQ,IAAI,oBAAoB;;AAGrF,QAAO,aAAa,KAAK,KAAK;;;;;AAM/B,SAAS,2BAA2B,qBAAsC;AACzE,KAAI,CAAC,oBACJ,QAAO;AAGR,QAAO;;;;;;;;;;;;AAaR,SAAS,oBAAoB,qBAA6B,qBAAsC;AAC/F,KAAI,oBACH,QAAO;;4BAEmB,oBAAoB;;;;;AAO/C,QAAO;;4BAEoB,oBAAoB;;;;;;;;;;;;;AAiChD,SAAgB,kBACf,cACA,eACA,cAAwB,EAAE,EAC1B,SACS;CACT,MAAM,gBAAgB,mBAAmB,cAAc;CACvD,MAAM,sBAAsB,uBAAuB,cAAc;CACjE,MAAM,kBAAkB,qBAAqB,YAAY;CACzD,MAAM,sBAAsB,SAAS,uBAAuB;AAI5D,QAAO;;;;;;;;GAQL,cAAc;;;;;EAKf,gBAAgB;EAhBa,2BAA2B,oBAAoB,CAiBtD;;;;;;;;;;;;;;;;;;;;;;EAsBtB,aAAa;;;;GAtCS,oBAAoB,qBAAqB,oBAAoB,CA0CnE;;;;;;;AAQlB,SAAgB,yBACf,cACA,eACA,cAAwB,EAAE,EAC1B,SAIoB;CACpB,MAAM,OAAO,kBAAkB,cAAc,eAAe,aAAa,QAAQ;CACjF,MAAM,qBAAqB,KAAK,QAAQ,aAAa;CACrD,MAAM,YAAY,KAAK,MAAM,KAAK,CAAC;CACnC,MAAM,UAAyB,IAAI,MAAM,YAAY,EAAE,CAAC,KAAK,KAAK;AAElE,KAAI,uBAAuB,GAC1B,QAAO;EACN;EACA;EACA;CAGF,MAAM,oBAAoB,KAAK,MAAM,GAAG,mBAAmB,CAAC,MAAM,KAAK,CAAC;CACxE,MAAM,oBAAoB,aAAa,MAAM,KAAK,CAAC;AAEnD,MAAK,IAAI,OAAO,GAAG,OAAO,mBAAmB,QAAQ,GAAG;EACvD,MAAM,gBAAgB,oBAAoB;AAC1C,UAAQ,iBAAiB,SAAS,kBAAkB,OAAO,MAAM;GAChE,MAAM;GACN,MAAM,OAAO;GACb;;AAGF,QAAO;EACN;EACA;EACA;;;;;AAMF,SAAgB,2BAA2B,UAAwD;AAClG,KAAI,CAAC,SACJ,QAAO;AAGR,KAAI,SAAS,SAAS,WACrB,QAAO,iBAAiB,SAAS;AAGlC,KAAI,SAAS,SAAS,UACrB,QAAO,YAAY,SAAS,QAAQ,SAAS,SAAS;AAGvD,QAAO,WAAW,SAAS,OAAO,SAAS,SAAS"}
|
|
1
|
+
{"version":3,"file":"shader.js","names":[],"sources":["../../src/lib/core/shader.ts"],"sourcesContent":["import { assertUniformName } from './uniforms.js';\nimport type { MaterialLineMap, MaterialSourceLocation } from './material-preprocess.js';\nimport type { StorageBufferType, UniformLayout } from './types.js';\n\n/**\n * Fallback uniform field used when no custom uniforms are provided.\n */\nconst DEFAULT_UNIFORM_FIELD = 'motiongpu_unused: vec4f,';\n\n/**\n * Builds WGSL struct fields for user uniforms.\n */\nfunction buildUniformStruct(layout: UniformLayout): string {\n\tif (layout.entries.length === 0) {\n\t\treturn DEFAULT_UNIFORM_FIELD;\n\t}\n\n\treturn layout.entries\n\t\t.map((entry) => {\n\t\t\tassertUniformName(entry.name);\n\t\t\treturn `${entry.name}: ${entry.type},`;\n\t\t})\n\t\t.join('\\n\\t');\n}\n\n/**\n * Builds a numeric expression that references one uniform value to keep bindings alive.\n */\nfunction getKeepAliveExpression(layout: UniformLayout): string {\n\tif (layout.entries.length === 0) {\n\t\treturn 'motiongpuUniforms.motiongpu_unused.x';\n\t}\n\n\tconst [firstEntry] = layout.entries;\n\tif (!firstEntry) {\n\t\treturn 'motiongpuUniforms.motiongpu_unused.x';\n\t}\n\n\tif (firstEntry.type === 'f32') {\n\t\treturn `motiongpuUniforms.${firstEntry.name}`;\n\t}\n\n\tif (firstEntry.type === 'mat4x4f') {\n\t\treturn `motiongpuUniforms.${firstEntry.name}[0].x`;\n\t}\n\n\treturn `motiongpuUniforms.${firstEntry.name}.x`;\n}\n\n/**\n * Builds texture sampler/texture binding declarations.\n */\nfunction buildTextureBindings(textureKeys: string[]): string {\n\tif (textureKeys.length === 0) {\n\t\treturn '';\n\t}\n\n\tconst declarations: string[] = [];\n\n\tfor (let index = 0; index < textureKeys.length; index += 1) {\n\t\tconst key = textureKeys[index];\n\t\tif (key === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tassertUniformName(key);\n\t\tconst binding = 2 + index * 2;\n\t\tdeclarations.push(`@group(0) @binding(${binding}) var ${key}Sampler: sampler;`);\n\t\tdeclarations.push(`@group(0) @binding(${binding + 1}) var ${key}: texture_2d<f32>;`);\n\t}\n\n\treturn declarations.join('\\n');\n}\n\n/**\n * Builds read-only storage buffer bindings for fragment shader.\n */\nfunction buildFragmentStorageBufferBindings(\n\tstorageBufferKeys: string[],\n\tdefinitions: Record<string, { type: StorageBufferType }>\n): string {\n\tif (storageBufferKeys.length === 0) {\n\t\treturn '';\n\t}\n\n\tconst declarations: string[] = [];\n\n\tfor (let index = 0; index < storageBufferKeys.length; index += 1) {\n\t\tconst key = storageBufferKeys[index];\n\t\tif (key === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst definition = definitions[key];\n\t\tif (!definition) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tdeclarations.push(\n\t\t\t`@group(1) @binding(${index}) var<storage, read> ${key}: ${definition.type};`\n\t\t);\n\t}\n\n\treturn declarations.join('\\n');\n}\n\n/**\n * Optionally returns helper WGSL for linear-to-sRGB conversion.\n */\nfunction buildColorTransformHelpers(enableSrgbTransform: boolean): string {\n\tif (!enableSrgbTransform) {\n\t\treturn '';\n\t}\n\n\treturn `\nfn motiongpuLinearToSrgb(linearColor: vec3f) -> vec3f {\n\tlet cutoff = vec3f(0.0031308);\n\tlet lower = linearColor * 12.92;\n\tlet higher = vec3f(1.055) * pow(linearColor, vec3f(1.0 / 2.4)) - vec3f(0.055);\n\treturn select(lower, higher, linearColor > cutoff);\n}\n`;\n}\n\n/**\n * Builds fragment output code with optional color-space conversion.\n */\nfunction buildFragmentOutput(keepAliveExpression: string, enableSrgbTransform: boolean): string {\n\tif (enableSrgbTransform) {\n\t\treturn `\n\tlet fragColor = frag(in.uv);\n\tlet motiongpuKeepAlive = ${keepAliveExpression};\n\tlet motiongpuLinear = vec4f(fragColor.rgb + motiongpuKeepAlive * 0.0, fragColor.a);\n\tlet motiongpuSrgb = motiongpuLinearToSrgb(max(motiongpuLinear.rgb, vec3f(0.0)));\n\treturn vec4f(motiongpuSrgb, motiongpuLinear.a);\n`;\n\t}\n\n\treturn `\n\tlet fragColor = frag(in.uv);\n\tlet motiongpuKeepAlive = ${keepAliveExpression};\n\treturn vec4f(fragColor.rgb + motiongpuKeepAlive * 0.0, fragColor.a);\n`;\n}\n\n/**\n * 1-based map from generated WGSL lines to original material source lines.\n */\nexport type ShaderLineMap = Array<MaterialSourceLocation | null>;\n\n/**\n * Result of shader source generation with line mapping metadata.\n */\nexport interface BuiltShaderSource {\n\t/**\n\t * Full WGSL source code.\n\t */\n\tcode: string;\n\t/**\n\t * 1-based generated-line map to material source locations.\n\t */\n\tlineMap: ShaderLineMap;\n}\n\n/**\n * Assembles complete WGSL shader source used by the fullscreen renderer pipeline.\n *\n * @param fragmentWgsl - User fragment shader code containing `frag(uv: vec2f) -> vec4f`.\n * @param uniformLayout - Resolved uniform layout.\n * @param textureKeys - Sorted texture keys.\n * @param options - Shader build options.\n * @returns Complete WGSL source for vertex + fragment stages.\n */\nexport function buildShaderSource(\n\tfragmentWgsl: string,\n\tuniformLayout: UniformLayout,\n\ttextureKeys: string[] = [],\n\toptions?: {\n\t\tconvertLinearToSrgb?: boolean;\n\t\tstorageBufferKeys?: string[];\n\t\tstorageBufferDefinitions?: Record<string, { type: StorageBufferType }>;\n\t}\n): string {\n\tconst uniformFields = buildUniformStruct(uniformLayout);\n\tconst keepAliveExpression = getKeepAliveExpression(uniformLayout);\n\tconst textureBindings = buildTextureBindings(textureKeys);\n\tconst enableSrgbTransform = options?.convertLinearToSrgb ?? false;\n\tconst colorTransformHelpers = buildColorTransformHelpers(enableSrgbTransform);\n\tconst fragmentOutput = buildFragmentOutput(keepAliveExpression, enableSrgbTransform);\n\tconst storageBufferBindings = buildFragmentStorageBufferBindings(\n\t\toptions?.storageBufferKeys ?? [],\n\t\toptions?.storageBufferDefinitions ?? {}\n\t);\n\n\treturn `\nstruct MotionGPUFrame {\n\ttime: f32,\n\tdelta: f32,\n\tresolution: vec2f,\n};\n\nstruct MotionGPUUniforms {\n\t${uniformFields}\n};\n\n@group(0) @binding(0) var<uniform> motiongpuFrame: MotionGPUFrame;\n@group(0) @binding(1) var<uniform> motiongpuUniforms: MotionGPUUniforms;\n${textureBindings}\n${storageBufferBindings ? '\\n' + storageBufferBindings : ''}\n${colorTransformHelpers}\n\nstruct MotionGPUVertexOut {\n\t@builtin(position) position: vec4f,\n\t@location(0) uv: vec2f,\n};\n\n@vertex\nfn motiongpuVertex(@builtin(vertex_index) index: u32) -> MotionGPUVertexOut {\n\tvar positions = array<vec2f, 3>(\n\t\tvec2f(-1.0, -3.0),\n\t\tvec2f(-1.0, 1.0),\n\t\tvec2f(3.0, 1.0)\n\t);\n\n\tlet position = positions[index];\n\tvar out: MotionGPUVertexOut;\n\tout.position = vec4f(position, 0.0, 1.0);\n\tout.uv = (position + vec2f(1.0, 1.0)) * 0.5;\n\treturn out;\n}\n\n${fragmentWgsl}\n\n@fragment\nfn motiongpuFragment(in: MotionGPUVertexOut) -> @location(0) vec4f {\n\t${fragmentOutput}\n}\n`;\n}\n\n/**\n * Assembles complete WGSL shader source with material-source line mapping metadata.\n */\nexport function buildShaderSourceWithMap(\n\tfragmentWgsl: string,\n\tuniformLayout: UniformLayout,\n\ttextureKeys: string[] = [],\n\toptions?: {\n\t\tconvertLinearToSrgb?: boolean;\n\t\tfragmentLineMap?: MaterialLineMap;\n\t\tstorageBufferKeys?: string[];\n\t\tstorageBufferDefinitions?: Record<string, { type: StorageBufferType }>;\n\t}\n): BuiltShaderSource {\n\tconst code = buildShaderSource(fragmentWgsl, uniformLayout, textureKeys, options);\n\tconst fragmentStartIndex = code.indexOf(fragmentWgsl);\n\tconst lineCount = code.split('\\n').length;\n\tconst lineMap: ShaderLineMap = new Array(lineCount + 1).fill(null);\n\n\tif (fragmentStartIndex === -1) {\n\t\treturn {\n\t\t\tcode,\n\t\t\tlineMap\n\t\t};\n\t}\n\n\tconst fragmentStartLine = code.slice(0, fragmentStartIndex).split('\\n').length;\n\tconst fragmentLineCount = fragmentWgsl.split('\\n').length;\n\n\tfor (let line = 0; line < fragmentLineCount; line += 1) {\n\t\tconst generatedLine = fragmentStartLine + line;\n\t\tlineMap[generatedLine] = options?.fragmentLineMap?.[line + 1] ?? {\n\t\t\tkind: 'fragment',\n\t\t\tline: line + 1\n\t\t};\n\t}\n\n\treturn {\n\t\tcode,\n\t\tlineMap\n\t};\n}\n\n/**\n * Converts source location metadata to user-facing diagnostics label.\n */\nexport function formatShaderSourceLocation(location: MaterialSourceLocation | null): string | null {\n\tif (!location) {\n\t\treturn null;\n\t}\n\n\tif (location.kind === 'fragment') {\n\t\treturn `fragment line ${location.line}`;\n\t}\n\n\tif (location.kind === 'include') {\n\t\treturn `include <${location.include}> line ${location.line}`;\n\t}\n\n\treturn `define \"${location.define}\" line ${location.line}`;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/// <reference types="@webgpu/types" />
|
|
2
|
+
import type { StorageBufferDefinition, StorageBufferDefinitionMap } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Storage-compatible texture formats for `texture_storage_2d`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const STORAGE_TEXTURE_FORMATS: ReadonlySet<GPUTextureFormat>;
|
|
7
|
+
/**
|
|
8
|
+
* Validates a single storage buffer definition.
|
|
9
|
+
*
|
|
10
|
+
* @param name - Buffer identifier.
|
|
11
|
+
* @param definition - Storage buffer definition to validate.
|
|
12
|
+
* @throws {Error} When any field is invalid.
|
|
13
|
+
*/
|
|
14
|
+
export declare function assertStorageBufferDefinition(name: string, definition: StorageBufferDefinition): void;
|
|
15
|
+
/**
|
|
16
|
+
* Validates and returns sorted storage buffer keys.
|
|
17
|
+
*
|
|
18
|
+
* @param definitions - Storage buffer definition map.
|
|
19
|
+
* @returns Lexicographically sorted buffer keys.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveStorageBufferKeys(definitions: StorageBufferDefinitionMap): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Normalizes a storage buffer definition with defaults applied.
|
|
24
|
+
*
|
|
25
|
+
* @param definition - Raw definition.
|
|
26
|
+
* @returns Normalized definition with access default.
|
|
27
|
+
*/
|
|
28
|
+
export declare function normalizeStorageBufferDefinition(definition: StorageBufferDefinition): Required<Pick<StorageBufferDefinition, 'size' | 'type' | 'access'>>;
|
|
29
|
+
/**
|
|
30
|
+
* Validates that a texture format is storage-compatible.
|
|
31
|
+
*
|
|
32
|
+
* @param name - Texture identifier.
|
|
33
|
+
* @param format - GPU texture format.
|
|
34
|
+
* @throws {Error} When format is not storage-compatible.
|
|
35
|
+
*/
|
|
36
|
+
export declare function assertStorageTextureFormat(name: string, format: GPUTextureFormat): void;
|
|
37
|
+
//# sourceMappingURL=storage-buffers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-buffers.d.ts","sourceRoot":"","sources":["../../src/lib/core/storage-buffers.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,EACX,uBAAuB,EACvB,0BAA0B,EAE1B,MAAM,YAAY,CAAC;AAgBpB;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,gBAAgB,CAkB1C,CAAC;AAEzB;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC5C,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,uBAAuB,GACjC,IAAI,CAsCN;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,0BAA0B,GAAG,MAAM,EAAE,CAS1F;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC/C,UAAU,EAAE,uBAAuB,GACjC,QAAQ,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC,CAMrE;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAOvF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { assertUniformName } from "./uniforms.js";
|
|
2
|
+
//#region src/lib/core/storage-buffers.ts
|
|
3
|
+
/**
|
|
4
|
+
* Valid WGSL storage buffer element types.
|
|
5
|
+
*/
|
|
6
|
+
var VALID_STORAGE_BUFFER_TYPES = new Set([
|
|
7
|
+
"array<f32>",
|
|
8
|
+
"array<vec2f>",
|
|
9
|
+
"array<vec3f>",
|
|
10
|
+
"array<vec4f>",
|
|
11
|
+
"array<u32>",
|
|
12
|
+
"array<i32>",
|
|
13
|
+
"array<vec4u>",
|
|
14
|
+
"array<vec4i>"
|
|
15
|
+
]);
|
|
16
|
+
/**
|
|
17
|
+
* Storage-compatible texture formats for `texture_storage_2d`.
|
|
18
|
+
*/
|
|
19
|
+
var STORAGE_TEXTURE_FORMATS = new Set([
|
|
20
|
+
"r32float",
|
|
21
|
+
"r32sint",
|
|
22
|
+
"r32uint",
|
|
23
|
+
"rg32float",
|
|
24
|
+
"rg32sint",
|
|
25
|
+
"rg32uint",
|
|
26
|
+
"rgba8unorm",
|
|
27
|
+
"rgba8snorm",
|
|
28
|
+
"rgba8uint",
|
|
29
|
+
"rgba8sint",
|
|
30
|
+
"rgba16float",
|
|
31
|
+
"rgba16uint",
|
|
32
|
+
"rgba16sint",
|
|
33
|
+
"rgba32float",
|
|
34
|
+
"rgba32uint",
|
|
35
|
+
"rgba32sint",
|
|
36
|
+
"bgra8unorm"
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* Validates a single storage buffer definition.
|
|
40
|
+
*
|
|
41
|
+
* @param name - Buffer identifier.
|
|
42
|
+
* @param definition - Storage buffer definition to validate.
|
|
43
|
+
* @throws {Error} When any field is invalid.
|
|
44
|
+
*/
|
|
45
|
+
function assertStorageBufferDefinition(name, definition) {
|
|
46
|
+
assertUniformName(name);
|
|
47
|
+
if (!Number.isFinite(definition.size) || definition.size <= 0) throw new Error(`Storage buffer "${name}" size must be a finite number greater than 0, got ${definition.size}`);
|
|
48
|
+
if (definition.size % 4 !== 0) throw new Error(`Storage buffer "${name}" size must be a multiple of 4, got ${definition.size}`);
|
|
49
|
+
if (!VALID_STORAGE_BUFFER_TYPES.has(definition.type)) throw new Error(`Storage buffer "${name}" has unknown type "${definition.type}". Supported types: ${[...VALID_STORAGE_BUFFER_TYPES].join(", ")}`);
|
|
50
|
+
if (definition.access !== void 0 && definition.access !== "read" && definition.access !== "read-write") throw new Error(`Storage buffer "${name}" has invalid access mode "${definition.access}". Use 'read' or 'read-write'.`);
|
|
51
|
+
if (definition.initialData !== void 0) {
|
|
52
|
+
if (definition.initialData.byteLength > definition.size) throw new Error(`Storage buffer "${name}" initialData byte length (${definition.initialData.byteLength}) exceeds buffer size (${definition.size})`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validates and returns sorted storage buffer keys.
|
|
57
|
+
*
|
|
58
|
+
* @param definitions - Storage buffer definition map.
|
|
59
|
+
* @returns Lexicographically sorted buffer keys.
|
|
60
|
+
*/
|
|
61
|
+
function resolveStorageBufferKeys(definitions) {
|
|
62
|
+
const keys = Object.keys(definitions).sort();
|
|
63
|
+
for (const key of keys) {
|
|
64
|
+
const definition = definitions[key];
|
|
65
|
+
if (definition) assertStorageBufferDefinition(key, definition);
|
|
66
|
+
}
|
|
67
|
+
return keys;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Normalizes a storage buffer definition with defaults applied.
|
|
71
|
+
*
|
|
72
|
+
* @param definition - Raw definition.
|
|
73
|
+
* @returns Normalized definition with access default.
|
|
74
|
+
*/
|
|
75
|
+
function normalizeStorageBufferDefinition(definition) {
|
|
76
|
+
return {
|
|
77
|
+
size: definition.size,
|
|
78
|
+
type: definition.type,
|
|
79
|
+
access: definition.access ?? "read-write"
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Validates that a texture format is storage-compatible.
|
|
84
|
+
*
|
|
85
|
+
* @param name - Texture identifier.
|
|
86
|
+
* @param format - GPU texture format.
|
|
87
|
+
* @throws {Error} When format is not storage-compatible.
|
|
88
|
+
*/
|
|
89
|
+
function assertStorageTextureFormat(name, format) {
|
|
90
|
+
if (!STORAGE_TEXTURE_FORMATS.has(format)) throw new Error(`Texture "${name}" with storage:true requires a storage-compatible format, but got "${format}". Supported formats: ${[...STORAGE_TEXTURE_FORMATS].join(", ")}`);
|
|
91
|
+
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { STORAGE_TEXTURE_FORMATS, assertStorageBufferDefinition, assertStorageTextureFormat, normalizeStorageBufferDefinition, resolveStorageBufferKeys };
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=storage-buffers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-buffers.js","names":[],"sources":["../../src/lib/core/storage-buffers.ts"],"sourcesContent":["import { assertUniformName } from './uniforms.js';\nimport type {\n\tStorageBufferDefinition,\n\tStorageBufferDefinitionMap,\n\tStorageBufferType\n} from './types.js';\n\n/**\n * Valid WGSL storage buffer element types.\n */\nconst VALID_STORAGE_BUFFER_TYPES: ReadonlySet<string> = new Set<StorageBufferType>([\n\t'array<f32>',\n\t'array<vec2f>',\n\t'array<vec3f>',\n\t'array<vec4f>',\n\t'array<u32>',\n\t'array<i32>',\n\t'array<vec4u>',\n\t'array<vec4i>'\n]);\n\n/**\n * Storage-compatible texture formats for `texture_storage_2d`.\n */\nexport const STORAGE_TEXTURE_FORMATS: ReadonlySet<GPUTextureFormat> = new Set([\n\t'r32float',\n\t'r32sint',\n\t'r32uint',\n\t'rg32float',\n\t'rg32sint',\n\t'rg32uint',\n\t'rgba8unorm',\n\t'rgba8snorm',\n\t'rgba8uint',\n\t'rgba8sint',\n\t'rgba16float',\n\t'rgba16uint',\n\t'rgba16sint',\n\t'rgba32float',\n\t'rgba32uint',\n\t'rgba32sint',\n\t'bgra8unorm'\n] as GPUTextureFormat[]);\n\n/**\n * Validates a single storage buffer definition.\n *\n * @param name - Buffer identifier.\n * @param definition - Storage buffer definition to validate.\n * @throws {Error} When any field is invalid.\n */\nexport function assertStorageBufferDefinition(\n\tname: string,\n\tdefinition: StorageBufferDefinition\n): void {\n\tassertUniformName(name);\n\n\tif (!Number.isFinite(definition.size) || definition.size <= 0) {\n\t\tthrow new Error(\n\t\t\t`Storage buffer \"${name}\" size must be a finite number greater than 0, got ${definition.size}`\n\t\t);\n\t}\n\n\tif (definition.size % 4 !== 0) {\n\t\tthrow new Error(\n\t\t\t`Storage buffer \"${name}\" size must be a multiple of 4, got ${definition.size}`\n\t\t);\n\t}\n\n\tif (!VALID_STORAGE_BUFFER_TYPES.has(definition.type)) {\n\t\tthrow new Error(\n\t\t\t`Storage buffer \"${name}\" has unknown type \"${definition.type}\". Supported types: ${[...VALID_STORAGE_BUFFER_TYPES].join(', ')}`\n\t\t);\n\t}\n\n\tif (\n\t\tdefinition.access !== undefined &&\n\t\tdefinition.access !== 'read' &&\n\t\tdefinition.access !== 'read-write'\n\t) {\n\t\tthrow new Error(\n\t\t\t`Storage buffer \"${name}\" has invalid access mode \"${definition.access}\". Use 'read' or 'read-write'.`\n\t\t);\n\t}\n\n\tif (definition.initialData !== undefined) {\n\t\tif (definition.initialData.byteLength > definition.size) {\n\t\t\tthrow new Error(\n\t\t\t\t`Storage buffer \"${name}\" initialData byte length (${definition.initialData.byteLength}) exceeds buffer size (${definition.size})`\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Validates and returns sorted storage buffer keys.\n *\n * @param definitions - Storage buffer definition map.\n * @returns Lexicographically sorted buffer keys.\n */\nexport function resolveStorageBufferKeys(definitions: StorageBufferDefinitionMap): string[] {\n\tconst keys = Object.keys(definitions).sort();\n\tfor (const key of keys) {\n\t\tconst definition = definitions[key];\n\t\tif (definition) {\n\t\t\tassertStorageBufferDefinition(key, definition);\n\t\t}\n\t}\n\treturn keys;\n}\n\n/**\n * Normalizes a storage buffer definition with defaults applied.\n *\n * @param definition - Raw definition.\n * @returns Normalized definition with access default.\n */\nexport function normalizeStorageBufferDefinition(\n\tdefinition: StorageBufferDefinition\n): Required<Pick<StorageBufferDefinition, 'size' | 'type' | 'access'>> {\n\treturn {\n\t\tsize: definition.size,\n\t\ttype: definition.type,\n\t\taccess: definition.access ?? 'read-write'\n\t};\n}\n\n/**\n * Validates that a texture format is storage-compatible.\n *\n * @param name - Texture identifier.\n * @param format - GPU texture format.\n * @throws {Error} When format is not storage-compatible.\n */\nexport function assertStorageTextureFormat(name: string, format: GPUTextureFormat): void {\n\tif (!STORAGE_TEXTURE_FORMATS.has(format)) {\n\t\tthrow new Error(\n\t\t\t`Texture \"${name}\" with storage:true requires a storage-compatible format, but got \"${format}\". ` +\n\t\t\t\t`Supported formats: ${[...STORAGE_TEXTURE_FORMATS].join(', ')}`\n\t\t);\n\t}\n}\n"],"mappings":";;;;;AAUA,IAAM,6BAAkD,IAAI,IAAuB;CAClF;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,CAAC;;;;AAKF,IAAa,0BAAyD,IAAI,IAAI;CAC7E;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA,CAAuB;;;;;;;;AASxB,SAAgB,8BACf,MACA,YACO;AACP,mBAAkB,KAAK;AAEvB,KAAI,CAAC,OAAO,SAAS,WAAW,KAAK,IAAI,WAAW,QAAQ,EAC3D,OAAM,IAAI,MACT,mBAAmB,KAAK,qDAAqD,WAAW,OACxF;AAGF,KAAI,WAAW,OAAO,MAAM,EAC3B,OAAM,IAAI,MACT,mBAAmB,KAAK,sCAAsC,WAAW,OACzE;AAGF,KAAI,CAAC,2BAA2B,IAAI,WAAW,KAAK,CACnD,OAAM,IAAI,MACT,mBAAmB,KAAK,sBAAsB,WAAW,KAAK,sBAAsB,CAAC,GAAG,2BAA2B,CAAC,KAAK,KAAK,GAC9H;AAGF,KACC,WAAW,WAAW,UACtB,WAAW,WAAW,UACtB,WAAW,WAAW,aAEtB,OAAM,IAAI,MACT,mBAAmB,KAAK,6BAA6B,WAAW,OAAO,gCACvE;AAGF,KAAI,WAAW,gBAAgB,QAC9B;MAAI,WAAW,YAAY,aAAa,WAAW,KAClD,OAAM,IAAI,MACT,mBAAmB,KAAK,6BAA6B,WAAW,YAAY,WAAW,yBAAyB,WAAW,KAAK,GAChI;;;;;;;;;AAWJ,SAAgB,yBAAyB,aAAmD;CAC3F,MAAM,OAAO,OAAO,KAAK,YAAY,CAAC,MAAM;AAC5C,MAAK,MAAM,OAAO,MAAM;EACvB,MAAM,aAAa,YAAY;AAC/B,MAAI,WACH,+BAA8B,KAAK,WAAW;;AAGhD,QAAO;;;;;;;;AASR,SAAgB,iCACf,YACsE;AACtE,QAAO;EACN,MAAM,WAAW;EACjB,MAAM,WAAW;EACjB,QAAQ,WAAW,UAAU;EAC7B;;;;;;;;;AAUF,SAAgB,2BAA2B,MAAc,QAAgC;AACxF,KAAI,CAAC,wBAAwB,IAAI,OAAO,CACvC,OAAM,IAAI,MACT,YAAY,KAAK,qEAAqE,OAAO,wBACtE,CAAC,GAAG,wBAAwB,CAAC,KAAK,KAAK,GAC9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"texture-loader.d.ts","sourceRoot":"","sources":["../../src/lib/core/texture-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC;;OAEG;IACH,oBAAoB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1C;;OAEG;IACH,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,aAAa,CAAC;IACtD;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;OAEG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B;;OAEG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB;AAiCD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKpD;AA+FD;;GAEG;AACH,wBAAgB,4BAA4B,CAC3C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC9B,MAAM,CAQR;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAO5C;AAsGD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"texture-loader.d.ts","sourceRoot":"","sources":["../../src/lib/core/texture-loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC;;OAEG;IACH,oBAAoB,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1C;;OAEG;IACH,gBAAgB,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,aAAa,CAAC;IACtD;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;OAEG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B;;OAEG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB;AAiCD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKpD;AA+FD;;GAEG;AACH,wBAAgB,4BAA4B,CAC3C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC9B,MAAM,CAQR;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAO5C;AAsGD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,aAAa,CAAC,CAsExB;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACzC,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,kBAAuB,GAC9B,OAAO,CAAC,aAAa,EAAE,CAAC,CAsB1B"}
|
|
@@ -175,6 +175,7 @@ async function loadTextureFromUrl(url, options = {}) {
|
|
|
175
175
|
bitmap.close();
|
|
176
176
|
throw createAbortError();
|
|
177
177
|
}
|
|
178
|
+
let disposed = false;
|
|
178
179
|
const loaded = {
|
|
179
180
|
url,
|
|
180
181
|
source: bitmap,
|
|
@@ -182,7 +183,10 @@ async function loadTextureFromUrl(url, options = {}) {
|
|
|
182
183
|
height: bitmap.height,
|
|
183
184
|
colorSpace: normalized.colorSpace,
|
|
184
185
|
dispose: () => {
|
|
186
|
+
if (disposed) return;
|
|
187
|
+
disposed = true;
|
|
185
188
|
bitmap?.close();
|
|
189
|
+
bitmap = null;
|
|
186
190
|
}
|
|
187
191
|
};
|
|
188
192
|
if (normalized.update !== void 0) loaded.update = normalized.update;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"texture-loader.js","names":[],"sources":["../../src/lib/core/texture-loader.ts"],"sourcesContent":["import type { TextureUpdateMode } from './types.js';\n\n/**\n * Options controlling bitmap decode behavior.\n */\nexport interface TextureDecodeOptions {\n\t/**\n\t * Controls color-space conversion during decode.\n\t */\n\tcolorSpaceConversion?: 'default' | 'none';\n\t/**\n\t * Controls alpha premultiplication during decode.\n\t */\n\tpremultiplyAlpha?: 'default' | 'none' | 'premultiply';\n\t/**\n\t * Controls bitmap orientation during decode.\n\t */\n\timageOrientation?: 'none' | 'flipY';\n}\n\n/**\n * Options controlling URL-based texture loading and decode behavior.\n */\nexport interface TextureLoadOptions {\n\t/**\n\t * Desired texture color space.\n\t */\n\tcolorSpace?: 'srgb' | 'linear';\n\t/**\n\t * Fetch options forwarded to `fetch`.\n\t */\n\trequestInit?: RequestInit;\n\t/**\n\t * Decode options forwarded to `createImageBitmap`.\n\t */\n\tdecode?: TextureDecodeOptions;\n\t/**\n\t * Optional cancellation signal for this request.\n\t */\n\tsignal?: AbortSignal;\n\t/**\n\t * Optional runtime update strategy metadata attached to loaded textures.\n\t */\n\tupdate?: TextureUpdateMode;\n\t/**\n\t * Optional runtime flip-y metadata attached to loaded textures.\n\t */\n\tflipY?: boolean;\n\t/**\n\t * Optional runtime premultiplied-alpha metadata attached to loaded textures.\n\t */\n\tpremultipliedAlpha?: boolean;\n\t/**\n\t * Optional runtime mipmap metadata attached to loaded textures.\n\t */\n\tgenerateMipmaps?: boolean;\n}\n\n/**\n * Loaded texture payload returned by URL loaders.\n */\nexport interface LoadedTexture {\n\t/**\n\t * Source URL.\n\t */\n\turl: string;\n\t/**\n\t * Decoded bitmap source.\n\t */\n\tsource: ImageBitmap;\n\t/**\n\t * Bitmap width in pixels.\n\t */\n\twidth: number;\n\t/**\n\t * Bitmap height in pixels.\n\t */\n\theight: number;\n\t/**\n\t * Effective color space.\n\t */\n\tcolorSpace: 'srgb' | 'linear';\n\t/**\n\t * Effective runtime update strategy.\n\t */\n\tupdate?: TextureUpdateMode;\n\t/**\n\t * Effective runtime flip-y metadata.\n\t */\n\tflipY?: boolean;\n\t/**\n\t * Effective runtime premultiplied-alpha metadata.\n\t */\n\tpremultipliedAlpha?: boolean;\n\t/**\n\t * Effective runtime mipmap metadata.\n\t */\n\tgenerateMipmaps?: boolean;\n\t/**\n\t * Releases bitmap resources.\n\t */\n\tdispose: () => void;\n}\n\ninterface NormalizedTextureLoadOptions {\n\tcolorSpace: 'srgb' | 'linear';\n\trequestInit?: RequestInit;\n\tdecode: Required<TextureDecodeOptions>;\n\tsignal?: AbortSignal;\n\tupdate?: TextureUpdateMode;\n\tflipY?: boolean;\n\tpremultipliedAlpha?: boolean;\n\tgenerateMipmaps?: boolean;\n}\n\ninterface TextureResourceCacheEntry {\n\tkey: string;\n\trefs: number;\n\tcontroller: AbortController;\n\tsettled: boolean;\n\tblobPromise: Promise<Blob>;\n}\n\nconst resourceCache = new Map<string, TextureResourceCacheEntry>();\n\nfunction createAbortError(): Error {\n\ttry {\n\t\treturn new DOMException('Texture request was aborted', 'AbortError');\n\t} catch {\n\t\tconst error = new Error('Texture request was aborted');\n\t\t(error as Error & { name: string }).name = 'AbortError';\n\t\treturn error;\n\t}\n}\n\n/**\n * Checks whether error represents abort cancellation.\n */\nexport function isAbortError(error: unknown): boolean {\n\treturn (\n\t\terror instanceof Error &&\n\t\t(error.name === 'AbortError' || error.message.toLowerCase().includes('aborted'))\n\t);\n}\n\nfunction toBodyFingerprint(body: BodyInit | null | undefined): string | null {\n\tif (body == null) {\n\t\treturn null;\n\t}\n\n\tif (typeof body === 'string') {\n\t\treturn `string:${body}`;\n\t}\n\n\tif (body instanceof URLSearchParams) {\n\t\treturn `urlsearchparams:${body.toString()}`;\n\t}\n\n\tif (typeof FormData !== 'undefined' && body instanceof FormData) {\n\t\tconst entries = Array.from(body.entries()).map(([key, value]) => `${key}:${String(value)}`);\n\t\treturn `formdata:${entries.join('&')}`;\n\t}\n\n\tif (body instanceof Blob) {\n\t\treturn `blob:${body.type}:${body.size}`;\n\t}\n\n\tif (body instanceof ArrayBuffer) {\n\t\treturn `arraybuffer:${body.byteLength}`;\n\t}\n\n\tif (ArrayBuffer.isView(body)) {\n\t\treturn `view:${body.byteLength}`;\n\t}\n\n\treturn `opaque:${Object.prototype.toString.call(body)}`;\n}\n\nfunction normalizeRequestInit(requestInit: RequestInit | undefined): Record<string, unknown> {\n\tif (!requestInit) {\n\t\treturn {};\n\t}\n\n\tconst headers = new Headers(requestInit.headers);\n\tconst headerEntries = Array.from(headers.entries()).sort(([a], [b]) => a.localeCompare(b));\n\tconst normalized: Record<string, unknown> = {};\n\n\tnormalized.method = (requestInit.method ?? 'GET').toUpperCase();\n\tnormalized.mode = requestInit.mode ?? null;\n\tnormalized.cache = requestInit.cache ?? null;\n\tnormalized.credentials = requestInit.credentials ?? null;\n\tnormalized.redirect = requestInit.redirect ?? null;\n\tnormalized.referrer = requestInit.referrer ?? null;\n\tnormalized.referrerPolicy = requestInit.referrerPolicy ?? null;\n\tnormalized.integrity = requestInit.integrity ?? null;\n\tnormalized.keepalive = requestInit.keepalive ?? false;\n\tnormalized.priority = requestInit.priority ?? null;\n\tnormalized.headers = headerEntries;\n\tnormalized.body = toBodyFingerprint(requestInit.body);\n\n\treturn normalized;\n}\n\nfunction normalizeTextureLoadOptions(options: TextureLoadOptions): NormalizedTextureLoadOptions {\n\tconst colorSpace = options.colorSpace ?? 'srgb';\n\n\tconst normalized: NormalizedTextureLoadOptions = {\n\t\tcolorSpace,\n\t\tdecode: {\n\t\t\tcolorSpaceConversion:\n\t\t\t\toptions.decode?.colorSpaceConversion ?? (colorSpace === 'linear' ? 'none' : 'default'),\n\t\t\tpremultiplyAlpha: options.decode?.premultiplyAlpha ?? 'default',\n\t\t\timageOrientation: options.decode?.imageOrientation ?? 'none'\n\t\t}\n\t};\n\n\tif (options.requestInit !== undefined) {\n\t\tnormalized.requestInit = options.requestInit;\n\t}\n\tif (options.signal !== undefined) {\n\t\tnormalized.signal = options.signal;\n\t}\n\tif (options.update !== undefined) {\n\t\tnormalized.update = options.update;\n\t}\n\tif (options.flipY !== undefined) {\n\t\tnormalized.flipY = options.flipY;\n\t}\n\tif (options.premultipliedAlpha !== undefined) {\n\t\tnormalized.premultipliedAlpha = options.premultipliedAlpha;\n\t}\n\tif (options.generateMipmaps !== undefined) {\n\t\tnormalized.generateMipmaps = options.generateMipmaps;\n\t}\n\n\treturn normalized;\n}\n\n/**\n * Builds deterministic resource cache key from full URL IO config.\n */\nexport function buildTextureResourceCacheKey(\n\turl: string,\n\toptions: TextureLoadOptions = {}\n): string {\n\tconst normalized = normalizeTextureLoadOptions(options);\n\treturn JSON.stringify({\n\t\turl,\n\t\tcolorSpace: normalized.colorSpace,\n\t\trequestInit: normalizeRequestInit(normalized.requestInit),\n\t\tdecode: normalized.decode\n\t});\n}\n\n/**\n * Clears the internal texture resource cache.\n */\nexport function clearTextureBlobCache(): void {\n\tfor (const entry of resourceCache.values()) {\n\t\tif (!entry.settled) {\n\t\t\tentry.controller.abort();\n\t\t}\n\t}\n\tresourceCache.clear();\n}\n\nfunction acquireTextureBlob(\n\turl: string,\n\toptions: TextureLoadOptions\n): {\n\tentry: TextureResourceCacheEntry;\n\trelease: () => void;\n} {\n\tconst key = buildTextureResourceCacheKey(url, options);\n\tconst existing = resourceCache.get(key);\n\tif (existing) {\n\t\texisting.refs += 1;\n\t\tlet released = false;\n\t\treturn {\n\t\t\tentry: existing,\n\t\t\trelease: () => {\n\t\t\t\tif (released) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\treleased = true;\n\t\t\t\texisting.refs = Math.max(0, existing.refs - 1);\n\t\t\t\tif (existing.refs === 0) {\n\t\t\t\t\tif (!existing.settled) {\n\t\t\t\t\t\texisting.controller.abort();\n\t\t\t\t\t}\n\t\t\t\t\tresourceCache.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\tconst normalized = normalizeTextureLoadOptions(options);\n\tconst controller = new AbortController();\n\tconst requestInit = {\n\t\t...(normalized.requestInit ?? {}),\n\t\tsignal: controller.signal\n\t} satisfies RequestInit;\n\tconst entry: TextureResourceCacheEntry = {\n\t\tkey,\n\t\trefs: 1,\n\t\tcontroller,\n\t\tsettled: false,\n\t\tblobPromise: fetch(url, requestInit)\n\t\t\t.then(async (response) => {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`Texture request failed (${response.status}) for ${url}`);\n\t\t\t\t}\n\t\t\t\treturn response.blob();\n\t\t\t})\n\t\t\t.then((blob) => {\n\t\t\t\tentry.settled = true;\n\t\t\t\treturn blob;\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tresourceCache.delete(key);\n\t\t\t\tthrow error;\n\t\t\t})\n\t};\n\n\tresourceCache.set(key, entry);\n\tlet released = false;\n\treturn {\n\t\tentry,\n\t\trelease: () => {\n\t\t\tif (released) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treleased = true;\n\t\t\tentry.refs = Math.max(0, entry.refs - 1);\n\t\t\tif (entry.refs === 0) {\n\t\t\t\tif (!entry.settled) {\n\t\t\t\t\tentry.controller.abort();\n\t\t\t\t}\n\t\t\t\tresourceCache.delete(key);\n\t\t\t}\n\t\t}\n\t};\n}\n\nasync function awaitWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\n\tif (signal.aborted) {\n\t\tthrow createAbortError();\n\t}\n\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst onAbort = (): void => {\n\t\t\treject(createAbortError());\n\t\t};\n\n\t\tsignal.addEventListener('abort', onAbort, { once: true });\n\n\t\tpromise.then(resolve, reject).finally(() => {\n\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t});\n\t});\n}\n\n/**\n * Loads a single texture from URL and converts it to an `ImageBitmap`.\n *\n * @param url - Texture URL.\n * @param options - Loading options.\n * @returns Loaded texture object.\n * @throws {Error} When runtime does not support `createImageBitmap` or request fails.\n */\nexport async function loadTextureFromUrl(\n\turl: string,\n\toptions: TextureLoadOptions = {}\n): Promise<LoadedTexture> {\n\tif (typeof createImageBitmap !== 'function') {\n\t\tthrow new Error('createImageBitmap is not available in this runtime');\n\t}\n\n\tconst normalized = normalizeTextureLoadOptions(options);\n\tconst { entry, release } = acquireTextureBlob(url, options);\n\tlet bitmap: ImageBitmap | null = null;\n\n\ttry {\n\t\tconst blob = await awaitWithAbort(entry.blobPromise, normalized.signal);\n\n\t\tconst bitmapOptions: ImageBitmapOptions = {\n\t\t\tcolorSpaceConversion: normalized.decode.colorSpaceConversion,\n\t\t\tpremultiplyAlpha: normalized.decode.premultiplyAlpha,\n\t\t\timageOrientation: normalized.decode.imageOrientation\n\t\t};\n\t\tconst allDefaults =\n\t\t\tbitmapOptions.colorSpaceConversion === 'default' &&\n\t\t\tbitmapOptions.premultiplyAlpha === 'default' &&\n\t\t\tbitmapOptions.imageOrientation === 'none';\n\n\t\tbitmap = allDefaults\n\t\t\t? await createImageBitmap(blob)\n\t\t\t: await createImageBitmap(blob, bitmapOptions);\n\n\t\tif (normalized.signal?.aborted) {\n\t\t\tbitmap.close();\n\t\t\tthrow createAbortError();\n\t\t}\n\n\t\tconst loaded: LoadedTexture = {\n\t\t\turl,\n\t\t\tsource: bitmap,\n\t\t\twidth: bitmap.width,\n\t\t\theight: bitmap.height,\n\t\t\tcolorSpace: normalized.colorSpace,\n\t\t\tdispose: () => {\n\t\t\t\tbitmap?.close();\n\t\t\t}\n\t\t};\n\n\t\tif (normalized.update !== undefined) {\n\t\t\tloaded.update = normalized.update;\n\t\t}\n\t\tif (normalized.flipY !== undefined) {\n\t\t\tloaded.flipY = normalized.flipY;\n\t\t}\n\t\tif (normalized.premultipliedAlpha !== undefined) {\n\t\t\tloaded.premultipliedAlpha = normalized.premultipliedAlpha;\n\t\t}\n\t\tif (normalized.generateMipmaps !== undefined) {\n\t\t\tloaded.generateMipmaps = normalized.generateMipmaps;\n\t\t}\n\n\t\treturn loaded;\n\t} catch (error) {\n\t\tif (bitmap) {\n\t\t\tbitmap.close();\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\trelease();\n\t}\n}\n\n/**\n * Loads many textures in parallel from URLs.\n *\n * @param urls - Texture URLs.\n * @param options - Shared loading options.\n * @returns Promise resolving to loaded textures in input order.\n */\nexport async function loadTexturesFromUrls(\n\turls: string[],\n\toptions: TextureLoadOptions = {}\n): Promise<LoadedTexture[]> {\n\tconst settled = await Promise.allSettled(urls.map((url) => loadTextureFromUrl(url, options)));\n\tconst loaded: LoadedTexture[] = [];\n\tlet firstError: unknown = null;\n\n\tfor (const entry of settled) {\n\t\tif (entry.status === 'fulfilled') {\n\t\t\tloaded.push(entry.value);\n\t\t\tcontinue;\n\t\t}\n\n\t\tfirstError ??= entry.reason;\n\t}\n\n\tif (firstError) {\n\t\tfor (const texture of loaded) {\n\t\t\ttexture.dispose();\n\t\t}\n\t\tthrow firstError;\n\t}\n\n\treturn loaded;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"texture-loader.js","names":[],"sources":["../../src/lib/core/texture-loader.ts"],"sourcesContent":["import type { TextureUpdateMode } from './types.js';\n\n/**\n * Options controlling bitmap decode behavior.\n */\nexport interface TextureDecodeOptions {\n\t/**\n\t * Controls color-space conversion during decode.\n\t */\n\tcolorSpaceConversion?: 'default' | 'none';\n\t/**\n\t * Controls alpha premultiplication during decode.\n\t */\n\tpremultiplyAlpha?: 'default' | 'none' | 'premultiply';\n\t/**\n\t * Controls bitmap orientation during decode.\n\t */\n\timageOrientation?: 'none' | 'flipY';\n}\n\n/**\n * Options controlling URL-based texture loading and decode behavior.\n */\nexport interface TextureLoadOptions {\n\t/**\n\t * Desired texture color space.\n\t */\n\tcolorSpace?: 'srgb' | 'linear';\n\t/**\n\t * Fetch options forwarded to `fetch`.\n\t */\n\trequestInit?: RequestInit;\n\t/**\n\t * Decode options forwarded to `createImageBitmap`.\n\t */\n\tdecode?: TextureDecodeOptions;\n\t/**\n\t * Optional cancellation signal for this request.\n\t */\n\tsignal?: AbortSignal;\n\t/**\n\t * Optional runtime update strategy metadata attached to loaded textures.\n\t */\n\tupdate?: TextureUpdateMode;\n\t/**\n\t * Optional runtime flip-y metadata attached to loaded textures.\n\t */\n\tflipY?: boolean;\n\t/**\n\t * Optional runtime premultiplied-alpha metadata attached to loaded textures.\n\t */\n\tpremultipliedAlpha?: boolean;\n\t/**\n\t * Optional runtime mipmap metadata attached to loaded textures.\n\t */\n\tgenerateMipmaps?: boolean;\n}\n\n/**\n * Loaded texture payload returned by URL loaders.\n */\nexport interface LoadedTexture {\n\t/**\n\t * Source URL.\n\t */\n\turl: string;\n\t/**\n\t * Decoded bitmap source.\n\t */\n\tsource: ImageBitmap;\n\t/**\n\t * Bitmap width in pixels.\n\t */\n\twidth: number;\n\t/**\n\t * Bitmap height in pixels.\n\t */\n\theight: number;\n\t/**\n\t * Effective color space.\n\t */\n\tcolorSpace: 'srgb' | 'linear';\n\t/**\n\t * Effective runtime update strategy.\n\t */\n\tupdate?: TextureUpdateMode;\n\t/**\n\t * Effective runtime flip-y metadata.\n\t */\n\tflipY?: boolean;\n\t/**\n\t * Effective runtime premultiplied-alpha metadata.\n\t */\n\tpremultipliedAlpha?: boolean;\n\t/**\n\t * Effective runtime mipmap metadata.\n\t */\n\tgenerateMipmaps?: boolean;\n\t/**\n\t * Releases bitmap resources.\n\t */\n\tdispose: () => void;\n}\n\ninterface NormalizedTextureLoadOptions {\n\tcolorSpace: 'srgb' | 'linear';\n\trequestInit?: RequestInit;\n\tdecode: Required<TextureDecodeOptions>;\n\tsignal?: AbortSignal;\n\tupdate?: TextureUpdateMode;\n\tflipY?: boolean;\n\tpremultipliedAlpha?: boolean;\n\tgenerateMipmaps?: boolean;\n}\n\ninterface TextureResourceCacheEntry {\n\tkey: string;\n\trefs: number;\n\tcontroller: AbortController;\n\tsettled: boolean;\n\tblobPromise: Promise<Blob>;\n}\n\nconst resourceCache = new Map<string, TextureResourceCacheEntry>();\n\nfunction createAbortError(): Error {\n\ttry {\n\t\treturn new DOMException('Texture request was aborted', 'AbortError');\n\t} catch {\n\t\tconst error = new Error('Texture request was aborted');\n\t\t(error as Error & { name: string }).name = 'AbortError';\n\t\treturn error;\n\t}\n}\n\n/**\n * Checks whether error represents abort cancellation.\n */\nexport function isAbortError(error: unknown): boolean {\n\treturn (\n\t\terror instanceof Error &&\n\t\t(error.name === 'AbortError' || error.message.toLowerCase().includes('aborted'))\n\t);\n}\n\nfunction toBodyFingerprint(body: BodyInit | null | undefined): string | null {\n\tif (body == null) {\n\t\treturn null;\n\t}\n\n\tif (typeof body === 'string') {\n\t\treturn `string:${body}`;\n\t}\n\n\tif (body instanceof URLSearchParams) {\n\t\treturn `urlsearchparams:${body.toString()}`;\n\t}\n\n\tif (typeof FormData !== 'undefined' && body instanceof FormData) {\n\t\tconst entries = Array.from(body.entries()).map(([key, value]) => `${key}:${String(value)}`);\n\t\treturn `formdata:${entries.join('&')}`;\n\t}\n\n\tif (body instanceof Blob) {\n\t\treturn `blob:${body.type}:${body.size}`;\n\t}\n\n\tif (body instanceof ArrayBuffer) {\n\t\treturn `arraybuffer:${body.byteLength}`;\n\t}\n\n\tif (ArrayBuffer.isView(body)) {\n\t\treturn `view:${body.byteLength}`;\n\t}\n\n\treturn `opaque:${Object.prototype.toString.call(body)}`;\n}\n\nfunction normalizeRequestInit(requestInit: RequestInit | undefined): Record<string, unknown> {\n\tif (!requestInit) {\n\t\treturn {};\n\t}\n\n\tconst headers = new Headers(requestInit.headers);\n\tconst headerEntries = Array.from(headers.entries()).sort(([a], [b]) => a.localeCompare(b));\n\tconst normalized: Record<string, unknown> = {};\n\n\tnormalized.method = (requestInit.method ?? 'GET').toUpperCase();\n\tnormalized.mode = requestInit.mode ?? null;\n\tnormalized.cache = requestInit.cache ?? null;\n\tnormalized.credentials = requestInit.credentials ?? null;\n\tnormalized.redirect = requestInit.redirect ?? null;\n\tnormalized.referrer = requestInit.referrer ?? null;\n\tnormalized.referrerPolicy = requestInit.referrerPolicy ?? null;\n\tnormalized.integrity = requestInit.integrity ?? null;\n\tnormalized.keepalive = requestInit.keepalive ?? false;\n\tnormalized.priority = requestInit.priority ?? null;\n\tnormalized.headers = headerEntries;\n\tnormalized.body = toBodyFingerprint(requestInit.body);\n\n\treturn normalized;\n}\n\nfunction normalizeTextureLoadOptions(options: TextureLoadOptions): NormalizedTextureLoadOptions {\n\tconst colorSpace = options.colorSpace ?? 'srgb';\n\n\tconst normalized: NormalizedTextureLoadOptions = {\n\t\tcolorSpace,\n\t\tdecode: {\n\t\t\tcolorSpaceConversion:\n\t\t\t\toptions.decode?.colorSpaceConversion ?? (colorSpace === 'linear' ? 'none' : 'default'),\n\t\t\tpremultiplyAlpha: options.decode?.premultiplyAlpha ?? 'default',\n\t\t\timageOrientation: options.decode?.imageOrientation ?? 'none'\n\t\t}\n\t};\n\n\tif (options.requestInit !== undefined) {\n\t\tnormalized.requestInit = options.requestInit;\n\t}\n\tif (options.signal !== undefined) {\n\t\tnormalized.signal = options.signal;\n\t}\n\tif (options.update !== undefined) {\n\t\tnormalized.update = options.update;\n\t}\n\tif (options.flipY !== undefined) {\n\t\tnormalized.flipY = options.flipY;\n\t}\n\tif (options.premultipliedAlpha !== undefined) {\n\t\tnormalized.premultipliedAlpha = options.premultipliedAlpha;\n\t}\n\tif (options.generateMipmaps !== undefined) {\n\t\tnormalized.generateMipmaps = options.generateMipmaps;\n\t}\n\n\treturn normalized;\n}\n\n/**\n * Builds deterministic resource cache key from full URL IO config.\n */\nexport function buildTextureResourceCacheKey(\n\turl: string,\n\toptions: TextureLoadOptions = {}\n): string {\n\tconst normalized = normalizeTextureLoadOptions(options);\n\treturn JSON.stringify({\n\t\turl,\n\t\tcolorSpace: normalized.colorSpace,\n\t\trequestInit: normalizeRequestInit(normalized.requestInit),\n\t\tdecode: normalized.decode\n\t});\n}\n\n/**\n * Clears the internal texture resource cache.\n */\nexport function clearTextureBlobCache(): void {\n\tfor (const entry of resourceCache.values()) {\n\t\tif (!entry.settled) {\n\t\t\tentry.controller.abort();\n\t\t}\n\t}\n\tresourceCache.clear();\n}\n\nfunction acquireTextureBlob(\n\turl: string,\n\toptions: TextureLoadOptions\n): {\n\tentry: TextureResourceCacheEntry;\n\trelease: () => void;\n} {\n\tconst key = buildTextureResourceCacheKey(url, options);\n\tconst existing = resourceCache.get(key);\n\tif (existing) {\n\t\texisting.refs += 1;\n\t\tlet released = false;\n\t\treturn {\n\t\t\tentry: existing,\n\t\t\trelease: () => {\n\t\t\t\tif (released) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\treleased = true;\n\t\t\t\texisting.refs = Math.max(0, existing.refs - 1);\n\t\t\t\tif (existing.refs === 0) {\n\t\t\t\t\tif (!existing.settled) {\n\t\t\t\t\t\texisting.controller.abort();\n\t\t\t\t\t}\n\t\t\t\t\tresourceCache.delete(key);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\n\tconst normalized = normalizeTextureLoadOptions(options);\n\tconst controller = new AbortController();\n\tconst requestInit = {\n\t\t...(normalized.requestInit ?? {}),\n\t\tsignal: controller.signal\n\t} satisfies RequestInit;\n\tconst entry: TextureResourceCacheEntry = {\n\t\tkey,\n\t\trefs: 1,\n\t\tcontroller,\n\t\tsettled: false,\n\t\tblobPromise: fetch(url, requestInit)\n\t\t\t.then(async (response) => {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(`Texture request failed (${response.status}) for ${url}`);\n\t\t\t\t}\n\t\t\t\treturn response.blob();\n\t\t\t})\n\t\t\t.then((blob) => {\n\t\t\t\tentry.settled = true;\n\t\t\t\treturn blob;\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tresourceCache.delete(key);\n\t\t\t\tthrow error;\n\t\t\t})\n\t};\n\n\tresourceCache.set(key, entry);\n\tlet released = false;\n\treturn {\n\t\tentry,\n\t\trelease: () => {\n\t\t\tif (released) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treleased = true;\n\t\t\tentry.refs = Math.max(0, entry.refs - 1);\n\t\t\tif (entry.refs === 0) {\n\t\t\t\tif (!entry.settled) {\n\t\t\t\t\tentry.controller.abort();\n\t\t\t\t}\n\t\t\t\tresourceCache.delete(key);\n\t\t\t}\n\t\t}\n\t};\n}\n\nasync function awaitWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\n\tif (signal.aborted) {\n\t\tthrow createAbortError();\n\t}\n\n\treturn new Promise<T>((resolve, reject) => {\n\t\tconst onAbort = (): void => {\n\t\t\treject(createAbortError());\n\t\t};\n\n\t\tsignal.addEventListener('abort', onAbort, { once: true });\n\n\t\tpromise.then(resolve, reject).finally(() => {\n\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t});\n\t});\n}\n\n/**\n * Loads a single texture from URL and converts it to an `ImageBitmap`.\n *\n * @param url - Texture URL.\n * @param options - Loading options.\n * @returns Loaded texture object.\n * @throws {Error} When runtime does not support `createImageBitmap` or request fails.\n */\nexport async function loadTextureFromUrl(\n\turl: string,\n\toptions: TextureLoadOptions = {}\n): Promise<LoadedTexture> {\n\tif (typeof createImageBitmap !== 'function') {\n\t\tthrow new Error('createImageBitmap is not available in this runtime');\n\t}\n\n\tconst normalized = normalizeTextureLoadOptions(options);\n\tconst { entry, release } = acquireTextureBlob(url, options);\n\tlet bitmap: ImageBitmap | null = null;\n\n\ttry {\n\t\tconst blob = await awaitWithAbort(entry.blobPromise, normalized.signal);\n\n\t\tconst bitmapOptions: ImageBitmapOptions = {\n\t\t\tcolorSpaceConversion: normalized.decode.colorSpaceConversion,\n\t\t\tpremultiplyAlpha: normalized.decode.premultiplyAlpha,\n\t\t\timageOrientation: normalized.decode.imageOrientation\n\t\t};\n\t\tconst allDefaults =\n\t\t\tbitmapOptions.colorSpaceConversion === 'default' &&\n\t\t\tbitmapOptions.premultiplyAlpha === 'default' &&\n\t\t\tbitmapOptions.imageOrientation === 'none';\n\n\t\tbitmap = allDefaults\n\t\t\t? await createImageBitmap(blob)\n\t\t\t: await createImageBitmap(blob, bitmapOptions);\n\n\t\tif (normalized.signal?.aborted) {\n\t\t\tbitmap.close();\n\t\t\tthrow createAbortError();\n\t\t}\n\n\t\tlet disposed = false;\n\t\tconst loaded: LoadedTexture = {\n\t\t\turl,\n\t\t\tsource: bitmap,\n\t\t\twidth: bitmap.width,\n\t\t\theight: bitmap.height,\n\t\t\tcolorSpace: normalized.colorSpace,\n\t\t\tdispose: () => {\n\t\t\t\tif (disposed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdisposed = true;\n\t\t\t\tbitmap?.close();\n\t\t\t\tbitmap = null;\n\t\t\t}\n\t\t};\n\n\t\tif (normalized.update !== undefined) {\n\t\t\tloaded.update = normalized.update;\n\t\t}\n\t\tif (normalized.flipY !== undefined) {\n\t\t\tloaded.flipY = normalized.flipY;\n\t\t}\n\t\tif (normalized.premultipliedAlpha !== undefined) {\n\t\t\tloaded.premultipliedAlpha = normalized.premultipliedAlpha;\n\t\t}\n\t\tif (normalized.generateMipmaps !== undefined) {\n\t\t\tloaded.generateMipmaps = normalized.generateMipmaps;\n\t\t}\n\n\t\treturn loaded;\n\t} catch (error) {\n\t\tif (bitmap) {\n\t\t\tbitmap.close();\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\trelease();\n\t}\n}\n\n/**\n * Loads many textures in parallel from URLs.\n *\n * @param urls - Texture URLs.\n * @param options - Shared loading options.\n * @returns Promise resolving to loaded textures in input order.\n */\nexport async function loadTexturesFromUrls(\n\turls: string[],\n\toptions: TextureLoadOptions = {}\n): Promise<LoadedTexture[]> {\n\tconst settled = await Promise.allSettled(urls.map((url) => loadTextureFromUrl(url, options)));\n\tconst loaded: LoadedTexture[] = [];\n\tlet firstError: unknown = null;\n\n\tfor (const entry of settled) {\n\t\tif (entry.status === 'fulfilled') {\n\t\t\tloaded.push(entry.value);\n\t\t\tcontinue;\n\t\t}\n\n\t\tfirstError ??= entry.reason;\n\t}\n\n\tif (firstError) {\n\t\tfor (const texture of loaded) {\n\t\t\ttexture.dispose();\n\t\t}\n\t\tthrow firstError;\n\t}\n\n\treturn loaded;\n}\n"],"mappings":"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"}
|
package/dist/core/textures.d.ts
CHANGED
|
@@ -48,6 +48,18 @@ export interface NormalizedTextureDefinition {
|
|
|
48
48
|
* Effective V address mode.
|
|
49
49
|
*/
|
|
50
50
|
addressModeV: GPUAddressMode;
|
|
51
|
+
/**
|
|
52
|
+
* Whether this texture is a storage texture (writable by compute).
|
|
53
|
+
*/
|
|
54
|
+
storage: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Explicit width for storage textures. Undefined when derived from source.
|
|
57
|
+
*/
|
|
58
|
+
width?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Explicit height for storage textures. Undefined when derived from source.
|
|
61
|
+
*/
|
|
62
|
+
height?: number;
|
|
51
63
|
}
|
|
52
64
|
/**
|
|
53
65
|
* Validates and returns sorted texture keys.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"textures.d.ts","sourceRoot":"","sources":["../../src/lib/core/textures.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,EACX,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C;;OAEG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B;;OAEG;IACH,MAAM,EAAE,gBAAgB,CAAC;IACzB;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB;;OAEG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAC5B;;OAEG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,MAAM,EAAE,aAAa,CAAC;IACtB;;OAEG;IACH,YAAY,EAAE,cAAc,CAAC;IAC7B;;OAEG;IACH,YAAY,EAAE,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"textures.d.ts","sourceRoot":"","sources":["../../src/lib/core/textures.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,EACX,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,EACZ,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C;;OAEG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;OAEG;IACH,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC9B;;OAEG;IACH,MAAM,EAAE,gBAAgB,CAAC;IACzB;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB;;OAEG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAC5B;;OAEG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,MAAM,EAAE,aAAa,CAAC;IACtB;;OAEG;IACH,YAAY,EAAE,cAAc,CAAC;IAC7B;;OAEG;IACH,YAAY,EAAE,cAAc,CAAC;IAC7B;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAYD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAM3E;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACzC,UAAU,EAAE,iBAAiB,GAAG,SAAS,GACvC,2BAA2B,CA6B7B;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAC1C,QAAQ,EAAE,oBAAoB,EAC9B,WAAW,EAAE,MAAM,EAAE,GACnB,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,CAM7C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,IAAI,WAAW,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,GAAG,IAAI,CAUrE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,WAAW,CAAC,EAAE,iBAAiB,CAAC;CAChC,GAAG,iBAAiB,CAcpB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf,CAkBA;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAY7E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,MAAM,IAAI,gBAAgB,CAE9F"}
|
package/dist/core/textures.js
CHANGED
|
@@ -26,18 +26,23 @@ function resolveTextureKeys(textures) {
|
|
|
26
26
|
* @returns Normalized definition with deterministic defaults.
|
|
27
27
|
*/
|
|
28
28
|
function normalizeTextureDefinition(definition) {
|
|
29
|
+
const isStorage = definition?.storage === true;
|
|
30
|
+
const defaultFormat = definition?.colorSpace === "linear" ? "rgba8unorm" : "rgba8unorm-srgb";
|
|
29
31
|
const normalized = {
|
|
30
32
|
source: definition?.source ?? null,
|
|
31
33
|
colorSpace: definition?.colorSpace ?? "srgb",
|
|
32
|
-
format: definition?.
|
|
34
|
+
format: definition?.format ?? defaultFormat,
|
|
33
35
|
flipY: definition?.flipY ?? true,
|
|
34
36
|
generateMipmaps: definition?.generateMipmaps ?? false,
|
|
35
37
|
premultipliedAlpha: definition?.premultipliedAlpha ?? false,
|
|
36
38
|
anisotropy: Math.max(1, Math.min(16, Math.floor(definition?.anisotropy ?? 1))),
|
|
37
39
|
filter: definition?.filter ?? DEFAULT_TEXTURE_FILTER,
|
|
38
40
|
addressModeU: definition?.addressModeU ?? DEFAULT_TEXTURE_ADDRESS_MODE,
|
|
39
|
-
addressModeV: definition?.addressModeV ?? DEFAULT_TEXTURE_ADDRESS_MODE
|
|
41
|
+
addressModeV: definition?.addressModeV ?? DEFAULT_TEXTURE_ADDRESS_MODE,
|
|
42
|
+
storage: isStorage
|
|
40
43
|
};
|
|
44
|
+
if (definition?.width !== void 0) normalized.width = definition.width;
|
|
45
|
+
if (definition?.height !== void 0) normalized.height = definition.height;
|
|
41
46
|
if (definition?.update !== void 0) normalized.update = definition.update;
|
|
42
47
|
return normalized;
|
|
43
48
|
}
|