@zenfg/inspector 0.1.0-beta.2 → 0.1.0-beta.3
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 +63 -1
- package/dist/FrameGraphInspector.d.ts +2 -5
- package/dist/FrameGraphInspector.d.ts.map +1 -1
- package/dist/FrameGraphInspector.js +16 -38
- package/dist/FrameGraphInspector.js.map +1 -1
- package/dist/debugCaptureModel.d.ts +9 -4
- package/dist/debugCaptureModel.d.ts.map +1 -1
- package/dist/debugCaptureModel.js +36 -14
- package/dist/debugCaptureModel.js.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts +3 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.js +39 -19
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -1
- package/dist/panelDiagnosticsView.js +2 -2
- package/dist/panelDiagnosticsView.js.map +1 -1
- package/dist/panelGraphLayout.d.ts.map +1 -1
- package/dist/panelGraphLayout.js +15 -1
- package/dist/panelGraphLayout.js.map +1 -1
- package/dist/panelGraphScene.d.ts +14 -26
- package/dist/panelGraphScene.d.ts.map +1 -1
- package/dist/panelGraphScene.js +141 -195
- package/dist/panelGraphScene.js.map +1 -1
- package/dist/panelGraphView.d.ts.map +1 -1
- package/dist/panelGraphView.js +31 -9
- package/dist/panelGraphView.js.map +1 -1
- package/dist/panelGraphVisuals.d.ts +7 -3
- package/dist/panelGraphVisuals.d.ts.map +1 -1
- package/dist/panelGraphVisuals.js +45 -80
- package/dist/panelGraphVisuals.js.map +1 -1
- package/dist/panelInspectorView.d.ts +5 -0
- package/dist/panelInspectorView.d.ts.map +1 -1
- package/dist/panelInspectorView.js +99 -13
- package/dist/panelInspectorView.js.map +1 -1
- package/dist/panelSelection.js +2 -2
- package/dist/panelSelection.js.map +1 -1
- package/dist/panelTypes.d.ts +8 -3
- package/dist/panelTypes.d.ts.map +1 -1
- package/dist/panelTypes.js.map +1 -1
- package/dist/panelVisualTheme.d.ts +21 -13
- package/dist/panelVisualTheme.d.ts.map +1 -1
- package/dist/panelVisualTheme.js +15 -7
- package/dist/panelVisualTheme.js.map +1 -1
- package/dist/panelWorkbenchHelpers.js +1 -1
- package/dist/panelWorkbenchHelpers.js.map +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +16 -12
- package/dist/styles.js.map +1 -1
- package/package.json +2 -2
- package/src/FrameGraphInspector.ts +17 -39
- package/src/debugCaptureModel.ts +44 -19
- package/src/panelCytoscapeGraphRenderer.ts +38 -18
- package/src/panelDiagnosticsView.ts +3 -3
- package/src/panelGraphLayout.ts +15 -1
- package/src/panelGraphScene.ts +153 -279
- package/src/panelGraphView.ts +31 -9
- package/src/panelGraphVisuals.ts +46 -82
- package/src/panelInspectorView.ts +104 -16
- package/src/panelSelection.ts +2 -2
- package/src/panelTypes.ts +9 -3
- package/src/panelVisualTheme.ts +18 -7
- package/src/panelWorkbenchHelpers.ts +2 -2
- package/src/styles.ts +16 -12
package/src/panelGraphScene.ts
CHANGED
|
@@ -4,11 +4,11 @@ import type {
|
|
|
4
4
|
FrameGraphDebugGroup,
|
|
5
5
|
FrameGraphDebugNode,
|
|
6
6
|
FrameGraphDebugResource,
|
|
7
|
-
FrameGraphDebugResourceRef,
|
|
8
7
|
FrameGraphDebugViewModel,
|
|
9
8
|
} from './debugCaptureModel.ts';
|
|
10
9
|
import { formatGpuDuration, labelNode, labelResource } from './panelDomHelpers.ts';
|
|
11
|
-
import
|
|
10
|
+
import { declarationEntrances } from './debugCaptureModel.ts';
|
|
11
|
+
import type { GraphFlowRelation, Selection } from './panelTypes.ts';
|
|
12
12
|
|
|
13
13
|
export type GraphSceneElementId = string;
|
|
14
14
|
|
|
@@ -28,14 +28,6 @@ export type PassSceneNode = GraphSceneNodeBase & {
|
|
|
28
28
|
readonly gpuDurationMicros?: number;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export type CulledPassSceneNode = GraphSceneNodeBase & {
|
|
32
|
-
readonly kind: 'culled-pass';
|
|
33
|
-
readonly culledIndex: number;
|
|
34
|
-
readonly nodeId: string;
|
|
35
|
-
readonly passKind: FrameGraphDebugNode['kind'];
|
|
36
|
-
readonly reason: string;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
31
|
export type GroupSceneNode = GraphSceneNodeBase & {
|
|
40
32
|
readonly kind: 'group';
|
|
41
33
|
readonly groupId: string;
|
|
@@ -57,34 +49,29 @@ export type ResourceSceneNode = GraphSceneNodeBase & {
|
|
|
57
49
|
readonly resourceKind: FrameGraphDebugResource['kind'];
|
|
58
50
|
};
|
|
59
51
|
|
|
60
|
-
export type
|
|
52
|
+
export type RootSceneNode = GraphSceneNodeBase & {
|
|
53
|
+
readonly kind: 'root';
|
|
54
|
+
readonly rootKey: string;
|
|
55
|
+
readonly resourceId: string;
|
|
56
|
+
readonly resourceKind: FrameGraphDebugResource['kind'];
|
|
57
|
+
};
|
|
58
|
+
export type GraphSceneNode = PassSceneNode | GroupSceneNode | ResourceSceneNode | RootSceneNode;
|
|
61
59
|
|
|
62
60
|
type GraphSceneEdgeBase = {
|
|
63
61
|
readonly id: GraphSceneElementId;
|
|
64
62
|
readonly from: GraphSceneElementId;
|
|
65
63
|
readonly to: GraphSceneElementId;
|
|
66
|
-
readonly label?: string;
|
|
67
64
|
readonly title: string;
|
|
68
65
|
readonly resourceId: string;
|
|
69
66
|
};
|
|
70
67
|
|
|
71
|
-
export type
|
|
72
|
-
readonly kind: '
|
|
73
|
-
readonly
|
|
68
|
+
export type GraphSceneEdge = GraphSceneEdgeBase & {
|
|
69
|
+
readonly kind: 'flow' | 'ordering';
|
|
70
|
+
readonly relations: readonly GraphFlowRelation[];
|
|
74
71
|
readonly underlyingDependencies: readonly FrameGraphDebugEdge[];
|
|
75
72
|
readonly underlyingDependencyCount: number;
|
|
76
73
|
};
|
|
77
74
|
|
|
78
|
-
export type AccessSceneEdge = GraphSceneEdgeBase & {
|
|
79
|
-
readonly kind: 'access';
|
|
80
|
-
readonly accessId: string;
|
|
81
|
-
readonly accessMode: FrameGraphDebugAccessEdge['mode'];
|
|
82
|
-
readonly access: FrameGraphDebugAccessEdge['access'];
|
|
83
|
-
readonly dashed: boolean;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export type GraphSceneEdge = DependencySceneEdge | AccessSceneEdge;
|
|
87
|
-
|
|
88
75
|
export type GraphSemanticReferences = {
|
|
89
76
|
readonly nodeIds: readonly string[];
|
|
90
77
|
readonly groupPathKeys: readonly string[];
|
|
@@ -96,12 +83,12 @@ export type GraphSemanticReferences = {
|
|
|
96
83
|
export type GraphSceneInteractionIndex = {
|
|
97
84
|
readonly selectionByElementId: ReadonlyMap<GraphSceneElementId, Selection>;
|
|
98
85
|
readonly primaryElementIdsBySelection: ReadonlyMap<string, readonly GraphSceneElementId[]>;
|
|
99
|
-
readonly
|
|
86
|
+
readonly hoverElementIdsBySelection: ReadonlyMap<string, readonly GraphSceneElementId[]>;
|
|
87
|
+
readonly resourceElementIdsByResourceId: ReadonlyMap<string, readonly GraphSceneElementId[]>;
|
|
100
88
|
readonly semanticReferencesByElementId: ReadonlyMap<GraphSceneElementId, GraphSemanticReferences>;
|
|
101
89
|
};
|
|
102
90
|
|
|
103
91
|
export type GraphScene = {
|
|
104
|
-
readonly mode: GraphViewMode;
|
|
105
92
|
readonly nodes: readonly GraphSceneNode[];
|
|
106
93
|
readonly edges: readonly GraphSceneEdge[];
|
|
107
94
|
readonly topologyKey: string;
|
|
@@ -110,7 +97,6 @@ export type GraphScene = {
|
|
|
110
97
|
};
|
|
111
98
|
|
|
112
99
|
export type CreateGraphSceneOptions = {
|
|
113
|
-
readonly mode: GraphViewMode;
|
|
114
100
|
readonly groupsEnabled: boolean;
|
|
115
101
|
readonly expandedGroupPaths: ReadonlySet<string>;
|
|
116
102
|
};
|
|
@@ -118,7 +104,8 @@ export type CreateGraphSceneOptions = {
|
|
|
118
104
|
type MutableInteractionIndex = {
|
|
119
105
|
readonly selectionByElementId: Map<GraphSceneElementId, Selection>;
|
|
120
106
|
readonly primaryElementIdsBySelection: Map<string, GraphSceneElementId[]>;
|
|
121
|
-
readonly
|
|
107
|
+
readonly hoverElementIdsBySelection: Map<string, GraphSceneElementId[]>;
|
|
108
|
+
readonly resourceElementIdsByResourceId: Map<string, GraphSceneElementId[]>;
|
|
122
109
|
readonly semanticReferencesByElementId: Map<GraphSceneElementId, GraphSemanticReferences>;
|
|
123
110
|
};
|
|
124
111
|
|
|
@@ -126,9 +113,7 @@ export function createGraphScene(
|
|
|
126
113
|
snapshot: FrameGraphDebugViewModel,
|
|
127
114
|
options: CreateGraphSceneOptions,
|
|
128
115
|
): GraphScene {
|
|
129
|
-
return options.
|
|
130
|
-
? createResourceGraphScene(snapshot)
|
|
131
|
-
: createPassGraphScene(snapshot, options.groupsEnabled, options.expandedGroupPaths);
|
|
116
|
+
return createFrameFlowScene(snapshot, options.groupsEnabled, options.expandedGroupPaths);
|
|
132
117
|
}
|
|
133
118
|
|
|
134
119
|
export function graphGroupElementId(pathKey: string): GraphSceneElementId {
|
|
@@ -146,7 +131,7 @@ export function selectionKey(selection: Selection): string {
|
|
|
146
131
|
case 'allocation':
|
|
147
132
|
return `allocation:${selection.id}`;
|
|
148
133
|
case 'root':
|
|
149
|
-
return `root:${selection.
|
|
134
|
+
return `root:${selection.key}`;
|
|
150
135
|
case 'culled':
|
|
151
136
|
return `culled:${selection.index}`;
|
|
152
137
|
case 'segment':
|
|
@@ -154,7 +139,7 @@ export function selectionKey(selection: Selection): string {
|
|
|
154
139
|
}
|
|
155
140
|
}
|
|
156
141
|
|
|
157
|
-
function
|
|
142
|
+
function createFrameFlowScene(
|
|
158
143
|
snapshot: FrameGraphDebugViewModel,
|
|
159
144
|
groupsEnabled: boolean,
|
|
160
145
|
expandedGroupPaths: ReadonlySet<string>,
|
|
@@ -167,8 +152,20 @@ function createPassGraphScene(
|
|
|
167
152
|
const hasVisibleAncestors = (group: FrameGraphDebugGroup) => group.ancestorIds
|
|
168
153
|
.slice(0, -1)
|
|
169
154
|
.every((id) => isExpanded(groupsById.get(id)!));
|
|
155
|
+
const retainedIds = new Set(snapshot.nodes.map((node) => node.id));
|
|
156
|
+
const usedResourceIds = new Set([
|
|
157
|
+
...snapshot.accessEdges.filter((access) => retainedIds.has(access.nodeId)).map((access) => access.resource.id),
|
|
158
|
+
...snapshot.roots.flatMap((root) => root.resource ? [root.resource.id] : []),
|
|
159
|
+
]);
|
|
160
|
+
const resources = snapshot.resources.filter((resource) => usedResourceIds.has(resource.id));
|
|
161
|
+
const populatedGroupIds = new Set<string>();
|
|
162
|
+
for (const item of [...snapshot.nodes, ...resources]) {
|
|
163
|
+
for (const id of groupsById.get(item.debugGroupId ?? '')?.ancestorIds ?? []) populatedGroupIds.add(id);
|
|
164
|
+
}
|
|
165
|
+
const visibleResources = resources.filter((resource) => !useGroups || resource.debugGroupId === undefined
|
|
166
|
+
|| groupsById.get(resource.debugGroupId)!.ancestorIds.every((id) => isExpanded(groupsById.get(id)!)));
|
|
170
167
|
const includedGroups = useGroups
|
|
171
|
-
? snapshot.debugGroups.filter((group) => group.
|
|
168
|
+
? snapshot.debugGroups.filter((group) => populatedGroupIds.has(group.id) && hasVisibleAncestors(group))
|
|
172
169
|
: [];
|
|
173
170
|
const includedGroupIds = new Set(includedGroups.map((group) => group.id));
|
|
174
171
|
const representedNodeIdsByGroupId = new Map<string, string[]>();
|
|
@@ -197,6 +194,7 @@ function createPassGraphScene(
|
|
|
197
194
|
.map((child) => graphGroupElementId(child.pathKey)),
|
|
198
195
|
...(visiblePassesByGroupId.get(group.id) ?? [])
|
|
199
196
|
.map((node) => passElementId(node.id)),
|
|
197
|
+
...visibleResources.filter((resource) => resource.debugGroupId === group.id).map((resource) => resourceElementId(resource.id)),
|
|
200
198
|
];
|
|
201
199
|
const node: GroupSceneNode = {
|
|
202
200
|
id,
|
|
@@ -267,7 +265,7 @@ function createPassGraphScene(
|
|
|
267
265
|
|
|
268
266
|
for (const node of snapshot.nodes) {
|
|
269
267
|
const representative = representativeByNodeId.get(node.id)!;
|
|
270
|
-
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative]
|
|
268
|
+
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative]);
|
|
271
269
|
}
|
|
272
270
|
if (useGroups) {
|
|
273
271
|
for (const group of snapshot.debugGroups) {
|
|
@@ -275,218 +273,128 @@ function createPassGraphScene(
|
|
|
275
273
|
const collapsedAncestorId = group.ancestorIds.find((id) => !isExpanded(groupsById.get(id)!));
|
|
276
274
|
if (collapsedAncestorId !== undefined) {
|
|
277
275
|
const representative = graphGroupElementId(groupsById.get(collapsedAncestorId)!.pathKey);
|
|
278
|
-
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative]
|
|
276
|
+
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative]);
|
|
279
277
|
}
|
|
280
278
|
}
|
|
281
279
|
}
|
|
282
280
|
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
passElementId(edge.toNodeId),
|
|
289
|
-
[edge],
|
|
290
|
-
index,
|
|
291
|
-
));
|
|
292
|
-
for (const edge of dependencyEdges) {
|
|
293
|
-
const selection: Selection = { kind: 'resource', id: edge.resourceId };
|
|
294
|
-
interaction.selectionByElementId.set(edge.id, selection);
|
|
295
|
-
appendSelectionElement(interaction.primaryElementIdsBySelection, selection, edge.id);
|
|
296
|
-
appendSelectionElement(interaction.relatedElementIdsBySelection, selection, edge.id);
|
|
297
|
-
interaction.semanticReferencesByElementId.set(edge.id, {
|
|
298
|
-
nodeIds: unique(edge.underlyingDependencies.flatMap((dependency) => [dependency.fromNodeId, dependency.toNodeId])),
|
|
299
|
-
groupPathKeys: [],
|
|
300
|
-
resourceIds: [edge.resourceId],
|
|
301
|
-
accessIds: [],
|
|
302
|
-
dependencies: edge.underlyingDependencies,
|
|
303
|
-
});
|
|
281
|
+
const representativeByResourceId = new Map<string, string>();
|
|
282
|
+
for (const resource of resources) {
|
|
283
|
+
const collapsed = useGroups ? groupsById.get(resource.debugGroupId ?? '')?.ancestorIds.find((id) => !isExpanded(groupsById.get(id)!)) : undefined;
|
|
284
|
+
const representative = collapsed ? graphGroupElementId(groupsById.get(collapsed)!.pathKey) : resourceElementId(resource.id);
|
|
285
|
+
representativeByResourceId.set(resource.id, representative);
|
|
304
286
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const segment = segmentByNodeId.get(node.id);
|
|
315
|
-
const sceneNode: PassSceneNode = {
|
|
316
|
-
id: passElementId(node.id),
|
|
317
|
-
kind: 'pass',
|
|
318
|
-
nodeId: node.id,
|
|
319
|
-
passKind: node.kind,
|
|
320
|
-
executionSegmentIndex: segment?.index,
|
|
321
|
-
gpuDurationMicros: node.gpuDurationMicros,
|
|
322
|
-
label: formatGraphNodeLabel(`${segmentLabel(segment)} ${labelNode(node)}`),
|
|
323
|
-
overviewLabel: shortGraphLabel(labelNode(node)),
|
|
324
|
-
title: createNodeTitle(node, segment, snapshot),
|
|
325
|
-
};
|
|
326
|
-
registerElement(
|
|
327
|
-
interaction,
|
|
328
|
-
sceneNode.id,
|
|
329
|
-
{ kind: 'node', id: node.id },
|
|
330
|
-
emptyReferences({ nodeIds: [node.id] }),
|
|
331
|
-
);
|
|
332
|
-
return sceneNode;
|
|
333
|
-
});
|
|
334
|
-
const culledNodes: CulledPassSceneNode[] = snapshot.culledNodes.map((culled, index) => {
|
|
335
|
-
const sceneNode: CulledPassSceneNode = {
|
|
336
|
-
id: culledPassElementId(index, culled.node.id),
|
|
337
|
-
kind: 'culled-pass',
|
|
338
|
-
culledIndex: index,
|
|
339
|
-
nodeId: culled.node.id,
|
|
340
|
-
passKind: culled.node.kind,
|
|
341
|
-
reason: culled.reason,
|
|
342
|
-
label: `${formatGraphNodeLabel(labelNode(culled.node))}\n(culled)`,
|
|
343
|
-
overviewLabel: `${shortGraphLabel(labelNode(culled.node))} (culled)`,
|
|
344
|
-
title: createCulledNodeTitle(culled),
|
|
287
|
+
const resourceNodes: ResourceSceneNode[] = visibleResources.map((resource) => {
|
|
288
|
+
const origin = resource.origin === 'transient' ? 'Created' : resource.origin === 'imported' ? 'Imported' : 'Surface';
|
|
289
|
+
const node: ResourceSceneNode = {
|
|
290
|
+
id: resourceElementId(resource.id), kind: 'resource', resourceId: resource.id, resourceKind: resource.kind,
|
|
291
|
+
label: origin + ' · ' + (resource.kind === 'buffer' ? 'Buffer' : 'Texture') + '\n' + formatGraphResourceLabel(labelResource(resource)),
|
|
292
|
+
overviewLabel: origin + '\n' + formatGraphResourceLabel(shortGraphLabel(labelResource(resource))),
|
|
293
|
+
title: createResourceTitle(resource, snapshot, snapshot.accessesByResourceId.get(resource.id) ?? [])
|
|
294
|
+
+ '\nDeclaration entrance, not a complete initial-content provenance graph.',
|
|
295
|
+
parentId: useGroups && resource.debugGroupId ? graphGroupElementId(groupsById.get(resource.debugGroupId)!.pathKey) : undefined,
|
|
345
296
|
};
|
|
346
|
-
registerElement(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
{ kind: 'culled', index },
|
|
350
|
-
emptyReferences({ nodeIds: [culled.node.id] }),
|
|
351
|
-
);
|
|
352
|
-
return sceneNode;
|
|
353
|
-
});
|
|
354
|
-
const resourceNodes: ResourceSceneNode[] = snapshot.resources.map((resource) => {
|
|
355
|
-
const sceneNode: ResourceSceneNode = {
|
|
356
|
-
id: resourceElementId(resource.id),
|
|
357
|
-
kind: 'resource',
|
|
358
|
-
resourceId: resource.id,
|
|
359
|
-
resourceKind: resource.kind,
|
|
360
|
-
label: formatGraphResourceLabel(labelResource(resource)),
|
|
361
|
-
overviewLabel: shortGraphLabel(labelResource(resource)),
|
|
362
|
-
title: createResourceTitle(resource, snapshot, accessesByResourceId.get(resource.id) ?? []),
|
|
363
|
-
};
|
|
364
|
-
registerElement(
|
|
365
|
-
interaction,
|
|
366
|
-
sceneNode.id,
|
|
367
|
-
{ kind: 'resource', id: resource.id },
|
|
368
|
-
emptyReferences({ resourceIds: [resource.id] }),
|
|
369
|
-
);
|
|
370
|
-
return sceneNode;
|
|
297
|
+
registerElement(interaction, node.id, { kind: 'resource', id: resource.id }, emptyReferences({ resourceIds: [resource.id] }));
|
|
298
|
+
interaction.resourceElementIdsByResourceId.set(resource.id, [node.id]);
|
|
299
|
+
return node;
|
|
371
300
|
});
|
|
372
|
-
const
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
301
|
+
const rootNodes: RootSceneNode[] = [];
|
|
302
|
+
const aggregates = new Map<string, { from: string; to: string; resourceId: string; relations: GraphFlowRelation[] }>();
|
|
303
|
+
const addRelation = (from: string, to: string, resourceId: string, relation: GraphFlowRelation) => {
|
|
304
|
+
if (from === to) return;
|
|
305
|
+
const key = JSON.stringify([from, to, resourceId]);
|
|
306
|
+
const entry = aggregates.get(key) ?? { from, to, resourceId, relations: [] };
|
|
307
|
+
entry.relations.push(relation);
|
|
308
|
+
aggregates.set(key, entry);
|
|
309
|
+
};
|
|
310
|
+
for (const dependency of snapshot.edges) {
|
|
311
|
+
addRelation(representativeByNodeId.get(dependency.fromNodeId)!, representativeByNodeId.get(dependency.toNodeId)!, dependency.resource.id,
|
|
312
|
+
{ role: dependency.kind, nodeIds: [dependency.fromNodeId, dependency.toNodeId], dependency });
|
|
313
|
+
}
|
|
314
|
+
for (const access of declarationEntrances(snapshot.nodes, snapshot.accessEdges, snapshot.edges)) {
|
|
315
|
+
addRelation(representativeByResourceId.get(access.resource.id)!, representativeByNodeId.get(access.nodeId)!, access.resource.id,
|
|
316
|
+
{ role: 'declaration', nodeIds: [access.nodeId] });
|
|
317
|
+
}
|
|
318
|
+
const rootOccurrences = new Map<string, number>();
|
|
319
|
+
const rangesByRootFamily = new Map<string, Set<string>>();
|
|
320
|
+
for (const root of snapshot.roots) {
|
|
321
|
+
if (!root.resource) continue;
|
|
322
|
+
const family = JSON.stringify([root.resource.id, root.reason]);
|
|
323
|
+
const ranges = rangesByRootFamily.get(family) ?? new Set<string>();
|
|
324
|
+
ranges.add(root.key);
|
|
325
|
+
rangesByRootFamily.set(family, ranges);
|
|
326
|
+
}
|
|
327
|
+
for (const root of snapshot.roots) {
|
|
328
|
+
if (!root.resource) {
|
|
329
|
+
if (root.nodeId) {
|
|
330
|
+
const representative = representativeByNodeId.get(root.nodeId)!;
|
|
331
|
+
setSelectionElements(interaction, { kind: 'root', key: root.key }, [representative]);
|
|
332
|
+
}
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const occurrence = rootOccurrences.get(root.key) ?? 0;
|
|
336
|
+
rootOccurrences.set(root.key, occurrence + 1);
|
|
337
|
+
const id = 'root:' + root.key + (occurrence ? ':' + occurrence : '');
|
|
338
|
+
const resource = snapshot.resourceById.get(root.resource.id)!;
|
|
339
|
+
const rangeLabel = root.range?.kind === 'buffer' ? `bytes ${root.range.offset}–${root.range.offset + root.range.size}`
|
|
340
|
+
: root.range ? root.range.regions.map((r) => `mip ${r.baseMipLevel}+${r.mipLevelCount} · ${r.baseDepthSlice !== undefined ? 'D' + r.baseDepthSlice + '+' + r.depthSliceCount : 'L' + r.baseArrayLayer + '+' + r.arrayLayerCount} · ${r.aspect}`).join('; ') : 'Range unavailable';
|
|
341
|
+
const rangeSummary = root.range?.kind === 'texture'
|
|
342
|
+
? root.range.regions.map((r) => `m${r.baseMipLevel}+${r.mipLevelCount} ${r.baseDepthSlice !== undefined ? 'D' + r.baseDepthSlice + '+' + r.depthSliceCount : 'L' + r.baseArrayLayer + '+' + r.arrayLayerCount} ${r.aspect === 'depth-only' ? 'depth' : r.aspect === 'stencil-only' ? 'stencil' : 'all'}`).join('; ')
|
|
343
|
+
: rangeLabel;
|
|
344
|
+
const reasonLabel = root.reason.split('-').map((word) => word[0]!.toUpperCase() + word.slice(1)).join(' ');
|
|
345
|
+
const disambiguate = rangesByRootFamily.get(JSON.stringify([resource.id, root.reason]))!.size > 1;
|
|
346
|
+
rootNodes.push({
|
|
347
|
+
id, kind: 'root', rootKey: root.key, resourceId: resource.id, resourceKind: resource.kind,
|
|
348
|
+
label: reasonLabel + '\n' + formatGraphResourceLabel(labelResource(resource))
|
|
349
|
+
+ (disambiguate ? '\n' + formatGraphResourceLabel(rangeSummary) : ''),
|
|
350
|
+
overviewLabel: reasonLabel + '\n' + formatGraphResourceLabel(shortGraphLabel(labelResource(resource))),
|
|
351
|
+
title: labelResource(resource) + '\n' + root.reason + '\n' + rangeLabel
|
|
352
|
+
+ (root.resolution ? '\nProducers: ' + (root.resolution.producerNodeIds.join(', ') || 'none') + '\nInitial contents: ' + root.resolution.usesInitialContents : '\nOutput sources unavailable in Legacy capture.'),
|
|
401
353
|
});
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
export function indexAccessesByResourceId(
|
|
409
|
-
accesses: readonly FrameGraphDebugAccessEdge[],
|
|
410
|
-
): ReadonlyMap<string, readonly FrameGraphDebugAccessEdge[]> {
|
|
411
|
-
const result = new Map<string, FrameGraphDebugAccessEdge[]>();
|
|
412
|
-
for (const access of accesses) {
|
|
413
|
-
const resourceAccesses = result.get(access.resource.id);
|
|
414
|
-
if (resourceAccesses) {
|
|
415
|
-
resourceAccesses.push(access);
|
|
416
|
-
} else {
|
|
417
|
-
result.set(access.resource.id, [access]);
|
|
354
|
+
registerElement(interaction, id, { kind: 'root', key: root.key },
|
|
355
|
+
emptyReferences({ resourceIds: [resource.id], nodeIds: root.resolution?.producerNodeIds ?? [] }));
|
|
356
|
+
for (const nodeId of root.resolution?.producerNodeIds ?? []) {
|
|
357
|
+
addRelation(representativeByNodeId.get(nodeId)!, id, resource.id, { role: 'output-producer', nodeIds: [nodeId], rootKey: root.key });
|
|
418
358
|
}
|
|
359
|
+
if (root.resolution?.usesInitialContents) addRelation(representativeByResourceId.get(resource.id)!, id, resource.id,
|
|
360
|
+
{ role: 'output-initial', nodeIds: [], rootKey: root.key });
|
|
419
361
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
dependenciesByKey.set(key, { from, to, resource: dependency.resource, dependencies: [dependency] });
|
|
443
|
-
}
|
|
362
|
+
const edges: GraphSceneEdge[] = [...aggregates.entries()].map(([key, entry]) => {
|
|
363
|
+
const underlyingDependencies = entry.relations.flatMap((relation) => relation.dependency ? [relation.dependency] : []);
|
|
364
|
+
const edge: GraphSceneEdge = {
|
|
365
|
+
id: 'flow:' + key, ...entry,
|
|
366
|
+
kind: entry.relations.every((relation) => relation.role === 'ordering') ? 'ordering' : 'flow',
|
|
367
|
+
title: labelResource(snapshot.resourceById.get(entry.resourceId)!) + '\n'
|
|
368
|
+
+ unique(entry.relations.map((relation) => relation.role)).join(' · '),
|
|
369
|
+
underlyingDependencies, underlyingDependencyCount: underlyingDependencies.length,
|
|
370
|
+
};
|
|
371
|
+
const selection: Selection = { kind: 'resource', id: entry.resourceId };
|
|
372
|
+
registerElement(interaction, edge.id, selection, emptyReferences({
|
|
373
|
+
nodeIds: unique(entry.relations.flatMap((relation) => relation.nodeIds)), resourceIds: [entry.resourceId], dependencies: underlyingDependencies,
|
|
374
|
+
}));
|
|
375
|
+
const resourceElements = interaction.resourceElementIdsByResourceId.get(entry.resourceId) ?? [];
|
|
376
|
+
resourceElements.push(edge.id);
|
|
377
|
+
interaction.resourceElementIdsByResourceId.set(entry.resourceId, resourceElements);
|
|
378
|
+
return edge;
|
|
379
|
+
});
|
|
380
|
+
for (const [resourceId, elements] of interaction.resourceElementIdsByResourceId) {
|
|
381
|
+
const selection: Selection = { kind: 'resource', id: resourceId };
|
|
382
|
+
setSelectionElements(interaction, selection, elements);
|
|
383
|
+
interaction.hoverElementIdsBySelection.set(selectionKey(selection), [...elements]);
|
|
444
384
|
}
|
|
445
|
-
return [...
|
|
446
|
-
entry.dependencies.find((dependency) => dependency.kind === 'value') ?? entry.dependencies[0]!,
|
|
447
|
-
entry.from,
|
|
448
|
-
entry.to,
|
|
449
|
-
entry.dependencies,
|
|
450
|
-
index,
|
|
451
|
-
));
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function createDependencySceneEdge(
|
|
455
|
-
dependency: FrameGraphDebugEdge,
|
|
456
|
-
from: GraphSceneElementId,
|
|
457
|
-
to: GraphSceneElementId,
|
|
458
|
-
underlyingDependencies: readonly FrameGraphDebugEdge[],
|
|
459
|
-
occurrence: number,
|
|
460
|
-
): DependencySceneEdge {
|
|
461
|
-
const dependencyKind = underlyingDependencies.some((edge) => edge.kind === 'value') ? 'value' : 'ordering';
|
|
462
|
-
return {
|
|
463
|
-
id: `dependency:${JSON.stringify([from, to, dependency.resource.id, occurrence])}`,
|
|
464
|
-
kind: 'dependency',
|
|
465
|
-
from,
|
|
466
|
-
to,
|
|
467
|
-
title: `${labelResource(dependency.resource)}${underlyingDependencies.length > 1
|
|
468
|
-
? `\n${underlyingDependencies.length} folded dependencies`
|
|
469
|
-
: ''}`,
|
|
470
|
-
resourceId: dependency.resource.id,
|
|
471
|
-
dependencyKind,
|
|
472
|
-
underlyingDependencies,
|
|
473
|
-
underlyingDependencyCount: underlyingDependencies.length,
|
|
474
|
-
};
|
|
385
|
+
return finalizeScene([...groupNodes, ...passNodes, ...resourceNodes, ...rootNodes], edges, interaction);
|
|
475
386
|
}
|
|
476
387
|
|
|
477
388
|
function finalizeScene(
|
|
478
|
-
mode: GraphViewMode,
|
|
479
389
|
nodes: readonly GraphSceneNode[],
|
|
480
390
|
edges: readonly GraphSceneEdge[],
|
|
481
391
|
interaction: MutableInteractionIndex,
|
|
482
392
|
): GraphScene {
|
|
483
393
|
const topologyKey = JSON.stringify({
|
|
484
|
-
mode,
|
|
485
394
|
nodes: nodes.map((node) => [node.id, node.kind, node.parentId, node.kind === 'group' ? node.collapsed : undefined]),
|
|
486
395
|
edges: edges.map((edge) => [edge.id, edge.kind, edge.from, edge.to]),
|
|
487
396
|
});
|
|
488
397
|
const contentKey = JSON.stringify({
|
|
489
|
-
mode,
|
|
490
398
|
nodes: nodes.map((node) => [
|
|
491
399
|
node.id,
|
|
492
400
|
node.kind,
|
|
@@ -494,8 +402,8 @@ function finalizeScene(
|
|
|
494
402
|
node.label,
|
|
495
403
|
node.overviewLabel,
|
|
496
404
|
node.title,
|
|
497
|
-
node.kind === 'pass'
|
|
498
|
-
node.kind === 'resource' ? node.resourceKind : undefined,
|
|
405
|
+
node.kind === 'pass' ? node.passKind : undefined,
|
|
406
|
+
(node.kind === 'resource' || node.kind === 'root') ? node.resourceKind : undefined,
|
|
499
407
|
node.kind === 'group' ? node.collapsed : undefined,
|
|
500
408
|
node.kind === 'group' ? node.depthBand : undefined,
|
|
501
409
|
node.kind === 'group' ? node.culledNodeCount > 0 : undefined,
|
|
@@ -505,15 +413,11 @@ function finalizeScene(
|
|
|
505
413
|
edge.kind,
|
|
506
414
|
edge.from,
|
|
507
415
|
edge.to,
|
|
508
|
-
edge.label,
|
|
509
416
|
edge.title,
|
|
510
|
-
edge.
|
|
511
|
-
edge.kind === 'access' ? edge.accessMode : undefined,
|
|
512
|
-
edge.kind === 'access' ? edge.dashed : undefined,
|
|
417
|
+
edge.relations,
|
|
513
418
|
]),
|
|
514
419
|
});
|
|
515
420
|
return {
|
|
516
|
-
mode,
|
|
517
421
|
nodes,
|
|
518
422
|
edges,
|
|
519
423
|
topologyKey,
|
|
@@ -526,7 +430,8 @@ function createMutableInteractionIndex(): MutableInteractionIndex {
|
|
|
526
430
|
return {
|
|
527
431
|
selectionByElementId: new Map(),
|
|
528
432
|
primaryElementIdsBySelection: new Map(),
|
|
529
|
-
|
|
433
|
+
hoverElementIdsBySelection: new Map(),
|
|
434
|
+
resourceElementIdsByResourceId: new Map(),
|
|
530
435
|
semanticReferencesByElementId: new Map(),
|
|
531
436
|
};
|
|
532
437
|
}
|
|
@@ -538,7 +443,8 @@ function registerElement(
|
|
|
538
443
|
references: GraphSemanticReferences,
|
|
539
444
|
): void {
|
|
540
445
|
interaction.selectionByElementId.set(elementId, selection);
|
|
541
|
-
setSelectionElements(interaction, selection, [elementId]
|
|
446
|
+
setSelectionElements(interaction, selection, [elementId]);
|
|
447
|
+
interaction.hoverElementIdsBySelection.set(selectionKey(selection), [elementId]);
|
|
542
448
|
interaction.semanticReferencesByElementId.set(elementId, references);
|
|
543
449
|
}
|
|
544
450
|
|
|
@@ -546,21 +452,8 @@ function setSelectionElements(
|
|
|
546
452
|
interaction: MutableInteractionIndex,
|
|
547
453
|
selection: Selection,
|
|
548
454
|
primary: readonly GraphSceneElementId[],
|
|
549
|
-
related: readonly GraphSceneElementId[],
|
|
550
455
|
): void {
|
|
551
456
|
interaction.primaryElementIdsBySelection.set(selectionKey(selection), [...primary]);
|
|
552
|
-
interaction.relatedElementIdsBySelection.set(selectionKey(selection), [...related]);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function appendSelectionElement(
|
|
556
|
-
map: Map<string, GraphSceneElementId[]>,
|
|
557
|
-
selection: Selection,
|
|
558
|
-
elementId: GraphSceneElementId,
|
|
559
|
-
): void {
|
|
560
|
-
const key = selectionKey(selection);
|
|
561
|
-
const elementIds = map.get(key) ?? [];
|
|
562
|
-
if (!elementIds.includes(elementId)) elementIds.push(elementId);
|
|
563
|
-
map.set(key, elementIds);
|
|
564
457
|
}
|
|
565
458
|
|
|
566
459
|
function emptyReferences(overrides: Partial<GraphSemanticReferences>): GraphSemanticReferences {
|
|
@@ -578,24 +471,28 @@ function passElementId(nodeId: string): GraphSceneElementId {
|
|
|
578
471
|
return `pass:${nodeId}`;
|
|
579
472
|
}
|
|
580
473
|
|
|
581
|
-
function culledPassElementId(index: number, nodeId: string): GraphSceneElementId {
|
|
582
|
-
return `culled-pass:${index}:${nodeId}`;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
474
|
function resourceElementId(resourceId: string): GraphSceneElementId {
|
|
586
475
|
return `resource:${resourceId}`;
|
|
587
476
|
}
|
|
588
477
|
|
|
589
|
-
function accessElementId(accessId: string, resourceId: string): GraphSceneElementId {
|
|
590
|
-
return `access:${accessId}:${resourceId}`;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
478
|
function formatGraphNodeLabel(label: string): string {
|
|
594
479
|
return formatGraphLabel(label, 20, 3);
|
|
595
480
|
}
|
|
596
481
|
|
|
597
482
|
function formatGraphResourceLabel(label: string): string {
|
|
598
|
-
|
|
483
|
+
const singleLine = label.replace(/\s+/g, ' ').trim();
|
|
484
|
+
// Reserve conservative monospace cells for CJK/emoji fallback glyphs as well.
|
|
485
|
+
const glyphs = [...singleLine];
|
|
486
|
+
const cells = (glyph: string) => /\p{Mark}/u.test(glyph) ? 0 : glyph.codePointAt(0)! > 255 ? 2 : 1;
|
|
487
|
+
if (glyphs.reduce((sum, glyph) => sum + cells(glyph), 0) <= 18) return singleLine;
|
|
488
|
+
let result = '';
|
|
489
|
+
let width = 0;
|
|
490
|
+
for (const glyph of glyphs) {
|
|
491
|
+
if (width + cells(glyph) > 17) break;
|
|
492
|
+
result += glyph;
|
|
493
|
+
width += cells(glyph);
|
|
494
|
+
}
|
|
495
|
+
return result + '…';
|
|
599
496
|
}
|
|
600
497
|
|
|
601
498
|
function formatGraphLabel(label: string, maxLineLength: number, maxLines: number): string {
|
|
@@ -715,16 +612,6 @@ function createGroupTitle(group: FrameGraphDebugGroup): string {
|
|
|
715
612
|
].join('\n');
|
|
716
613
|
}
|
|
717
614
|
|
|
718
|
-
function createCulledNodeTitle(culled: FrameGraphDebugViewModel['culledNodes'][number]): string {
|
|
719
|
-
return [
|
|
720
|
-
`${labelNode(culled.node)} (culled)`,
|
|
721
|
-
`kind: ${culled.node.kind}`,
|
|
722
|
-
`reason: ${culled.reason}`,
|
|
723
|
-
`reads: ${culled.node.reads.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
724
|
-
`writes: ${culled.node.writes.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
725
|
-
].join('\n');
|
|
726
|
-
}
|
|
727
|
-
|
|
728
615
|
function createResourceTitle(
|
|
729
616
|
resource: FrameGraphDebugResource,
|
|
730
617
|
snapshot: FrameGraphDebugViewModel,
|
|
@@ -742,19 +629,6 @@ function createResourceTitle(
|
|
|
742
629
|
].join('\n');
|
|
743
630
|
}
|
|
744
631
|
|
|
745
|
-
function createAccessTitle(access: FrameGraphDebugAccessEdge): string {
|
|
746
|
-
return [
|
|
747
|
-
`#${access.accessId} ${access.mode}`,
|
|
748
|
-
labelResource(access.resource),
|
|
749
|
-
`access: ${access.access}`,
|
|
750
|
-
access.mode === 'write' ? `contents: ${access.contents}` : undefined,
|
|
751
|
-
access.mode === 'write' ? `result: ${access.producesValue ? 'produced' : 'discarded'}` : undefined,
|
|
752
|
-
access.textureViewId === undefined ? undefined : `texture view: #${access.textureViewId}`,
|
|
753
|
-
access.textureRegion ? `texture: mip ${access.textureRegion.baseMipLevel}+${access.textureRegion.mipLevelCount}, layer ${access.textureRegion.baseArrayLayer ?? 0}+${access.textureRegion.arrayLayerCount ?? 1}, depth ${access.textureRegion.baseDepthSlice ?? 0}+${access.textureRegion.depthSliceCount ?? 1}, ${access.textureRegion.aspect}` : undefined,
|
|
754
|
-
access.bufferRange ? `buffer: ${access.bufferRange.offset}+${access.bufferRange.size ?? 'end'}` : undefined,
|
|
755
|
-
].filter(Boolean).join('\n');
|
|
756
|
-
}
|
|
757
|
-
|
|
758
632
|
function debugGroupPathForId(groupId: string | undefined, snapshot: FrameGraphDebugViewModel): string {
|
|
759
633
|
if (groupId === undefined) return '-';
|
|
760
634
|
return snapshot.debugGroups.find((group) => group.id === groupId)?.path.join(' / ') ?? `#${groupId}`;
|