@forgeax/engine-rhi-debug 0.1.27 → 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.
- package/README.md +13 -0
- package/dist/__tests__/query-set-lifecycle.unit.test.d.ts +2 -0
- package/dist/__tests__/query-set-lifecycle.unit.test.d.ts.map +1 -0
- package/dist/__tests__/query-set-replay-fixture.d.ts +39 -0
- package/dist/__tests__/query-set-replay-fixture.d.ts.map +1 -0
- package/dist/__tests__/recorder-canvas-context.unit.test.d.ts +2 -0
- package/dist/__tests__/recorder-canvas-context.unit.test.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +465 -53
- package/dist/index.mjs.map +1 -1
- package/dist/protocol/event-semantics.d.ts +1 -1
- package/dist/protocol/event-semantics.d.ts.map +1 -1
- package/dist/protocol/types.d.ts +1 -1
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/recorder/closure.d.ts.map +1 -1
- package/dist/recorder/core.d.ts +10 -1
- package/dist/recorder/core.d.ts.map +1 -1
- package/dist/recorder/device.d.ts.map +1 -1
- package/dist/recorder/encoder.d.ts.map +1 -1
- package/dist/recorder/pass.d.ts.map +1 -1
- package/dist/recorder/pipeline.d.ts +5 -0
- package/dist/recorder/pipeline.d.ts.map +1 -0
- package/dist/recorder/proxy.d.ts +3 -1
- package/dist/recorder/proxy.d.ts.map +1 -1
- package/dist/recorder/session.d.ts +1 -1
- package/dist/recorder/session.d.ts.map +1 -1
- package/dist/recorder/shader.d.ts +3 -1
- package/dist/recorder/shader.d.ts.map +1 -1
- package/dist/recorder.d.ts +2 -2
- package/dist/recorder.d.ts.map +1 -1
- package/dist/replay/execute.d.ts.map +1 -1
- package/dist/replay/resources.d.ts +4 -1
- package/dist/replay/resources.d.ts.map +1 -1
- package/dist/replay/session.d.ts +7 -0
- package/dist/replay/session.d.ts.map +1 -1
- package/dist/types.d.ts +39 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/coverage-invariant.test-d.ts +4 -5
- package/src/__tests__/coverage-invariant.unit.test.ts +6 -17
- package/src/__tests__/e2e.browser.test.ts +50 -0
- package/src/__tests__/guard-gates.test.ts +1 -1
- package/src/__tests__/query-set-lifecycle.unit.test.ts +265 -0
- package/src/__tests__/query-set-replay-fixture.ts +553 -0
- package/src/__tests__/readback-format-matrix.dawn.test.ts +7 -0
- package/src/__tests__/recorder-canvas-context.unit.test.ts +67 -0
- package/src/__tests__/recorder-session.integration.test.ts +55 -0
- package/src/__tests__/recorder-session.unit.test.ts +18 -1
- package/src/__tests__/replay-session.dawn.test.ts +49 -0
- package/src/__tests__/replay-session.test-d.ts +1 -0
- package/src/__tests__/rhi-debug-fresh-replay.dawn.test.ts +61 -0
- package/src/index.ts +1 -0
- package/src/protocol/event-semantics.ts +20 -2
- package/src/protocol/types.ts +1 -0
- package/src/protocol/validation.ts +31 -0
- package/src/recorder/assemble.ts +8 -1
- package/src/recorder/closure.ts +17 -0
- package/src/recorder/core.ts +11 -0
- package/src/recorder/device.ts +33 -6
- package/src/recorder/encoder.ts +22 -4
- package/src/recorder/pass.ts +7 -2
- package/src/recorder/pipeline.ts +31 -0
- package/src/recorder/proxy.ts +58 -2
- package/src/recorder/session.ts +1 -1
- package/src/recorder/shader.ts +29 -22
- package/src/recorder.ts +6 -2
- package/src/replay/execute.ts +111 -0
- package/src/replay/readback.ts +8 -3
- package/src/replay/resources.ts +6 -0
- package/src/replay/session.ts +234 -25
- package/src/types.ts +53 -12
package/src/recorder/proxy.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { err, ok, type Result } from '@forgeax/engine-types';
|
|
|
3
3
|
import { createRhiDebugError, type RhiDebugError } from '../errors';
|
|
4
4
|
import {
|
|
5
5
|
type CreateShaderModuleFn,
|
|
6
|
+
type CreateShaderModuleImmediateFn,
|
|
6
7
|
type DebugRhiInstance,
|
|
7
8
|
wrap,
|
|
8
9
|
wrapCreateShaderModule,
|
|
@@ -15,6 +16,8 @@ export interface RecordableBackend {
|
|
|
15
16
|
) => Result<RhiCanvasContext, RhiError>;
|
|
16
17
|
};
|
|
17
18
|
readonly createShaderModule: CreateShaderModuleFn;
|
|
19
|
+
/** Optional synchronous factory retained for render's first-use path. */
|
|
20
|
+
readonly createShaderModuleImmediate?: CreateShaderModuleImmediateFn;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export interface RecorderBackend extends RecordableBackend {
|
|
@@ -29,21 +32,74 @@ export interface RecorderProxy {
|
|
|
29
32
|
export function createRecorderProxy(backend: RecordableBackend): RecorderProxy {
|
|
30
33
|
const recorder = wrap(backend.rhi);
|
|
31
34
|
const wrappedCreateShaderModule = wrapCreateShaderModule(backend.createShaderModule, recorder);
|
|
35
|
+
const originalCreateShaderModuleImmediate = backend.createShaderModuleImmediate;
|
|
36
|
+
const wrappedCreateShaderModuleImmediate =
|
|
37
|
+
originalCreateShaderModuleImmediate === undefined
|
|
38
|
+
? undefined
|
|
39
|
+
: (device: RhiDevice, desc: { code: string; label?: string | undefined }) => {
|
|
40
|
+
const realDevice =
|
|
41
|
+
(device as RhiDevice & { readonly _realDevice?: RhiDevice })._realDevice ?? device;
|
|
42
|
+
const result = originalCreateShaderModuleImmediate(realDevice, desc);
|
|
43
|
+
if (!result.ok) return result;
|
|
44
|
+
const hId = recorder.pushExternalCreateEvent(result.value, 'shaderModule', {
|
|
45
|
+
kind: 'createShaderModule',
|
|
46
|
+
handleId: '' as import('../types').HandleId,
|
|
47
|
+
wgslCode: desc.code,
|
|
48
|
+
});
|
|
49
|
+
recorder.registerShaderModule(result.value, hId);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
32
52
|
const acquireCanvasContext = backend.rhi.acquireCanvasContext;
|
|
33
53
|
// Runtime's explicit-RHI seam receives the wrapped singleton as one value.
|
|
34
54
|
// Keep the standalone shader factory enumerable on that value so the
|
|
35
55
|
// renderer can preserve the complete typed backend pack after capture
|
|
36
56
|
// attachment; the public RhiInstance contract remains unchanged.
|
|
37
|
-
const rhi: RecordableBackend['rhi'] & {
|
|
57
|
+
const rhi: RecordableBackend['rhi'] & {
|
|
58
|
+
readonly createShaderModule: CreateShaderModuleFn;
|
|
59
|
+
readonly createShaderModuleImmediate?: CreateShaderModuleImmediateFn;
|
|
60
|
+
} = {
|
|
38
61
|
requestAdapter: recorder.requestAdapter.bind(recorder),
|
|
39
62
|
createShaderModule: wrappedCreateShaderModule,
|
|
63
|
+
...(wrappedCreateShaderModuleImmediate === undefined
|
|
64
|
+
? {}
|
|
65
|
+
: { createShaderModuleImmediate: wrappedCreateShaderModuleImmediate }),
|
|
40
66
|
...(acquireCanvasContext === undefined
|
|
41
67
|
? {}
|
|
42
|
-
: {
|
|
68
|
+
: {
|
|
69
|
+
acquireCanvasContext(canvas) {
|
|
70
|
+
const result = acquireCanvasContext.call(backend.rhi, canvas);
|
|
71
|
+
if (!result.ok) return result;
|
|
72
|
+
const context = result.value;
|
|
73
|
+
// rhi-webgpu's canvas shim translates the forgeax RhiDevice to the
|
|
74
|
+
// native GPUDevice through its own WeakMap. The recorder device is
|
|
75
|
+
// an intentional proxy, so passing it directly makes configure()
|
|
76
|
+
// reject with a foreign-device error before the first frame. Keep
|
|
77
|
+
// the context owned by the selected backend, but unwrap only the
|
|
78
|
+
// device argument at this boundary; all resource calls continue to
|
|
79
|
+
// flow through the recorder device and remain capturable.
|
|
80
|
+
return ok({
|
|
81
|
+
...context,
|
|
82
|
+
configure(configuration) {
|
|
83
|
+
const device = (
|
|
84
|
+
configuration.device as RhiDevice & {
|
|
85
|
+
readonly _realDevice?: RhiDevice;
|
|
86
|
+
}
|
|
87
|
+
)._realDevice;
|
|
88
|
+
return context.configure({
|
|
89
|
+
...configuration,
|
|
90
|
+
...(device === undefined ? {} : { device }),
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
43
96
|
};
|
|
44
97
|
const wrappedBackend: RecorderBackend = {
|
|
45
98
|
rhi,
|
|
46
99
|
createShaderModule: wrappedCreateShaderModule,
|
|
100
|
+
...(wrappedCreateShaderModuleImmediate === undefined
|
|
101
|
+
? {}
|
|
102
|
+
: { createShaderModuleImmediate: wrappedCreateShaderModuleImmediate }),
|
|
47
103
|
unwrapDeviceForSurface(device) {
|
|
48
104
|
const raw = (device as RhiDevice & { readonly _realDevice?: RhiDevice })._realDevice;
|
|
49
105
|
if (raw !== undefined) return ok(raw);
|
package/src/recorder/session.ts
CHANGED
|
@@ -147,6 +147,6 @@ export function attachRecorder(
|
|
|
147
147
|
return ok(attachment);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
export type { CreateShaderModuleFn } from '../recorder';
|
|
150
|
+
export type { CreateShaderModuleFn, CreateShaderModuleImmediateFn } from '../recorder';
|
|
151
151
|
export type { EncodedTape } from './assemble';
|
|
152
152
|
export type { RecordableBackend, RecorderBackend } from './proxy';
|
package/src/recorder/shader.ts
CHANGED
|
@@ -2,7 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
import type { Result, RhiDevice, ShaderModule } from '@forgeax/engine-rhi';
|
|
4
4
|
import type { HandleId } from '../types';
|
|
5
|
-
import type { CreateShaderModuleFn, DebugRhiInstance } from './core';
|
|
5
|
+
import type { CreateShaderModuleFn, CreateShaderModuleImmediateFn, DebugRhiInstance } from './core';
|
|
6
|
+
|
|
7
|
+
function recordShaderModule(
|
|
8
|
+
debugInst: DebugRhiInstance,
|
|
9
|
+
result: Result<ShaderModule, import('@forgeax/engine-rhi').RhiError>,
|
|
10
|
+
desc: { code: string; label?: string | undefined },
|
|
11
|
+
): Result<ShaderModule, import('@forgeax/engine-rhi').RhiError> {
|
|
12
|
+
if (!result.ok) return result;
|
|
13
|
+
const hId = debugInst.pushExternalCreateEvent(result.value, 'shaderModule', {
|
|
14
|
+
kind: 'createShaderModule',
|
|
15
|
+
handleId: '' as HandleId,
|
|
16
|
+
wgslCode: desc.code,
|
|
17
|
+
});
|
|
18
|
+
// Register the shader module handle in the recorder's handleMap so
|
|
19
|
+
// downstream pipeline events can resolve the handleId during replay.
|
|
20
|
+
debugInst.registerShaderModule(result.value, hId);
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
6
23
|
|
|
7
24
|
export function wrapCreateShaderModule(
|
|
8
25
|
originalFn: CreateShaderModuleFn,
|
|
@@ -21,27 +38,17 @@ export function wrapCreateShaderModule(
|
|
|
21
38
|
// proxyDevice exposes for exactly this purpose.
|
|
22
39
|
const realDevice = (device as RhiDevice & { _realDevice?: RhiDevice })._realDevice ?? device;
|
|
23
40
|
const result = await originalFn(realDevice, desc);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// event through the same `pushExternalEvent` helper that wraps the
|
|
28
|
-
// internal `pushEvent` (and therefore the `_skipRecord` + state-machine
|
|
29
|
-
// guard). The previous `(events as RhiCallEvent[]).push(...)` cast
|
|
30
|
-
// bypassed those guards — recorder-internal RHI calls during shader
|
|
31
|
-
// construction would have self-polluted the tape.
|
|
32
|
-
const hId = debugInst.pushExternalCreateEvent(result.value, 'shaderModule', {
|
|
33
|
-
kind: 'createShaderModule',
|
|
34
|
-
handleId: '' as HandleId,
|
|
35
|
-
wgslCode: desc.code,
|
|
36
|
-
});
|
|
37
|
-
// Register the shader module handle in the recorder's handleMap so
|
|
38
|
-
// downstream pipeline events (createRenderPipeline / createComputePipeline)
|
|
39
|
-
// can resolve the handleId via getHandleId. Required for cross-device
|
|
40
|
-
// replay: the tape's pipeline desc carries live GPU module objects which
|
|
41
|
-
// are device-bound, so the replayer must swap them with re-created shader
|
|
42
|
-
// modules from the handleMap using the handleId.
|
|
43
|
-
debugInst.registerShaderModule(result.value, hId);
|
|
41
|
+
return recordShaderModule(debugInst, result, desc);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
/** Wrap the synchronous render-path factory while preserving tape ownership. */
|
|
46
|
+
export function wrapCreateShaderModuleImmediate(
|
|
47
|
+
originalFn: CreateShaderModuleImmediateFn,
|
|
48
|
+
debugInst: DebugRhiInstance,
|
|
49
|
+
): CreateShaderModuleImmediateFn {
|
|
50
|
+
return (device, desc) => {
|
|
51
|
+
const realDevice = (device as RhiDevice & { _realDevice?: RhiDevice })._realDevice ?? device;
|
|
52
|
+
return recordShaderModule(debugInst, originalFn(realDevice, desc), desc);
|
|
46
53
|
};
|
|
47
54
|
}
|
package/src/recorder.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// @forgeax/engine-rhi-debug/src/recorder -- split recorder public seam.
|
|
2
2
|
|
|
3
|
-
export type {
|
|
3
|
+
export type {
|
|
4
|
+
CreateShaderModuleFn,
|
|
5
|
+
CreateShaderModuleImmediateFn,
|
|
6
|
+
DebugRhiInstance,
|
|
7
|
+
} from './recorder/core';
|
|
4
8
|
export { PER_EVENT_OVERHEAD, TAPE_FORMAT_VERSION } from './recorder/core';
|
|
5
9
|
export type {
|
|
6
10
|
CaptureFrameOptions,
|
|
@@ -11,5 +15,5 @@ export type {
|
|
|
11
15
|
RecorderOptions,
|
|
12
16
|
} from './recorder/session';
|
|
13
17
|
export { attachRecorder } from './recorder/session';
|
|
14
|
-
export { wrapCreateShaderModule } from './recorder/shader';
|
|
18
|
+
export { wrapCreateShaderModule, wrapCreateShaderModuleImmediate } from './recorder/shader';
|
|
15
19
|
export { wrap } from './recorder/wrap';
|
package/src/replay/execute.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
RhiDevice,
|
|
11
11
|
RhiError,
|
|
12
12
|
RhiQueue,
|
|
13
|
+
RhiRenderPipelineOps,
|
|
13
14
|
Texture,
|
|
14
15
|
} from '@forgeax/engine-rhi';
|
|
15
16
|
import { ok, type Result } from '@forgeax/engine-types';
|
|
@@ -70,10 +71,20 @@ export async function executeEvent(
|
|
|
70
71
|
kind: 'texture',
|
|
71
72
|
},
|
|
72
73
|
);
|
|
74
|
+
case 'createQuerySet':
|
|
75
|
+
return createResource(
|
|
76
|
+
context,
|
|
77
|
+
eventIndex,
|
|
78
|
+
event,
|
|
79
|
+
context.device.createQuerySet(event.desc),
|
|
80
|
+
{ kind: 'query-set' },
|
|
81
|
+
);
|
|
73
82
|
case 'destroyBuffer':
|
|
74
83
|
return destroyResource(context, eventIndex, event, 'buffer');
|
|
75
84
|
case 'destroyTexture':
|
|
76
85
|
return destroyResource(context, eventIndex, event, 'texture');
|
|
86
|
+
case 'destroyQuerySet':
|
|
87
|
+
return destroyQuerySet(context, event, eventIndex);
|
|
77
88
|
case 'createTextureView': {
|
|
78
89
|
const texture = requireResource<Texture>(
|
|
79
90
|
context,
|
|
@@ -110,6 +121,28 @@ export async function executeEvent(
|
|
|
110
121
|
context.device.createBindGroupLayout(event.desc as never),
|
|
111
122
|
{ kind: 'binding', role: 'bind-group-layout' },
|
|
112
123
|
);
|
|
124
|
+
case 'getBindGroupLayout': {
|
|
125
|
+
const pipeline = requireReplayResource(
|
|
126
|
+
context,
|
|
127
|
+
event.pipelineHandleId,
|
|
128
|
+
'pipeline',
|
|
129
|
+
eventIndex,
|
|
130
|
+
event.kind,
|
|
131
|
+
);
|
|
132
|
+
if (!pipeline.ok) return pipeline;
|
|
133
|
+
return createResource(
|
|
134
|
+
context,
|
|
135
|
+
eventIndex,
|
|
136
|
+
event,
|
|
137
|
+
ok(
|
|
138
|
+
(
|
|
139
|
+
pipeline.value.value as import('@forgeax/engine-rhi').RenderPipeline &
|
|
140
|
+
RhiRenderPipelineOps
|
|
141
|
+
).getBindGroupLayout(event.index),
|
|
142
|
+
),
|
|
143
|
+
{ kind: 'binding', role: 'bind-group-layout' },
|
|
144
|
+
);
|
|
145
|
+
}
|
|
113
146
|
case 'createBindGroup':
|
|
114
147
|
return createBindGroup(context, event, eventIndex);
|
|
115
148
|
case 'createPipelineLayout':
|
|
@@ -141,6 +174,22 @@ export async function executeEvent(
|
|
|
141
174
|
return beginRenderPass(context, event, eventIndex);
|
|
142
175
|
case 'beginComputePass':
|
|
143
176
|
return beginComputePass(context, event, eventIndex);
|
|
177
|
+
case 'resolveQuerySet':
|
|
178
|
+
return resolveQuerySet(context, event, eventIndex);
|
|
179
|
+
case 'beginOcclusionQuery':
|
|
180
|
+
return passCall(context, event, eventIndex, 'render-pass', (pass) => {
|
|
181
|
+
const result = pass.beginOcclusionQuery(event.queryIndex);
|
|
182
|
+
return result.ok
|
|
183
|
+
? ok(undefined)
|
|
184
|
+
: eventFailure(eventIndex, event, 'encode', result.error);
|
|
185
|
+
});
|
|
186
|
+
case 'endOcclusionQuery':
|
|
187
|
+
return passCall(context, event, eventIndex, 'render-pass', (pass) => {
|
|
188
|
+
const result = pass.endOcclusionQuery();
|
|
189
|
+
return result.ok
|
|
190
|
+
? ok(undefined)
|
|
191
|
+
: eventFailure(eventIndex, event, 'encode', result.error);
|
|
192
|
+
});
|
|
144
193
|
case 'copyBufferToBuffer':
|
|
145
194
|
return copyBufferToBuffer(context, event, eventIndex);
|
|
146
195
|
case 'copyBufferToTexture':
|
|
@@ -386,6 +435,60 @@ function destroyResource(
|
|
|
386
435
|
return ok(undefined);
|
|
387
436
|
}
|
|
388
437
|
|
|
438
|
+
function destroyQuerySet(
|
|
439
|
+
context: ReplayExecutionContext,
|
|
440
|
+
event: Extract<RhiCallEvent, { kind: 'destroyQuerySet' }>,
|
|
441
|
+
eventIndex: number,
|
|
442
|
+
): Result<void, RhiDebugError> {
|
|
443
|
+
const entry = context.table.get(event.handleId);
|
|
444
|
+
if (entry?.resource.kind !== 'query-set')
|
|
445
|
+
return missingResource(eventIndex, event, event.handleId, 'query-set');
|
|
446
|
+
const result = context.device.destroyQuerySet(entry.resource.value);
|
|
447
|
+
if (!result.ok) return eventFailure(eventIndex, event, 'create', result.error);
|
|
448
|
+
context.table.delete(event.handleId);
|
|
449
|
+
return ok(undefined);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function resolveQuerySet(
|
|
453
|
+
context: ReplayExecutionContext,
|
|
454
|
+
event: Extract<RhiCallEvent, { kind: 'resolveQuerySet' }>,
|
|
455
|
+
eventIndex: number,
|
|
456
|
+
): Result<void, RhiDebugError> {
|
|
457
|
+
const encoder = requireResource<RhiCommandEncoder>(
|
|
458
|
+
context,
|
|
459
|
+
event.cmdHandleId,
|
|
460
|
+
'encoder',
|
|
461
|
+
eventIndex,
|
|
462
|
+
event.kind,
|
|
463
|
+
'command',
|
|
464
|
+
);
|
|
465
|
+
const querySet = requireResource<import('@forgeax/engine-rhi').QuerySet>(
|
|
466
|
+
context,
|
|
467
|
+
event.querySetHandleId,
|
|
468
|
+
'query-set',
|
|
469
|
+
eventIndex,
|
|
470
|
+
event.kind,
|
|
471
|
+
);
|
|
472
|
+
const destination = requireResource<Buffer>(
|
|
473
|
+
context,
|
|
474
|
+
event.destinationHandleId,
|
|
475
|
+
'buffer',
|
|
476
|
+
eventIndex,
|
|
477
|
+
event.kind,
|
|
478
|
+
);
|
|
479
|
+
if (!encoder.ok) return encoder;
|
|
480
|
+
if (!querySet.ok) return querySet;
|
|
481
|
+
if (!destination.ok) return destination;
|
|
482
|
+
const result = encoder.value.resolveQuerySet(
|
|
483
|
+
querySet.value,
|
|
484
|
+
event.firstQuery,
|
|
485
|
+
event.queryCount,
|
|
486
|
+
destination.value,
|
|
487
|
+
event.destinationOffset,
|
|
488
|
+
);
|
|
489
|
+
return result.ok ? ok(undefined) : eventFailure(eventIndex, event, 'encode', result.error);
|
|
490
|
+
}
|
|
491
|
+
|
|
389
492
|
async function createShaderModule(
|
|
390
493
|
context: ReplayExecutionContext,
|
|
391
494
|
event: Extract<RhiCallEvent, { kind: 'createShaderModule' }>,
|
|
@@ -673,6 +776,13 @@ function beginRenderPass(
|
|
|
673
776
|
event.depthStencilViewHandleId === undefined
|
|
674
777
|
? undefined
|
|
675
778
|
: context.table.get(event.depthStencilViewHandleId)?.resource;
|
|
779
|
+
const querySet =
|
|
780
|
+
event.occlusionQuerySetHandleId === undefined
|
|
781
|
+
? undefined
|
|
782
|
+
: context.table.get(event.occlusionQuerySetHandleId)?.resource;
|
|
783
|
+
if (event.occlusionQuerySetHandleId !== undefined && querySet?.kind !== 'query-set') {
|
|
784
|
+
return missingResource(eventIndex, event, event.occlusionQuerySetHandleId, 'query-set');
|
|
785
|
+
}
|
|
676
786
|
const descriptor = {
|
|
677
787
|
...event.desc,
|
|
678
788
|
colorAttachments: colors,
|
|
@@ -684,6 +794,7 @@ function beginRenderPass(
|
|
|
684
794
|
} as RenderPassDepthStencilAttachment,
|
|
685
795
|
}
|
|
686
796
|
: {}),
|
|
797
|
+
...(querySet?.kind === 'query-set' ? { occlusionQuerySet: querySet.value } : {}),
|
|
687
798
|
};
|
|
688
799
|
const pass = encoder.value.beginRenderPass(descriptor as never);
|
|
689
800
|
return context.table.set(
|
package/src/replay/readback.ts
CHANGED
|
@@ -100,7 +100,7 @@ function resolveTextureViewSubresource(
|
|
|
100
100
|
source: ResourceTableEntry,
|
|
101
101
|
requested: ReplayReadbackRequest | undefined,
|
|
102
102
|
): Result<ReplayReadbackRequest | undefined, RhiDebugError> {
|
|
103
|
-
if (
|
|
103
|
+
if (isBufferRange(requested)) return ok(requested);
|
|
104
104
|
const sourceDescriptor = recordField(source.descriptor, 'desc');
|
|
105
105
|
const sourceSize = textureSize(sourceDescriptor?.size);
|
|
106
106
|
const sourceMipCount = numberField(sourceDescriptor, 'mipLevelCount') ?? 1;
|
|
@@ -112,8 +112,8 @@ function resolveTextureViewSubresource(
|
|
|
112
112
|
const arrayLayerCount =
|
|
113
113
|
numberField(viewDescriptor, 'arrayLayerCount') ??
|
|
114
114
|
sourceSize.depthOrArrayLayers - baseArrayLayer;
|
|
115
|
-
const localMipLevel = requested
|
|
116
|
-
const localArrayLayer = requested
|
|
115
|
+
const localMipLevel = requested?.mipLevel ?? 0;
|
|
116
|
+
const localArrayLayer = requested?.arrayLayer ?? 0;
|
|
117
117
|
if (
|
|
118
118
|
!validIndex(baseMipLevel, sourceMipCount + 1) ||
|
|
119
119
|
!validIndex(baseArrayLayer, sourceSize.depthOrArrayLayers + 1) ||
|
|
@@ -128,6 +128,11 @@ function resolveTextureViewSubresource(
|
|
|
128
128
|
...requested,
|
|
129
129
|
mipLevel: baseMipLevel + localMipLevel,
|
|
130
130
|
arrayLayer: baseArrayLayer + localArrayLayer,
|
|
131
|
+
aspect:
|
|
132
|
+
requested?.aspect ??
|
|
133
|
+
(viewDescriptor?.aspect === 'depth-only' || viewDescriptor?.aspect === 'stencil-only'
|
|
134
|
+
? viewDescriptor.aspect
|
|
135
|
+
: 'all'),
|
|
131
136
|
});
|
|
132
137
|
}
|
|
133
138
|
|
package/src/replay/resources.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
CommandBuffer,
|
|
6
6
|
ComputePipeline,
|
|
7
7
|
PipelineLayout,
|
|
8
|
+
QuerySet,
|
|
8
9
|
RenderPipeline,
|
|
9
10
|
RhiCommandEncoder,
|
|
10
11
|
RhiComputePassEncoder,
|
|
@@ -24,6 +25,7 @@ export type ReplayResource = {
|
|
|
24
25
|
} & (
|
|
25
26
|
| { readonly kind: 'buffer'; readonly value: Buffer }
|
|
26
27
|
| { readonly kind: 'texture'; readonly value: Texture }
|
|
28
|
+
| { readonly kind: 'query-set'; readonly value: QuerySet }
|
|
27
29
|
| { readonly kind: 'texture-view'; readonly value: TextureView }
|
|
28
30
|
| { readonly kind: 'sampler'; readonly value: Sampler }
|
|
29
31
|
| { readonly kind: 'shader-module'; readonly value: ShaderModule }
|
|
@@ -132,6 +134,10 @@ export class ResourceTable {
|
|
|
132
134
|
const result = this.device.destroyTexture(entry.resource.value);
|
|
133
135
|
if (!result.ok && firstFailure === undefined)
|
|
134
136
|
firstFailure = resourceDisposeFailure(entry.resourceId, result.error.code);
|
|
137
|
+
} else if (entry.resource.kind === 'query-set') {
|
|
138
|
+
const result = this.device.destroyQuerySet(entry.resource.value);
|
|
139
|
+
if (!result.ok && firstFailure === undefined)
|
|
140
|
+
firstFailure = resourceDisposeFailure(entry.resourceId, result.error.code);
|
|
135
141
|
}
|
|
136
142
|
}
|
|
137
143
|
return firstFailure ?? ok(undefined);
|