@forgeax/engine-shader 0.1.19 → 0.1.21
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 +10 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/ShaderRegistry.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +52 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/ShaderRegistry.ts +65 -0
- package/src/__tests__/bloom-fxaa-tonemap.unit.test.ts +30 -4
- package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +7 -0
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +71 -22
- package/src/__tests__/fog-oracle.unit.test.ts +110 -114
- package/src/__tests__/fog-orthographic-ray.unit.test.ts +12 -37
- package/src/__tests__/fog-producer-matrix.integration.test.ts +16 -51
- package/src/__tests__/lighting-point-spot-volume.unit.test.ts +56 -0
- package/src/__tests__/lighting-punctual.unit.test.ts +32 -6
- package/src/__tests__/lighting-spot-volume-attenuation.unit.test.ts +26 -0
- package/src/__tests__/lighting-spot-volume.unit.test.ts +25 -0
- package/src/__tests__/material-derived-builtins.integration.test.ts +27 -0
- package/src/__tests__/reflection-probe-sampling.unit.test.ts +23 -0
- package/src/__tests__/scene-temporal.unit.test.ts +20 -0
- package/src/__tests__/shader.unit.test.ts +35 -13
- package/src/__tests__/shadow-pcss.unit.test.ts +131 -0
- package/src/__tests__/sprite-lit-shader.test.ts +24 -11
- package/src/__tests__/sprite-variants.unit.test.ts +2 -0
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +7 -1
- package/src/__tests__/transparent-pbr.unit.test.ts +7 -0
- package/src/common.wgsl +72 -73
- package/src/default-standard-pbr-skin.wgsl +31 -89
- package/src/default-standard-pbr.wgsl +94 -146
- package/src/fxaa.wgsl +27 -6
- package/src/hdrp-deferred-lighting.wgsl +3 -27
- package/src/ibl-sampling.wgsl +54 -0
- package/src/index.ts +6 -0
- package/src/lighting-attenuation.wgsl +58 -0
- package/src/lighting-directional.wgsl +188 -21
- package/src/lighting-punctual.wgsl +94 -19
- package/src/msdf-text.wgsl +4 -23
- package/src/scene-temporal.wgsl +14 -8
- package/src/shadow-pcf.wgsl +75 -11
- package/src/skybox.wgsl +2 -7
- package/src/sprite-lit.wgsl +38 -73
- package/src/sprite.wgsl +3 -22
- package/src/{hdrp-cluster-forward.wgsl → standard-cluster.wgsl} +74 -23
- package/src/tonemap.wgsl +8 -3
- package/src/unlit.wgsl +2 -24
- package/src/volume/volume-composite.wgsl +113 -0
- package/src/volume/volume-inject.wgsl +261 -0
- package/src/volume/volume-integrate.wgsl +337 -0
- package/src/volume/volume-temporal.wgsl +70 -0
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/material-schemas.ts","../src/errors.ts","../src/material/artifact-registry.ts","../src/material/artifact-types.ts","../src/register-default-sprite-lit.ts","../src/register-default-standard-pbr-skin.ts","../src/ShaderRegistry.ts","../src/types.ts","../src/tonemap.ts","../src/index.ts"],"names":["ok","err","RESERVED_ID"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ParamSchemaEntry } from '@forgeax/engine-types';\n\nexport const STANDARD_PBR_ALPHA_CUTOFF_DEFAULT = 0;\n\n/** Shared material contract for the standard PBR and skinned PBR shaders. */\nexport const DEFAULT_STANDARD_PBR_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [\n { name: 'baseColor', type: 'color', default: [1, 1, 1, 1] },\n { name: 'metallic', type: 'f32', default: 0 },\n { name: 'roughness', type: 'f32', default: 0.5 },\n { name: 'metallicChannel', type: 'f32', default: 2 },\n { name: 'roughnessChannel', type: 'f32', default: 1 },\n { name: 'aoChannel', type: 'f32', default: 0 },\n { name: 'extraChannel', type: 'f32', default: 0 },\n { name: 'emissive', type: 'vec3', colorSpace: 'srgb', default: [0, 0, 0] },\n { name: 'emissiveIntensity', type: 'f32', default: 0 },\n { name: 'occlusionStrength', type: 'f32', default: 1 },\n { name: 'alphaCutoff', type: 'f32', default: STANDARD_PBR_ALPHA_CUTOFF_DEFAULT },\n { name: 'clearcoat', type: 'f32', default: 0 },\n { name: 'clearcoatRoughness', type: 'f32', default: 0.5 },\n { name: 'specularTint', type: 'vec3', colorSpace: 'srgb', default: [1, 1, 1] },\n { name: 'normalScale', type: 'f32', default: 1 },\n { name: 'transmission', type: 'f32', default: 0 },\n { name: 'ior', type: 'f32', default: 1.5 },\n { name: 'thickness', type: 'f32', default: 0 },\n { name: 'attenuationColor', type: 'vec3', colorSpace: 'linear', default: [1, 1, 1] },\n { name: 'attenuationDistance', type: 'f32' },\n { name: 'baseColorTexture', type: 'texture2d' },\n { name: 'metallicRoughnessTexture', type: 'texture2d' },\n { name: 'normalTexture', type: 'texture2d' },\n { name: 'specularTintTexture', type: 'texture2d' },\n { name: 'emissiveTexture', type: 'texture2d' },\n { name: 'occlusionTexture', type: 'texture2d' },\n { name: 'transmissionTexture', type: 'texture2d' },\n { name: 'thicknessTexture', type: 'texture2d' },\n];\n\nexport const DEFAULT_UNLIT_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [\n { name: 'baseColor', type: 'color', default: [1, 1, 1, 1] },\n { name: 'alphaCutoff', type: 'f32', default: 0 },\n { name: 'baseColorTexture', type: 'texture2d' },\n];\n\nexport const DEFAULT_SPRITE_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [\n { name: 'colorTint', type: 'vec4', colorSpace: 'srgb', default: [1, 1, 1, 1] },\n { name: 'region', type: 'vec4', default: [0, 0, 1, 1] },\n { name: 'pivotAndSize', type: 'vec4', default: [0.5, 0.5, 1, 1] },\n { name: 'slicesAndMode', type: 'vec4', default: [0, 0, 0, 0] },\n { name: 'baseColorTexture', type: 'texture2d' },\n];\n\nexport const DEFAULT_MSDF_TEXT_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [\n { name: 'tintColor', type: 'color', default: [1, 1, 1, 1] },\n { name: 'distanceRange', type: 'vec4', default: [4, 512, 512, 0] },\n { name: 'baseColorTexture', type: 'texture2d' },\n];\n","// @forgeax/engine-shader/errors — ShaderError + factories (physically isolated from the\n// same-named class in @forgeax/engine-shader-compiler, but shape-aligned 1:1 / charter\n// proposition 5: consistent abstraction).\n//\n// Shape rules (plan-strategy §S-7 / D-R7 / OQ-2 close):\n// - ShaderError extends Error, with the 5-field top-level projection\n// {code, lineNum, linePos, message, hint} (MVP-2.3) + 3 internal fields\n// {expected, detail, name}.\n// - ShaderErrorCode is a closed 4-member union imported from @forgeax/engine-types\n// (SSOT single-source policy).\n//\n// Physical isolation — this file does **not** import from\n// @forgeax/engine-shader-compiler; the type / factories are 1:1 mirrored but\n// independently implemented (guarded by the AC-06 triple-grep gate; charter\n// proposition 1: progressive disclosure).\n\nimport type { ShaderErrorCode } from '@forgeax/engine-types';\n\nexport type { ShaderErrorCode };\n\n/**\n * Path-specific `detail` shape for `manifest-malformed` / `shader-not-found`.\n * `reason` is supplemental prose (it does not replace `hint`).\n */\nexport interface ShaderErrorDetail {\n readonly reason?: string | undefined;\n /**\n * Machine-consumable list of currently-registered material-shader identifiers.\n * Populated by `materialShaderNotFound()` factory so AI users can enumerate\n * available shaders via `err.detail.registeredShaderIds` for autocomplete /\n * fuzzy-match recovery without parsing the human-formatted `err.expected`\n * string (charter P3 structured failure). `err.expected` still carries the\n * pre-formatted `\"ShaderRegistry has registered identifiers: [...]\"` message\n * for human-readable log output — the two channels are complementary.\n */\n readonly registeredShaderIds?: readonly string[] | undefined;\n}\n\ninterface ShaderErrorInit {\n readonly code: ShaderErrorCode;\n readonly expected: string;\n readonly hint: string;\n readonly message: string;\n readonly lineNum?: number | undefined;\n readonly linePos?: number | undefined;\n readonly detail?: ShaderErrorDetail | undefined;\n}\n\n/**\n * Structured shader error (runtime path, aligned with build-time\n * `@forgeax/engine-shader-compiler.ShaderError`).\n *\n * **5 surface fields** (MVP-2.3 top-level projection, AI-user consumption path):\n * - `.code` — member of the closed `ShaderErrorCode` union (4 variants)\n * - `.message` — display text (`Error` base-class field, populated at construction)\n * - `.hint` — actionable recovery guidance (charter proposition 3:\n * machine-readable hint > prose)\n * - `.lineNum` / `.linePos` — error source location (undefined on the runtime\n * path; only populated by the build-time compile-failed path; the field is\n * retained here for shape alignment).\n *\n * **3 internal fields**:\n * - `.name` = `'ShaderError'` (debug label)\n * - `.expected` — expected-state description (symmetric with RhiError)\n * - `.detail` — path-specific extras (`reason` prose)\n *\n * **Do not `new` this directly** — construct via the 2 factory helpers\n * (`manifestMalformed` / `shaderNotFound`); the runtime path of this loop does\n * not trigger compile-failed / init-failed (those two paths belong to the\n * build-time `ShaderError` shape in `@forgeax/engine-shader-compiler`).\n */\nexport class ShaderError extends Error {\n override readonly name: 'ShaderError' = 'ShaderError';\n readonly code: ShaderErrorCode;\n readonly expected: string;\n readonly hint: string;\n readonly lineNum: number | undefined;\n readonly linePos: number | undefined;\n readonly detail: ShaderErrorDetail | undefined;\n\n constructor(init: ShaderErrorInit) {\n super(init.message);\n this.code = init.code;\n this.expected = init.expected;\n this.hint = init.hint;\n this.lineNum = init.lineNum;\n this.linePos = init.linePos;\n this.detail = init.detail;\n }\n}\n\n// === factory helpers (2 variants for the runtime path) ============================\n\n/** `manifest-malformed`: manifest.json schema validation failed (4 fields missing or JSON unparseable). */\nexport function manifestMalformed(args: {\n readonly message: string;\n readonly hint: string;\n readonly reason?: string | undefined;\n}): ShaderError {\n return new ShaderError({\n code: 'manifest-malformed',\n expected: 'manifest.json parses + every entry has {hash, wgsl, glsl, bindings}',\n message: args.message,\n hint: args.hint,\n ...(args.reason !== undefined ? { detail: { reason: args.reason } } : {}),\n });\n}\n\n/** `shader-not-found`: ShaderRegistry.get(hash) hash miss. */\nexport function shaderNotFound(args: {\n readonly hash: string;\n readonly hint: string;\n}): ShaderError {\n return new ShaderError({\n code: 'shader-not-found',\n expected: `manifest.entries contains entry with hash '${args.hash}'`,\n message: `ShaderRegistry: hash '${args.hash}' not present in manifest`,\n hint: args.hint,\n });\n}\n\n/**\n * `material-shader-not-found`: `ShaderRegistry.findMaterialArtifact(identifier)`\n * miss. Returned by the runtime path when a `MaterialAsset.payload.materialShader`\n * `::`-routed identifier (or any registered identifier) has no matching entry\n * in the registry. AI-user recovery: the `.hint` cites the\n * `forgeax::default-standard-pbr` reservation + the\n * `installMaterialArtifact(...)` API path so the caller can register the missing\n * shader at engine boot (feat-20260523-shader-template-instance-split M5 / T05).\n *\n * feat-20260528-material-shader-registration-unification M3 / w12 +\n * feat-20260624-sprite-lit F-P1 fix: dual-channel exposure of the registered\n * identifier set. `.expected` carries the pre-formatted string\n * `\"ShaderRegistry has registered identifiers: [id1, id2, ...]\"` for human /\n * log output; `.detail.registeredShaderIds` carries the raw `readonly string[]`\n * for AI users to enumerate via property access without regex-parsing the\n * message (charter P3 structured failure).\n */\nexport function materialShaderNotFound(args: {\n readonly identifier: string;\n readonly hint: string;\n readonly expected: string[];\n}): ShaderError {\n return new ShaderError({\n code: 'material-shader-not-found',\n expected: `ShaderRegistry has registered identifiers: [${args.expected.join(', ')}]`,\n message: `ShaderRegistry: material shader identifier '${args.identifier}' not registered`,\n hint: args.hint,\n detail: { reason: `identifier=${args.identifier}`, registeredShaderIds: args.expected },\n });\n}\n\n// === Result<T, E> ====================================================================\n//\n// Round 3 fix-up F-P0-1: Result is re-exported from @forgeax/engine-rhi so the\n// `.unwrap()` / `.unwrapOr()` method-chain surface (AGENTS.md §Adapter /\n// Device IDE-friendly template + 50+ JSDoc @example anchors) is identical\n// across the @forgeax/engine-rhi + @forgeax/engine-shader public surfaces (charter\n// proposition 5: consistent abstraction). The previous local duplicate\n// (plain field-only Result) caused TS structural-mismatch when ShaderRegistry\n// values flowed into RHI-typed signatures.\n\nexport {\n err,\n ok,\n type Result,\n type ResultErr,\n type ResultOk,\n} from '@forgeax/engine-rhi';\n","import { err, ok, type Result } from '@forgeax/engine-types';\n\nexport interface MaterialRuntimeArtifact {\n readonly key: string;\n readonly bytes: Uint8Array;\n readonly digest?: string;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\nexport interface MaterialArtifactInspection {\n readonly key: string;\n readonly digest?: string;\n readonly byteLength: number;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\nexport interface MaterialArtifactConflictError {\n readonly code: 'material-artifact-conflict';\n readonly expected: string;\n readonly hint: string;\n readonly detail: {\n readonly key: string;\n readonly existingDigest?: string;\n readonly incomingDigest?: string;\n };\n}\n\nfunction equalBytes(left: Uint8Array, right: Uint8Array): boolean {\n return left.length === right.length && left.every((value, index) => value === right[index]);\n}\n\nexport class MaterialArtifactRegistry {\n readonly #artifacts = new Map<string, MaterialRuntimeArtifact>();\n\n register(\n artifact: MaterialRuntimeArtifact,\n ): Result<MaterialRuntimeArtifact, MaterialArtifactConflictError> {\n const previous = this.#artifacts.get(artifact.key);\n if (previous !== undefined) {\n if (equalBytes(previous.bytes, artifact.bytes)) return ok(previous);\n return err({\n code: 'material-artifact-conflict',\n expected: 'one immutable artifact byte sequence per specialization key',\n hint: 're-cook the conflicting specialization and publish one artifact digest',\n detail: {\n key: artifact.key,\n ...(previous.digest ? { existingDigest: previous.digest } : {}),\n ...(artifact.digest ? { incomingDigest: artifact.digest } : {}),\n },\n });\n }\n const immutable = Object.freeze(artifact);\n this.#artifacts.set(artifact.key, immutable);\n return ok(immutable);\n }\n\n get(key: string): MaterialRuntimeArtifact | undefined {\n return this.#artifacts.get(key);\n }\n\n inspect(key: string): MaterialArtifactInspection | undefined {\n const artifact = this.#artifacts.get(key);\n if (artifact === undefined) return undefined;\n return {\n key: artifact.key,\n ...(artifact.digest === undefined ? {} : { digest: artifact.digest }),\n byteLength: artifact.bytes.byteLength,\n ...(artifact.metadata === undefined ? {} : { metadata: artifact.metadata }),\n };\n }\n}\n","import type { BindGroupLayoutDescriptor } from '@forgeax/engine-types';\n\nexport interface MaterialShaderArtifact {\n readonly material: string;\n readonly pass: string;\n readonly wgsl: string;\n readonly layoutIdentity: string;\n readonly bindings: readonly BindGroupLayoutDescriptor[];\n readonly deps: readonly string[];\n readonly vertexInputs: readonly Readonly<Record<string, unknown>>[];\n readonly specializationKey?: string;\n}\n\nexport function isMaterialShaderArtifact(value: unknown): value is MaterialShaderArtifact {\n if (value === null || typeof value !== 'object') return false;\n const artifact = value as Partial<MaterialShaderArtifact>;\n return (\n typeof artifact.material === 'string' &&\n typeof artifact.pass === 'string' &&\n typeof artifact.wgsl === 'string' &&\n typeof artifact.layoutIdentity === 'string' &&\n Array.isArray(artifact.bindings) &&\n Array.isArray(artifact.deps) &&\n Array.isArray(artifact.vertexInputs)\n );\n}\n","// @forgeax/engine-shader - registerDefaultSpriteLit helper.\n//\n// feat-20260624-sprite-lit-shading-model-pure-2d-lighting M1' / w4\n// (plan-strategy D-10 application registration path).\n//\n// Mirrors registerDefaultStandardPbrSkin (same package; identical\n// registration shape). paramSchema mirrors sprite.material.json minus\n// PBR-only fields; the BGL is derived from paramSchema via\n// `derive(paramSchema).bglEntries` inside the runtime pipeline-builder.\n//\n// Anchors:\n// - requirements section 2 #2 / #10 (sprite-lit.wgsl new + 4 BGL byte-identical)\n// - plan-strategy D-3 (sprite-lit single Half-Lambert formula lock)\n// - plan-strategy D-5 (STORAGE_BUFFER_AVAILABLE variant axis day-1)\n// - plan-strategy D-6 (4 PBR-unused slots default sampler + white tex)\n// - plan-strategy D-10 (application registration path)\n\nimport type { ShaderRegistry } from './index.js';\nimport { DEFAULT_SPRITE_PARAM_SCHEMA } from './material-schemas.js';\n\n/**\n * paramSchema for forgeax::sprite-lit -- mirrors sprite.material.json\n * exactly (same 5 fields: colorTint / region / pivotAndSize /\n * slicesAndMode / baseColorTexture). sprite-lit and sprite share the\n * same Material UBO layout byte-for-byte so the host-side\n * SpriteMaterialAsset writer + 4 BGL JSON are byte-identical (AC-07).\n *\n * OOS-1 (feat-future-sprite-lit-normal-map) will extend this with\n * normalTexture + normalStrength. M1' deliberately keeps the schema\n * byte-identical to sprite so AC-13 \"1 string-change between sprite\n * and sprite-lit materials\" is provably the only delta required.\n */\nconst RESERVED_ID = 'forgeax::sprite-lit' as const;\n\n/**\n * Caps shape exposed for sprite-lit registration. Currently only\n * `storageBuffer` matters (STORAGE_BUFFER_AVAILABLE variant axis); the\n * per-shader BGL is derived from paramSchema at pipeline-build time, so\n * the runtime layout factory consumes this caps shape downstream.\n *\n * Kept as a separate type rather than reusing `SkinCaps` to preserve\n * a clear contract surface; the structural shape is currently identical\n * but future divergence (sprite-lit may opt in to OOS-7 cluster-forward\n * later) should not silently leak via a shared type.\n */\nexport interface SpriteLitCaps {\n readonly storageBuffer: boolean;\n}\n\n/**\n * Register the engine-shipped `forgeax::sprite-lit` material shader in\n * the ShaderRegistry. Must be called once at engine boot (createRenderer),\n * after `ShaderRegistry.loadManifest()` has completed.\n *\n * The `composedWgsl` parameter is the post-naga_oil composed WGSL source,\n * compiled by @forgeax/engine-vite-plugin-shader at build time and surfaced\n * in manifest.json. The caller reads it from the manifest entries.\n *\n * `caps.storageBuffer` switches the runtime BGL buffer types for\n * @group(0) bindings 1+2 (pointLightsBuffer / spotLightsBuffer):\n * `false` -> uniform fallback (AC-10 WebGL2 path); `true` -> read-only-storage.\n * The BGL itself is built downstream in runtime/pbr-pipeline.ts (sprite-lit\n * reuses pbr-view / pbr-mesh-array / pbr-instances unchanged; AC-07\n * BGL byte-identical).\n *\n * @param registry ShaderRegistry instance (engine boot path).\n * @param composedWgsl Post-naga_oil composed WGSL source string.\n * @param caps sprite-lit caps (storageBuffer boolean) for BGL entry type selection.\n *\n * Throws if the identifier is already registered (fail-fast per\n * ShaderRegistry.installMaterialArtifact contract).\n */\nexport function registerDefaultSpriteLit(\n registry: ShaderRegistry,\n composedWgsl: string,\n caps: SpriteLitCaps,\n): void {\n // The caps argument is retained for API symmetry with\n // registerDefaultStandardPbrSkin -- equivalent caps gating lives in\n // buildPbrPipelineLayouts since the per-shader BGL is paramSchema-derived\n // and the storage-vs-uniform branching is a runtime-level concern.\n void caps;\n registry.installMaterialArtifact(RESERVED_ID, {\n source: composedWgsl,\n paramSchema: DEFAULT_SPRITE_PARAM_SCHEMA,\n });\n}\n","// @forgeax/engine-shader - registerDefaultStandardPbrSkin helper.\n//\n// feat-20260523-skin-skeleton-animation M3 / T-30; updated by\n// feat-20260613-material-paramschema-driven-binding M3 / w13: the\n// bindingLayout sidecar field has been removed from MaterialShaderEntry;\n// the BGL is derived from paramSchema via `derive(paramSchema).bglEntries`\n// inside the runtime pipeline-builder. paramSchema remains shared with\n// default-standard-pbr (identical PBR material interface).\n//\n// Anchors:\n// - plan-strategy D-3: register independent WGSL template for skin variant\n// - plan-strategy D-1 / D-2: paramSchema is the single source of truth\n// - R-10: paramSchema reuses default-standard-pbr schema (same params)\n\nimport type { ShaderRegistry } from './index.js';\nimport { DEFAULT_STANDARD_PBR_PARAM_SCHEMA } from './material-schemas.js';\n\n/**\n * paramSchema for forgeax::pbr-skin — mirrors default-standard-pbr 8 fields.\n * Previously sourced from default-standard-pbr.schema.json; now inlined because\n * the .schema.json file has been deleted (feat-20260528-material-shader-registration-unification M1/w4).\n * The SSOT is default-standard-pbr.material.json paramSchema[].\n */\nconst RESERVED_ID = 'forgeax::pbr-skin' as const;\n\n/**\n * Caps shape historically consumed by the pbr-skin BGL builder. The\n * BGL builder itself moved to runtime/pbr-pipeline.ts (buildPbrSkinLayouts);\n * this type is preserved as the public registration-API contract so\n * existing callers compile unchanged. M3 / w13: per-shader BGL is\n * derived from paramSchema; the storage-buffer-vs-uniform branching\n * the BGL needs lives entirely in the runtime layout factory.\n */\nexport interface SkinCaps {\n readonly storageBuffer: boolean;\n}\n\n/**\n * Register the engine-shipped `forgeax::pbr-skin` material shader in the\n * ShaderRegistry. Must be called once at engine boot (createRenderer), after\n * `ShaderRegistry.loadManifest()` has completed.\n *\n * The `composedWgsl` parameter is the post-naga_oil composed WGSL source,\n * compiled by @forgeax/engine-vite-plugin-shader at build time and surfaced\n * in manifest.json. The caller reads it from the manifest entries.\n *\n * `caps.storageBuffer` switches the runtime BGL buffer types:\n * `false` -> uniform fallback; `true` -> read-only-storage. The BGL itself\n * is built downstream in runtime/pbr-pipeline.ts (M3 / w13).\n *\n * @param registry ShaderRegistry instance (engine boot path).\n * @param composedWgsl Post-naga_oil composed WGSL source string.\n * @param caps PBR caps (storageBuffer boolean) for BGL entry type selection.\n *\n * Throws if the identifier is already registered (fail-fast per\n * ShaderRegistry.installMaterialArtifact contract).\n */\nexport function registerDefaultStandardPbrSkin(\n registry: ShaderRegistry,\n composedWgsl: string,\n caps: SkinCaps,\n): void {\n // M3 / w13 (D-1 / D-2): bindingLayout sidecar deleted from\n // MaterialShaderEntry; paramSchema is the SSOT and downstream\n // pipeline-builder reads `derive(paramSchema).bglEntries` on demand.\n // The `caps` argument is retained for API compat — historically it\n // selected the storage-buffer vs uniform-buffer skin variant of the\n // BGL; equivalent caps gating now lives in buildPbrSkinLayouts.\n void caps;\n registry.installMaterialArtifact(RESERVED_ID, {\n source: composedWgsl,\n paramSchema: DEFAULT_STANDARD_PBR_PARAM_SCHEMA,\n });\n}\n","// @forgeax/engine-shader/ShaderRegistry — runtime shader registry, instance-per-engine shape.\n//\n// Shape rules (plan-strategy §S-10 / D-R10 / OQ-5 close + AC-03):\n// - instance-per-engine — must be created via\n// `new ShaderRegistry({device, manifestUrl})`; module-level singletons\n// (`export const registry = ...`) and static methods (`static get`) are\n// forbidden — aligned with the instance-based style of\n// `Engine.create({ rhi })`.\n// - Physical isolation — this file does **not** import @forgeax/engine-shader-compiler /\n// @forgeax/engine-naga / @forgeax/engine-wgpu-wasm (guarded by the AC-06 triple-grep gate;\n// feat-20260511-naga-rhi-wgpu-merge M4 replaced the legacy single-shim ban\n// with the merged ban list).\n// - Result model — `loadManifest()` / `get()` always go through Result.ok /\n// Result.err and **never throw** (AGENTS.md \"Errors are structured\" / charter\n// proposition 4: explicit failure).\n// - 9-member error union — `get()` returns\n// `Result<ShaderModule, RhiError | ShaderError>` (AGENTS.md \"RHI / Shader /\n// error-model contract\").\n\nimport type { Result, RhiError, ShaderModule } from '@forgeax/engine-rhi';\nimport type {\n ImmutableParamSchemaProjection,\n ManifestEntry,\n ParamSchemaEntry,\n ParamSchemaProjectionOwnerStats,\n} from '@forgeax/engine-types';\nimport { findUndeclaredSampledTextures, ParamSchemaProjectionOwner } from '@forgeax/engine-types';\nimport {\n err,\n manifestMalformed,\n materialShaderNotFound,\n ok,\n type ShaderError,\n type Result as ShaderResult,\n shaderNotFound,\n} from './errors.js';\nimport type { MaterialShaderManifestEntry } from './types.js';\n\n// ─── Device dependency-injection interface ──────────────────────────────────────\n\n/**\n * Device dependency-injection interface — ShaderRegistry does not directly\n * depend on `@forgeax/engine-rhi-webgpu` (physical isolation); the caller (engine or\n * tests) supplies a device-like object that implements\n * `createShaderModule(desc) → Result<ShaderModule, RhiError>`.\n *\n * Difference from the top-level async `createShaderModule(device, desc)` in\n * `@forgeax/engine-rhi-webgpu`:\n * - This interface is synchronous — so `registry.get()` can return a cached\n * module synchronously.\n * - The caller is responsible for wrapping the async real path into a sync one\n * (e.g. the engine pre-compiles and caches modules during `loadManifest`).\n *\n * Mock test shape: a direct sync implementation (see `createMockDevice` in\n * src/__tests__/registry.test.ts).\n */\nexport interface ShaderRegistryDevice {\n createShaderModule(desc: {\n readonly code: string;\n readonly label?: string | undefined;\n }): Result<ShaderModule, RhiError>;\n}\n\n// ─── MaterialShader registry types (feat-20260523-shader-template-instance-split M5) ──\n\n/**\n * The `forgeax::` prefix reserves engine-shipped material shader identifiers\n * (plan-strategy D-DefaultStandardPbr-Identifier + §8 charter F1 grep gate).\n * AI users grep `forgeax::` to enumerate every reserved shader identifier in\n * one shot. Custom user-side shaders pick their own identifier path\n * (typically `<package>::<id>`) — anything that is not a `<guid>`\n * (UUIDv5/v7) shape and not `forgeax::*`.\n */\nexport const FORGEAX_RESERVED_PATH_PREFIX = 'forgeax::' as const;\n\n/**\n * Material shader admission input accepted by\n * `installMaterialArtifact(identifier, entry)`. `findMaterialArtifact`\n * returns a {@link RegisteredMaterialShaderEntry} with the immutable schema\n * projection attached.\n *\n * - `source`: composed WGSL source (post-naga_oil); the final form fed to\n * `device.createShaderModule({ code })` at pipeline-build time.\n * - `paramSchema`: closed list of `{name, type, default?}` triples that the\n * `MaterialAsset.payload.values` is validated against at register-time\n * (feat-20260523-shader-template-instance-split M4 3-tier validation). Per\n * feat-20260613-material-paramschema-driven-binding M3 / w12-w13, the\n * paramSchema is also the SSOT for the pipeline BGL via\n * `derive(paramSchema).bglEntries` — there is no longer a separate\n * `bindingLayout` field on this entry (D-1 / D-2).\n *\n * The shape is symmetric across engine-default + user-custom paths\n * (charter P4 consistent abstraction): both go through the same\n * `installMaterialArtifact` API regardless of whether the host wires the\n * default-standard-pbr triple at boot or the user registers a custom\n * shader after asset import.\n */\nexport interface MaterialShaderEntry {\n readonly source: string;\n readonly paramSchema: readonly ParamSchemaEntry[];\n}\n\n/** Registry-owned runtime shape published after immutable schema admission. */\nexport interface RegisteredMaterialShaderEntry extends MaterialShaderEntry {\n readonly paramSchemaProjection: ImmutableParamSchemaProjection;\n}\n\n// ─── Public registry types ──────────────────────────────────────────────────────\n\nexport interface ShaderRegistryOptions {\n readonly device: ShaderRegistryDevice;\n /**\n * URL the registry fetches `manifest.json` from. `undefined` puts the\n * registry into the **zero-entry** mode: `loadManifest()` resolves\n * `Result.ok(undefined)` without issuing a fetch and `entries()` returns\n * an empty iterator. This is the bug-20260519 clear-pass-only path\n * where AI users build a `Camera`-only world (no PBR / unlit pipeline\n * is ever needed). `createRenderer.ts` Step 2 narrows on\n * `manifestEntries.length > 0` to skip the dual `createShaderModule`\n * compile in the same path (charter P3 + plan-strategy D-1 + D-2).\n */\n readonly manifestUrl: string | undefined;\n}\n\n// ─── ShaderRegistry main class (instance-per-engine) ============================\n\n/**\n * Runtime shader registry — content-addressable manifest lookup →\n * `device.createShaderModule`.\n *\n * Usage:\n * ```\n * const registry = new ShaderRegistry({ device, manifestUrl: '/shaders/manifest.json' });\n * const loaded = await registry.loadManifest();\n * if (!loaded.ok) handleError(loaded.error); // ShaderError.manifest-malformed\n *\n * const result = registry.get('abc12345');\n * if (!result.ok) handleError(result.error); // RhiError | ShaderError.shader-not-found\n * else useModule(result.value);\n * ```\n *\n * `static get` / module-level singletons are **forbidden** (plan-strategy\n * §S-10).\n */\nexport class ShaderRegistry {\n readonly #device: ShaderRegistryDevice;\n readonly #manifestUrl: string | undefined;\n // hash → ManifestEntry index (populated after loadManifest).\n readonly #entries = new Map<string, ManifestEntry>();\n // hash → ShaderModule cache (populated lazily on first get()).\n readonly #moduleCache = new Map<string, ShaderModule>();\n // identifier → MaterialShaderEntry index (populated by\n // installMaterialArtifact; feat-20260523-shader-template-instance-split M5 / T05).\n readonly #materialShaders = new Map<string, RegisteredMaterialShaderEntry>();\n readonly #paramSchemaProjectionOwner = new ParamSchemaProjectionOwner();\n readonly #materialShaderManifestEntries: MaterialShaderManifestEntry[] = [];\n #manifestLoaded = false;\n\n constructor(opts: ShaderRegistryOptions) {\n this.#device = opts.device;\n this.#manifestUrl = opts.manifestUrl;\n }\n\n /**\n * Loads manifest.json and populates the hash → ManifestEntry index.\n *\n * Failure paths (charter proposition 4: explicit failure):\n * - fetch / data: URL parse failed → `Result.err(ShaderError.manifest-malformed)`\n * - JSON.parse failed → ditto\n * - schema missing the `entries` field / an entries element missing\n * hash/wgsl/bindings → ditto\n * - a material shader row is incomplete → ditto\n *\n * Validation is atomic: the registry does not publish any entry or\n * material shader row until the complete document has been validated.\n *\n * Idempotent: subsequent calls reuse the first result (never re-fetch;\n * charter proposition 6: idempotency).\n */\n async loadManifest(): Promise<ShaderResult<void, ShaderError>> {\n if (this.#manifestLoaded) return ok(undefined);\n\n // bug-20260519 D-2: zero-entry mode. `manifestUrl === undefined` means\n // the host opted out of shipping a shader manifest (Camera-only /\n // clear-pass-only LO 1.1 path); resolve `Result.ok` without issuing a\n // fetch. `entries()` then yields nothing and createRenderer Step 2\n // skips the dual `createShaderModule` compile (D-1 + D-3 nullable\n // PipelineState). Subsequent `get(hash)` calls fall through to the\n // shader-not-found arm via the empty entries map (charter P3\n // explicit failure on misuse).\n if (this.#manifestUrl === undefined) {\n this.#manifestLoaded = true;\n return ok(undefined);\n }\n\n let raw: string;\n try {\n const response = await fetch(this.#manifestUrl);\n raw = await response.text();\n } catch (e) {\n return err(\n manifestMalformed({\n message: `ShaderRegistry: failed to fetch manifest at ${this.#manifestUrl}`,\n hint: 'verify manifest URL is reachable; check bundler emitFile output path',\n reason: e instanceof Error ? e.message : String(e),\n }),\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest JSON parse failed',\n hint: 'manifest.json must be valid JSON; rebuild via @forgeax/engine-vite-plugin-shader generateBundle',\n reason: e instanceof Error ? e.message : String(e),\n }),\n );\n }\n\n if (typeof parsed !== 'object' || parsed === null || !('entries' in parsed)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest missing required `entries` field',\n hint: 'manifest schema requires top-level `entries: ManifestEntry[]`',\n }),\n );\n }\n\n const entries = (parsed as { entries: unknown }).entries;\n if (!Array.isArray(entries)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest.entries is not an array',\n hint: 'manifest schema requires `entries: ManifestEntry[]`',\n }),\n );\n }\n\n const validatedEntries: ManifestEntry[] = [];\n for (const entry of entries) {\n if (!isValidManifestEntry(entry)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest entry missing required fields',\n hint: 'every entry needs {hash, wgsl, glsl, bindings} per @forgeax/engine-types.ManifestEntry',\n reason: `bad entry: ${JSON.stringify(entry)}`,\n }),\n );\n }\n validatedEntries.push(entry);\n }\n\n // feat-20260526-pbr-uniform-fallback-no-storage-buffer M3 / w12:\n // parse and store materialShaders[] from the manifest for\n // createRenderer variant resolution. Non-existent / non-array\n // materialShaders is treated as empty (backward-compatible with\n // manifests from pre-M2 build runs).\n const parsedSlim = parsed as { materialShaders?: unknown };\n const validatedMaterialShaderEntries: MaterialShaderManifestEntry[] = [];\n if (Array.isArray(parsedSlim.materialShaders)) {\n const materialShaderIdentifiers = new Set<string>();\n for (const ms of parsedSlim.materialShaders) {\n if (!isValidMaterialShaderManifestEntry(ms)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest material shader entry missing required fields',\n hint: 'every material shader entry needs {identifier, sourcePath, composedWgsl, paramSchema, variants}',\n reason: `bad material shader entry: ${JSON.stringify(ms)}`,\n }),\n );\n }\n if (materialShaderIdentifiers.has(ms.identifier)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest contains duplicate material shader identifiers',\n hint: 'materialShaders[] must contain one row per identifier',\n reason: `duplicate material shader identifier: ${ms.identifier}`,\n }),\n );\n }\n materialShaderIdentifiers.add(ms.identifier);\n const variantKeys = new Set<string>();\n for (const variant of ms.variants) {\n const canonicalKey = buildMaterialShaderVariantKey(variant.defines);\n if (variant.definesKey !== canonicalKey || variantKeys.has(variant.definesKey)) {\n return err(\n manifestMalformed({\n message:\n 'ShaderRegistry: material shader variants contain a duplicate or non-canonical key',\n hint: 'each variant definesKey must be the exact sorted key derived from defines',\n reason: `invalid material shader variant key '${variant.definesKey}' for ${ms.identifier}`,\n }),\n );\n }\n variantKeys.add(variant.definesKey);\n }\n if (ms.identifier === 'forgeax::default-standard-pbr') {\n const transmissionStates = new Set(\n ms.variants\n .filter((variant) => 'TRANSMISSION_AVAILABLE' in variant.defines)\n .map((variant) => variant.defines.TRANSMISSION_AVAILABLE),\n );\n if (!transmissionStates.has(false) || !transmissionStates.has(true)) {\n return err(\n manifestMalformed({\n message:\n 'ShaderRegistry: Standard material shader is missing a transmission variant state',\n hint: 'forgeax::default-standard-pbr must declare TRANSMISSION_AVAILABLE=false and true',\n reason: `declared transmission states: ${JSON.stringify([...transmissionStates])}`,\n }),\n );\n }\n }\n validatedMaterialShaderEntries.push(ms);\n }\n }\n\n this.#entries.clear();\n for (const entry of validatedEntries) {\n this.#entries.set(entry.hash, entry);\n }\n this.#materialShaderManifestEntries.length = 0;\n this.#materialShaderManifestEntries.push(...validatedMaterialShaderEntries);\n this.#manifestLoaded = true;\n return ok(undefined);\n }\n\n /**\n * Iterate every `ManifestEntry` populated by the most recent successful\n * `loadManifest()` call. Order matches the manifest JSON `entries` array\n * (insertion order; charter P2 structured-over-prose: AI users walk this\n * to discover engine-shipped entries by content marker without\n * re-fetching the manifest).\n *\n * Returns an empty iterator when no manifest has been loaded yet\n * (charter P3 explicit failure: the empty case is not an error — the\n * absent SSOT is the absent contract).\n */\n entries(): IterableIterator<ManifestEntry> {\n return this.#entries.values();\n }\n\n /**\n * Looks up a hash and forwards to `device.createShaderModule` (creates lazily\n * on first hit, then caches).\n *\n * Error paths:\n * - hash miss → `Result.err(ShaderError.shader-not-found)`\n * - device.createShaderModule failed → `Result.err(RhiError)` passes through\n * (it is **not** wrapped as a ShaderError).\n *\n * Sync shape: on cache miss, the underlying `device.createShaderModule` is\n * called synchronously; the caller is responsible for handling the async\n * real path at the device-wrapper layer (e.g. the engine pre-compiles\n * during `loadManifest`).\n */\n get(hash: string): Result<ShaderModule, RhiError | ShaderError> {\n const cachedModule = this.#moduleCache.get(hash);\n if (cachedModule !== undefined) {\n return ok(cachedModule);\n }\n\n const entry = this.#entries.get(hash);\n if (entry === undefined) {\n return err(\n shaderNotFound({\n hash,\n hint: 'verify manifest.json contains the expected hash; rerun build to regenerate manifest',\n }),\n );\n }\n\n const result = this.#device.createShaderModule({ code: entry.wgsl, label: hash });\n if (!result.ok) {\n return result;\n }\n this.#moduleCache.set(hash, result.value);\n return result;\n }\n\n // ─── MaterialShader registry surface ───────────────────────────────────────\n // (feat-20260523-shader-template-instance-split M5 / T05 — plan-strategy\n // D-DefaultStandardPbr-Identifier).\n //\n // Two-tier identifier namespace:\n // - `forgeax::*` — engine-shipped reserved prefix; the host wires\n // `forgeax::default-standard-pbr` at engine boot from the\n // vite-plugin-shader manifest's `materialShaders[]` row + the\n // `default-standard-pbr.schema.json` SSOT (M5 / T08 + M6).\n // - any other identifier — user-side custom material shader, registered\n // after asset import.\n //\n // The registry is the runtime SSOT consumed by:\n // - `parseAssetPayload`'s `'::' two-way dispatch` (M4 / T01) — the path\n // branch validates against `paramSchema` and feeds `MaterialSnapshot.\n // materialShaderId` (M4 / T05).\n // - `render-system-record`'s pipeline cache key (M4 / T06) — same\n // identifier feeds the per-pipeline `(materialShaderId, stateHash)` key.\n\n /**\n * Register a material shader entry under a stable identifier.\n *\n * **Throws** on:\n * - duplicate registration of the same identifier (programmer error per\n * AGENTS.md \"explicit registration\" + Inspector \"fail-fast no overwrite\"\n * pattern; charter P3 explicit failure on misuse).\n *\n * Successful path returns void — the registry mutates in-place. Use\n * {@link findMaterialArtifact} to read back.\n *\n * Identifier conventions (charter F1 / plan-strategy §8 §2):\n * - `forgeax::<kebab-case>` — engine-shipped reserved prefix; the host\n * registers `forgeax::default-standard-pbr` at boot.\n * - `<package>::<id>` or `<guid>` — user-side custom shaders.\n *\n * @example\n * ```ts\n * const registry = new ShaderRegistry({ device, manifestUrl });\n * registry.installMaterialArtifact('forgeax::default-standard-pbr', {\n * source: composedWgsl,\n * paramSchema: defaultStandardPbrSchema,\n * });\n * ```\n */\n installMaterialArtifact(identifier: string, entry: MaterialShaderEntry): void {\n if (this.#materialShaders.has(identifier)) {\n throw new Error(\n `ShaderRegistry: material shader identifier '${identifier}' already registered; same-name re-register is forbidden (AGENTS.md \"explicit registration\" + Inspector \"fail-fast no overwrite\" pattern). Use findMaterialArtifact to read back the existing entry.`,\n );\n }\n // bug-20260619: user shaders registered directly here bypass the build-time\n // superset gate (vite-plugin-shader's WGSL reflection). A schema that omits\n // a texture field the WGSL actually samples would let the extract stage's\n // `validateTextureHandle` silently drop the handle and fall back to the\n // default white texture (opaque-white grass/windows in the LO 4.3 blending\n // demo). Fail fast at register time instead (charter P3 explicit failure).\n // Scoped to user shaders: engine `forgeax::*` shaders go through the\n // build-time gate and may sample engine-injected textures (emissive /\n // occlusion) absent from their schema by design.\n const paramSchemaProjection = this.#paramSchemaProjectionOwner.admit({\n ownerId: identifier,\n revision: 1,\n schema: entry.paramSchema,\n });\n if (!identifier.startsWith(FORGEAX_RESERVED_PATH_PREFIX)) {\n const undeclared = findUndeclaredSampledTextures(entry.source, paramSchemaProjection.schema);\n if (undeclared.length > 0) {\n throw new Error(\n `ShaderRegistry: material shader '${identifier}' samples texture(s) [${undeclared.join(', ')}] in its WGSL but its paramSchema does not declare them as texture entries. Add { name: '${undeclared[0]}', type: 'texture2d' } (and any others listed) to the paramSchema, or the engine would silently bind the default white texture (charter P3 explicit failure; see docs/handover/2026-06-19-blending-transparency-regression-bisect.md).`,\n );\n }\n }\n this.#materialShaders.set(\n identifier,\n Object.freeze({\n source: entry.source,\n paramSchema: paramSchemaProjection.schema,\n paramSchemaProjection,\n }),\n );\n }\n\n /**\n * Lookup a previously-registered material shader entry by identifier.\n *\n * Returns `Result.ok(entry)` on hit, `Result.err(material-shader-not-found)`\n * on miss. The `Result` shape mirrors `get(hash)` for consistent\n * abstraction across the runtime registry surface (charter P4).\n *\n * @example\n * ```ts\n * const r = registry.findMaterialArtifact('forgeax::default-standard-pbr');\n * if (!r.ok) return handleError(r.error);\n * const { source, paramSchema } = r.value;\n * ```\n */\n findMaterialArtifact(\n identifier: string,\n ): ShaderResult<RegisteredMaterialShaderEntry, ShaderError> {\n const entry = this.#materialShaders.get(identifier);\n if (entry === undefined) {\n return err(\n materialShaderNotFound({\n identifier,\n expected: Array.from(this.#materialShaders.keys()),\n hint: `register the shader via ShaderRegistry.installMaterialArtifact('${identifier}', ...) at engine boot, or grep '${FORGEAX_RESERVED_PATH_PREFIX}' to enumerate engine-shipped reserved identifiers`,\n }),\n );\n }\n return ok(entry);\n }\n\n /**\n * Iterate every registered material shader identifier in registration\n * order (charter F1 grep gate: `for (const id of registry.materialShaderIdentifiers())`\n * lists every `forgeax::*` + user shader without a private-field reach-in).\n */\n materialShaderIdentifiers(): IterableIterator<string> {\n return this.#materialShaders.keys();\n }\n\n /** Runtime diagnostic counters for the immutable ParamSchema owner. */\n paramSchemaProjectionStats(): ParamSchemaProjectionOwnerStats {\n return this.#paramSchemaProjectionOwner.stats();\n }\n\n /**\n * Iterate every MaterialShaderManifestEntry parsed from manifest.json's\n * `materialShaders[]` field during `loadManifest()`.\n *\n * feat-20260526-pbr-uniform-fallback-no-storage-buffer M3 / w12:\n * createRenderer variant resolution consumes this list to select the\n * correct variant (by `caps.storageBuffer`) and register the resolved\n * WGSL via `installMaterialArtifact`.\n *\n * Returns an empty iterator when no manifest has been loaded yet or\n * the manifest lacks `materialShaders` (backward-compatible).\n */\n materialShaderManifestEntries(): IterableIterator<MaterialShaderManifestEntry> {\n return this.#materialShaderManifestEntries.values();\n }\n}\n\n// ─── helpers ────────────────────────────────────────────────────────────────────\n\nfunction isValidManifestEntry(value: unknown): value is ManifestEntry {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.hash !== 'string') return false;\n if (typeof v.wgsl !== 'string') return false;\n // glsl allows string | undefined | null (manifest.json may serialize null as\n // 'glsl: null').\n if (v.glsl !== undefined && v.glsl !== null && typeof v.glsl !== 'string') return false;\n if (typeof v.bindings !== 'string') return false;\n return true;\n}\n\nfunction isValidMaterialShaderManifestEntry(value: unknown): value is MaterialShaderManifestEntry {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.identifier !== 'string') return false;\n if (typeof v.sourcePath !== 'string') return false;\n if (typeof v.composedWgsl !== 'string') return false;\n if (typeof v.paramSchema !== 'string') return false;\n if (!Array.isArray(v.variants)) return false;\n if (!v.variants.every(isValidMaterialShaderManifestVariant)) return false;\n return v.uvSetCount === undefined || typeof v.uvSetCount === 'number';\n}\n\nfunction isValidMaterialShaderManifestVariant(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.definesKey !== 'string') return false;\n if (typeof v.composedWgsl !== 'string') return false;\n if (typeof v.defines !== 'object' || v.defines === null || Array.isArray(v.defines)) {\n return false;\n }\n return Object.values(v.defines).every((define) => typeof define === 'boolean');\n}\n\nfunction buildMaterialShaderVariantKey(defines: Record<string, boolean>): string {\n const entries = Object.entries(defines).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return entries.every(([, value]) => value)\n ? ''\n : entries.map(([key, value]) => `${key}=${value}`).join('+');\n}\n","// @forgeax/engine-shader/types — shared variant types consumed by the runtime\n// variant resolution layer (createRenderer.ts M3). The SSOT for the manifest\n// schema stays in @forgeax/engine-vite-plugin-shader; this file re-declares the\n// runtime-visible subset so the engine-shader package avoids a physical import\n// of the build-time plugin (physical-isolation grep gate).\n\n/**\n * Single variant within a material-shader manifest entry.\n * `definesKey` is the canonical defines string (sorted `key=value` pairs joined\n * with `+`). Empty key `\"\"` denotes the default variant (all axes `true`).\n *\n * Authored by vite-plugin-shader Cartesian compile at buildStart; consumed by\n * createRenderer variant resolution at engine boot (plan-strategy D-6).\n *\n * feat-20260613 fix-issue-2 (D-1 / D-2 bindingLayout cut): the runtime\n * derives the BGL from `derive(paramSchema).bglEntries`; manifest entries\n * no longer carry a bindingLayout sidecar (§Change stance: no v1/v2\n * dual-path, no incremental drop window).\n */\nexport interface MaterialShaderManifestVariant {\n readonly definesKey: string;\n readonly defines: Record<string, boolean>;\n readonly composedWgsl: string;\n}\n\n/**\n * A material-shader entry in manifest.json carrying zero or more variants.\n *\n * When `variants` is empty the entry is single-variant (no `#pragma variant_axis`\n * in the source). Otherwise `composedWgsl` carries the default\n * (all-true) variant for backward compat; the `variants[]` array contains every\n * compiled combination.\n *\n * For Engine.create() internal use only; AI users never see this type.\n */\nexport interface MaterialShaderManifestEntry {\n readonly identifier: string;\n readonly sourcePath: string;\n readonly composedWgsl: string;\n readonly paramSchema: string;\n readonly variants: readonly MaterialShaderManifestVariant[];\n /**\n * feat-20260629 M4: uvSetCount from naga vertex @location reflection.\n * Populated by vite-plugin-shader at build time from compileShader output.\n * The runtime reads this for clamp-to-last alias in deriveVertexBufferLayout.\n */\n readonly uvSetCount?: number;\n}\n\n/**\n * Look up a variant by canonical defines key.\n * Returns `undefined` when no variant matches the requested key.\n */\nexport function findVariantByKey(\n entry: MaterialShaderManifestEntry,\n definesKey: string,\n): MaterialShaderManifestVariant | undefined {\n return entry.variants.find((v) => v.definesKey === definesKey);\n}\n","/** Numeric mode IDs consumed by the built-in tonemap WGSL module. */\nexport const TONEMAP_SHADER_MODE = {\n none: 0,\n reinhardExtended: 1,\n linear: 2,\n cineon: 3,\n acesFilmic: 4,\n agx: 5,\n neutral: 6,\n reinhard: 7,\n} as const;\n\nexport type TonemapShaderMode = (typeof TONEMAP_SHADER_MODE)[keyof typeof TONEMAP_SHADER_MODE];\n","// @forgeax/engine-shader — runtime shader registry public surface.\n//\n// Shape rules (plan-strategy §S-10 / D-R10 / OQ-5 close):\n// - instance-per-engine — exposed through the lazy `engine.shader:\n// ShaderRegistry` property; module-level singletons / static methods are\n// forbidden (aligned with `Engine.create({ rhi })`'s instance-based style).\n// - Physical isolation — this package's deps only contain `@forgeax/engine-rhi` +\n// `@forgeax/engine-types`; importing `@forgeax/engine-shader-compiler` / `@forgeax/engine-naga`\n// / `@forgeax/engine-wgpu-wasm` directly or transitively is **forbidden**\n// (guarded by the AC-06 triple-grep gate; feat-20260511-naga-rhi-wgpu-merge\n// M4 replaced the legacy single-shim ban with the merged ban triple above).\n// - Result model — expected failures go through\n// `Result.err(RhiError | ShaderError)` and **never throw** (AGENTS.md\n// \"Errors are structured\" / charter proposition 4: explicit failure).\n//\n// Top-level surface (charter proposition 1: progressive disclosure):\n// - ShaderRegistry / ShaderRegistryOptions / ShaderRegistryDevice — main class\n// + injection interface\n// - ShaderError / ShaderErrorCode / 2 factories — runtime error types\n// - Result<T, E> + ok / err — binary result type and constructors\n// - ManifestEntry — re-exported from `@forgeax/engine-types` (manifest schema SSOT)\n\nexport type { ManifestEntry, ParamSchemaEntry } from '@forgeax/engine-types';\n\nimport type { MaterialAsset, MaterialParameter, MaterialValue } from '@forgeax/engine-types';\nimport { STANDARD_PBR_ALPHA_CUTOFF_DEFAULT } from './material-schemas.js';\n\nexport { MATERIAL_PARAM_TYPES } from '@forgeax/engine-types';\nexport {\n err,\n manifestMalformed,\n materialShaderNotFound,\n ok,\n type Result,\n type ResultErr,\n type ResultOk,\n ShaderError,\n type ShaderErrorCode,\n type ShaderErrorDetail,\n shaderNotFound,\n} from './errors.js';\nexport {\n type MaterialArtifactConflictError,\n type MaterialArtifactInspection,\n MaterialArtifactRegistry,\n type MaterialRuntimeArtifact,\n} from './material/artifact-registry.js';\nexport {\n isMaterialShaderArtifact,\n type MaterialShaderArtifact,\n} from './material/artifact-types.js';\nexport {\n DEFAULT_MSDF_TEXT_PARAM_SCHEMA,\n DEFAULT_SPRITE_PARAM_SCHEMA,\n DEFAULT_STANDARD_PBR_PARAM_SCHEMA,\n DEFAULT_UNLIT_PARAM_SCHEMA,\n STANDARD_PBR_ALPHA_CUTOFF_DEFAULT,\n} from './material-schemas.js';\nexport {\n registerDefaultSpriteLit,\n type SpriteLitCaps,\n} from './register-default-sprite-lit.js';\nexport { registerDefaultStandardPbrSkin } from './register-default-standard-pbr-skin.js';\nexport {\n FORGEAX_RESERVED_PATH_PREFIX,\n type MaterialShaderEntry,\n type RegisteredMaterialShaderEntry,\n ShaderRegistry,\n ShaderRegistry as ShaderCatalog,\n type ShaderRegistryDevice,\n type ShaderRegistryDevice as ShaderCatalogDevice,\n type ShaderRegistryOptions,\n type ShaderRegistryOptions as ShaderCatalogOptions,\n} from './ShaderRegistry.js';\nexport {\n findVariantByKey,\n type MaterialShaderManifestEntry,\n type MaterialShaderManifestVariant,\n} from './types.js';\n\nexport const BUILTIN_MATERIAL_MODULES = {\n standard: 'forgeax_material::standard',\n unlit: 'forgeax_material::unlit',\n sprite: 'forgeax_material::sprite',\n} as const;\n\n/** Stable build-time module id for the temporal-v1 accessor owner. */\nexport const SCENE_DATA_TEMPORAL_V1_SHADER_MODULE = 'forgeax_scene_temporal' as const;\n\n/**\n * Material modules whose shader source and parameter contract are owned by\n * the Engine. They live in the runtime ShaderRegistry, so a Pack containing\n * one of these materials carries authored values only and does not require a\n * project material-cook artifact.\n */\nexport const ENGINE_MATERIAL_MODULES = [\n 'forgeax::default-standard-pbr',\n 'forgeax::pbr-skin',\n 'forgeax::default-standard-pbr-skin',\n 'forgeax::default-unlit',\n 'forgeax::default-shadow-caster',\n 'forgeax::sprite',\n 'forgeax::sprite-lit',\n 'forgeax::msdf-text',\n BUILTIN_MATERIAL_MODULES.standard,\n BUILTIN_MATERIAL_MODULES.unlit,\n BUILTIN_MATERIAL_MODULES.sprite,\n 'forgeax_material::sprite-lit',\n] as const;\n\nexport function isEngineMaterialModule(module: string): boolean {\n return (ENGINE_MATERIAL_MODULES as readonly string[]).includes(module);\n}\n\nexport function isEngineMaterial(material: Pick<MaterialAsset, 'passes'>): boolean {\n const passes = material.passes ?? [];\n return passes.length > 0 && passes.every((pass) => isEngineMaterialModule(pass.program.module));\n}\n\nexport type BuiltinMaterialKind = keyof typeof BUILTIN_MATERIAL_MODULES;\n\nconst BUILTIN_PARAMETERS: Readonly<Record<BuiltinMaterialKind, readonly MaterialParameter[]>> = {\n standard: [\n { name: 'baseColor', type: 'color' },\n { name: 'metallic', type: 'f32' },\n { name: 'roughness', type: 'f32' },\n { name: 'alphaCutoff', type: 'f32', optional: true },\n ],\n unlit: [{ name: 'baseColor', type: 'color' }],\n sprite: [{ name: 'colorTint', type: 'vec4', colorSpace: 'srgb' }],\n};\n\nconst BUILTIN_VALUES: Readonly<\n Record<BuiltinMaterialKind, Readonly<Record<string, MaterialValue>>>\n> = {\n standard: {\n baseColor: [1, 1, 1, 1],\n metallic: 0,\n roughness: 0.5,\n alphaCutoff: STANDARD_PBR_ALPHA_CUTOFF_DEFAULT,\n },\n unlit: { baseColor: [1, 1, 1, 1] },\n sprite: { colorTint: [1, 1, 1, 1] },\n};\n\nexport function createBuiltinMaterialAsset(kind: BuiltinMaterialKind): MaterialAsset {\n return {\n kind: 'material',\n passes: [{ name: 'forward', program: { module: BUILTIN_MATERIAL_MODULES[kind] } }],\n parameters: BUILTIN_PARAMETERS[kind],\n values: BUILTIN_VALUES[kind],\n };\n}\n\n/**\n * Shared luminance epsilon floor for the extended Reinhard tone-map\n * (feat-20260519-tonemap-reinhard-mvp / D-O3).\n *\n * Both the TS port at `packages/runtime/src/systems/tonemap.ts` and the WGSL\n * fragment stage in `packages/shader/src/tonemap.wgsl` apply\n * `max(Y, TONEMAP_LUMINANCE_EPSILON)` before dividing the luminance ratio.\n * The floor keeps the divisor finite at degenerate inputs (`Y = 0` from black\n * pixels, `Y < 0` from rare numerical artefacts). Single SSOT here so a\n * single `import { TONEMAP_LUMINANCE_EPSILON } from '@forgeax/engine-shader'`\n * keeps TS / WGSL byte-equivalent.\n *\n * Value: `1e-5` — small enough to not perturb any plausible HDR luminance.\n */\nexport const TONEMAP_LUMINANCE_EPSILON = 1e-5;\n\nexport { TONEMAP_SHADER_MODE, type TonemapShaderMode } from './tonemap.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/material-schemas.ts","../src/errors.ts","../src/material/artifact-registry.ts","../src/material/artifact-types.ts","../src/register-default-sprite-lit.ts","../src/register-default-standard-pbr-skin.ts","../src/ShaderRegistry.ts","../src/types.ts","../src/tonemap.ts","../src/index.ts"],"names":["ok","err","RESERVED_ID"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ParamSchemaEntry } from '@forgeax/engine-types';\n\nexport const STANDARD_PBR_ALPHA_CUTOFF_DEFAULT = 0;\n\n/** Shared material contract for the standard PBR and skinned PBR shaders. */\nexport const DEFAULT_STANDARD_PBR_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [\n { name: 'baseColor', type: 'color', default: [1, 1, 1, 1] },\n { name: 'metallic', type: 'f32', default: 0 },\n { name: 'roughness', type: 'f32', default: 0.5 },\n { name: 'metallicChannel', type: 'f32', default: 2 },\n { name: 'roughnessChannel', type: 'f32', default: 1 },\n { name: 'aoChannel', type: 'f32', default: 0 },\n { name: 'extraChannel', type: 'f32', default: 0 },\n { name: 'emissive', type: 'vec3', colorSpace: 'srgb', default: [0, 0, 0] },\n { name: 'emissiveIntensity', type: 'f32', default: 0 },\n { name: 'occlusionStrength', type: 'f32', default: 1 },\n { name: 'alphaCutoff', type: 'f32', default: STANDARD_PBR_ALPHA_CUTOFF_DEFAULT },\n { name: 'clearcoat', type: 'f32', default: 0 },\n { name: 'clearcoatRoughness', type: 'f32', default: 0.5 },\n { name: 'specularTint', type: 'vec3', colorSpace: 'srgb', default: [1, 1, 1] },\n { name: 'normalScale', type: 'f32', default: 1 },\n { name: 'transmission', type: 'f32', default: 0 },\n { name: 'ior', type: 'f32', default: 1.5 },\n { name: 'thickness', type: 'f32', default: 0 },\n { name: 'attenuationColor', type: 'vec3', colorSpace: 'linear', default: [1, 1, 1] },\n { name: 'attenuationDistance', type: 'f32' },\n { name: 'baseColorTexture', type: 'texture2d' },\n { name: 'metallicRoughnessTexture', type: 'texture2d' },\n { name: 'normalTexture', type: 'texture2d' },\n { name: 'specularTintTexture', type: 'texture2d' },\n { name: 'emissiveTexture', type: 'texture2d' },\n { name: 'occlusionTexture', type: 'texture2d' },\n { name: 'transmissionTexture', type: 'texture2d' },\n { name: 'thicknessTexture', type: 'texture2d' },\n];\n\nexport const DEFAULT_UNLIT_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [\n { name: 'baseColor', type: 'color', default: [1, 1, 1, 1] },\n { name: 'alphaCutoff', type: 'f32', default: 0 },\n { name: 'baseColorTexture', type: 'texture2d' },\n];\n\nexport const DEFAULT_SPRITE_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [\n { name: 'colorTint', type: 'vec4', colorSpace: 'srgb', default: [1, 1, 1, 1] },\n { name: 'region', type: 'vec4', default: [0, 0, 1, 1] },\n { name: 'pivotAndSize', type: 'vec4', default: [0.5, 0.5, 1, 1] },\n { name: 'slicesAndMode', type: 'vec4', default: [0, 0, 0, 0] },\n { name: 'baseColorTexture', type: 'texture2d' },\n];\n\nexport const DEFAULT_MSDF_TEXT_PARAM_SCHEMA: readonly ParamSchemaEntry[] = [\n { name: 'tintColor', type: 'color', default: [1, 1, 1, 1] },\n { name: 'distanceRange', type: 'vec4', default: [4, 512, 512, 0] },\n { name: 'baseColorTexture', type: 'texture2d' },\n];\n","// @forgeax/engine-shader/errors — ShaderError + factories (physically isolated from the\n// same-named class in @forgeax/engine-shader-compiler, but shape-aligned 1:1 / charter\n// proposition 5: consistent abstraction).\n//\n// Shape rules (plan-strategy §S-7 / D-R7 / OQ-2 close):\n// - ShaderError extends Error, with the 5-field top-level projection\n// {code, lineNum, linePos, message, hint} (MVP-2.3) + 3 internal fields\n// {expected, detail, name}.\n// - ShaderErrorCode is a closed 4-member union imported from @forgeax/engine-types\n// (SSOT single-source policy).\n//\n// Physical isolation — this file does **not** import from\n// @forgeax/engine-shader-compiler; the type / factories are 1:1 mirrored but\n// independently implemented (guarded by the AC-06 triple-grep gate; charter\n// proposition 1: progressive disclosure).\n\nimport type { ShaderErrorCode } from '@forgeax/engine-types';\n\nexport type { ShaderErrorCode };\n\n/**\n * Path-specific `detail` shape for `manifest-malformed` / `shader-not-found`.\n * `reason` is supplemental prose (it does not replace `hint`).\n */\nexport interface ShaderErrorDetail {\n readonly reason?: string | undefined;\n /**\n * Machine-consumable list of currently-registered material-shader identifiers.\n * Populated by `materialShaderNotFound()` factory so AI users can enumerate\n * available shaders via `err.detail.registeredShaderIds` for autocomplete /\n * fuzzy-match recovery without parsing the human-formatted `err.expected`\n * string (charter P3 structured failure). `err.expected` still carries the\n * pre-formatted `\"ShaderRegistry has registered identifiers: [...]\"` message\n * for human-readable log output — the two channels are complementary.\n */\n readonly registeredShaderIds?: readonly string[] | undefined;\n}\n\ninterface ShaderErrorInit {\n readonly code: ShaderErrorCode;\n readonly expected: string;\n readonly hint: string;\n readonly message: string;\n readonly lineNum?: number | undefined;\n readonly linePos?: number | undefined;\n readonly detail?: ShaderErrorDetail | undefined;\n}\n\n/**\n * Structured shader error (runtime path, aligned with build-time\n * `@forgeax/engine-shader-compiler.ShaderError`).\n *\n * **5 surface fields** (MVP-2.3 top-level projection, AI-user consumption path):\n * - `.code` — member of the closed `ShaderErrorCode` union (4 variants)\n * - `.message` — display text (`Error` base-class field, populated at construction)\n * - `.hint` — actionable recovery guidance (charter proposition 3:\n * machine-readable hint > prose)\n * - `.lineNum` / `.linePos` — error source location (undefined on the runtime\n * path; only populated by the build-time compile-failed path; the field is\n * retained here for shape alignment).\n *\n * **3 internal fields**:\n * - `.name` = `'ShaderError'` (debug label)\n * - `.expected` — expected-state description (symmetric with RhiError)\n * - `.detail` — path-specific extras (`reason` prose)\n *\n * **Do not `new` this directly** — construct via the 2 factory helpers\n * (`manifestMalformed` / `shaderNotFound`); the runtime path of this loop does\n * not trigger compile-failed / init-failed (those two paths belong to the\n * build-time `ShaderError` shape in `@forgeax/engine-shader-compiler`).\n */\nexport class ShaderError extends Error {\n override readonly name: 'ShaderError' = 'ShaderError';\n readonly code: ShaderErrorCode;\n readonly expected: string;\n readonly hint: string;\n readonly lineNum: number | undefined;\n readonly linePos: number | undefined;\n readonly detail: ShaderErrorDetail | undefined;\n\n constructor(init: ShaderErrorInit) {\n super(init.message);\n this.code = init.code;\n this.expected = init.expected;\n this.hint = init.hint;\n this.lineNum = init.lineNum;\n this.linePos = init.linePos;\n this.detail = init.detail;\n }\n}\n\n// === factory helpers (2 variants for the runtime path) ============================\n\n/** `manifest-malformed`: manifest.json schema validation failed (4 fields missing or JSON unparseable). */\nexport function manifestMalformed(args: {\n readonly message: string;\n readonly hint: string;\n readonly reason?: string | undefined;\n}): ShaderError {\n return new ShaderError({\n code: 'manifest-malformed',\n expected: 'manifest.json parses + every entry has {hash, wgsl, glsl, bindings}',\n message: args.message,\n hint: args.hint,\n ...(args.reason !== undefined ? { detail: { reason: args.reason } } : {}),\n });\n}\n\n/** `shader-not-found`: ShaderRegistry.get(hash) hash miss. */\nexport function shaderNotFound(args: {\n readonly hash: string;\n readonly hint: string;\n}): ShaderError {\n return new ShaderError({\n code: 'shader-not-found',\n expected: `manifest.entries contains entry with hash '${args.hash}'`,\n message: `ShaderRegistry: hash '${args.hash}' not present in manifest`,\n hint: args.hint,\n });\n}\n\n/**\n * `material-shader-not-found`: `ShaderRegistry.findMaterialArtifact(identifier)`\n * miss. Returned by the runtime path when a `MaterialAsset.payload.materialShader`\n * `::`-routed identifier (or any registered identifier) has no matching entry\n * in the registry. AI-user recovery: the `.hint` cites the\n * `forgeax::default-standard-pbr` reservation + the\n * `installMaterialArtifact(...)` API path so the caller can register the missing\n * shader at engine boot (feat-20260523-shader-template-instance-split M5 / T05).\n *\n * feat-20260528-material-shader-registration-unification M3 / w12 +\n * feat-20260624-sprite-lit F-P1 fix: dual-channel exposure of the registered\n * identifier set. `.expected` carries the pre-formatted string\n * `\"ShaderRegistry has registered identifiers: [id1, id2, ...]\"` for human /\n * log output; `.detail.registeredShaderIds` carries the raw `readonly string[]`\n * for AI users to enumerate via property access without regex-parsing the\n * message (charter P3 structured failure).\n */\nexport function materialShaderNotFound(args: {\n readonly identifier: string;\n readonly hint: string;\n readonly expected: string[];\n}): ShaderError {\n return new ShaderError({\n code: 'material-shader-not-found',\n expected: `ShaderRegistry has registered identifiers: [${args.expected.join(', ')}]`,\n message: `ShaderRegistry: material shader identifier '${args.identifier}' not registered`,\n hint: args.hint,\n detail: { reason: `identifier=${args.identifier}`, registeredShaderIds: args.expected },\n });\n}\n\n// === Result<T, E> ====================================================================\n//\n// Round 3 fix-up F-P0-1: Result is re-exported from @forgeax/engine-rhi so the\n// `.unwrap()` / `.unwrapOr()` method-chain surface (AGENTS.md §Adapter /\n// Device IDE-friendly template + 50+ JSDoc @example anchors) is identical\n// across the @forgeax/engine-rhi + @forgeax/engine-shader public surfaces (charter\n// proposition 5: consistent abstraction). The previous local duplicate\n// (plain field-only Result) caused TS structural-mismatch when ShaderRegistry\n// values flowed into RHI-typed signatures.\n\nexport {\n err,\n ok,\n type Result,\n type ResultErr,\n type ResultOk,\n} from '@forgeax/engine-rhi';\n","import { err, ok, type Result } from '@forgeax/engine-types';\n\nexport interface MaterialRuntimeArtifact {\n readonly key: string;\n readonly bytes: Uint8Array;\n readonly digest?: string;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\nexport interface MaterialArtifactInspection {\n readonly key: string;\n readonly digest?: string;\n readonly byteLength: number;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}\n\nexport interface MaterialArtifactConflictError {\n readonly code: 'material-artifact-conflict';\n readonly expected: string;\n readonly hint: string;\n readonly detail: {\n readonly key: string;\n readonly existingDigest?: string;\n readonly incomingDigest?: string;\n };\n}\n\nfunction equalBytes(left: Uint8Array, right: Uint8Array): boolean {\n return left.length === right.length && left.every((value, index) => value === right[index]);\n}\n\nexport class MaterialArtifactRegistry {\n readonly #artifacts = new Map<string, MaterialRuntimeArtifact>();\n\n register(\n artifact: MaterialRuntimeArtifact,\n ): Result<MaterialRuntimeArtifact, MaterialArtifactConflictError> {\n const previous = this.#artifacts.get(artifact.key);\n if (previous !== undefined) {\n if (equalBytes(previous.bytes, artifact.bytes)) return ok(previous);\n return err({\n code: 'material-artifact-conflict',\n expected: 'one immutable artifact byte sequence per specialization key',\n hint: 're-cook the conflicting specialization and publish one artifact digest',\n detail: {\n key: artifact.key,\n ...(previous.digest ? { existingDigest: previous.digest } : {}),\n ...(artifact.digest ? { incomingDigest: artifact.digest } : {}),\n },\n });\n }\n const immutable = Object.freeze(artifact);\n this.#artifacts.set(artifact.key, immutable);\n return ok(immutable);\n }\n\n get(key: string): MaterialRuntimeArtifact | undefined {\n return this.#artifacts.get(key);\n }\n\n inspect(key: string): MaterialArtifactInspection | undefined {\n const artifact = this.#artifacts.get(key);\n if (artifact === undefined) return undefined;\n return {\n key: artifact.key,\n ...(artifact.digest === undefined ? {} : { digest: artifact.digest }),\n byteLength: artifact.bytes.byteLength,\n ...(artifact.metadata === undefined ? {} : { metadata: artifact.metadata }),\n };\n }\n}\n","import type { BindGroupLayoutDescriptor } from '@forgeax/engine-types';\n\nexport interface MaterialShaderArtifact {\n readonly material: string;\n readonly pass: string;\n readonly wgsl: string;\n readonly layoutIdentity: string;\n readonly bindings: readonly BindGroupLayoutDescriptor[];\n readonly deps: readonly string[];\n readonly vertexInputs: readonly Readonly<Record<string, unknown>>[];\n readonly specializationKey?: string;\n}\n\nexport function isMaterialShaderArtifact(value: unknown): value is MaterialShaderArtifact {\n if (value === null || typeof value !== 'object') return false;\n const artifact = value as Partial<MaterialShaderArtifact>;\n return (\n typeof artifact.material === 'string' &&\n typeof artifact.pass === 'string' &&\n typeof artifact.wgsl === 'string' &&\n typeof artifact.layoutIdentity === 'string' &&\n Array.isArray(artifact.bindings) &&\n Array.isArray(artifact.deps) &&\n Array.isArray(artifact.vertexInputs)\n );\n}\n","// @forgeax/engine-shader - registerDefaultSpriteLit helper.\n//\n// feat-20260624-sprite-lit-shading-model-pure-2d-lighting M1' / w4\n// (plan-strategy D-10 application registration path).\n//\n// Mirrors registerDefaultStandardPbrSkin (same package; identical\n// registration shape). paramSchema mirrors sprite.material.json minus\n// PBR-only fields; the BGL is derived from paramSchema via\n// `derive(paramSchema).bglEntries` inside the runtime pipeline-builder.\n//\n// Anchors:\n// - requirements section 2 #2 / #10 (sprite-lit.wgsl new + 4 BGL byte-identical)\n// - plan-strategy D-3 (sprite-lit single Half-Lambert formula lock)\n// - plan-strategy D-5 (STORAGE_BUFFER_AVAILABLE variant axis day-1)\n// - plan-strategy D-6 (4 PBR-unused slots default sampler + white tex)\n// - plan-strategy D-10 (application registration path)\n\nimport type { ShaderRegistry } from './index.js';\nimport { DEFAULT_SPRITE_PARAM_SCHEMA } from './material-schemas.js';\n\n/**\n * paramSchema for forgeax::sprite-lit -- mirrors sprite.material.json\n * exactly (same 5 fields: colorTint / region / pivotAndSize /\n * slicesAndMode / baseColorTexture). sprite-lit and sprite share the\n * same Material UBO layout byte-for-byte so the host-side\n * SpriteMaterialAsset writer + 4 BGL JSON are byte-identical (AC-07).\n *\n * OOS-1 (feat-future-sprite-lit-normal-map) will extend this with\n * normalTexture + normalStrength. M1' deliberately keeps the schema\n * byte-identical to sprite so AC-13 \"1 string-change between sprite\n * and sprite-lit materials\" is provably the only delta required.\n */\nconst RESERVED_ID = 'forgeax::sprite-lit' as const;\n\n/**\n * Caps shape exposed for sprite-lit registration. Currently only\n * `storageBuffer` matters (STORAGE_BUFFER_AVAILABLE variant axis); the\n * per-shader BGL is derived from paramSchema at pipeline-build time, so\n * the runtime layout factory consumes this caps shape downstream.\n *\n * Kept as a separate type rather than reusing `SkinCaps` to preserve\n * a clear contract surface; the structural shape is currently identical\n * but future divergence (sprite-lit may opt in to OOS-7 cluster-forward\n * later) should not silently leak via a shared type.\n */\nexport interface SpriteLitCaps {\n readonly storageBuffer: boolean;\n}\n\n/**\n * Register the engine-shipped `forgeax::sprite-lit` material shader in\n * the ShaderRegistry. Must be called once at engine boot (createRenderer),\n * after `ShaderRegistry.loadManifest()` has completed.\n *\n * The `composedWgsl` parameter is the post-naga_oil composed WGSL source,\n * compiled by @forgeax/engine-vite-plugin-shader at build time and surfaced\n * in manifest.json. The caller reads it from the manifest entries.\n *\n * `caps.storageBuffer` switches the runtime BGL buffer types for\n * @group(0) bindings 1+2 (pointLightsBuffer / spotLightsBuffer):\n * `false` -> uniform fallback (AC-10 WebGL2 path); `true` -> read-only-storage.\n * The BGL itself is built downstream in runtime/pbr-pipeline.ts (sprite-lit\n * reuses pbr-view / pbr-mesh-array / pbr-instances unchanged; AC-07\n * BGL byte-identical).\n *\n * @param registry ShaderRegistry instance (engine boot path).\n * @param composedWgsl Post-naga_oil composed WGSL source string.\n * @param caps sprite-lit caps (storageBuffer boolean) for BGL entry type selection.\n *\n * Throws if the identifier is already registered (fail-fast per\n * ShaderRegistry.installMaterialArtifact contract).\n */\nexport function registerDefaultSpriteLit(\n registry: ShaderRegistry,\n composedWgsl: string,\n caps: SpriteLitCaps,\n): void {\n // The caps argument is retained for API symmetry with\n // registerDefaultStandardPbrSkin -- equivalent caps gating lives in\n // buildPbrPipelineLayouts since the per-shader BGL is paramSchema-derived\n // and the storage-vs-uniform branching is a runtime-level concern.\n void caps;\n registry.installMaterialArtifact(RESERVED_ID, {\n source: composedWgsl,\n paramSchema: DEFAULT_SPRITE_PARAM_SCHEMA,\n });\n}\n","// @forgeax/engine-shader - registerDefaultStandardPbrSkin helper.\n//\n// feat-20260523-skin-skeleton-animation M3 / T-30; updated by\n// feat-20260613-material-paramschema-driven-binding M3 / w13: the\n// bindingLayout sidecar field has been removed from MaterialShaderEntry;\n// the BGL is derived from paramSchema via `derive(paramSchema).bglEntries`\n// inside the runtime pipeline-builder. paramSchema remains shared with\n// default-standard-pbr (identical PBR material interface).\n//\n// Anchors:\n// - plan-strategy D-3: register independent WGSL template for skin variant\n// - plan-strategy D-1 / D-2: paramSchema is the single source of truth\n// - R-10: paramSchema reuses default-standard-pbr schema (same params)\n\nimport type { ShaderRegistry } from './index.js';\nimport { DEFAULT_STANDARD_PBR_PARAM_SCHEMA } from './material-schemas.js';\n\n/**\n * paramSchema for forgeax::pbr-skin — mirrors default-standard-pbr 8 fields.\n * Previously sourced from default-standard-pbr.schema.json; now inlined because\n * the .schema.json file has been deleted (feat-20260528-material-shader-registration-unification M1/w4).\n * The SSOT is default-standard-pbr.material.json paramSchema[].\n */\nconst RESERVED_ID = 'forgeax::pbr-skin' as const;\n\n/**\n * Caps shape historically consumed by the pbr-skin BGL builder. The\n * BGL builder itself moved to runtime/pbr-pipeline.ts (buildPbrSkinLayouts);\n * this type is preserved as the public registration-API contract so\n * existing callers compile unchanged. M3 / w13: per-shader BGL is\n * derived from paramSchema; the storage-buffer-vs-uniform branching\n * the BGL needs lives entirely in the runtime layout factory.\n */\nexport interface SkinCaps {\n readonly storageBuffer: boolean;\n}\n\n/**\n * Register the engine-shipped `forgeax::pbr-skin` material shader in the\n * ShaderRegistry. Must be called once at engine boot (createRenderer), after\n * `ShaderRegistry.loadManifest()` has completed.\n *\n * The `composedWgsl` parameter is the post-naga_oil composed WGSL source,\n * compiled by @forgeax/engine-vite-plugin-shader at build time and surfaced\n * in manifest.json. The caller reads it from the manifest entries.\n *\n * `caps.storageBuffer` switches the runtime BGL buffer types:\n * `false` -> uniform fallback; `true` -> read-only-storage. The BGL itself\n * is built downstream in runtime/pbr-pipeline.ts (M3 / w13).\n *\n * @param registry ShaderRegistry instance (engine boot path).\n * @param composedWgsl Post-naga_oil composed WGSL source string.\n * @param caps PBR caps (storageBuffer boolean) for BGL entry type selection.\n *\n * Throws if the identifier is already registered (fail-fast per\n * ShaderRegistry.installMaterialArtifact contract).\n */\nexport function registerDefaultStandardPbrSkin(\n registry: ShaderRegistry,\n composedWgsl: string,\n caps: SkinCaps,\n): void {\n // M3 / w13 (D-1 / D-2): bindingLayout sidecar deleted from\n // MaterialShaderEntry; paramSchema is the SSOT and downstream\n // pipeline-builder reads `derive(paramSchema).bglEntries` on demand.\n // The `caps` argument is retained for API compat — historically it\n // selected the storage-buffer vs uniform-buffer skin variant of the\n // BGL; equivalent caps gating now lives in buildPbrSkinLayouts.\n void caps;\n registry.installMaterialArtifact(RESERVED_ID, {\n source: composedWgsl,\n paramSchema: DEFAULT_STANDARD_PBR_PARAM_SCHEMA,\n });\n}\n","// @forgeax/engine-shader/ShaderRegistry — runtime shader registry, instance-per-engine shape.\n//\n// Shape rules (plan-strategy §S-10 / D-R10 / OQ-5 close + AC-03):\n// - instance-per-engine — must be created via\n// `new ShaderRegistry({device, manifestUrl})`; module-level singletons\n// (`export const registry = ...`) and static methods (`static get`) are\n// forbidden — aligned with the instance-based style of\n// `Engine.create({ rhi })`.\n// - Physical isolation — this file does **not** import @forgeax/engine-shader-compiler /\n// @forgeax/engine-naga / @forgeax/engine-wgpu-wasm (guarded by the AC-06 triple-grep gate;\n// feat-20260511-naga-rhi-wgpu-merge M4 replaced the legacy single-shim ban\n// with the merged ban list).\n// - Result model — `loadManifest()` / `get()` always go through Result.ok /\n// Result.err and **never throw** (AGENTS.md \"Errors are structured\" / charter\n// proposition 4: explicit failure).\n// - 9-member error union — `get()` returns\n// `Result<ShaderModule, RhiError | ShaderError>` (AGENTS.md \"RHI / Shader /\n// error-model contract\").\n\nimport type { Result, RhiError, ShaderModule } from '@forgeax/engine-rhi';\nimport type {\n ImmutableParamSchemaProjection,\n ManifestEntry,\n ParamSchemaEntry,\n ParamSchemaProjectionOwnerStats,\n} from '@forgeax/engine-types';\nimport { findUndeclaredSampledTextures, ParamSchemaProjectionOwner } from '@forgeax/engine-types';\nimport {\n err,\n manifestMalformed,\n materialShaderNotFound,\n ok,\n type ShaderError,\n type Result as ShaderResult,\n shaderNotFound,\n} from './errors.js';\nimport type { MaterialShaderManifestEntry } from './types.js';\n\n// ─── Device dependency-injection interface ──────────────────────────────────────\n\n/**\n * Device dependency-injection interface — ShaderRegistry does not directly\n * depend on `@forgeax/engine-rhi-webgpu` (physical isolation); the caller (engine or\n * tests) supplies a device-like object that implements\n * `createShaderModule(desc) → Result<ShaderModule, RhiError>`.\n *\n * Difference from the top-level async `createShaderModule(device, desc)` in\n * `@forgeax/engine-rhi-webgpu`:\n * - This interface is synchronous — so `registry.get()` can return a cached\n * module synchronously.\n * - The caller is responsible for wrapping the async real path into a sync one\n * (e.g. the engine pre-compiles and caches modules during `loadManifest`).\n *\n * Mock test shape: a direct sync implementation (see `createMockDevice` in\n * src/__tests__/registry.test.ts).\n */\nexport interface ShaderRegistryDevice {\n createShaderModule(desc: {\n readonly code: string;\n readonly label?: string | undefined;\n }): Result<ShaderModule, RhiError>;\n}\n\n// ─── MaterialShader registry types (feat-20260523-shader-template-instance-split M5) ──\n\n/**\n * The `forgeax::` prefix reserves engine-shipped material shader identifiers\n * (plan-strategy D-DefaultStandardPbr-Identifier + §8 charter F1 grep gate).\n * AI users grep `forgeax::` to enumerate every reserved shader identifier in\n * one shot. Custom user-side shaders pick their own identifier path\n * (typically `<package>::<id>`) — anything that is not a `<guid>`\n * (UUIDv5/v7) shape and not `forgeax::*`.\n */\nexport const FORGEAX_RESERVED_PATH_PREFIX = 'forgeax::' as const;\n\n/**\n * Material shader admission input accepted by\n * `installMaterialArtifact(identifier, entry)`. `findMaterialArtifact`\n * returns a {@link RegisteredMaterialShaderEntry} with the immutable schema\n * projection attached.\n *\n * - `source`: composed WGSL source (post-naga_oil); the final form fed to\n * `device.createShaderModule({ code })` at pipeline-build time.\n * - `paramSchema`: closed list of `{name, type, default?}` triples that the\n * `MaterialAsset.payload.values` is validated against at register-time\n * (feat-20260523-shader-template-instance-split M4 3-tier validation). Per\n * feat-20260613-material-paramschema-driven-binding M3 / w12-w13, the\n * paramSchema is also the SSOT for the pipeline BGL via\n * `derive(paramSchema).bglEntries` — there is no longer a separate\n * `bindingLayout` field on this entry (D-1 / D-2).\n *\n * The shape is symmetric across engine-default + user-custom paths\n * (charter P4 consistent abstraction): both go through the same\n * `installMaterialArtifact` API regardless of whether the host wires the\n * default-standard-pbr triple at boot or the user registers a custom\n * shader after asset import.\n */\nexport interface MaterialShaderEntry {\n readonly source: string;\n readonly paramSchema: readonly ParamSchemaEntry[];\n}\n\n/** Registry-owned runtime shape published after immutable schema admission. */\nexport interface RegisteredMaterialShaderEntry extends MaterialShaderEntry {\n readonly paramSchemaProjection: ImmutableParamSchemaProjection;\n}\n\n// ─── Public registry types ──────────────────────────────────────────────────────\n\nexport interface ShaderRegistryOptions {\n readonly device: ShaderRegistryDevice;\n /**\n * URL the registry fetches `manifest.json` from. `undefined` puts the\n * registry into the **zero-entry** mode: `loadManifest()` resolves\n * `Result.ok(undefined)` without issuing a fetch and `entries()` returns\n * an empty iterator. This is the bug-20260519 clear-pass-only path\n * where AI users build a `Camera`-only world (no PBR / unlit pipeline\n * is ever needed). `createRenderer.ts` Step 2 narrows on\n * `manifestEntries.length > 0` to skip the dual `createShaderModule`\n * compile in the same path (charter P3 + plan-strategy D-1 + D-2).\n */\n readonly manifestUrl: string | undefined;\n}\n\ntype ShaderManifestDiagnostic = {\n readonly name: string;\n readonly startedAt: number;\n readonly previousElapsedMs: number;\n readonly url?: string;\n readonly status?: number;\n readonly responseUrl?: string;\n readonly entryCount?: number;\n readonly materialShaderCount?: number;\n readonly error?: string;\n};\n\nfunction markShaderManifestStage(\n name: string,\n detail: Omit<ShaderManifestDiagnostic, 'name' | 'startedAt' | 'previousElapsedMs'> = {},\n): void {\n if (typeof globalThis === 'undefined') return;\n const host = globalThis as {\n __forgeaxShaderManifest?: ShaderManifestDiagnostic;\n };\n const previous = host.__forgeaxShaderManifest;\n const now = Date.now();\n host.__forgeaxShaderManifest = {\n name,\n startedAt: now,\n previousElapsedMs: previous === undefined ? 0 : now - previous.startedAt,\n ...detail,\n };\n}\n\n// ─── ShaderRegistry main class (instance-per-engine) ============================\n\n/**\n * Runtime shader registry — content-addressable manifest lookup →\n * `device.createShaderModule`.\n *\n * Usage:\n * ```\n * const registry = new ShaderRegistry({ device, manifestUrl: '/shaders/manifest.json' });\n * const loaded = await registry.loadManifest();\n * if (!loaded.ok) handleError(loaded.error); // ShaderError.manifest-malformed\n *\n * const result = registry.get('abc12345');\n * if (!result.ok) handleError(result.error); // RhiError | ShaderError.shader-not-found\n * else useModule(result.value);\n * ```\n *\n * `static get` / module-level singletons are **forbidden** (plan-strategy\n * §S-10).\n */\nexport class ShaderRegistry {\n readonly #device: ShaderRegistryDevice;\n readonly #manifestUrl: string | undefined;\n // hash → ManifestEntry index (populated after loadManifest).\n readonly #entries = new Map<string, ManifestEntry>();\n // hash → ShaderModule cache (populated lazily on first get()).\n readonly #moduleCache = new Map<string, ShaderModule>();\n // identifier → MaterialShaderEntry index (populated by\n // installMaterialArtifact; feat-20260523-shader-template-instance-split M5 / T05).\n readonly #materialShaders = new Map<string, RegisteredMaterialShaderEntry>();\n readonly #paramSchemaProjectionOwner = new ParamSchemaProjectionOwner();\n readonly #materialShaderManifestEntries: MaterialShaderManifestEntry[] = [];\n #manifestLoaded = false;\n\n constructor(opts: ShaderRegistryOptions) {\n this.#device = opts.device;\n this.#manifestUrl = opts.manifestUrl;\n }\n\n /**\n * Loads manifest.json and populates the hash → ManifestEntry index.\n *\n * Failure paths (charter proposition 4: explicit failure):\n * - fetch / data: URL parse failed → `Result.err(ShaderError.manifest-malformed)`\n * - JSON.parse failed → ditto\n * - schema missing the `entries` field / an entries element missing\n * hash/wgsl/bindings → ditto\n * - a material shader row is incomplete → ditto\n *\n * Validation is atomic: the registry does not publish any entry or\n * material shader row until the complete document has been validated.\n *\n * Idempotent: subsequent calls reuse the first result (never re-fetch;\n * charter proposition 6: idempotency).\n */\n async loadManifest(): Promise<ShaderResult<void, ShaderError>> {\n if (this.#manifestLoaded) return ok(undefined);\n\n // bug-20260519 D-2: zero-entry mode. `manifestUrl === undefined` means\n // the host opted out of shipping a shader manifest (Camera-only /\n // clear-pass-only LO 1.1 path); resolve `Result.ok` without issuing a\n // fetch. `entries()` then yields nothing and createRenderer Step 2\n // skips the dual `createShaderModule` compile (D-1 + D-3 nullable\n // PipelineState). Subsequent `get(hash)` calls fall through to the\n // shader-not-found arm via the empty entries map (charter P3\n // explicit failure on misuse).\n if (this.#manifestUrl === undefined) {\n this.#manifestLoaded = true;\n return ok(undefined);\n }\n\n let raw: string;\n markShaderManifestStage('shader-manifest-fetch-start', { url: this.#manifestUrl });\n try {\n const response = await fetch(this.#manifestUrl);\n markShaderManifestStage('shader-manifest-response', {\n url: this.#manifestUrl,\n status: response.status,\n responseUrl: response.url,\n });\n if (!response.ok) {\n markShaderManifestStage('shader-manifest-http-failed', {\n url: this.#manifestUrl,\n status: response.status,\n responseUrl: response.url,\n error: `HTTP ${response.status} ${response.statusText}`,\n });\n return err(\n manifestMalformed({\n message: `ShaderRegistry: manifest request returned HTTP ${response.status}`,\n hint: 'verify the dev manifest middleware or production manifest asset is reachable',\n reason: `${response.statusText || 'HTTP error'} at ${this.#manifestUrl}`,\n }),\n );\n }\n raw = await response.text();\n } catch (e) {\n markShaderManifestStage('shader-manifest-fetch-failed', {\n url: this.#manifestUrl,\n error: e instanceof Error ? e.message : String(e),\n });\n return err(\n manifestMalformed({\n message: `ShaderRegistry: failed to fetch manifest at ${this.#manifestUrl}`,\n hint: 'verify manifest URL is reachable; check bundler emitFile output path',\n reason: e instanceof Error ? e.message : String(e),\n }),\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n markShaderManifestStage('shader-manifest-json-failed', {\n url: this.#manifestUrl,\n error: e instanceof Error ? e.message : String(e),\n });\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest JSON parse failed',\n hint: 'manifest.json must be valid JSON; rebuild via @forgeax/engine-vite-plugin-shader generateBundle',\n reason: e instanceof Error ? e.message : String(e),\n }),\n );\n }\n\n if (typeof parsed !== 'object' || parsed === null || !('entries' in parsed)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest missing required `entries` field',\n hint: 'manifest schema requires top-level `entries: ManifestEntry[]`',\n }),\n );\n }\n\n const entries = (parsed as { entries: unknown }).entries;\n if (!Array.isArray(entries)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest.entries is not an array',\n hint: 'manifest schema requires `entries: ManifestEntry[]`',\n }),\n );\n }\n\n const validatedEntries: ManifestEntry[] = [];\n for (const entry of entries) {\n if (!isValidManifestEntry(entry)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest entry missing required fields',\n hint: 'every entry needs {hash, wgsl, glsl, bindings} per @forgeax/engine-types.ManifestEntry',\n reason: `bad entry: ${JSON.stringify(entry)}`,\n }),\n );\n }\n validatedEntries.push(entry);\n }\n\n // feat-20260526-pbr-uniform-fallback-no-storage-buffer M3 / w12:\n // parse and store materialShaders[] from the manifest for\n // createRenderer variant resolution. Non-existent / non-array\n // materialShaders is treated as empty (backward-compatible with\n // manifests from pre-M2 build runs).\n const parsedSlim = parsed as { materialShaders?: unknown };\n const validatedMaterialShaderEntries: MaterialShaderManifestEntry[] = [];\n if (Array.isArray(parsedSlim.materialShaders)) {\n const materialShaderIdentifiers = new Set<string>();\n for (const ms of parsedSlim.materialShaders) {\n if (!isValidMaterialShaderManifestEntry(ms)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest material shader entry missing required fields',\n hint: 'every material shader entry needs {identifier, sourcePath, composedWgsl, paramSchema, variants}',\n reason: `bad material shader entry: ${JSON.stringify(ms)}`,\n }),\n );\n }\n if (materialShaderIdentifiers.has(ms.identifier)) {\n return err(\n manifestMalformed({\n message: 'ShaderRegistry: manifest contains duplicate material shader identifiers',\n hint: 'materialShaders[] must contain one row per identifier',\n reason: `duplicate material shader identifier: ${ms.identifier}`,\n }),\n );\n }\n materialShaderIdentifiers.add(ms.identifier);\n const variantKeys = new Set<string>();\n for (const variant of ms.variants) {\n const canonicalKey = buildMaterialShaderVariantKey(variant.defines);\n if (variant.definesKey !== canonicalKey || variantKeys.has(variant.definesKey)) {\n return err(\n manifestMalformed({\n message:\n 'ShaderRegistry: material shader variants contain a duplicate or non-canonical key',\n hint: 'each variant definesKey must be the exact sorted key derived from defines',\n reason: `invalid material shader variant key '${variant.definesKey}' for ${ms.identifier}`,\n }),\n );\n }\n variantKeys.add(variant.definesKey);\n }\n if (ms.identifier === 'forgeax::default-standard-pbr') {\n const transmissionStates = new Set(\n ms.variants\n .filter((variant) => 'TRANSMISSION_AVAILABLE' in variant.defines)\n .map((variant) => variant.defines.TRANSMISSION_AVAILABLE),\n );\n if (!transmissionStates.has(false) || !transmissionStates.has(true)) {\n return err(\n manifestMalformed({\n message:\n 'ShaderRegistry: Standard material shader is missing a transmission variant state',\n hint: 'forgeax::default-standard-pbr must declare TRANSMISSION_AVAILABLE=false and true',\n reason: `declared transmission states: ${JSON.stringify([...transmissionStates])}`,\n }),\n );\n }\n }\n validatedMaterialShaderEntries.push(ms);\n }\n }\n\n this.#entries.clear();\n for (const entry of validatedEntries) {\n this.#entries.set(entry.hash, entry);\n }\n this.#materialShaderManifestEntries.length = 0;\n this.#materialShaderManifestEntries.push(...validatedMaterialShaderEntries);\n this.#manifestLoaded = true;\n markShaderManifestStage('shader-manifest-validated', {\n url: this.#manifestUrl,\n status: 200,\n entryCount: validatedEntries.length,\n materialShaderCount: validatedMaterialShaderEntries.length,\n });\n return ok(undefined);\n }\n\n /**\n * Iterate every `ManifestEntry` populated by the most recent successful\n * `loadManifest()` call. Order matches the manifest JSON `entries` array\n * (insertion order; charter P2 structured-over-prose: AI users walk this\n * to discover engine-shipped entries by content marker without\n * re-fetching the manifest).\n *\n * Returns an empty iterator when no manifest has been loaded yet\n * (charter P3 explicit failure: the empty case is not an error — the\n * absent SSOT is the absent contract).\n */\n entries(): IterableIterator<ManifestEntry> {\n return this.#entries.values();\n }\n\n /**\n * Looks up a hash and forwards to `device.createShaderModule` (creates lazily\n * on first hit, then caches).\n *\n * Error paths:\n * - hash miss → `Result.err(ShaderError.shader-not-found)`\n * - device.createShaderModule failed → `Result.err(RhiError)` passes through\n * (it is **not** wrapped as a ShaderError).\n *\n * Sync shape: on cache miss, the underlying `device.createShaderModule` is\n * called synchronously; the caller is responsible for handling the async\n * real path at the device-wrapper layer (e.g. the engine pre-compiles\n * during `loadManifest`).\n */\n get(hash: string): Result<ShaderModule, RhiError | ShaderError> {\n const cachedModule = this.#moduleCache.get(hash);\n if (cachedModule !== undefined) {\n return ok(cachedModule);\n }\n\n const entry = this.#entries.get(hash);\n if (entry === undefined) {\n return err(\n shaderNotFound({\n hash,\n hint: 'verify manifest.json contains the expected hash; rerun build to regenerate manifest',\n }),\n );\n }\n\n const result = this.#device.createShaderModule({ code: entry.wgsl, label: hash });\n if (!result.ok) {\n return result;\n }\n this.#moduleCache.set(hash, result.value);\n return result;\n }\n\n // ─── MaterialShader registry surface ───────────────────────────────────────\n // (feat-20260523-shader-template-instance-split M5 / T05 — plan-strategy\n // D-DefaultStandardPbr-Identifier).\n //\n // Two-tier identifier namespace:\n // - `forgeax::*` — engine-shipped reserved prefix; the host wires\n // `forgeax::default-standard-pbr` at engine boot from the\n // vite-plugin-shader manifest's `materialShaders[]` row + the\n // `default-standard-pbr.schema.json` SSOT (M5 / T08 + M6).\n // - any other identifier — user-side custom material shader, registered\n // after asset import.\n //\n // The registry is the runtime SSOT consumed by:\n // - `parseAssetPayload`'s `'::' two-way dispatch` (M4 / T01) — the path\n // branch validates against `paramSchema` and feeds `MaterialSnapshot.\n // materialShaderId` (M4 / T05).\n // - `render-system-record`'s pipeline cache key (M4 / T06) — same\n // identifier feeds the per-pipeline `(materialShaderId, stateHash)` key.\n\n /**\n * Register a material shader entry under a stable identifier.\n *\n * **Throws** on:\n * - duplicate registration of the same identifier (programmer error per\n * AGENTS.md \"explicit registration\" + Inspector \"fail-fast no overwrite\"\n * pattern; charter P3 explicit failure on misuse).\n *\n * Successful path returns void — the registry mutates in-place. Use\n * {@link findMaterialArtifact} to read back.\n *\n * Identifier conventions (charter F1 / plan-strategy §8 §2):\n * - `forgeax::<kebab-case>` — engine-shipped reserved prefix; the host\n * registers `forgeax::default-standard-pbr` at boot.\n * - `<package>::<id>` or `<guid>` — user-side custom shaders.\n *\n * @example\n * ```ts\n * const registry = new ShaderRegistry({ device, manifestUrl });\n * registry.installMaterialArtifact('forgeax::default-standard-pbr', {\n * source: composedWgsl,\n * paramSchema: defaultStandardPbrSchema,\n * });\n * ```\n */\n installMaterialArtifact(identifier: string, entry: MaterialShaderEntry): void {\n if (this.#materialShaders.has(identifier)) {\n throw new Error(\n `ShaderRegistry: material shader identifier '${identifier}' already registered; same-name re-register is forbidden (AGENTS.md \"explicit registration\" + Inspector \"fail-fast no overwrite\" pattern). Use findMaterialArtifact to read back the existing entry.`,\n );\n }\n // bug-20260619: user shaders registered directly here bypass the build-time\n // superset gate (vite-plugin-shader's WGSL reflection). A schema that omits\n // a texture field the WGSL actually samples would let the extract stage's\n // `validateTextureHandle` silently drop the handle and fall back to the\n // default white texture (opaque-white grass/windows in the LO 4.3 blending\n // demo). Fail fast at register time instead (charter P3 explicit failure).\n // Scoped to user shaders: engine `forgeax::*` shaders go through the\n // build-time gate and may sample engine-injected textures (emissive /\n // occlusion) absent from their schema by design.\n const paramSchemaProjection = this.#paramSchemaProjectionOwner.admit({\n ownerId: identifier,\n revision: 1,\n schema: entry.paramSchema,\n });\n if (!identifier.startsWith(FORGEAX_RESERVED_PATH_PREFIX)) {\n const undeclared = findUndeclaredSampledTextures(entry.source, paramSchemaProjection.schema);\n if (undeclared.length > 0) {\n throw new Error(\n `ShaderRegistry: material shader '${identifier}' samples texture(s) [${undeclared.join(', ')}] in its WGSL but its paramSchema does not declare them as texture entries. Add { name: '${undeclared[0]}', type: 'texture2d' } (and any others listed) to the paramSchema, or the engine would silently bind the default white texture (charter P3 explicit failure; see docs/handover/2026-06-19-blending-transparency-regression-bisect.md).`,\n );\n }\n }\n this.#materialShaders.set(\n identifier,\n Object.freeze({\n source: entry.source,\n paramSchema: paramSchemaProjection.schema,\n paramSchemaProjection,\n }),\n );\n }\n\n /**\n * Lookup a previously-registered material shader entry by identifier.\n *\n * Returns `Result.ok(entry)` on hit, `Result.err(material-shader-not-found)`\n * on miss. The `Result` shape mirrors `get(hash)` for consistent\n * abstraction across the runtime registry surface (charter P4).\n *\n * @example\n * ```ts\n * const r = registry.findMaterialArtifact('forgeax::default-standard-pbr');\n * if (!r.ok) return handleError(r.error);\n * const { source, paramSchema } = r.value;\n * ```\n */\n findMaterialArtifact(\n identifier: string,\n ): ShaderResult<RegisteredMaterialShaderEntry, ShaderError> {\n const entry = this.#materialShaders.get(identifier);\n if (entry === undefined) {\n return err(\n materialShaderNotFound({\n identifier,\n expected: Array.from(this.#materialShaders.keys()),\n hint: `register the shader via ShaderRegistry.installMaterialArtifact('${identifier}', ...) at engine boot, or grep '${FORGEAX_RESERVED_PATH_PREFIX}' to enumerate engine-shipped reserved identifiers`,\n }),\n );\n }\n return ok(entry);\n }\n\n /**\n * Iterate every registered material shader identifier in registration\n * order (charter F1 grep gate: `for (const id of registry.materialShaderIdentifiers())`\n * lists every `forgeax::*` + user shader without a private-field reach-in).\n */\n materialShaderIdentifiers(): IterableIterator<string> {\n return this.#materialShaders.keys();\n }\n\n /** Runtime diagnostic counters for the immutable ParamSchema owner. */\n paramSchemaProjectionStats(): ParamSchemaProjectionOwnerStats {\n return this.#paramSchemaProjectionOwner.stats();\n }\n\n /**\n * Iterate every MaterialShaderManifestEntry parsed from manifest.json's\n * `materialShaders[]` field during `loadManifest()`.\n *\n * feat-20260526-pbr-uniform-fallback-no-storage-buffer M3 / w12:\n * createRenderer variant resolution consumes this list to select the\n * correct variant (by `caps.storageBuffer`) and register the resolved\n * WGSL via `installMaterialArtifact`.\n *\n * Returns an empty iterator when no manifest has been loaded yet or\n * the manifest lacks `materialShaders` (backward-compatible).\n */\n materialShaderManifestEntries(): IterableIterator<MaterialShaderManifestEntry> {\n return this.#materialShaderManifestEntries.values();\n }\n}\n\n// ─── helpers ────────────────────────────────────────────────────────────────────\n\nfunction isValidManifestEntry(value: unknown): value is ManifestEntry {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.hash !== 'string') return false;\n if (typeof v.wgsl !== 'string') return false;\n // glsl allows string | undefined | null (manifest.json may serialize null as\n // 'glsl: null').\n if (v.glsl !== undefined && v.glsl !== null && typeof v.glsl !== 'string') return false;\n if (typeof v.bindings !== 'string') return false;\n return true;\n}\n\nfunction isValidMaterialShaderManifestEntry(value: unknown): value is MaterialShaderManifestEntry {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.identifier !== 'string') return false;\n if (typeof v.sourcePath !== 'string') return false;\n if (typeof v.composedWgsl !== 'string') return false;\n if (typeof v.paramSchema !== 'string') return false;\n if (!Array.isArray(v.variants)) return false;\n if (!v.variants.every(isValidMaterialShaderManifestVariant)) return false;\n return v.uvSetCount === undefined || typeof v.uvSetCount === 'number';\n}\n\nfunction isValidMaterialShaderManifestVariant(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false;\n const v = value as Record<string, unknown>;\n if (typeof v.definesKey !== 'string') return false;\n if (typeof v.composedWgsl !== 'string') return false;\n if (typeof v.defines !== 'object' || v.defines === null || Array.isArray(v.defines)) {\n return false;\n }\n return Object.values(v.defines).every((define) => typeof define === 'boolean');\n}\n\nfunction buildMaterialShaderVariantKey(defines: Record<string, boolean>): string {\n const entries = Object.entries(defines).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n return entries.every(([, value]) => value)\n ? ''\n : entries.map(([key, value]) => `${key}=${value}`).join('+');\n}\n","// @forgeax/engine-shader/types — shared variant types consumed by the runtime\n// variant resolution layer (createRenderer.ts M3). The SSOT for the manifest\n// schema stays in @forgeax/engine-vite-plugin-shader; this file re-declares the\n// runtime-visible subset so the engine-shader package avoids a physical import\n// of the build-time plugin (physical-isolation grep gate).\n\n/**\n * Single variant within a material-shader manifest entry.\n * `definesKey` is the canonical defines string (sorted `key=value` pairs joined\n * with `+`). Empty key `\"\"` denotes the default variant (all axes `true`).\n *\n * Authored by vite-plugin-shader Cartesian compile at buildStart; consumed by\n * createRenderer variant resolution at engine boot (plan-strategy D-6).\n *\n * feat-20260613 fix-issue-2 (D-1 / D-2 bindingLayout cut): the runtime\n * derives the BGL from `derive(paramSchema).bglEntries`; manifest entries\n * no longer carry a bindingLayout sidecar (§Change stance: no v1/v2\n * dual-path, no incremental drop window).\n */\nexport interface MaterialShaderManifestVariant {\n readonly definesKey: string;\n readonly defines: Record<string, boolean>;\n readonly composedWgsl: string;\n}\n\n/**\n * A material-shader entry in manifest.json carrying zero or more variants.\n *\n * When `variants` is empty the entry is single-variant (no `#pragma variant_axis`\n * in the source). Otherwise `composedWgsl` carries the default\n * (all-true) variant for backward compat; the `variants[]` array contains every\n * compiled combination.\n *\n * For Engine.create() internal use only; AI users never see this type.\n */\nexport interface MaterialShaderManifestEntry {\n readonly identifier: string;\n readonly sourcePath: string;\n readonly composedWgsl: string;\n readonly paramSchema: string;\n readonly variants: readonly MaterialShaderManifestVariant[];\n /**\n * feat-20260629 M4: uvSetCount from naga vertex @location reflection.\n * Populated by vite-plugin-shader at build time from compileShader output.\n * The runtime reads this for clamp-to-last alias in deriveVertexBufferLayout.\n */\n readonly uvSetCount?: number;\n}\n\n/**\n * Look up a variant by canonical defines key.\n * Returns `undefined` when no variant matches the requested key.\n */\nexport function findVariantByKey(\n entry: MaterialShaderManifestEntry,\n definesKey: string,\n): MaterialShaderManifestVariant | undefined {\n return entry.variants.find((v) => v.definesKey === definesKey);\n}\n","/** Numeric mode IDs consumed by the built-in tonemap WGSL module. */\nexport const TONEMAP_SHADER_MODE = {\n none: 0,\n reinhardExtended: 1,\n linear: 2,\n cineon: 3,\n acesFilmic: 4,\n agx: 5,\n neutral: 6,\n reinhard: 7,\n} as const;\n\nexport type TonemapShaderMode = (typeof TONEMAP_SHADER_MODE)[keyof typeof TONEMAP_SHADER_MODE];\n","// @forgeax/engine-shader — runtime shader registry public surface.\n//\n// Shape rules (plan-strategy §S-10 / D-R10 / OQ-5 close):\n// - instance-per-engine — exposed through the lazy `engine.shader:\n// ShaderRegistry` property; module-level singletons / static methods are\n// forbidden (aligned with `Engine.create({ rhi })`'s instance-based style).\n// - Physical isolation — this package's deps only contain `@forgeax/engine-rhi` +\n// `@forgeax/engine-types`; importing `@forgeax/engine-shader-compiler` / `@forgeax/engine-naga`\n// / `@forgeax/engine-wgpu-wasm` directly or transitively is **forbidden**\n// (guarded by the AC-06 triple-grep gate; feat-20260511-naga-rhi-wgpu-merge\n// M4 replaced the legacy single-shim ban with the merged ban triple above).\n// - Result model — expected failures go through\n// `Result.err(RhiError | ShaderError)` and **never throw** (AGENTS.md\n// \"Errors are structured\" / charter proposition 4: explicit failure).\n//\n// Top-level surface (charter proposition 1: progressive disclosure):\n// - ShaderRegistry / ShaderRegistryOptions / ShaderRegistryDevice — main class\n// + injection interface\n// - ShaderError / ShaderErrorCode / 2 factories — runtime error types\n// - Result<T, E> + ok / err — binary result type and constructors\n// - ManifestEntry — re-exported from `@forgeax/engine-types` (manifest schema SSOT)\n\nexport type { ManifestEntry, ParamSchemaEntry } from '@forgeax/engine-types';\n\nimport type { MaterialAsset, MaterialParameter, MaterialValue } from '@forgeax/engine-types';\nimport { STANDARD_PBR_ALPHA_CUTOFF_DEFAULT } from './material-schemas.js';\n\nexport { MATERIAL_PARAM_TYPES } from '@forgeax/engine-types';\nexport {\n err,\n manifestMalformed,\n materialShaderNotFound,\n ok,\n type Result,\n type ResultErr,\n type ResultOk,\n ShaderError,\n type ShaderErrorCode,\n type ShaderErrorDetail,\n shaderNotFound,\n} from './errors.js';\nexport {\n type MaterialArtifactConflictError,\n type MaterialArtifactInspection,\n MaterialArtifactRegistry,\n type MaterialRuntimeArtifact,\n} from './material/artifact-registry.js';\nexport {\n isMaterialShaderArtifact,\n type MaterialShaderArtifact,\n} from './material/artifact-types.js';\nexport {\n DEFAULT_MSDF_TEXT_PARAM_SCHEMA,\n DEFAULT_SPRITE_PARAM_SCHEMA,\n DEFAULT_STANDARD_PBR_PARAM_SCHEMA,\n DEFAULT_UNLIT_PARAM_SCHEMA,\n STANDARD_PBR_ALPHA_CUTOFF_DEFAULT,\n} from './material-schemas.js';\nexport {\n registerDefaultSpriteLit,\n type SpriteLitCaps,\n} from './register-default-sprite-lit.js';\nexport { registerDefaultStandardPbrSkin } from './register-default-standard-pbr-skin.js';\nexport {\n FORGEAX_RESERVED_PATH_PREFIX,\n type MaterialShaderEntry,\n type RegisteredMaterialShaderEntry,\n ShaderRegistry,\n ShaderRegistry as ShaderCatalog,\n type ShaderRegistryDevice,\n type ShaderRegistryDevice as ShaderCatalogDevice,\n type ShaderRegistryOptions,\n type ShaderRegistryOptions as ShaderCatalogOptions,\n} from './ShaderRegistry.js';\nexport {\n findVariantByKey,\n type MaterialShaderManifestEntry,\n type MaterialShaderManifestVariant,\n} from './types.js';\n\nexport const BUILTIN_MATERIAL_MODULES = {\n standard: 'forgeax_material::standard',\n unlit: 'forgeax_material::unlit',\n sprite: 'forgeax_material::sprite',\n} as const;\n\n/** Stable build-time module id for the temporal-v1 accessor owner. */\nexport const SCENE_DATA_TEMPORAL_V1_SHADER_MODULE = 'forgeax_scene_temporal' as const;\n\n/**\n * Material modules whose shader source and parameter contract are owned by\n * the Engine. They live in the runtime ShaderRegistry, so a Pack containing\n * one of these materials carries authored values only and does not require a\n * project material-cook artifact.\n */\nexport const ENGINE_MATERIAL_MODULES = [\n 'forgeax::default-standard-pbr',\n 'forgeax::pbr-skin',\n 'forgeax::default-standard-pbr-skin',\n 'forgeax::default-unlit',\n 'forgeax::default-shadow-caster',\n 'forgeax::sprite',\n 'forgeax::sprite-lit',\n 'forgeax::msdf-text',\n BUILTIN_MATERIAL_MODULES.standard,\n BUILTIN_MATERIAL_MODULES.unlit,\n BUILTIN_MATERIAL_MODULES.sprite,\n 'forgeax_material::sprite-lit',\n] as const;\n\n/** Names of the zero-binding Standard reflection-probe shader helpers. */\nexport const REFLECTION_PROBE_SHADER_HELPERS = Object.freeze([\n 'box_project',\n 'sampleReflectionProbeSpecular',\n] as const);\n\nexport function isEngineMaterialModule(module: string): boolean {\n return (ENGINE_MATERIAL_MODULES as readonly string[]).includes(module);\n}\n\nexport function isEngineMaterial(material: Pick<MaterialAsset, 'passes'>): boolean {\n const passes = material.passes ?? [];\n return passes.length > 0 && passes.every((pass) => isEngineMaterialModule(pass.program.module));\n}\n\nexport type BuiltinMaterialKind = keyof typeof BUILTIN_MATERIAL_MODULES;\n\nconst BUILTIN_PARAMETERS: Readonly<Record<BuiltinMaterialKind, readonly MaterialParameter[]>> = {\n standard: [\n { name: 'baseColor', type: 'color' },\n { name: 'metallic', type: 'f32' },\n { name: 'roughness', type: 'f32' },\n { name: 'alphaCutoff', type: 'f32', optional: true },\n ],\n unlit: [{ name: 'baseColor', type: 'color' }],\n sprite: [{ name: 'colorTint', type: 'vec4', colorSpace: 'srgb' }],\n};\n\nconst BUILTIN_VALUES: Readonly<\n Record<BuiltinMaterialKind, Readonly<Record<string, MaterialValue>>>\n> = {\n standard: {\n baseColor: [1, 1, 1, 1],\n metallic: 0,\n roughness: 0.5,\n alphaCutoff: STANDARD_PBR_ALPHA_CUTOFF_DEFAULT,\n },\n unlit: { baseColor: [1, 1, 1, 1] },\n sprite: { colorTint: [1, 1, 1, 1] },\n};\n\nexport function createBuiltinMaterialAsset(kind: BuiltinMaterialKind): MaterialAsset {\n return {\n kind: 'material',\n passes: [{ name: 'forward', program: { module: BUILTIN_MATERIAL_MODULES[kind] } }],\n parameters: BUILTIN_PARAMETERS[kind],\n values: BUILTIN_VALUES[kind],\n };\n}\n\n/**\n * Shared luminance epsilon floor for the extended Reinhard tone-map\n * (feat-20260519-tonemap-reinhard-mvp / D-O3).\n *\n * Both the TS port at `packages/runtime/src/systems/tonemap.ts` and the WGSL\n * fragment stage in `packages/shader/src/tonemap.wgsl` apply\n * `max(Y, TONEMAP_LUMINANCE_EPSILON)` before dividing the luminance ratio.\n * The floor keeps the divisor finite at degenerate inputs (`Y = 0` from black\n * pixels, `Y < 0` from rare numerical artefacts). Single SSOT here so a\n * single `import { TONEMAP_LUMINANCE_EPSILON } from '@forgeax/engine-shader'`\n * keeps TS / WGSL byte-equivalent.\n *\n * Value: `1e-5` — small enough to not perturb any plausible HDR luminance.\n */\nexport const TONEMAP_LUMINANCE_EPSILON = 1e-5;\n\nexport { TONEMAP_SHADER_MODE, type TonemapShaderMode } from './tonemap.js';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeax/engine-shader",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"LICENSE"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@forgeax/engine-rhi": "0.1.
|
|
26
|
-
"@forgeax/engine-types": "0.1.
|
|
25
|
+
"@forgeax/engine-rhi": "0.1.21",
|
|
26
|
+
"@forgeax/engine-types": "0.1.21",
|
|
27
27
|
"@webgpu/types": "^0.1.71"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@forgeax/engine-vite-plugin-shader": "0.1.
|
|
30
|
+
"@forgeax/engine-vite-plugin-shader": "0.1.21"
|
|
31
31
|
},
|
|
32
32
|
"forgeax": {
|
|
33
33
|
"metrics": {
|
package/src/ShaderRegistry.ts
CHANGED
|
@@ -122,6 +122,36 @@ export interface ShaderRegistryOptions {
|
|
|
122
122
|
readonly manifestUrl: string | undefined;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
type ShaderManifestDiagnostic = {
|
|
126
|
+
readonly name: string;
|
|
127
|
+
readonly startedAt: number;
|
|
128
|
+
readonly previousElapsedMs: number;
|
|
129
|
+
readonly url?: string;
|
|
130
|
+
readonly status?: number;
|
|
131
|
+
readonly responseUrl?: string;
|
|
132
|
+
readonly entryCount?: number;
|
|
133
|
+
readonly materialShaderCount?: number;
|
|
134
|
+
readonly error?: string;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
function markShaderManifestStage(
|
|
138
|
+
name: string,
|
|
139
|
+
detail: Omit<ShaderManifestDiagnostic, 'name' | 'startedAt' | 'previousElapsedMs'> = {},
|
|
140
|
+
): void {
|
|
141
|
+
if (typeof globalThis === 'undefined') return;
|
|
142
|
+
const host = globalThis as {
|
|
143
|
+
__forgeaxShaderManifest?: ShaderManifestDiagnostic;
|
|
144
|
+
};
|
|
145
|
+
const previous = host.__forgeaxShaderManifest;
|
|
146
|
+
const now = Date.now();
|
|
147
|
+
host.__forgeaxShaderManifest = {
|
|
148
|
+
name,
|
|
149
|
+
startedAt: now,
|
|
150
|
+
previousElapsedMs: previous === undefined ? 0 : now - previous.startedAt,
|
|
151
|
+
...detail,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
125
155
|
// ─── ShaderRegistry main class (instance-per-engine) ============================
|
|
126
156
|
|
|
127
157
|
/**
|
|
@@ -194,10 +224,35 @@ export class ShaderRegistry {
|
|
|
194
224
|
}
|
|
195
225
|
|
|
196
226
|
let raw: string;
|
|
227
|
+
markShaderManifestStage('shader-manifest-fetch-start', { url: this.#manifestUrl });
|
|
197
228
|
try {
|
|
198
229
|
const response = await fetch(this.#manifestUrl);
|
|
230
|
+
markShaderManifestStage('shader-manifest-response', {
|
|
231
|
+
url: this.#manifestUrl,
|
|
232
|
+
status: response.status,
|
|
233
|
+
responseUrl: response.url,
|
|
234
|
+
});
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
markShaderManifestStage('shader-manifest-http-failed', {
|
|
237
|
+
url: this.#manifestUrl,
|
|
238
|
+
status: response.status,
|
|
239
|
+
responseUrl: response.url,
|
|
240
|
+
error: `HTTP ${response.status} ${response.statusText}`,
|
|
241
|
+
});
|
|
242
|
+
return err(
|
|
243
|
+
manifestMalformed({
|
|
244
|
+
message: `ShaderRegistry: manifest request returned HTTP ${response.status}`,
|
|
245
|
+
hint: 'verify the dev manifest middleware or production manifest asset is reachable',
|
|
246
|
+
reason: `${response.statusText || 'HTTP error'} at ${this.#manifestUrl}`,
|
|
247
|
+
}),
|
|
248
|
+
);
|
|
249
|
+
}
|
|
199
250
|
raw = await response.text();
|
|
200
251
|
} catch (e) {
|
|
252
|
+
markShaderManifestStage('shader-manifest-fetch-failed', {
|
|
253
|
+
url: this.#manifestUrl,
|
|
254
|
+
error: e instanceof Error ? e.message : String(e),
|
|
255
|
+
});
|
|
201
256
|
return err(
|
|
202
257
|
manifestMalformed({
|
|
203
258
|
message: `ShaderRegistry: failed to fetch manifest at ${this.#manifestUrl}`,
|
|
@@ -211,6 +266,10 @@ export class ShaderRegistry {
|
|
|
211
266
|
try {
|
|
212
267
|
parsed = JSON.parse(raw);
|
|
213
268
|
} catch (e) {
|
|
269
|
+
markShaderManifestStage('shader-manifest-json-failed', {
|
|
270
|
+
url: this.#manifestUrl,
|
|
271
|
+
error: e instanceof Error ? e.message : String(e),
|
|
272
|
+
});
|
|
214
273
|
return err(
|
|
215
274
|
manifestMalformed({
|
|
216
275
|
message: 'ShaderRegistry: manifest JSON parse failed',
|
|
@@ -325,6 +384,12 @@ export class ShaderRegistry {
|
|
|
325
384
|
this.#materialShaderManifestEntries.length = 0;
|
|
326
385
|
this.#materialShaderManifestEntries.push(...validatedMaterialShaderEntries);
|
|
327
386
|
this.#manifestLoaded = true;
|
|
387
|
+
markShaderManifestStage('shader-manifest-validated', {
|
|
388
|
+
url: this.#manifestUrl,
|
|
389
|
+
status: 200,
|
|
390
|
+
entryCount: validatedEntries.length,
|
|
391
|
+
materialShaderCount: validatedMaterialShaderEntries.length,
|
|
392
|
+
});
|
|
328
393
|
return ok(undefined);
|
|
329
394
|
}
|
|
330
395
|
|
|
@@ -51,7 +51,10 @@ describe('post shader color-domain contract', () => {
|
|
|
51
51
|
}
|
|
52
52
|
expect(body).toMatch(/default:\s*\{\s*mapped\s*=\s*sample;\s*\}/);
|
|
53
53
|
expect(body).toContain('let encoded = linearToSrgbOetf(mapped);');
|
|
54
|
-
expect(body).toContain('
|
|
54
|
+
expect(body).toContain('let output = select(');
|
|
55
|
+
expect(body).toContain('ditherUnorm8(encoded, in.position.xy)');
|
|
56
|
+
expect(body).toContain('params.ditherEnabled > 0.5');
|
|
57
|
+
expect(body).toContain('return vec4<f32>(output, source.a);');
|
|
55
58
|
expect(body).not.toContain('linearToSrgbOetf(exposed)');
|
|
56
59
|
});
|
|
57
60
|
|
|
@@ -84,13 +87,36 @@ describe('post shader color-domain contract', () => {
|
|
|
84
87
|
expect(`${tonemap}\n${bloom}\n${fxaa}`).not.toContain('encodedDestinationBlend');
|
|
85
88
|
});
|
|
86
89
|
|
|
87
|
-
it('returns display-encoded RGB
|
|
90
|
+
it('returns display-encoded RGB from both FXAA exits through the dither gate', () => {
|
|
88
91
|
const body = functionBody(codeOnly(fxaa), 'fs_main');
|
|
89
|
-
expect(
|
|
90
|
-
expect(
|
|
92
|
+
expect(fxaa).toContain('struct FxaaParams');
|
|
93
|
+
expect(fxaa).toMatch(/@group\(0\)\s+@binding\(2\)\s+var<uniform> params/);
|
|
94
|
+
expect(body).toContain('params.ditherEnabled > 0.5');
|
|
95
|
+
expect(body).toContain('select(centerColor, ditherUnorm8(centerColor, in.position.xy)');
|
|
96
|
+
expect(body).toContain('select(finalColor, ditherUnorm8(finalColor, in.position.xy)');
|
|
91
97
|
expect(body).not.toMatch(/linearToSrgbOetf\s*\(/);
|
|
92
98
|
});
|
|
93
99
|
|
|
100
|
+
it('dithers the final FXAA output before the 8-bit surface quantization', () => {
|
|
101
|
+
const code = codeOnly(fxaa);
|
|
102
|
+
const body = functionBody(code, 'fs_main');
|
|
103
|
+
expect(common).toContain('fn ditherUnorm8');
|
|
104
|
+
expect(common).toContain('fn ditherNoise');
|
|
105
|
+
expect(code).not.toContain('fn ditherUnorm8');
|
|
106
|
+
expect(code).not.toContain('fn ditherNoise');
|
|
107
|
+
expect(code).toContain('in.position.xy');
|
|
108
|
+
expect(body).toContain('ditherUnorm8(centerColor, in.position.xy)');
|
|
109
|
+
expect(body).toContain('ditherUnorm8(finalColor, in.position.xy)');
|
|
110
|
+
|
|
111
|
+
const quantize = (value: number, noise: number): number =>
|
|
112
|
+
Math.round(Math.min(1, Math.max(0, value + (0.5 - noise) * (1 / 255))) * 255);
|
|
113
|
+
expect(quantize(0.5, 0)).toBe(128);
|
|
114
|
+
expect(quantize(0.5, 1)).toBe(127);
|
|
115
|
+
expect(quantize(0, 0)).toBe(1);
|
|
116
|
+
expect(quantize(0, 1)).toBe(0);
|
|
117
|
+
expect(quantize(1, 1)).toBe(255);
|
|
118
|
+
});
|
|
119
|
+
|
|
94
120
|
it('retains FXAA luma, edge search, and subpixel contract markers', () => {
|
|
95
121
|
const code = codeOnly(fxaa);
|
|
96
122
|
expect(code).toContain('fn rgb2luma');
|
|
@@ -16,4 +16,11 @@ describe('standard PBR MASK alpha contract', () => {
|
|
|
16
16
|
expect(shader).toContain('alpha <= material.alphaCutoff');
|
|
17
17
|
expect(shader).not.toContain('alpha < material.alphaCutoff');
|
|
18
18
|
});
|
|
19
|
+
|
|
20
|
+
it('keeps alpha PBR on the shared Directional factor path', () => {
|
|
21
|
+
expect(shader).toContain('lighting_directional');
|
|
22
|
+
expect(shader.match(/evalDirectionalShadowFactor\(/g)).toHaveLength(1);
|
|
23
|
+
expect(shader).toContain('directionalBase');
|
|
24
|
+
expect(shader).toContain('directionalClearcoat');
|
|
25
|
+
});
|
|
19
26
|
});
|