@forgeax/engine-rhi-debug 0.1.7 → 0.1.19

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.
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/replay/session.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/replay/session.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/engine-rhi-debug",
3
- "version": "0.1.7",
3
+ "version": "0.1.19",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -26,19 +26,19 @@
26
26
  "LICENSE"
27
27
  ],
28
28
  "dependencies": {
29
- "@forgeax/engine-rhi": "0.1.7",
30
- "@forgeax/engine-types": "0.1.7",
29
+ "@forgeax/engine-rhi": "0.1.19",
30
+ "@forgeax/engine-types": "0.1.19",
31
31
  "@noble/hashes": "^2.2.0",
32
32
  "pako": "^1.0.11"
33
33
  },
34
34
  "devDependencies": {
35
- "@forgeax/engine-rhi-null": "0.1.7",
35
+ "@forgeax/engine-rhi-null": "0.1.19",
36
36
  "@types/node": "^20.14.0",
37
37
  "@webgpu/types": "^0.1.71"
38
38
  },
39
39
  "peerDependencies": {
40
- "@forgeax/engine-rhi-webgpu": "0.1.7",
41
- "@forgeax/engine-rhi-wgpu": "0.1.7"
40
+ "@forgeax/engine-rhi-webgpu": "0.1.19",
41
+ "@forgeax/engine-rhi-wgpu": "0.1.19"
42
42
  },
43
43
  "forgeax": {
44
44
  "metrics": {
@@ -45,6 +45,57 @@ async function makeBackend(): Promise<ReplayBackend> {
45
45
  }
46
46
 
47
47
  describe('ReplaySession', () => {
48
+ it('does not require unused recorded optional capabilities', async () => {
49
+ const result = await openReplay(
50
+ {
51
+ ...makeWorkTape(),
52
+ header: {
53
+ ...makeWorkTape().header,
54
+ rhiCaps: {
55
+ rgba16floatRenderable: true,
56
+ float32Filterable: true,
57
+ textureCompressionBc: true,
58
+ textureCompressionEtc2: true,
59
+ textureCompressionAstc: true,
60
+ storageBuffer: true,
61
+ timestampQuery: true,
62
+ },
63
+ },
64
+ },
65
+ await makeBackend(),
66
+ );
67
+ expect(result.ok).toBe(true);
68
+ });
69
+
70
+ it('requires a recorded compression capability when the tape uses its format', async () => {
71
+ const result = await openReplay(
72
+ {
73
+ header: {
74
+ formatVersion: 7,
75
+ rhiCaps: { textureCompressionBc: true },
76
+ eventCount: 1,
77
+ blobCount: 0,
78
+ },
79
+ bootstrap: [],
80
+ events: [
81
+ {
82
+ kind: 'createTexture',
83
+ handleId: 'texture:compressed',
84
+ desc: {
85
+ size: { width: 4, height: 4, depthOrArrayLayers: 1 },
86
+ format: 'bc1-rgba-unorm',
87
+ usage: 0x04,
88
+ },
89
+ },
90
+ ],
91
+ blobs: [],
92
+ },
93
+ await makeBackend(),
94
+ );
95
+ expect(result.ok).toBe(false);
96
+ if (!result.ok) expect(result.error.code).toBe('replay-capability-mismatch');
97
+ });
98
+
48
99
  it('replays a depth-only render pipeline without requiring a fragment shader', async () => {
49
100
  const tape: Tape = {
50
101
  header: { formatVersion: 7, rhiCaps: {}, eventCount: 7, blobCount: 0 },
@@ -218,8 +218,13 @@ export async function openReplay(
218
218
 
219
219
  function checkCapabilities(tape: Tape, device: RhiDevice): RhiDebugError | undefined {
220
220
  const recorded = tape.header.rhiCaps;
221
+ const required = requiredReplayCapabilities(tape);
221
222
  const missing = Object.entries(recorded).filter(([key, value]) => {
222
- if (value !== true) return false;
223
+ // A recorder snapshots the device's available capabilities, but replay
224
+ // only needs to require a capability when the captured workload can use
225
+ // it. Otherwise a device-wide optional feature (for example ASTC support)
226
+ // makes an unrelated RGBA tape non-portable.
227
+ if (value !== true || (!required.has(key) && isKnownReplayCapability(key))) return false;
223
228
  const target = device.caps[key as keyof typeof device.caps];
224
229
  return target !== true;
225
230
  });
@@ -230,6 +235,93 @@ function checkCapabilities(tape: Tape, device: RhiDevice): RhiDebugError | undef
230
235
  });
231
236
  }
232
237
 
238
+ const BC_TEXTURE_FORMATS = new Set([
239
+ 'bc1-rgba-unorm',
240
+ 'bc1-rgba-unorm-srgb',
241
+ 'bc2-rgba-unorm',
242
+ 'bc2-rgba-unorm-srgb',
243
+ 'bc3-rgba-unorm',
244
+ 'bc3-rgba-unorm-srgb',
245
+ 'bc4-r-unorm',
246
+ 'bc4-r-snorm',
247
+ 'bc5-rg-unorm',
248
+ 'bc5-rg-snorm',
249
+ 'bc6h-rgb-ufloat',
250
+ 'bc6h-rgb-sfloat',
251
+ 'bc7-rgba-unorm',
252
+ 'bc7-rgba-unorm-srgb',
253
+ ]);
254
+
255
+ const ETC2_TEXTURE_FORMATS = new Set([
256
+ 'etc2-rgb8unorm',
257
+ 'etc2-rgb8unorm-srgb',
258
+ 'etc2-rgb8a1unorm',
259
+ 'etc2-rgb8a1unorm-srgb',
260
+ 'etc2-rgba8unorm',
261
+ 'etc2-rgba8unorm-srgb',
262
+ 'eac-r11unorm',
263
+ 'eac-r11snorm',
264
+ 'eac-rg11unorm',
265
+ 'eac-rg11snorm',
266
+ ]);
267
+
268
+ const KNOWN_REPLAY_CAPABILITIES = new Set([
269
+ 'rgba16floatRenderable',
270
+ 'float32Filterable',
271
+ 'textureCompressionBc',
272
+ 'textureCompressionEtc2',
273
+ 'textureCompressionAstc',
274
+ 'storageBuffer',
275
+ 'timestampQuery',
276
+ ]);
277
+
278
+ function isKnownReplayCapability(key: string): boolean {
279
+ return KNOWN_REPLAY_CAPABILITIES.has(key);
280
+ }
281
+
282
+ function requiredReplayCapabilities(tape: Tape): ReadonlySet<string> {
283
+ const required = new Set<string>();
284
+ const events = [
285
+ ...tape.bootstrap.map((resource) => resource.create as unknown as RhiCallEvent),
286
+ ...tape.events,
287
+ ];
288
+ for (const event of events) {
289
+ switch (event.kind) {
290
+ case 'createTexture': {
291
+ const formats = [event.desc.format, ...(event.desc.viewFormats ?? [])];
292
+ for (const format of formats) {
293
+ if (BC_TEXTURE_FORMATS.has(format)) required.add('textureCompressionBc');
294
+ if (ETC2_TEXTURE_FORMATS.has(format)) required.add('textureCompressionEtc2');
295
+ if (format.startsWith('astc-')) required.add('textureCompressionAstc');
296
+ if (format === 'rgba16float') required.add('rgba16floatRenderable');
297
+ if (format === 'r32float' || format === 'rg32float' || format === 'rgba32float') {
298
+ required.add('float32Filterable');
299
+ }
300
+ }
301
+ break;
302
+ }
303
+ case 'createBindGroupLayout':
304
+ if (
305
+ Array.from(event.desc.entries).some(
306
+ (entry) =>
307
+ entry.buffer?.type === 'storage' || entry.buffer?.type === 'read-only-storage',
308
+ )
309
+ ) {
310
+ required.add('storageBuffer');
311
+ }
312
+ break;
313
+ case 'createBuffer':
314
+ // GPUBufferUsage.STORAGE is 0x80 in the WebGPU enum. Keep the replay
315
+ // package independent of the browser-only global constant.
316
+ if ((event.desc.usage & 0x80) !== 0) required.add('storageBuffer');
317
+ break;
318
+ default:
319
+ break;
320
+ }
321
+ }
322
+ return required;
323
+ }
324
+
233
325
  async function executeBootstrapResource(
234
326
  context: ReplayExecutionContext,
235
327
  resource: BootstrapResource,