@forgeax/engine-runtime 0.1.20 → 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.
Files changed (58) hide show
  1. package/README.md +102 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/__tests__/create-renderer-gpu-pass-timing.unit.test.d.ts +2 -0
  4. package/dist/__tests__/create-renderer-gpu-pass-timing.unit.test.d.ts.map +1 -0
  5. package/dist/__tests__/lights-preservation.fixup.test.d.ts +2 -0
  6. package/dist/__tests__/lights-preservation.fixup.test.d.ts.map +1 -0
  7. package/dist/__tests__/render-error-exhaustive.test-d.d.ts.map +1 -1
  8. package/dist/createRenderer.d.ts.map +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.mjs +2 -1
  11. package/dist/index.mjs.map +1 -1
  12. package/package.json +30 -30
  13. package/src/__tests__/active-camera.unit.test.ts +6 -8
  14. package/src/__tests__/asset-registry.recursive.spec.ts +1 -1
  15. package/src/__tests__/clamp-to-last.e2e.browser.test.ts +2 -2
  16. package/src/__tests__/components.test-d.ts +10 -3
  17. package/src/__tests__/components.unit.test.ts +13 -7
  18. package/src/__tests__/create-renderer-assembly.integration.test.ts +24 -0
  19. package/src/__tests__/create-renderer-gpu-pass-timing.unit.test.ts +32 -0
  20. package/src/__tests__/createRenderer.browser-no-webgpu.test.ts +66 -0
  21. package/src/__tests__/dawn/hello-sprite-nineslice-section.dawn.test.ts +9 -1
  22. package/src/__tests__/dawn/instances-shadow.dawn.test.ts +15 -2
  23. package/src/__tests__/errors.unit.test.ts +51 -20
  24. package/src/__tests__/extract-frames-merge.test.ts +48 -40
  25. package/src/__tests__/extract-skin-mismatch.unit.test.ts +6 -2
  26. package/src/__tests__/frame-targets-shadow-regression.unit.test.ts +13 -0
  27. package/src/__tests__/geometry.unit.test.ts +7 -10
  28. package/src/__tests__/light-gpu-byte-neutral.unit.test.ts +2 -1
  29. package/src/__tests__/light-snapshot.test-d.ts +8 -0
  30. package/src/__tests__/lights-preservation.fixup.test.ts +127 -0
  31. package/src/__tests__/lights.unit.test.ts +75 -2235
  32. package/src/__tests__/pbr-pipeline.unit.test.ts +6 -1
  33. package/src/__tests__/pipeline.unit.test.ts +48 -202
  34. package/src/__tests__/point-light-shadow.browser.test.ts +208 -83
  35. package/src/__tests__/point-light-shadow.unit.test.ts +61 -4
  36. package/src/__tests__/render-error-exhaustive.test-d.ts +63 -9
  37. package/src/__tests__/render-system-mega.test.ts +30 -41
  38. package/src/__tests__/render-system-record-multi-material-textureview.test.ts +4 -1
  39. package/src/__tests__/render-system-record-per-submesh-transparency.test.ts +4 -1
  40. package/src/__tests__/render-system-skin-bg.unit.test.ts +14 -9
  41. package/src/__tests__/renderer-recover.unit.test.ts +8 -0
  42. package/src/__tests__/renderer-surface.unit.test.ts +229 -3
  43. package/src/__tests__/shadow-csm-atlas.test.ts +41 -0
  44. package/src/__tests__/shadow-csm-component.test.ts +31 -24
  45. package/src/__tests__/shadow-csm-extract.test.ts +156 -47
  46. package/src/__tests__/shadow-csm-runtime-vary.dawn.test.ts +72 -0
  47. package/src/__tests__/shadow-csm-shader.dawn.test.ts +19 -0
  48. package/src/__tests__/shadow-csm-ubo.test.ts +41 -45
  49. package/src/__tests__/shadow-csm-viewport.browser.test.ts +29 -0
  50. package/src/__tests__/shadow-fields-observable.dawn.test.ts +85 -20
  51. package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +3 -1
  52. package/src/__tests__/skylight-lazy-projection.test.ts +8 -5
  53. package/src/__tests__/ssao-bgl.test.ts +10 -16
  54. package/src/__tests__/ssao-passes.test.ts +19 -2
  55. package/src/__tests__/systems.unit.test.ts +44 -31
  56. package/src/__tests__/view-ubo-layout.browser.test.ts +20 -10
  57. package/src/createRenderer.ts +1 -0
  58. package/src/index.ts +1 -2
package/README.md CHANGED
@@ -45,6 +45,58 @@ stale receipt is a structured failure rather than an implicit read of the
45
45
  current frame. Repair the owner named by `error.detail`, then retry the same
46
46
  request.
47
47
 
48
+ ## Directional shadow authoring and recovery index
49
+
50
+ The cold-AI entry is `DirectionalLight.shadowFilter`, followed by one read of
51
+ `renderer.inspect().directionalShadow` read. The closed labels are
52
+ `pcf1`, `pcf3`, `pcf5`, `pcssMedium`, and `pcssHigh`; the default is `pcf3`.
53
+ `shadowAngularRadius` is measured in radians, defaults to `0.00465`, and must
54
+ be in `[0.0001, 0.05]`. `maxPenumbraTexels` is a finite integer measured in
55
+ texels, defaults to `32`, and must be in `[1, 64]`. These are Directional
56
+ author facts; Point/Spot `pcfKernelSize` remains a separate contract.
57
+
58
+ The detached `directionalShadow` projection explains the next action without
59
+ reading renderer private state:
60
+
61
+ | Inspection field | AI interpretation |
62
+ |:--|:--|
63
+ | `requested` / `effective` | Author request and admitted profile |
64
+ | `status` | `accepted`, explicit `fallback`, or `rejected` |
65
+ | `fallbackReason` | `webgl2-unsupported`, `rhi-null-structural`, or `candidate-failed` |
66
+ | `lastKnownGood` | Whether the previous admitted profile was retained |
67
+ | `pixelEvidence` | `available` or `not-available`; structural evidence is not pixel proof |
68
+ | `deviceGeneration` / `graphGeneration` | 当前 inspection result 的 generation identity |
69
+ | `cascadeCount`, `mapSize`, `atlasBytes`, `writerPasses`, `blockerTaps`, `filterTapUpperBound`, `seamTapUpperBound` | Bounded CSM and tap-budget facts |
70
+
71
+ For `error.code === 'shadow-invalid-config'`, use the structured properties
72
+ `error.expected`, `error.hint`, and `error.detail.field`, `actual`, `bound`, and
73
+ `reason`. Set the named field to the bound described by `detail`, then retry;
74
+ the prose `error.message` is not an API. For a failed capable candidate, keep
75
+ the `lastKnownGood` projection, repair or rebuild its producer, call
76
+ `renderer.recover()`, and retry the identical draw. WebGL2 may expose only its
77
+ explicit PCF fallback, while RhiNull is structural (`pixelEvidence:
78
+ not-available`); neither state may be rewritten as PCSS support. A
79
+ `not-run` Browser/Dawn/PNG/timing receipt stays `not-run`.
80
+
81
+ The complete author → inspect → recover path is:
82
+
83
+ ```ts
84
+ const renderer = (await createRenderer(canvas)).unwrap();
85
+ // Spawn DirectionalLight with a legal shadowFilter and PCSS units first.
86
+ const frame = renderer.draw(request);
87
+ if (!frame.ok) {
88
+ // Read frame.error.code, .expected, .hint, and typed .detail.
89
+ throw frame.error;
90
+ }
91
+ const shadow = renderer.inspect().directionalShadow;
92
+ // Repair the named owner, renderer.recover(), then retry request when needed.
93
+ void shadow;
94
+ ```
95
+
96
+ The labels, units, defaults, structured error shape, and fallback vocabulary
97
+ are shared with [`@forgeax/engine-render`](../render/README.md); runtime owns
98
+ host assembly and recovery invocation, not a second Directional schema.
99
+
48
100
  ## Render happy path
49
101
 
50
102
  `createRenderer -> attach -> draw -> inspect/observe/recover` assembles one
@@ -56,6 +108,41 @@ named by `error.detail`, rebuild or cold-cook its source, and retry.
56
108
  > [!IMPORTANT]
57
109
  > Runtime is the sole public host-assembly entry for `createRenderer`. It selects browser/backend services, invokes render's internal construction seam, and cleans up partial construction. It does not own scene, skinning, animation, or render-domain APIs.
58
110
 
111
+ ## Standard profile recovery
112
+
113
+ The host exposes one `forgeax::standard` profile. `lightCount` is one of
114
+ `1 | 32 | 256`, `renderPath` is `forward | deferred`, and local finite lights
115
+ are always prepared for the shared Cluster transport. Invalid profile input
116
+ returns `standard-profile-invalid` with `expected`, `hint`, and `detail`; the
117
+ failed transaction keeps the last-known-good profile and device generation.
118
+
119
+ ## Point-shadow creation recipe
120
+
121
+ Point shadows stay discoverable through the App recipe while the renderer
122
+ remains the sole owner of atlas admission and frame facts. Install
123
+ `pointShadowPlugin()` beside the existing renderer provider, then preflight a
124
+ caster count and read the detached inspection after a successful frame:
125
+
126
+ ```ts
127
+ import { pointShadowPlugin } from '@forgeax/engine-app';
128
+
129
+ // Include this in the same App/Worker plugin list as rendererPlugin(renderer).
130
+ const pointShadow = app.pluginContext.pointShadow;
131
+ const result = pointShadow?.admit(1);
132
+ if (result !== undefined && !result.ok) throw result.error;
133
+ const facts = pointShadow?.inspect();
134
+ void facts?.shadowed;
135
+ ```
136
+
137
+ `admit()` returns a structured `PointShadowRecipeError` for missing
138
+ `storageBuffer`, an invalid request, or a count above the renderer's shared
139
+ `ShadowAtlas` capacity. A World without `PointLightShadow` is the real inactive
140
+ case; there is no pseudo-disabled mode. `inspect()` reports `requested`,
141
+ `admitted`, `shadowed`, occupancy, and capacity from the last submitted frame;
142
+ it is not a second shadow registry. Directional CSM remains the renderer's
143
+ separate global path, and Runtime does not create another provider or submit
144
+ path.
145
+
59
146
  ## Assemble producer features
60
147
 
61
148
  The host receives a heterogeneous list of producer-owned
@@ -155,6 +242,21 @@ its structured construction error. After construction, `attach`, `draw`,
155
242
  `inspect`, `observe`, and `recover` remain receipt-bound and `dispose()` is
156
243
  idempotent.
157
244
 
245
+ ## GPU pass timing forwarding
246
+
247
+ `CreateRendererOptions.gpuPassTiming` is forwarded unchanged through the
248
+ existing renderer-options projection. Runtime does not request timestamp
249
+ features, create a timing session, retain RHI handles, inspect a pass catalog,
250
+ or change `ProfileCapture`. Use the Render-owned public route: opt in, keep the
251
+ `draw()` `FrameReceipt`, call `observe(receipt, { include: ['timings'] })`, and
252
+ branch on the four statuses and structured `code`/`hint`. The accepted bounded
253
+ contract and recovery path are documented in
254
+ [`packages/render/README.md`](../render/README.md).
255
+
256
+ Membership timing is a separate producer-specific capability. Its records are
257
+ not generic accepted GPU pass evidence and must not become a second runtime
258
+ owner or controller.
259
+
158
260
  ### Browser backend selection and diagnosis
159
261
 
160
262
  The default browser path prefers native WebGPU and can retry through the