@forgeax/engine-rhi-debug 0.1.33 → 0.1.35
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 +2 -0
- package/dist/index.mjs +38 -0
- package/dist/index.mjs.map +1 -1
- package/dist/replay/session.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/e2e.browser.test.ts +6 -0
- package/src/__tests__/guard-gates.test.ts +33 -10
- package/src/__tests__/rhi-debug-fresh-replay.dawn.test.ts +7 -1
- package/src/replay/session.ts +52 -0
|
@@ -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.
|
|
3
|
+
"version": "0.1.35",
|
|
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.
|
|
30
|
-
"@forgeax/engine-types": "0.1.
|
|
29
|
+
"@forgeax/engine-rhi": "0.1.35",
|
|
30
|
+
"@forgeax/engine-types": "0.1.35",
|
|
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.
|
|
35
|
+
"@forgeax/engine-rhi-null": "0.1.35",
|
|
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.
|
|
41
|
-
"@forgeax/engine-rhi-wgpu": "0.1.
|
|
40
|
+
"@forgeax/engine-rhi-webgpu": "0.1.35",
|
|
41
|
+
"@forgeax/engine-rhi-wgpu": "0.1.35"
|
|
42
42
|
},
|
|
43
43
|
"forgeax": {
|
|
44
44
|
"metrics": {
|
|
@@ -142,6 +142,7 @@ async function captureBrowserTape(): Promise<CapturedBrowserTape> {
|
|
|
142
142
|
'write source texture',
|
|
143
143
|
);
|
|
144
144
|
const encoder = must(device.createCommandEncoder({}), 'create command encoder');
|
|
145
|
+
encoder.pushDebugGroup('frame');
|
|
145
146
|
const pass = encoder.beginRenderPass({
|
|
146
147
|
colorAttachments: [
|
|
147
148
|
{
|
|
@@ -154,8 +155,13 @@ async function captureBrowserTape(): Promise<CapturedBrowserTape> {
|
|
|
154
155
|
} as never);
|
|
155
156
|
pass.setPipeline(pipeline);
|
|
156
157
|
pass.setBindGroup(0, bindGroup, []);
|
|
158
|
+
pass.pushDebugGroup('outer');
|
|
159
|
+
pass.pushDebugGroup('inner');
|
|
157
160
|
pass.draw(3, 1, 0, 0);
|
|
161
|
+
pass.popDebugGroup();
|
|
162
|
+
pass.popDebugGroup();
|
|
158
163
|
pass.end();
|
|
164
|
+
encoder.popDebugGroup();
|
|
159
165
|
const command = must(encoder.finish(), 'finish command encoder');
|
|
160
166
|
must(device.queue.submit([command]), 'submit command buffer');
|
|
161
167
|
await device.queue.onSubmittedWorkDone();
|
|
@@ -193,7 +193,7 @@ describe('AC-08 partial: import.meta.hot in rhiDebugFlag guard', () => {
|
|
|
193
193
|
});
|
|
194
194
|
|
|
195
195
|
describe('RHI-debug smoke roster gate', () => {
|
|
196
|
-
it('resolves every declared hello and learn-render smoke at
|
|
196
|
+
it('resolves every declared hello and learn-render smoke at 60 frames', () => {
|
|
197
197
|
const rosterPath = path.resolve(ENGINE_ROOT, 'scripts', 'rhi-debug-smoke-roster.mjs');
|
|
198
198
|
const result = spawnSync(
|
|
199
199
|
process.execPath,
|
|
@@ -204,7 +204,7 @@ describe('RHI-debug smoke roster gate', () => {
|
|
|
204
204
|
'--learn-root',
|
|
205
205
|
'apps/learn-render',
|
|
206
206
|
'--frames',
|
|
207
|
-
'
|
|
207
|
+
'60',
|
|
208
208
|
],
|
|
209
209
|
{ cwd: ENGINE_ROOT, encoding: 'utf8' },
|
|
210
210
|
);
|
|
@@ -223,12 +223,12 @@ describe('RHI-debug smoke roster gate', () => {
|
|
|
223
223
|
};
|
|
224
224
|
expect(roster.status).toBe('unavailable');
|
|
225
225
|
expect(roster.execution).toMatchObject({ status: 'not-executed', mode: 'declaration-only' });
|
|
226
|
-
expect(roster.frameCount).toBe(
|
|
226
|
+
expect(roster.frameCount).toBe(60);
|
|
227
227
|
expect(roster.entries.length).toBeGreaterThan(0);
|
|
228
228
|
expect(
|
|
229
229
|
roster.entries.every(
|
|
230
230
|
(entry) =>
|
|
231
|
-
entry.frames ===
|
|
231
|
+
entry.frames === 60 &&
|
|
232
232
|
entry.command === entry.invocation &&
|
|
233
233
|
entry.tokens.join(' ') === entry.invocation &&
|
|
234
234
|
entry.tokens[0] === 'pnpm' &&
|
|
@@ -285,7 +285,7 @@ describe('RHI-debug smoke roster gate', () => {
|
|
|
285
285
|
'--cwd',
|
|
286
286
|
fixture,
|
|
287
287
|
'--frames',
|
|
288
|
-
'
|
|
288
|
+
'60',
|
|
289
289
|
'--execute',
|
|
290
290
|
],
|
|
291
291
|
{ cwd: ENGINE_ROOT, encoding: 'utf8' },
|
|
@@ -320,7 +320,7 @@ describe('RHI-debug smoke roster gate', () => {
|
|
|
320
320
|
invocation: 'pnpm --filter @fake/smoke smoke:browser',
|
|
321
321
|
tokens: ['pnpm', '--filter', '@fake/smoke', 'smoke:browser'],
|
|
322
322
|
backend: 'browser',
|
|
323
|
-
frames:
|
|
323
|
+
frames: 60,
|
|
324
324
|
});
|
|
325
325
|
expect(passedEntry?.stdout).toContain('fake browser smoke');
|
|
326
326
|
|
|
@@ -330,7 +330,11 @@ describe('RHI-debug smoke roster gate', () => {
|
|
|
330
330
|
name: '@fake/smoke',
|
|
331
331
|
private: true,
|
|
332
332
|
scripts: {
|
|
333
|
-
|
|
333
|
+
// process managers may route a failing script's streams differently
|
|
334
|
+
// on each runner; write the diagnostic synchronously to both pipes
|
|
335
|
+
// so the fail-closed assertion always has portable evidence.
|
|
336
|
+
'smoke:browser':
|
|
337
|
+
"node -e \"const fs = require('node:fs'); fs.writeSync(1, 'fake failure\\n'); fs.writeSync(2, 'fake failure\\n'); process.exit(7)\"",
|
|
334
338
|
},
|
|
335
339
|
forgeax: { smokeInvocation: 'pnpm --filter @fake/smoke smoke:browser' },
|
|
336
340
|
}),
|
|
@@ -353,12 +357,31 @@ describe('RHI-debug smoke roster gate', () => {
|
|
|
353
357
|
const failedRoster = JSON.parse(failed.stdout) as {
|
|
354
358
|
status: string;
|
|
355
359
|
execution: { status: string; failedCount: number };
|
|
356
|
-
entries: readonly {
|
|
360
|
+
entries: readonly {
|
|
361
|
+
status: string;
|
|
362
|
+
returnCode: number | null;
|
|
363
|
+
stdout: string;
|
|
364
|
+
stderr: string;
|
|
365
|
+
}[];
|
|
357
366
|
};
|
|
358
367
|
expect(failedRoster.status).toBe('failed');
|
|
359
368
|
expect(failedRoster.execution).toMatchObject({ status: 'failed', failedCount: 1 });
|
|
360
|
-
|
|
361
|
-
|
|
369
|
+
// The roster records the declared pnpm command's status. pnpm 11 maps
|
|
370
|
+
// the inner script's exit 7 to 1; compare against that real boundary.
|
|
371
|
+
const directFailure = spawnSync('pnpm', ['--filter', '@fake/smoke', 'smoke:browser'], {
|
|
372
|
+
cwd: fixture,
|
|
373
|
+
encoding: 'utf8',
|
|
374
|
+
});
|
|
375
|
+
expect(directFailure.error).toBeUndefined();
|
|
376
|
+
expect(directFailure.status).not.toBeNull();
|
|
377
|
+
expect(directFailure.status).not.toBe(0);
|
|
378
|
+
expect(failedRoster.entries[0]).toMatchObject({
|
|
379
|
+
status: 'failed',
|
|
380
|
+
returnCode: directFailure.status,
|
|
381
|
+
});
|
|
382
|
+
expect(`${failedRoster.entries[0]?.stdout}\n${failedRoster.entries[0]?.stderr}`).toContain(
|
|
383
|
+
'fake failure',
|
|
384
|
+
);
|
|
362
385
|
} finally {
|
|
363
386
|
rmSync(fixture, { recursive: true, force: true });
|
|
364
387
|
}
|
|
@@ -103,6 +103,7 @@ async function makeRecording(pack: DawnPack): Promise<{
|
|
|
103
103
|
const encoderResult = device.createCommandEncoder({});
|
|
104
104
|
expect(encoderResult.ok).toBe(true);
|
|
105
105
|
if (!encoderResult.ok) throw new Error(encoderResult.error.hint);
|
|
106
|
+
encoderResult.value.pushDebugGroup('frame');
|
|
106
107
|
const pass = encoderResult.value.beginRenderPass({
|
|
107
108
|
colorAttachments: [
|
|
108
109
|
{
|
|
@@ -114,8 +115,13 @@ async function makeRecording(pack: DawnPack): Promise<{
|
|
|
114
115
|
],
|
|
115
116
|
} as never);
|
|
116
117
|
pass.setPipeline(pipeline.value);
|
|
118
|
+
pass.pushDebugGroup('outer');
|
|
119
|
+
pass.pushDebugGroup('inner');
|
|
117
120
|
pass.draw(3, 1, 0, 0);
|
|
121
|
+
pass.popDebugGroup();
|
|
122
|
+
pass.popDebugGroup();
|
|
118
123
|
pass.end();
|
|
124
|
+
encoderResult.value.popDebugGroup();
|
|
119
125
|
const command = encoderResult.value.finish();
|
|
120
126
|
expect(command.ok).toBe(true);
|
|
121
127
|
if (!command.ok) throw new Error(command.error.hint);
|
|
@@ -127,7 +133,7 @@ async function makeRecording(pack: DawnPack): Promise<{
|
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
describe.skipIf(SKIP_DAWN)('RHI debug v7 fresh-device evidence', () => {
|
|
130
|
-
it('records, encodes, strictly decodes, and replays pixels on a fresh Dawn device', async () => {
|
|
136
|
+
it('records, encodes, strictly decodes, and replays pixels through nested debug groups on a fresh Dawn device', async () => {
|
|
131
137
|
const pack = await loadDawn();
|
|
132
138
|
const recording = await makeRecording(pack);
|
|
133
139
|
const baseline = await readbackTexturePixels(
|
package/src/replay/session.ts
CHANGED
|
@@ -594,6 +594,13 @@ async function finalizeWorkPass(
|
|
|
594
594
|
finishEventIndex ?? context.tape.events.length,
|
|
595
595
|
);
|
|
596
596
|
if (!closure.ok) return closure;
|
|
597
|
+
const groupsClosed = closeReplayDebugGroups(
|
|
598
|
+
context,
|
|
599
|
+
begin.cmdHandleId,
|
|
600
|
+
work.eventIndex,
|
|
601
|
+
encoder.resource.value as RhiCommandEncoder,
|
|
602
|
+
);
|
|
603
|
+
if (!groupsClosed.ok) return groupsClosed;
|
|
597
604
|
const finished = (encoder.resource.value as RhiCommandEncoder).finish();
|
|
598
605
|
const finishEvent = context.tape.events[finishEventIndex ?? work.eventIndex] ?? begin;
|
|
599
606
|
if (!finished.ok)
|
|
@@ -656,6 +663,15 @@ function closeReplayPass(
|
|
|
656
663
|
);
|
|
657
664
|
}
|
|
658
665
|
}
|
|
666
|
+
if (entry.resource.role === 'render-pass') {
|
|
667
|
+
const groupsClosed = closeReplayDebugGroups(
|
|
668
|
+
context,
|
|
669
|
+
begin.passHandleId,
|
|
670
|
+
selectedEventIndex,
|
|
671
|
+
entry.resource.value as RhiRenderPassEncoder,
|
|
672
|
+
);
|
|
673
|
+
if (!groupsClosed.ok) return groupsClosed;
|
|
674
|
+
}
|
|
659
675
|
(entry.resource.value as RhiRenderPassEncoder | RhiComputePassEncoder).end();
|
|
660
676
|
} catch (cause) {
|
|
661
677
|
return eventFailure(pass.endEventIndex ?? selectedEventIndex, begin, 'encode', cause);
|
|
@@ -664,6 +680,42 @@ function closeReplayPass(
|
|
|
664
680
|
return ok(undefined);
|
|
665
681
|
}
|
|
666
682
|
|
|
683
|
+
// Inspection stops at the selected work, before the recorded scope exits.
|
|
684
|
+
// Derive only the still-open scopes from that prefix; never execute later work.
|
|
685
|
+
function closeReplayDebugGroups(
|
|
686
|
+
context: ReplayExecutionContext,
|
|
687
|
+
handleId: string,
|
|
688
|
+
throughEventIndex: number,
|
|
689
|
+
encoder: RhiCommandEncoder | RhiRenderPassEncoder,
|
|
690
|
+
): Result<void, RhiDebugError> {
|
|
691
|
+
const open: { event: RhiCallEvent; eventIndex: number }[] = [];
|
|
692
|
+
for (let eventIndex = 0; eventIndex <= throughEventIndex; eventIndex++) {
|
|
693
|
+
const event = context.tape.events[eventIndex];
|
|
694
|
+
if (event === undefined) continue;
|
|
695
|
+
if (
|
|
696
|
+
(event.kind === 'pushDebugGroup' && event.cmdHandleId === handleId) ||
|
|
697
|
+
(event.kind === 'passPushDebugGroup' && event.passHandleId === handleId)
|
|
698
|
+
) {
|
|
699
|
+
open.push({ event, eventIndex });
|
|
700
|
+
} else if (
|
|
701
|
+
(event.kind === 'popDebugGroup' && event.cmdHandleId === handleId) ||
|
|
702
|
+
(event.kind === 'passPopDebugGroup' && event.passHandleId === handleId)
|
|
703
|
+
) {
|
|
704
|
+
open.pop();
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
for (let index = open.length - 1; index >= 0; index--) {
|
|
708
|
+
const scope = open[index];
|
|
709
|
+
if (scope === undefined) continue;
|
|
710
|
+
try {
|
|
711
|
+
encoder.popDebugGroup();
|
|
712
|
+
} catch (cause) {
|
|
713
|
+
return eventFailure(scope.eventIndex, scope.event, 'encode', cause);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return ok(undefined);
|
|
717
|
+
}
|
|
718
|
+
|
|
667
719
|
async function replayCommandClosure(
|
|
668
720
|
context: ReplayExecutionContext,
|
|
669
721
|
cmdHandleId: string,
|