@zenfg/inspector 0.1.0-beta.2 → 0.1.0-beta.4
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/GUIDE.md +182 -0
- package/README.md +60 -26
- package/THEMING.md +124 -0
- package/dist/FrameGraphInspector.d.ts +24 -8
- package/dist/FrameGraphInspector.d.ts.map +1 -1
- package/dist/FrameGraphInspector.js +151 -65
- package/dist/FrameGraphInspector.js.map +1 -1
- package/dist/capture.d.ts +5 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +2 -0
- package/dist/capture.js.map +1 -0
- package/dist/debugCaptureModel.d.ts +28 -4
- package/dist/debugCaptureModel.d.ts.map +1 -1
- package/dist/debugCaptureModel.js +72 -16
- package/dist/debugCaptureModel.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/panelAliasAnalysis.d.ts +1 -0
- package/dist/panelAliasAnalysis.d.ts.map +1 -1
- package/dist/panelAliasAnalysis.js +23 -54
- package/dist/panelAliasAnalysis.js.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts +18 -3
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.js +183 -41
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -1
- package/dist/panelDetailLayout.d.ts +27 -0
- package/dist/panelDetailLayout.d.ts.map +1 -0
- package/dist/panelDetailLayout.js +174 -0
- package/dist/panelDetailLayout.js.map +1 -0
- package/dist/panelDetailStyles.d.ts +2 -0
- package/dist/panelDetailStyles.d.ts.map +1 -0
- package/dist/panelDetailStyles.js +57 -0
- package/dist/panelDetailStyles.js.map +1 -0
- package/dist/panelDiagnosticsView.d.ts +10 -1
- package/dist/panelDiagnosticsView.d.ts.map +1 -1
- package/dist/panelDiagnosticsView.js +167 -58
- package/dist/panelDiagnosticsView.js.map +1 -1
- package/dist/panelDomHelpers.d.ts.map +1 -1
- package/dist/panelDomHelpers.js +6 -5
- package/dist/panelDomHelpers.js.map +1 -1
- package/dist/panelGraphLayout.d.ts +3 -2
- package/dist/panelGraphLayout.d.ts.map +1 -1
- package/dist/panelGraphLayout.js +20 -5
- package/dist/panelGraphLayout.js.map +1 -1
- package/dist/panelGraphRenderer.d.ts +10 -0
- package/dist/panelGraphRenderer.d.ts.map +1 -1
- package/dist/panelGraphRenderer.js.map +1 -1
- package/dist/panelGraphScene.d.ts +15 -26
- package/dist/panelGraphScene.d.ts.map +1 -1
- package/dist/panelGraphScene.js +191 -208
- package/dist/panelGraphScene.js.map +1 -1
- package/dist/panelGraphSearch.d.ts +16 -0
- package/dist/panelGraphSearch.d.ts.map +1 -0
- package/dist/panelGraphSearch.js +93 -0
- package/dist/panelGraphSearch.js.map +1 -0
- package/dist/panelGraphView.d.ts +82 -0
- package/dist/panelGraphView.d.ts.map +1 -1
- package/dist/panelGraphView.js +42 -9
- package/dist/panelGraphView.js.map +1 -1
- package/dist/panelGraphVisuals.d.ts +18 -6
- package/dist/panelGraphVisuals.d.ts.map +1 -1
- package/dist/panelGraphVisuals.js +156 -116
- package/dist/panelGraphVisuals.js.map +1 -1
- package/dist/panelInspectorView.d.ts +13 -0
- package/dist/panelInspectorView.d.ts.map +1 -1
- package/dist/panelInspectorView.js +326 -46
- package/dist/panelInspectorView.js.map +1 -1
- package/dist/panelListStyles.d.ts +3 -0
- package/dist/panelListStyles.d.ts.map +1 -0
- package/dist/panelListStyles.js +155 -0
- package/dist/panelListStyles.js.map +1 -0
- package/dist/panelMemoryStyles.d.ts +3 -0
- package/dist/panelMemoryStyles.d.ts.map +1 -0
- package/dist/panelMemoryStyles.js +42 -0
- package/dist/panelMemoryStyles.js.map +1 -0
- package/dist/panelMemoryView.d.ts +13 -1
- package/dist/panelMemoryView.d.ts.map +1 -1
- package/dist/panelMemoryView.js +161 -41
- package/dist/panelMemoryView.js.map +1 -1
- package/dist/panelPassesView.d.ts +15 -0
- package/dist/panelPassesView.d.ts.map +1 -1
- package/dist/panelPassesView.js +202 -72
- package/dist/panelPassesView.js.map +1 -1
- package/dist/panelRawView.d.ts +15 -0
- package/dist/panelRawView.d.ts.map +1 -0
- package/dist/panelRawView.js +105 -0
- package/dist/panelRawView.js.map +1 -0
- package/dist/panelResourcesView.d.ts +8 -0
- package/dist/panelResourcesView.d.ts.map +1 -1
- package/dist/panelResourcesView.js +62 -31
- package/dist/panelResourcesView.js.map +1 -1
- package/dist/panelSelection.d.ts +9 -1
- package/dist/panelSelection.d.ts.map +1 -1
- package/dist/panelSelection.js +36 -6
- package/dist/panelSelection.js.map +1 -1
- package/dist/panelTheme.d.ts +13 -0
- package/dist/panelTheme.d.ts.map +1 -0
- package/dist/panelTheme.js +104 -0
- package/dist/panelTheme.js.map +1 -0
- package/dist/panelTypes.d.ts +18 -4
- package/dist/panelTypes.d.ts.map +1 -1
- package/dist/panelTypes.js.map +1 -1
- package/dist/panelVisualTheme.d.ts +176 -71
- package/dist/panelVisualTheme.d.ts.map +1 -1
- package/dist/panelVisualTheme.js +34 -36
- package/dist/panelVisualTheme.js.map +1 -1
- package/dist/panelWorkbenchHelpers.d.ts +12 -1
- package/dist/panelWorkbenchHelpers.d.ts.map +1 -1
- package/dist/panelWorkbenchHelpers.js +63 -2
- package/dist/panelWorkbenchHelpers.js.map +1 -1
- package/dist/panelWorkbenchStyles.d.ts +2 -0
- package/dist/panelWorkbenchStyles.d.ts.map +1 -0
- package/dist/panelWorkbenchStyles.js +101 -0
- package/dist/panelWorkbenchStyles.js.map +1 -0
- package/dist/panelWorkbenchView.d.ts +16 -0
- package/dist/panelWorkbenchView.d.ts.map +1 -1
- package/dist/panelWorkbenchView.js +228 -50
- package/dist/panelWorkbenchView.js.map +1 -1
- package/dist/query.js.map +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +161 -205
- package/dist/styles.js.map +1 -1
- package/dist/theme.d.ts +22 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +10 -0
- package/dist/theme.js.map +1 -0
- package/dist/themeDefinitions.d.ts +251 -0
- package/dist/themeDefinitions.d.ts.map +1 -0
- package/dist/themeDefinitions.js +96 -0
- package/dist/themeDefinitions.js.map +1 -0
- package/dist/themePalette.d.ts +66 -0
- package/dist/themePalette.d.ts.map +1 -0
- package/dist/themePalette.js +37 -0
- package/dist/themePalette.js.map +1 -0
- package/dist/themes.css +165 -0
- package/package.json +44 -10
- package/src/FrameGraphInspector.ts +145 -68
- package/src/capture.ts +4 -0
- package/src/debugCaptureModel.ts +97 -21
- package/src/index.ts +6 -2
- package/src/panelAliasAnalysis.ts +24 -70
- package/src/panelCytoscapeGraphRenderer.ts +177 -40
- package/src/panelDetailLayout.ts +155 -0
- package/src/panelDetailStyles.ts +56 -0
- package/src/panelDiagnosticsView.ts +167 -86
- package/src/panelDomHelpers.ts +4 -5
- package/src/panelGraphLayout.ts +20 -5
- package/src/panelGraphRenderer.ts +7 -0
- package/src/panelGraphScene.ts +204 -292
- package/src/panelGraphSearch.ts +96 -0
- package/src/panelGraphView.ts +41 -9
- package/src/panelGraphVisuals.ts +152 -118
- package/src/panelInspectorView.ts +315 -48
- package/src/panelListStyles.ts +154 -0
- package/src/panelMemoryStyles.ts +41 -0
- package/src/panelMemoryView.ts +178 -53
- package/src/panelPassesView.ts +190 -137
- package/src/panelRawView.ts +99 -0
- package/src/panelResourcesView.ts +69 -56
- package/src/panelSelection.ts +39 -7
- package/src/panelTheme.ts +85 -0
- package/src/panelTypes.ts +16 -4
- package/src/panelVisualTheme.ts +38 -38
- package/src/panelWorkbenchHelpers.ts +62 -4
- package/src/panelWorkbenchStyles.ts +100 -0
- package/src/panelWorkbenchView.ts +201 -52
- package/src/query.ts +11 -11
- package/src/styles.ts +162 -205
- package/src/theme.ts +24 -0
- package/src/themeDefinitions.ts +103 -0
- package/src/themePalette.ts +44 -0
package/src/debugCaptureModel.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
FrameGraphSnapshotNodeKind,
|
|
7
7
|
FrameGraphSnapshotResourceKind,
|
|
8
8
|
FrameGraphSnapshotResourceOrigin,
|
|
9
|
-
|
|
9
|
+
FrameGraphSnapshotRoot,
|
|
10
10
|
FrameGraphSnapshotSegment,
|
|
11
11
|
FrameGraphSnapshotTextureRegion,
|
|
12
12
|
FrameGraphSnapshotWriteContents,
|
|
@@ -59,12 +59,14 @@ export type FrameGraphDebugNode = {
|
|
|
59
59
|
readonly sideEffect: boolean;
|
|
60
60
|
readonly debugGroupId?: string;
|
|
61
61
|
readonly gpuDurationMicros?: number;
|
|
62
|
+
readonly cpuDurationMicros?: number;
|
|
62
63
|
readonly reads: readonly FrameGraphDebugAccess[];
|
|
63
64
|
readonly writes: readonly FrameGraphDebugAccess[];
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
export type FrameGraphDebugResource = FrameGraphDebugResourceRef & {
|
|
67
68
|
readonly origin: FrameGraphSnapshotResourceOrigin;
|
|
69
|
+
readonly initialContents?: 'defined' | 'undefined';
|
|
68
70
|
readonly debugGroupId?: string;
|
|
69
71
|
readonly usageFlags: readonly string[];
|
|
70
72
|
readonly lifetime?: { readonly firstUse: number; readonly lastUse: number };
|
|
@@ -80,12 +82,19 @@ export type FrameGraphDebugEdge = {
|
|
|
80
82
|
readonly kind: 'value' | 'ordering';
|
|
81
83
|
};
|
|
82
84
|
|
|
83
|
-
export type FrameGraphDebugRoot = {
|
|
84
|
-
readonly
|
|
85
|
-
readonly nodeId?: string;
|
|
85
|
+
export type FrameGraphDebugRoot = FrameGraphSnapshotRoot & {
|
|
86
|
+
readonly key: string;
|
|
86
87
|
readonly resource?: FrameGraphDebugResourceRef;
|
|
87
88
|
};
|
|
88
89
|
|
|
90
|
+
/** Semantic identity within one recording, independent of root array order. */
|
|
91
|
+
export function rootKey(root: FrameGraphSnapshotRoot): string {
|
|
92
|
+
return root.reason === 'side-effect' ? JSON.stringify([root.reason, root.nodeId])
|
|
93
|
+
: JSON.stringify([root.reason, root.resourceId, root.range?.kind === 'buffer'
|
|
94
|
+
? ['buffer', root.range.offset, root.range.size]
|
|
95
|
+
: root.range?.regions.map((r) => [r.baseMipLevel, r.mipLevelCount, r.aspect, r.baseArrayLayer, r.arrayLayerCount, r.baseDepthSlice, r.depthSliceCount]) ?? null]);
|
|
96
|
+
}
|
|
97
|
+
|
|
89
98
|
export type FrameGraphDebugCulledNode = {
|
|
90
99
|
readonly node: Omit<FrameGraphDebugNode, 'order'>;
|
|
91
100
|
readonly reason: string;
|
|
@@ -105,6 +114,7 @@ export type FrameGraphDebugGroupSummary = {
|
|
|
105
114
|
readonly nodeKindCounts: Readonly<Partial<Record<FrameGraphSnapshotNodeKind, number>>>;
|
|
106
115
|
readonly inputResources: readonly FrameGraphDebugResourceRef[];
|
|
107
116
|
readonly outputResources: readonly FrameGraphDebugResourceRef[];
|
|
117
|
+
readonly outputRoots: readonly FrameGraphDebugRoot[];
|
|
108
118
|
readonly registeredTransientResourceCount: number;
|
|
109
119
|
readonly accessedTransientResourceCount: number;
|
|
110
120
|
readonly physicalAllocationCount: number;
|
|
@@ -112,6 +122,8 @@ export type FrameGraphDebugGroupSummary = {
|
|
|
112
122
|
readonly externalSubmissionCount: number;
|
|
113
123
|
readonly gpuWorkDurationMicros: number;
|
|
114
124
|
readonly timedNodeCount: number;
|
|
125
|
+
readonly cpuTimedNodeCount: number;
|
|
126
|
+
readonly cpuWorkDurationMicros: number;
|
|
115
127
|
readonly timingEligibleNodeCount: number;
|
|
116
128
|
};
|
|
117
129
|
|
|
@@ -131,14 +143,23 @@ export type FrameGraphDebugGroup = {
|
|
|
131
143
|
export type FrameGraphDebugMetrics = {
|
|
132
144
|
readonly timingEligibleNodeCount: number;
|
|
133
145
|
readonly timedNodeCount: number;
|
|
146
|
+
readonly cpuTimedNodeCount: number;
|
|
147
|
+
readonly cpuWorkDurationMicros: number;
|
|
134
148
|
readonly slowestNode?: FrameGraphDebugNode;
|
|
135
149
|
readonly transientEstimatedByteSize?: number;
|
|
136
150
|
readonly logicalCapacityBytes?: number;
|
|
137
151
|
readonly physicalEstimatedBytes?: number;
|
|
138
152
|
readonly aliasReuseBytes?: number;
|
|
139
153
|
readonly aliasedAllocationCount: number;
|
|
154
|
+
readonly estimatedCoverage: {
|
|
155
|
+
readonly transient: EstimateCoverage;
|
|
156
|
+
readonly logical: EstimateCoverage;
|
|
157
|
+
readonly physical: EstimateCoverage;
|
|
158
|
+
};
|
|
140
159
|
};
|
|
141
160
|
|
|
161
|
+
export type EstimateCoverage = { readonly known: number; readonly total: number };
|
|
162
|
+
|
|
142
163
|
export type FrameGraphDebugSnapshotSource = {
|
|
143
164
|
readonly kind: 'live' | 'file' | 'programmatic';
|
|
144
165
|
readonly label: string;
|
|
@@ -160,6 +181,10 @@ export type FrameGraphDebugViewModel = {
|
|
|
160
181
|
readonly executionSegments: readonly FrameGraphDebugExecutionSegment[];
|
|
161
182
|
readonly textureViewById: ReadonlyMap<string, FrameGraphSnapshot['graph']['textureViews'][number]>;
|
|
162
183
|
readonly nodeById: ReadonlyMap<string, FrameGraphDebugNode>;
|
|
184
|
+
readonly canonicalNodeById: ReadonlyMap<string, FrameGraphSnapshot['graph']['nodes'][number]>;
|
|
185
|
+
readonly culledById: ReadonlyMap<string, FrameGraphDebugCulledNode>;
|
|
186
|
+
readonly diagnosticsByNodeId: ReadonlyMap<string, readonly FrameGraphSnapshot['diagnostics'][number][]>;
|
|
187
|
+
readonly diagnosticsByResourceId: ReadonlyMap<string, readonly FrameGraphSnapshot['diagnostics'][number][]>;
|
|
163
188
|
readonly resourceById: ReadonlyMap<string, FrameGraphDebugResource>;
|
|
164
189
|
readonly groupById: ReadonlyMap<string, FrameGraphDebugGroup>;
|
|
165
190
|
readonly groupByPathKey: ReadonlyMap<string, FrameGraphDebugGroup>;
|
|
@@ -169,6 +194,7 @@ export type FrameGraphDebugViewModel = {
|
|
|
169
194
|
readonly segmentByNodeId: ReadonlyMap<string, FrameGraphDebugExecutionSegment>;
|
|
170
195
|
readonly segmentByIndex: ReadonlyMap<number, FrameGraphDebugExecutionSegment>;
|
|
171
196
|
readonly metrics: FrameGraphDebugMetrics;
|
|
197
|
+
readonly cpuProfiling: FrameGraphSnapshot['timings']['cpu'];
|
|
172
198
|
readonly profiling:
|
|
173
199
|
| {
|
|
174
200
|
readonly status: 'available';
|
|
@@ -207,6 +233,7 @@ export function createDebugViewModel(
|
|
|
207
233
|
? { ...source, migratedFromLegacy: true }
|
|
208
234
|
: source;
|
|
209
235
|
const gpuTiming = snapshot.timings.gpu;
|
|
236
|
+
const cpuTimings = new Map(snapshot.timings.cpu.status === 'available' ? snapshot.timings.cpu.nodes.map(node => [node.nodeId, node.durationMicros]) : []);
|
|
210
237
|
const resourcePool = snapshot.memory.poolReport;
|
|
211
238
|
const normalizedGroups = normalizeDebugGroups(compilation);
|
|
212
239
|
const knownGroupIds = new Set(normalizedGroups.map((group) => group.id));
|
|
@@ -269,6 +296,7 @@ export function createDebugViewModel(
|
|
|
269
296
|
sideEffect: node.sideEffect,
|
|
270
297
|
debugGroupId: node.groupId,
|
|
271
298
|
gpuDurationMicros: timings.get(node.id),
|
|
299
|
+
cpuDurationMicros: cpuTimings.get(node.id),
|
|
272
300
|
reads: nodeAccesses(node.id, 'read'),
|
|
273
301
|
writes: nodeAccesses(node.id, 'write'),
|
|
274
302
|
}));
|
|
@@ -277,6 +305,7 @@ export function createDebugViewModel(
|
|
|
277
305
|
kind: resource.kind,
|
|
278
306
|
label: resource.label,
|
|
279
307
|
origin: resource.origin,
|
|
308
|
+
initialContents: resource.initialContents,
|
|
280
309
|
debugGroupId: resource.groupId,
|
|
281
310
|
usageFlags: resource.usageFlags,
|
|
282
311
|
lifetime: resource.lifetime,
|
|
@@ -289,8 +318,8 @@ export function createDebugViewModel(
|
|
|
289
318
|
resource: resourceRef(dependency.resourceId),
|
|
290
319
|
}));
|
|
291
320
|
const roots: FrameGraphDebugRoot[] = compilation.roots.map((root) => ({
|
|
292
|
-
|
|
293
|
-
|
|
321
|
+
...root,
|
|
322
|
+
key: rootKey(root),
|
|
294
323
|
resource: root.resourceId === undefined ? undefined : resourceRef(root.resourceId),
|
|
295
324
|
}));
|
|
296
325
|
const culledNodes: FrameGraphDebugCulledNode[] = compilation.nodes
|
|
@@ -335,6 +364,18 @@ export function createDebugViewModel(
|
|
|
335
364
|
executionSegments,
|
|
336
365
|
});
|
|
337
366
|
const nodeById = new Map(nodes.map((node) => [node.id, node]));
|
|
367
|
+
const canonicalNodeById = new Map(compilation.nodes.map((node) => [node.id, node]));
|
|
368
|
+
const culledById = new Map(culledNodes.map((entry) => [entry.node.id, entry]));
|
|
369
|
+
const diagnosticsByNodeId = new Map<string, FrameGraphSnapshot['diagnostics'][number][]>();
|
|
370
|
+
const diagnosticsByResourceId = new Map<string, FrameGraphSnapshot['diagnostics'][number][]>();
|
|
371
|
+
for (const diagnostic of snapshot.diagnostics) {
|
|
372
|
+
for (const [id, index] of [[diagnostic.nodeId, diagnosticsByNodeId], [diagnostic.resourceId, diagnosticsByResourceId]] as const) {
|
|
373
|
+
if (id === undefined) continue;
|
|
374
|
+
const entries = index.get(id) ?? [];
|
|
375
|
+
entries.push(diagnostic);
|
|
376
|
+
index.set(id, entries);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
338
379
|
const resourceById = new Map(resources.map((resource) => [resource.id, resource]));
|
|
339
380
|
const groupById = new Map(debugGroups.map((group) => [group.id, group]));
|
|
340
381
|
const groupByPathKey = new Map(debugGroups.map((group) => [group.pathKey, group]));
|
|
@@ -352,14 +393,18 @@ export function createDebugViewModel(
|
|
|
352
393
|
), undefined);
|
|
353
394
|
const transientResources = resources.filter((resource) => resource.origin === 'transient');
|
|
354
395
|
const transientEstimatedByteSize = sumKnown(transientResources.map((resource) => resource.estimatedByteSize));
|
|
355
|
-
const
|
|
396
|
+
const allocationAvailable = snapshot.memory.allocationReport.status === 'available';
|
|
397
|
+
const physicalSizes = physicalAllocations.map((allocation) => allocation.estimatedByteSize);
|
|
398
|
+
const physicalEstimatedBytes = allocationAvailable ? sumKnown(physicalSizes) : undefined;
|
|
356
399
|
const logicalAllocationSizes = transientResources
|
|
357
400
|
.filter((resource) => resource.physicalResourceId !== undefined)
|
|
358
401
|
.map((resource) => allocationById.get(resource.physicalResourceId!)?.estimatedByteSize);
|
|
359
|
-
const logicalCapacityBytes = sumKnown(logicalAllocationSizes);
|
|
402
|
+
const logicalCapacityBytes = allocationAvailable ? sumKnown(logicalAllocationSizes) : undefined;
|
|
360
403
|
const metrics: FrameGraphDebugMetrics = {
|
|
361
404
|
timingEligibleNodeCount: timingEligibleNodes.length,
|
|
362
405
|
timedNodeCount: timedNodes.length,
|
|
406
|
+
cpuTimedNodeCount: nodes.filter(node => node.cpuDurationMicros !== undefined).length,
|
|
407
|
+
cpuWorkDurationMicros: nodes.reduce((sum, node) => sum + (node.cpuDurationMicros ?? 0), 0),
|
|
363
408
|
slowestNode,
|
|
364
409
|
transientEstimatedByteSize,
|
|
365
410
|
logicalCapacityBytes,
|
|
@@ -368,6 +413,11 @@ export function createDebugViewModel(
|
|
|
368
413
|
? undefined
|
|
369
414
|
: Math.max(0, logicalCapacityBytes - physicalEstimatedBytes),
|
|
370
415
|
aliasedAllocationCount: physicalAllocations.filter((allocation) => allocation.resourceIds.length > 1).length,
|
|
416
|
+
estimatedCoverage: {
|
|
417
|
+
transient: estimateCoverage(transientResources.map((resource) => resource.estimatedByteSize)),
|
|
418
|
+
logical: estimateCoverage(logicalAllocationSizes),
|
|
419
|
+
physical: estimateCoverage(physicalSizes),
|
|
420
|
+
},
|
|
371
421
|
};
|
|
372
422
|
|
|
373
423
|
return {
|
|
@@ -385,6 +435,10 @@ export function createDebugViewModel(
|
|
|
385
435
|
executionSegments,
|
|
386
436
|
textureViewById,
|
|
387
437
|
nodeById,
|
|
438
|
+
canonicalNodeById,
|
|
439
|
+
culledById,
|
|
440
|
+
diagnosticsByNodeId,
|
|
441
|
+
diagnosticsByResourceId,
|
|
388
442
|
resourceById,
|
|
389
443
|
groupById,
|
|
390
444
|
groupByPathKey,
|
|
@@ -394,6 +448,7 @@ export function createDebugViewModel(
|
|
|
394
448
|
segmentByNodeId,
|
|
395
449
|
segmentByIndex,
|
|
396
450
|
metrics,
|
|
451
|
+
cpuProfiling: snapshot.timings.cpu,
|
|
397
452
|
profiling: gpuTiming.status === 'available'
|
|
398
453
|
? {
|
|
399
454
|
status: 'available',
|
|
@@ -415,6 +470,10 @@ export function createDebugViewModel(
|
|
|
415
470
|
};
|
|
416
471
|
}
|
|
417
472
|
|
|
473
|
+
function estimateCoverage(values: readonly (number | undefined)[]): EstimateCoverage {
|
|
474
|
+
return { known: values.filter((value) => value !== undefined).length, total: values.length };
|
|
475
|
+
}
|
|
476
|
+
|
|
418
477
|
function sumKnown(values: readonly (number | undefined)[]): number | undefined {
|
|
419
478
|
let total = 0;
|
|
420
479
|
for (const value of values) {
|
|
@@ -492,6 +551,23 @@ function validateDebugGroupReferences(
|
|
|
492
551
|
}
|
|
493
552
|
}
|
|
494
553
|
|
|
554
|
+
/** Declaration boundaries are topological, not per-range initial-value provenance. */
|
|
555
|
+
export function declarationEntrances(
|
|
556
|
+
nodes: readonly FrameGraphDebugNode[],
|
|
557
|
+
accesses: readonly FrameGraphDebugAccessEdge[],
|
|
558
|
+
edges: readonly FrameGraphDebugEdge[],
|
|
559
|
+
): readonly FrameGraphDebugAccessEdge[] {
|
|
560
|
+
const retained = new Set(nodes.map((node) => node.id));
|
|
561
|
+
const incoming = new Set(edges.map((edge) => JSON.stringify([edge.resource.id, edge.toNodeId])));
|
|
562
|
+
const seen = new Set<string>();
|
|
563
|
+
return accesses.filter((access) => {
|
|
564
|
+
const key = JSON.stringify([access.resource.id, access.nodeId]);
|
|
565
|
+
if (!retained.has(access.nodeId) || incoming.has(key) || seen.has(key)) return false;
|
|
566
|
+
seen.add(key);
|
|
567
|
+
return true;
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
495
571
|
function buildDebugGroupSummaries(input: {
|
|
496
572
|
readonly groups: readonly NormalizedDebugGroup[];
|
|
497
573
|
readonly nodes: readonly FrameGraphDebugNode[];
|
|
@@ -504,6 +580,7 @@ function buildDebugGroupSummaries(input: {
|
|
|
504
580
|
}): FrameGraphDebugGroup[] {
|
|
505
581
|
const groupsById = new Map(input.groups.map((group) => [group.id, group]));
|
|
506
582
|
const resourcesById = new Map(input.resources.map((resource) => [resource.id, resource]));
|
|
583
|
+
const entrances = declarationEntrances(input.nodes, input.accessEdges, input.edges);
|
|
507
584
|
const isInGroup = (candidateId: string | undefined, groupId: string) => (
|
|
508
585
|
candidateId !== undefined && groupsById.get(candidateId)?.ancestorIds.includes(groupId) === true
|
|
509
586
|
);
|
|
@@ -526,23 +603,19 @@ function buildDebugGroupSummaries(input: {
|
|
|
526
603
|
if (!fromInside && toInside) inputIds.add(edge.resource.id);
|
|
527
604
|
if (fromInside && !toInside) outputIds.add(edge.resource.id);
|
|
528
605
|
}
|
|
529
|
-
for (const access of
|
|
530
|
-
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
&& edge.resource.id === access.resource.id
|
|
535
|
-
&& edge.kind === 'value'
|
|
536
|
-
));
|
|
537
|
-
if (access.mode === 'read' && resource && resource.origin !== 'transient' && !hasRetainedValueProducer) {
|
|
538
|
-
inputIds.add(resource.id);
|
|
539
|
-
}
|
|
606
|
+
for (const access of entrances) {
|
|
607
|
+
const fromInside = isInGroup(resourcesById.get(access.resource.id)?.debugGroupId, group.id);
|
|
608
|
+
const toInside = retainedIds.has(access.nodeId);
|
|
609
|
+
if (!fromInside && toInside) inputIds.add(access.resource.id);
|
|
610
|
+
if (fromInside && !toInside) outputIds.add(access.resource.id);
|
|
540
611
|
}
|
|
612
|
+
const outputRoots: FrameGraphDebugRoot[] = [];
|
|
541
613
|
for (const root of input.roots) {
|
|
542
614
|
if (!root.resource) continue;
|
|
543
|
-
if (
|
|
544
|
-
|
|
615
|
+
if (root.resolution?.producerNodeIds.some((id) => retainedIds.has(id))
|
|
616
|
+
|| root.resolution?.usesInitialContents && isInGroup(resourcesById.get(root.resource.id)?.debugGroupId, group.id)) {
|
|
545
617
|
outputIds.add(root.resource.id);
|
|
618
|
+
outputRoots.push(root);
|
|
546
619
|
}
|
|
547
620
|
}
|
|
548
621
|
|
|
@@ -569,6 +642,7 @@ function buildDebugGroupSummaries(input: {
|
|
|
569
642
|
nodeKindCounts,
|
|
570
643
|
inputResources: [...inputIds].map((id) => resourcesById.get(id)!).filter(Boolean),
|
|
571
644
|
outputResources: [...outputIds].map((id) => resourcesById.get(id)!).filter(Boolean),
|
|
645
|
+
outputRoots,
|
|
572
646
|
registeredTransientResourceCount: registeredTransient.length,
|
|
573
647
|
accessedTransientResourceCount: accessedTransientIds.size,
|
|
574
648
|
physicalAllocationCount: allocationIds.size,
|
|
@@ -576,6 +650,8 @@ function buildDebugGroupSummaries(input: {
|
|
|
576
650
|
externalSubmissionCount: retained.filter((node) => node.kind === 'external-submission').length,
|
|
577
651
|
gpuWorkDurationMicros: timed.reduce((sum, node) => sum + node.gpuDurationMicros!, 0),
|
|
578
652
|
timedNodeCount: timed.length,
|
|
653
|
+
cpuTimedNodeCount: retained.filter(node => node.cpuDurationMicros !== undefined).length,
|
|
654
|
+
cpuWorkDurationMicros: retained.reduce((sum, node) => sum + (node.cpuDurationMicros ?? 0), 0),
|
|
579
655
|
timingEligibleNodeCount: timingEligible.length,
|
|
580
656
|
},
|
|
581
657
|
};
|
package/src/index.ts
CHANGED
|
@@ -17,5 +17,9 @@ export type {
|
|
|
17
17
|
} from '@zenfg/snapshot';
|
|
18
18
|
export { FrameGraphInspector, mountFrameGraphInspector } from './FrameGraphInspector.ts';
|
|
19
19
|
export type { FrameGraphInspectorOptions } from './FrameGraphInspector.ts';
|
|
20
|
-
export { ZENFG_INSPECTOR_QUERY_PARAM, isZenFGInspectorRequested } from './query.ts';
|
|
21
|
-
export { ensureFrameGraphInspectorStyles } from './styles.ts';
|
|
20
|
+
export { ZENFG_INSPECTOR_QUERY_PARAM, isZenFGInspectorRequested } from './query.ts';
|
|
21
|
+
export { ensureFrameGraphInspectorStyles } from './styles.ts';
|
|
22
|
+
|
|
23
|
+
export type { InspectorTheme, InspectorThemeVariables } from './theme.ts';
|
|
24
|
+
|
|
25
|
+
export type { FrameGraphCaptureRequest } from './capture.ts';
|
|
@@ -22,9 +22,16 @@ export type AliasAnalysis = {
|
|
|
22
22
|
readonly resources: ReadonlyMap<string, AliasAnalysisResource>;
|
|
23
23
|
readonly minUse: number;
|
|
24
24
|
readonly maxUse: number;
|
|
25
|
+
readonly hasLifetimes: boolean;
|
|
25
26
|
};
|
|
26
27
|
|
|
28
|
+
// View models are immutable for the lifetime of a capture. Search and selection
|
|
29
|
+
// changes should not rebuild allocation membership or the execution domain.
|
|
30
|
+
const analysisBySnapshot = new WeakMap<FrameGraphDebugViewModel, AliasAnalysis>();
|
|
31
|
+
|
|
27
32
|
export function analyzeSnapshotAliases(snapshot: FrameGraphDebugViewModel): AliasAnalysis {
|
|
33
|
+
const cached = analysisBySnapshot.get(snapshot);
|
|
34
|
+
if (cached) return cached;
|
|
28
35
|
const allocationById = snapshot.allocationById;
|
|
29
36
|
const resourcesById = snapshot.resourceById;
|
|
30
37
|
const allocationGroups = snapshot.physicalAllocations.map((allocation): AliasAnalysisAllocation => {
|
|
@@ -43,13 +50,14 @@ export function analyzeSnapshotAliases(snapshot: FrameGraphDebugViewModel): Alia
|
|
|
43
50
|
let minUse = Number.POSITIVE_INFINITY;
|
|
44
51
|
let maxUse = Number.NEGATIVE_INFINITY;
|
|
45
52
|
for (const resource of snapshot.resources) {
|
|
46
|
-
if (!resource.lifetime) {
|
|
53
|
+
if (resource.origin !== 'transient' || !resource.lifetime) {
|
|
47
54
|
continue;
|
|
48
55
|
}
|
|
49
56
|
minUse = Math.min(minUse, resource.lifetime.firstUse);
|
|
50
57
|
maxUse = Math.max(maxUse, resource.lifetime.lastUse);
|
|
51
58
|
}
|
|
52
|
-
|
|
59
|
+
const hasLifetimes = Number.isFinite(minUse) && Number.isFinite(maxUse);
|
|
60
|
+
if (!hasLifetimes) {
|
|
53
61
|
minUse = 0;
|
|
54
62
|
maxUse = 0;
|
|
55
63
|
}
|
|
@@ -65,16 +73,19 @@ export function analyzeSnapshotAliases(snapshot: FrameGraphDebugViewModel): Alia
|
|
|
65
73
|
resource,
|
|
66
74
|
allocation,
|
|
67
75
|
aliasStatus,
|
|
68
|
-
nonAliasReasons:
|
|
76
|
+
nonAliasReasons: describeAliasStatus(aliasStatus),
|
|
69
77
|
});
|
|
70
78
|
}
|
|
71
79
|
|
|
72
|
-
|
|
80
|
+
const analysis = {
|
|
73
81
|
allocations: allocationGroups,
|
|
74
82
|
resources: resourceAnalysis,
|
|
75
83
|
minUse,
|
|
76
84
|
maxUse,
|
|
85
|
+
hasLifetimes,
|
|
77
86
|
};
|
|
87
|
+
analysisBySnapshot.set(snapshot, analysis);
|
|
88
|
+
return analysis;
|
|
78
89
|
}
|
|
79
90
|
|
|
80
91
|
function resolveAliasStatus(
|
|
@@ -94,71 +105,14 @@ function resolveAliasStatus(
|
|
|
94
105
|
return allocationResources.length > 1 ? 'aliased' : 'single';
|
|
95
106
|
}
|
|
96
107
|
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return ['No lifetime is available for this resource in the retained graph.'];
|
|
107
|
-
}
|
|
108
|
-
if (!allocation) {
|
|
109
|
-
return ['No physical allocation is associated with this transient resource.'];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const reasons = new Set<string>();
|
|
113
|
-
for (const group of allocations) {
|
|
114
|
-
if (group.allocation.id === allocation.id) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
for (const other of group.resources) {
|
|
118
|
-
if (other.id === resource.id || other.origin !== 'transient') {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (other.kind !== resource.kind) {
|
|
122
|
-
reasons.add(`Different kind from ${labelDebugResource(other)}.`);
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (group.allocation.compatibilityClassId !== allocation.compatibilityClassId) {
|
|
126
|
-
reasons.add(`Different compatibility class from ${labelDebugResource(other)}.`);
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (!other.lifetime) {
|
|
130
|
-
reasons.add(`Missing lifetime for ${labelDebugResource(other)}.`);
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
if (lifetimesOverlap(resource.lifetime, other.lifetime)) {
|
|
134
|
-
reasons.add(`Lifetime overlaps ${labelDebugResource(other)} (${other.lifetime.firstUse}-${other.lifetime.lastUse}).`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
if (reasons.size === 0) {
|
|
140
|
-
return allocationResourcesFor(allocations, allocation.id).length > 1
|
|
141
|
-
? ['Aliases with resources in the same physical allocation.']
|
|
142
|
-
: ['No compatible non-overlapping transient resource was found in this snapshot.'];
|
|
108
|
+
function describeAliasStatus(status: AliasAnalysisResource['aliasStatus']): readonly string[] {
|
|
109
|
+
switch (status) {
|
|
110
|
+
case 'not-transient': return ['Imported and surface resources do not participate in transient aliasing.'];
|
|
111
|
+
case 'no-lifetime': return ['No lifetime is available for this resource in the retained graph.'];
|
|
112
|
+
case 'not-allocated': return ['No physical allocation is associated with this transient resource.'];
|
|
113
|
+
case 'aliased': return ['Shares a physical allocation with other logical resources.'];
|
|
114
|
+
// The snapshot states allocation membership; it cannot explain allocator
|
|
115
|
+
// decisions by comparing every pair of logical resources.
|
|
116
|
+
case 'single': return ['This physical allocation contains one logical resource.'];
|
|
143
117
|
}
|
|
144
|
-
|
|
145
|
-
return Array.from(reasons).slice(0, 6);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function allocationResourcesFor(
|
|
149
|
-
allocations: readonly AliasAnalysisAllocation[],
|
|
150
|
-
allocationId: string,
|
|
151
|
-
): readonly FrameGraphDebugResource[] {
|
|
152
|
-
return allocations.find((group) => group.allocation.id === allocationId)?.resources ?? [];
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function lifetimesOverlap(
|
|
156
|
-
a: NonNullable<FrameGraphDebugResource['lifetime']>,
|
|
157
|
-
b: NonNullable<FrameGraphDebugResource['lifetime']>,
|
|
158
|
-
): boolean {
|
|
159
|
-
return a.firstUse <= b.lastUse && b.firstUse <= a.lastUse;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function labelDebugResource(resource: FrameGraphDebugResource): string {
|
|
163
|
-
return resource.label ?? `${resource.kind}-${resource.id}`;
|
|
164
118
|
}
|