@forgeax/engine-runtime 0.1.28 → 0.1.29

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 (113) hide show
  1. package/README.md +98 -0
  2. package/dist/collect-scene-asset.d.ts.map +1 -1
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.mjs +480 -15
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/scene-utils/mount-override-fold.d.ts +3 -1
  8. package/dist/scene-utils/mount-override-fold.d.ts.map +1 -1
  9. package/package.json +37 -32
  10. package/src/__tests__/animation-graph-collect-roundtrip.test.ts +4 -2
  11. package/src/__tests__/asset-registry-aabb.cases.unit.test.ts +5 -4
  12. package/src/__tests__/asset-registry-guid-reverse.test.ts +22 -27
  13. package/src/__tests__/asset-registry-mounts.test.ts +174 -734
  14. package/src/__tests__/asset-registry-scene.cases.unit.test.ts +9 -18
  15. package/src/__tests__/asset-registry.recursive.spec.ts +23 -29
  16. package/src/__tests__/camera-ortho.cases.unit.test.ts +12 -3
  17. package/src/__tests__/collect-scene-asset.test.ts +96 -113
  18. package/src/__tests__/collect-scene-mount-collapse.test.ts +64 -66
  19. package/src/__tests__/collect-transient-children-absent.test.ts +11 -17
  20. package/src/__tests__/collect-transient-roundtrip.test.ts +12 -13
  21. package/src/__tests__/collect-transient-skip.test.ts +20 -27
  22. package/src/__tests__/components.test-d.ts +17 -1
  23. package/src/__tests__/components.unit.test.ts +75 -27
  24. package/src/__tests__/composite-skybox-cross-world.browser.test.ts +4 -2
  25. package/src/__tests__/dawn/instances-per-instance-pbr.dawn.test.ts +8 -4
  26. package/src/__tests__/dawn/instances-shadow.dawn.test.ts +4 -1
  27. package/src/__tests__/dawn/instances-uniform-fallback.dawn.test.ts +66 -92
  28. package/src/__tests__/dawn/material-cooked-fixture.dawn.test.ts +200 -4
  29. package/src/__tests__/errors.unit.test.ts +13 -0
  30. package/src/__tests__/feature-depth-material.dawn.test.ts +23 -0
  31. package/src/__tests__/feature-depth-material.fixture.ts +473 -0
  32. package/src/__tests__/fullscreen-post-process-pass.dawn.test.ts +14 -14
  33. package/src/__tests__/geometry.unit.test.ts +42 -43
  34. package/src/__tests__/gpu-resource-release.cases.unit.test.ts +16 -9
  35. package/src/__tests__/handle-migration.test.ts +0 -1
  36. package/src/__tests__/instances-renderer-lifecycle.integration.test.ts +20 -14
  37. package/src/__tests__/instances.test-d.ts +13 -52
  38. package/src/__tests__/instantiate-mount-deferred-wiring.test.ts +42 -80
  39. package/src/__tests__/lazy-catalog.cases.unit.test.ts +17 -3
  40. package/src/__tests__/loader-registry.cases.unit.test.ts +4 -4
  41. package/src/__tests__/material-publication.browser.test.ts +29 -0
  42. package/src/__tests__/material-publication.commands.ts +23 -0
  43. package/src/__tests__/material-publication.dawn.test.ts +24 -0
  44. package/src/__tests__/material-publication.fixture.ts +349 -0
  45. package/src/__tests__/material-publication.server.ts +109 -0
  46. package/src/__tests__/parse-scene-payload-refs.cases.unit.test.ts +73 -96
  47. package/src/__tests__/pbr-pipeline.unit.test.ts +25 -11
  48. package/src/__tests__/point-light-shadow.browser.test.ts +8 -2
  49. package/src/__tests__/point-light-shadow.unit.test.ts +37 -8
  50. package/src/__tests__/record-all-topology.cases.unit.test.ts +52 -0
  51. package/src/__tests__/record-draw-branch.cases.unit.test.ts +1 -0
  52. package/src/__tests__/record-worldid-isolation.test.ts +47 -28
  53. package/src/__tests__/render-error-exhaustive.test-d.ts +24 -0
  54. package/src/__tests__/render-system-extract-dispatch-ordering.test.ts +15 -4
  55. package/src/__tests__/render-system-extract.test.ts +61 -0
  56. package/src/__tests__/render-system-extract.unit.test.ts +21 -7
  57. package/src/__tests__/render-system-fold-bucket.unit.test.ts +33 -1
  58. package/src/__tests__/render-system-fold-material.unit.test.ts +32 -6
  59. package/src/__tests__/render-system-fold-modegate.unit.test.ts +26 -4
  60. package/src/__tests__/render-system-fold.dawn.test.ts +4 -1
  61. package/src/__tests__/render-system-mega.test.ts +50 -62
  62. package/src/__tests__/renderer-device-loss-provider-feasibility.dawn.test.ts +0 -1
  63. package/src/__tests__/renderer-device-loss-recovery.dawn.test.ts +0 -3
  64. package/src/__tests__/renderer-draw-world.cases.unit.test.ts +16 -513
  65. package/src/__tests__/resolve-scene-guids.cases.unit.test.ts +48 -43
  66. package/src/__tests__/rhi-null-command-flow.unit.test.ts +9 -7
  67. package/src/__tests__/roots-to-scene-asset.test.ts +27 -28
  68. package/src/__tests__/roundtrip-primitive.test.ts +24 -23
  69. package/src/__tests__/scene-equirect-roundtrip.test.ts +5 -16
  70. package/src/__tests__/scene-instantiate-cycle-runtime.test.ts +13 -29
  71. package/src/__tests__/scene-mount-fixed-point.test.ts +104 -227
  72. package/src/__tests__/scene-mount-roundtrip.test.ts +95 -961
  73. package/src/__tests__/scene-mount-wrapper-roundtrip.test.ts +36 -182
  74. package/src/__tests__/scene-sugar-instantiate.test.ts +20 -15
  75. package/src/__tests__/serialize-mounts-roundtrip.test.ts +108 -492
  76. package/src/__tests__/serialize-scene-asset.test.ts +36 -50
  77. package/src/__tests__/shadow-caster-empty-schema.test.ts +3 -1
  78. package/src/__tests__/shadow-fields-observable.dawn.test.ts +34 -19
  79. package/src/__tests__/shadow-mesh-ssbo-order.test.ts +5 -12
  80. package/src/__tests__/skin-extract-getarrayview-count.unit.test.ts +6 -4
  81. package/src/__tests__/skin-m2-extract-happy.cases.unit.test.ts +50 -2
  82. package/src/__tests__/skin-m2-extract-joint-error.cases.unit.test.ts +50 -2
  83. package/src/__tests__/skin-m2-extract-null-assets.cases.unit.test.ts +50 -2
  84. package/src/__tests__/skin-m2-extract-skeleton-error.cases.unit.test.ts +50 -2
  85. package/src/__tests__/skin-palette-extract.cases.unit.test.ts +2 -0
  86. package/src/__tests__/skinned-shadow-mixed.dawn.test.ts +93 -48
  87. package/src/__tests__/skylight-pipeline-layout.cases.unit.test.ts +6 -2
  88. package/src/__tests__/sparse-tag-scene-roundtrip.test.ts +1 -1
  89. package/src/__tests__/sprite-lit-bgl-byte-identical.test.ts +11 -3
  90. package/src/__tests__/template-game-default-api-contract.test.ts +21 -21
  91. package/src/__tests__/tonemap-hdr-target.cases.unit.test.ts +8 -5
  92. package/src/__tests__/tonemap-pipeline-split.cases.unit.test.ts +3 -0
  93. package/src/__tests__/transient-fields-collect.test.ts +4 -4
  94. package/src/__tests__/transient-runtime-behavior.test.ts +9 -9
  95. package/src/__tests__/vfx-depth-material.dawn.test.ts +42 -0
  96. package/src/__tests__/vfx-mesh-lighting.browser.test.ts +16 -0
  97. package/src/__tests__/vfx-mesh-lighting.commands.ts +14 -0
  98. package/src/__tests__/vfx-mesh-lighting.dawn.test.ts +16 -0
  99. package/src/__tests__/vfx-mesh-lighting.fixture.ts +883 -0
  100. package/src/__tests__/wave1-rendering-materials.browser.test.ts +617 -0
  101. package/src/__tests__/wave1-rendering-p0.browser.test.ts +751 -0
  102. package/src/__tests__/wave1-rendering-recovery.browser.test.ts +666 -0
  103. package/src/__tests__/wave1-shadow-diagnostic.browser.test.ts +582 -0
  104. package/src/__tests__/world-scene-despawn.test.ts +8 -8
  105. package/src/__tests__/world-scene-instantiate.test.ts +23 -29
  106. package/src/__tests__/world-scene-nested.test.ts +90 -163
  107. package/src/__tests__/world-scene-override.test.ts +17 -16
  108. package/src/__tests__/zero-compression-zero-load.integration.test.ts +1 -0
  109. package/src/collect-scene-asset.ts +637 -19
  110. package/src/components/__tests__/sprite-animation.test-d.ts +2 -2
  111. package/src/index.ts +1 -0
  112. package/src/scene-utils/mount-override-fold.ts +5 -14
  113. package/src/__tests__/node-shims.d.ts +0 -31
@@ -1,514 +1,17 @@
1
- // @ts-nocheck merged file: cross-source node:fs/node:path imports outside @types/node coverage in runtime tsconfig
2
- // Consolidated by feat-20260609-test-pool-startup-reduction-merge-tiny-test-files
3
- // biome-ignore-all lint/complexity/noUselessLoneBlockStatements: block-scope isolation between merged source files (consolidation paradigm)
4
- //
5
- // Source files (N=33):
6
- // - packages/runtime/__tests__/create-renderer-fallback.test.ts
7
- // - packages/runtime/src/__tests__/cluster-binner.test.ts
8
- // - packages/runtime/src/__tests__/create-renderer-fallback-shader-manifest.test.ts
9
- // - packages/runtime/src/__tests__/create-renderer-uniform-fallback.test.ts
10
- // - packages/runtime/src/__tests__/createRenderer.test.ts
11
- // - packages/runtime/src/__tests__/dispatch-sort.test.ts
12
- // - packages/runtime/src/__tests__/engine-metrics.test.ts
13
- // - packages/runtime/src/__tests__/device/gpu-residency-caps-guard.test.ts
14
- // - packages/runtime/src/__tests__/pass-selector.test.ts
15
- // - packages/runtime/src/__tests__/pipeline-builder.test.ts
16
- // - packages/runtime/src/__tests__/pipeline-cache-key-topology.test.ts
17
- // - packages/runtime/src/__tests__/pipeline-rename-grep-gate.test.ts
18
- // - packages/runtime/src/__tests__/pipeline-vertex-stride-branch.test.ts
19
- // - packages/runtime/src/__tests__/post-process-register.test.ts
20
- // - packages/runtime/src/__tests__/record-all-topology.test.ts
21
- // - packages/runtime/src/__tests__/record-draw-branch.test.ts
22
- // - packages/runtime/src/__tests__/record-strip-index-format.test.ts
23
- // - packages/runtime/src/__tests__/render-query-regression.test.ts
24
- // - packages/runtime/src/__tests__/renderer-draw-world.test.ts
25
- // - packages/runtime/src/__tests__/renderer-input-snapshot.test.ts
26
- // - packages/runtime/src/__tests__/renderer-read-pixels.test.ts
27
- // - packages/runtime/src/__tests__/renderer-ready.test.ts
28
- // - packages/runtime/src/__tests__/renderstate-pipeline-cache.test.ts
29
- // - packages/runtime/src/__tests__/storage-buffer-caps.test.ts
30
- // - packages/runtime/src/__tests__/device/gpu-residency.test.ts
31
- // - packages/runtime/src/__tests__/render-data.test.ts
32
- // - packages/runtime/src/__tests__/hdrp-bgl-slots.test.ts
33
- // - packages/runtime/src/__tests__/hdrp-caps-gate.test.ts
34
- // - packages/runtime/src/__tests__/hdrp-grid-invalid.test.ts
35
- // - packages/runtime/src/__tests__/hdrp-index-list-overflow-once.test.ts
36
- // - packages/runtime/src/__tests__/hdrp-light-budget.test.ts
37
- // - packages/runtime/src/__tests__/hdrp-pipeline-asset-config.test.ts
38
- // - packages/runtime/src/__tests__/m7-hdrp-demo-shape.test.ts
39
- //
40
- // Paradigm: each block-scope wraps a source file. ancestorTitles[0] is the
41
- // source-preserved inner describe (NOT the source filename for these 3 files
42
- // — recovery path: vitest report ancestorTitles -> grep this file -> upstream
43
- // `// ─── from <name>.test.ts ───` block separator -> source filename).
44
- // Top-level imports merged + deduped.
45
-
46
- import { existsSync, readFileSync } from 'node:fs';
47
- import { dirname, resolve } from 'node:path';
48
- import { fileURLToPath } from 'node:url';
49
- import { HANDLE_CUBE, HANDLE_TRIANGLE, resolveAssetHandle } from '@forgeax/engine-assets-runtime';
50
- import type { World as WorldType } from '@forgeax/engine-ecs';
51
- import { defineComponent, World } from '@forgeax/engine-ecs';
52
- import {
53
- buildMeshAttributeMapForUvSets,
54
- createBoxGeometry,
55
- createConeGeometry,
56
- createCylinderGeometry,
57
- createPlaneGeometry,
58
- createSphereGeometry,
59
- createTorusGeometry,
60
- PROCEDURAL_FLOATS_PER_VERTEX,
61
- } from '@forgeax/engine-geometry';
62
- import { type Mat4, mat4, type Vec3, vec3 } from '@forgeax/engine-math';
63
- import type { Renderer as RendererType } from '@forgeax/engine-render';
64
- import { Camera, MeshFilter, MeshRenderer } from '@forgeax/engine-render';
65
- import {
66
- err,
67
- ok,
68
- type PipelineLayout,
69
- type RenderPipeline,
70
- type Result,
71
- type RhiCaps,
72
- type RhiDevice,
73
- RhiError,
74
- ok as rhiOk,
75
- type ShaderModule,
76
- } from '@forgeax/engine-rhi';
77
- import {
78
- acquireCanvasContext as acquireNullCanvasContext,
79
- createShaderModule as createNullShaderModule,
80
- RhiNullAdapter,
81
- } from '@forgeax/engine-rhi-null';
82
- import { ChildOf, Transform } from '@forgeax/engine-scene';
83
- import {
84
- findVariantByKey,
85
- type MaterialShaderEntry,
86
- type MaterialShaderManifestEntry,
87
- type MaterialShaderManifestVariant,
88
- } from '@forgeax/engine-shader';
89
- import {
90
- type AssetError,
91
- type EquirectAsset,
92
- type Handle,
93
- type MaterialRenderState,
94
- type MeshAsset,
95
- type PassSelector,
96
- type PrimitiveTopology,
97
- type RenderPipelineAsset,
98
- type TextureAsset,
99
- toShared,
100
- } from '@forgeax/engine-types';
101
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
102
- import {
103
- bin,
104
- type ClusterBinError,
105
- calculateSphereClusterBounds,
106
- clusterSpaceObjectAabb,
107
- deriveCullingRadius,
108
- ndcPositionToCluster,
109
- viewZToZSlice,
110
- } from '../../../render/src/cluster-binner';
111
- import { GpuResidencyCache } from '../../../render/src/device/gpu-residency';
112
- import { createEngineMetrics } from '../../../render/src/engine-metrics';
113
- import { assertStorageBufferCap } from '../../../render/src/light-buffer-layout';
114
- import {
115
- buildPbrPipelineLayouts,
116
- buildPbrViewBglEntries,
117
- type PbrCaps,
118
- } from '../../../render/src/pbr-pipeline';
119
- import {
120
- HdrpInstallError,
121
- validateClusterGrid,
122
- } from '../../../render/src/pipeline/standard-pipeline';
123
- import {
124
- buildPipelineForMaterialShader,
125
- type PipelineBuilderContext,
126
- type PipelineBuilderShaderModuleFactory,
127
- } from '../../../render/src/pipeline-builder';
128
- import {
129
- cacheKeyOf,
130
- createHdrpBindGroupLayoutDescriptor,
131
- type PipelineSpec,
132
- } from '../../../render/src/pipeline-spec';
133
- import {
134
- deriveRenderDataCubemap,
135
- deriveRenderDataMesh,
136
- deriveRenderDataTexture,
137
- } from '../../../render/src/render-data';
138
- import { extractFrame, prepareExtractContext } from '../../../render/src/render-system-extract';
139
- import { matchPass } from '../../../render/src/systems/pass-selector';
140
- import { makeMockShaderRegistry } from './helpers/mock-shader-registry';
141
- import { standardMaterialShaderVariants } from './helpers/standard-material-manifest';
142
-
143
- function makeExplicitNullRhi(spies: {
144
- setIndexBuffer: ReturnType<typeof vi.fn>;
145
- setVertexBuffer: ReturnType<typeof vi.fn>;
146
- draw: ReturnType<typeof vi.fn>;
147
- drawIndexed: ReturnType<typeof vi.fn>;
148
- }): unknown {
149
- const adapter = new RhiNullAdapter();
150
- const requestDevice = adapter.requestDevice.bind(adapter);
151
- adapter.requestDevice = async () => {
152
- const result = await requestDevice();
153
- if (result.ok) {
154
- const device = result.value;
155
- const createCommandEncoder = device.createCommandEncoder.bind(device);
156
- device.createCommandEncoder = (desc) => {
157
- const encoderResult = createCommandEncoder(desc);
158
- if (!encoderResult.ok) return encoderResult;
159
- const encoder = encoderResult.value;
160
- const beginRenderPass = encoder.beginRenderPass.bind(encoder);
161
- encoder.beginRenderPass = (passDesc) => {
162
- const pass = beginRenderPass(passDesc);
163
- const draw = pass.draw.bind(pass);
164
- const drawIndexed = pass.drawIndexed.bind(pass);
165
- const setIndexBuffer = pass.setIndexBuffer.bind(pass);
166
- const setVertexBuffer = pass.setVertexBuffer.bind(pass);
167
- pass.draw = (...args) => {
168
- spies.draw(...args);
169
- draw(...args);
170
- };
171
- pass.drawIndexed = (...args) => {
172
- spies.drawIndexed(...args);
173
- drawIndexed(...args);
174
- };
175
- pass.setIndexBuffer = (...args) => {
176
- spies.setIndexBuffer(...args);
177
- setIndexBuffer(...args);
178
- };
179
- pass.setVertexBuffer = (...args) => {
180
- spies.setVertexBuffer(...args);
181
- setVertexBuffer(...args);
182
- };
183
- return pass;
184
- };
185
- return { ok: true, value: encoder };
186
- };
187
- }
188
- return result;
189
- };
190
- return {
191
- requestAdapter: async () => ({ ok: true, value: adapter }),
192
- acquireCanvasContext: acquireNullCanvasContext,
193
- createShaderModule: (device: unknown, desc: { code: string; label?: string }) =>
194
- createNullShaderModule(device as never, desc),
195
- };
196
- }
197
-
198
- vi.mock('@forgeax/engine-rhi-wgpu', () => {
199
- return {
200
- rhi: {
201
- requestAdapter: async () => ({
202
- ok: false,
203
- error: {
204
- code: 'adapter-unavailable',
205
- expected: 'adapter available',
206
- hint: 'default mock fail',
207
- },
208
- }),
209
- getPreferredCanvasFormat: () => 'bgra8unorm',
210
- acquireCanvasContext: () => ({
211
- ok: false,
212
- error: {
213
- code: 'webgpu-runtime-error',
214
- expected: 'context available',
215
- hint: 'default mock fail',
216
- },
217
- }),
218
- },
219
- ensureReady: async () => undefined,
220
- };
221
- });
222
-
223
- void [
224
- Camera,
225
- ChildOf,
226
- GpuResidencyCache,
227
- HANDLE_CUBE,
228
- HANDLE_TRIANGLE,
229
- HdrpInstallError,
230
- MeshFilter,
231
- MeshRenderer,
232
- PROCEDURAL_FLOATS_PER_VERTEX,
233
- RhiError,
234
- RhiNullAdapter,
235
- Transform,
236
- World,
237
- acquireNullCanvasContext,
238
- afterEach,
239
- assertStorageBufferCap,
240
- beforeEach,
241
- bin,
242
- buildMeshAttributeMapForUvSets,
243
- buildPbrPipelineLayouts,
244
- buildPbrViewBglEntries,
245
- buildPipelineForMaterialShader,
246
- cacheKeyOf,
247
- calculateSphereClusterBounds,
248
- clusterSpaceObjectAabb,
249
- createBoxGeometry,
250
- createConeGeometry,
251
- createCylinderGeometry,
252
- createEngineMetrics,
253
- createHdrpBindGroupLayoutDescriptor,
254
- createNullShaderModule,
255
- createPlaneGeometry,
256
- createSphereGeometry,
257
- createTorusGeometry,
258
- defineComponent,
259
- deriveCullingRadius,
260
- deriveRenderDataCubemap,
261
- deriveRenderDataMesh,
262
- deriveRenderDataTexture,
263
- describe,
264
- dirname,
265
- err,
266
- existsSync,
267
- expect,
268
- extractFrame,
269
- fileURLToPath,
270
- findVariantByKey,
271
- it,
272
- makeExplicitNullRhi,
273
- makeMockShaderRegistry,
274
- mat4,
275
- matchPass,
276
- ndcPositionToCluster,
277
- ok,
278
- prepareExtractContext,
279
- readFileSync,
280
- resolve,
281
- resolveAssetHandle,
282
- rhiOk,
283
- standardMaterialShaderVariants,
284
- toShared,
285
- validateClusterGrid,
286
- vec3,
287
- vi,
288
- viewZToZSlice,
289
- ];
290
- type __MergedKeep =
291
- | AssetError
292
- | ClusterBinError
293
- | EquirectAsset
294
- | Handle
295
- | Mat4
296
- | MaterialRenderState
297
- | MaterialShaderEntry
298
- | MaterialShaderManifestEntry
299
- | MaterialShaderManifestVariant
300
- | MeshAsset
301
- | PassSelector
302
- | PbrCaps
303
- | PipelineBuilderContext
304
- | PipelineBuilderShaderModuleFactory
305
- | PipelineLayout
306
- | PipelineSpec
307
- | PrimitiveTopology
308
- | RenderPipeline
309
- | RenderPipelineAsset
310
- | RendererType
311
- | Result
312
- | RhiCaps
313
- | RhiDevice
314
- | ShaderModule
315
- | TextureAsset
316
- | Vec3
317
- | WorldType;
318
-
319
- {
320
- // --- from renderer-draw-world.test.ts ---
321
- const _ENGINE = new URL('../../../render/src/assembly/factory.ts', import.meta.url).href;
322
- const RENDERER = new URL('../../../render/src/assembly/factory.ts', import.meta.url).href;
323
-
324
- // ─── Mock helpers ───────────────────────────────────────────────────────────
325
-
326
- interface MockGL2Context {
327
- __mockTag: 'webgl2';
328
- getExtension: () => null;
329
- getParameter: () => number;
330
- isContextLost: () => boolean;
331
- }
332
-
333
- function makeMockGL2(): MockGL2Context {
334
- return {
335
- __mockTag: 'webgl2',
336
- getExtension: () => null,
337
- getParameter: () => 1,
338
- isContextLost: () => false,
339
- };
340
- }
341
-
342
- interface CanvasOptions {
343
- webgl2: 'context' | 'null';
344
- webgpu?: 'context' | 'null';
345
- }
346
-
347
- function _makeMockCanvas(opts: CanvasOptions): HTMLCanvasElement {
348
- const canvas = {
349
- width: 800,
350
- height: 600,
351
- getContext(kind: string): unknown {
352
- if (kind === 'webgl2') {
353
- return opts.webgl2 === 'context' ? makeMockGL2() : null;
354
- }
355
- if (kind === 'webgpu') {
356
- if (opts.webgpu === 'context') {
357
- return {
358
- __mockTag: 'webgpu-canvas-context',
359
- configure: () => undefined,
360
- unconfigure: () => undefined,
361
- getCurrentTexture: () => ({ createView: () => ({}) }),
362
- };
363
- }
364
- return null;
365
- }
366
- return null;
367
- },
368
- addEventListener: () => undefined,
369
- removeEventListener: () => undefined,
370
- } as unknown as HTMLCanvasElement;
371
- return canvas;
372
- }
373
-
374
- function _makeMockGPUDevice(): { device: unknown } {
375
- const lost = new Promise<unknown>(() => undefined);
376
- const device = {
377
- __mockTag: 'gpu-device',
378
- lost,
379
- features: new Set(),
380
- limits: {},
381
- queue: {
382
- submit: () => undefined,
383
- writeBuffer: () => undefined,
384
- // bug-20260519 AC-03 path: zero-manifest path runs through fallback
385
- // texture seed (writeTexture) which the legacy 4 cases never reach.
386
- writeTexture: () => undefined,
387
- },
388
- createShaderModule: () => ({
389
- getCompilationInfo: async () => ({ messages: [] }),
390
- }),
391
- createBindGroupLayout: () => ({}),
392
- createPipelineLayout: () => ({}),
393
- createRenderPipeline: () => ({}),
394
- createBindGroup: () => ({}),
395
- createBuffer: () => ({
396
- getMappedRange: () => new ArrayBuffer(64),
397
- unmap: () => undefined,
398
- }),
399
- createCommandEncoder: () => ({
400
- beginRenderPass: () => ({
401
- setPipeline: () => undefined,
402
- setVertexBuffer: () => undefined,
403
- setIndexBuffer: () => undefined,
404
- setBindGroup: () => undefined,
405
- draw: () => undefined,
406
- drawIndexed: () => undefined,
407
- end: () => undefined,
408
- }),
409
- finish: () => ({}),
410
- }),
411
- createTexture: () => ({
412
- createView: () => ({}),
413
- }),
414
- createSampler: () => ({}),
415
- destroy: () => undefined,
416
- };
417
- return { device };
418
- }
419
-
420
- function _makeMockGPU(deviceObj: unknown): unknown {
421
- return {
422
- requestAdapter: async () => ({
423
- requestDevice: async () => deviceObj,
424
- }),
425
- getPreferredCanvasFormat: () => 'bgra8unorm',
426
- };
427
- }
428
-
429
- const baseNavigator = { userAgent: 'mock-engine-test' } as unknown as Navigator;
430
-
431
- function _buildManifestDataUrl(): string {
432
- // feat-20260518-pbr-direct-lighting-mvp M5 / w22.9: createRenderer's
433
- // post-fallback path requires both pbr (`f_schlick(` marker) + unlit
434
- // entries; seed two minimal stubs (mock device's createShaderModule does
435
- // not parse WGSL).
436
- const manifest = {
437
- schemaVersion: '1.0.0',
438
- entries: [
439
- { hash: 'pbr00000', wgsl: '/* pbr stub - calls f_schlick( */', glsl: '', bindings: '' },
440
- { hash: 'unlit000', wgsl: '/* unlit stub */', glsl: '', bindings: '' },
441
- {
442
- hash: 'tonemap0',
443
- wgsl: '/* tonemap stub - struct TonemapParams { exposure: f32 }; */',
444
- glsl: '',
445
- bindings: '',
446
- },
447
- ],
448
- };
449
- return `data:application/json,${encodeURIComponent(JSON.stringify(manifest))}`;
450
- }
451
-
452
- beforeEach(() => {
453
- vi.stubGlobal('navigator', { ...baseNavigator });
454
- });
455
-
456
- afterEach(() => {
457
- vi.unstubAllGlobals();
1
+ import { readFileSync } from 'node:fs';
2
+ import { describe, expect, it } from 'vitest';
3
+ import * as rendererFactory from '../../../render/src/assembly/factory';
4
+
5
+ // This is a source/export contract. It creates no renderer, World, canvas, or
6
+ // device and must not retain the unrelated fixtures from the old merged suite.
7
+ describe('Renderer.draw(world) K-4 contract rewrite (D-S11)', () => {
8
+ it('removes the RendererDrawTarget interface from the renderer source file', () => {
9
+ const text = readFileSync(
10
+ new URL('../../../render/src/assembly/webgpu-renderer.ts', import.meta.url),
11
+ 'utf8',
12
+ );
13
+ expect(text).not.toMatch(/interface RendererDrawTarget/);
14
+ expect(text).not.toMatch(/RendererDrawTarget/);
15
+ expect(rendererFactory).not.toHaveProperty('RendererDrawTarget');
458
16
  });
459
-
460
- // ─── Tests ──────────────────────────────────────────────────────────────────
461
-
462
- describe('Renderer.draw(world) K-4 contract rewrite (D-S11)', () => {
463
- it('removes the RendererDrawTarget interface from the renderer source file', async () => {
464
- // Source-level grep gate: the D-S11 + K-4 rewrite removes the
465
- // RendererDrawTarget interface declaration entirely (charter
466
- // proposition 5 single PBR main path; no double-shader branching).
467
- const fsId = 'node:fs';
468
- const pathId = 'node:path';
469
- const urlId = 'node:url';
470
- const fs = (await import(/* @vite-ignore */ fsId)) as {
471
- readFileSync: (p: string, enc: string) => string;
472
- };
473
- const path = (await import(/* @vite-ignore */ pathId)) as {
474
- resolve: (...parts: string[]) => string;
475
- dirname: (p: string) => string;
476
- };
477
- const url = (await import(/* @vite-ignore */ urlId)) as {
478
- fileURLToPath: (u: string) => string;
479
- };
480
- const here = url.fileURLToPath(import.meta.url);
481
- const rendererSrc = path.resolve(
482
- path.dirname(here),
483
- '..',
484
- '..',
485
- '..',
486
- 'render',
487
- 'src',
488
- 'assembly',
489
- 'webgpu-renderer.ts',
490
- );
491
- const text = fs.readFileSync(rendererSrc, 'utf8');
492
- expect(text).not.toMatch(/interface RendererDrawTarget/);
493
- expect(text).not.toMatch(/RendererDrawTarget/);
494
- // Module runtime export must also be absent (already erased by tsc for
495
- // type-only members; covers `export type` removal at the surface level).
496
- const mod = (await import(RENDERER)) as Record<string, unknown>;
497
- expect(mod.RendererDrawTarget).toBeUndefined();
498
- });
499
- });
500
-
501
- // bug-20260519 AC-03: when a world *does* carry a `MeshRenderer` entity but
502
- // the renderer was created without `shaderManifestUrl`, render-time access
503
- // to the now-nullable `pipelineState.{unlitPipeline,standardPipeline}` must
504
- // feat-20260529 D-3: materials without passes are caught at extract stage
505
- // as `material-no-effective-pass` (the shared MaterialError), before
506
- // reaching the record-stage pipeline-pick branch. The test's original
507
- // bug-20260519 AC-03 intent — "zero-manifest renderer fires structured error
508
- // not crash" — is preserved; the detection point has moved upstream.
509
- // Original test expected `shader-compile-failed` from the record stage.
510
- // After D-3, the material registered below has `kind:'material'` +
511
- // `baseColor` but zero passes — the extract stage
512
- // walks the parent chain (no parent), finds zero passes, and fires
513
- // `material-no-effective-pass` with a recovery hint for adding a pass.
514
- }
17
+ });