@zenfg/inspector 0.1.0-beta.1
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/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/FrameGraphInspector.d.ts +153 -0
- package/dist/FrameGraphInspector.d.ts.map +1 -0
- package/dist/FrameGraphInspector.js +580 -0
- package/dist/FrameGraphInspector.js.map +1 -0
- package/dist/debugCaptureModel.d.ts +166 -0
- package/dist/debugCaptureModel.d.ts.map +1 -0
- package/dist/debugCaptureModel.js +364 -0
- package/dist/debugCaptureModel.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/panelAliasAnalysis.d.ts +20 -0
- package/dist/panelAliasAnalysis.d.ts.map +1 -0
- package/dist/panelAliasAnalysis.js +113 -0
- package/dist/panelAliasAnalysis.js.map +1 -0
- package/dist/panelCytoscapeGraphRenderer.d.ts +71 -0
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -0
- package/dist/panelCytoscapeGraphRenderer.js +559 -0
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -0
- package/dist/panelDiagnosticsView.d.ts +22 -0
- package/dist/panelDiagnosticsView.d.ts.map +1 -0
- package/dist/panelDiagnosticsView.js +123 -0
- package/dist/panelDiagnosticsView.js.map +1 -0
- package/dist/panelDomHelpers.d.ts +26 -0
- package/dist/panelDomHelpers.d.ts.map +1 -0
- package/dist/panelDomHelpers.js +129 -0
- package/dist/panelDomHelpers.js.map +1 -0
- package/dist/panelGraphLayout.d.ts +28 -0
- package/dist/panelGraphLayout.d.ts.map +1 -0
- package/dist/panelGraphLayout.js +223 -0
- package/dist/panelGraphLayout.js.map +1 -0
- package/dist/panelGraphRenderer.d.ts +20 -0
- package/dist/panelGraphRenderer.d.ts.map +1 -0
- package/dist/panelGraphRenderer.js +2 -0
- package/dist/panelGraphRenderer.js.map +1 -0
- package/dist/panelGraphScene.d.ts +98 -0
- package/dist/panelGraphScene.d.ts.map +1 -0
- package/dist/panelGraphScene.js +559 -0
- package/dist/panelGraphScene.js.map +1 -0
- package/dist/panelGraphView.d.ts +9 -0
- package/dist/panelGraphView.d.ts.map +1 -0
- package/dist/panelGraphView.js +88 -0
- package/dist/panelGraphView.js.map +1 -0
- package/dist/panelGraphVisuals.d.ts +38 -0
- package/dist/panelGraphVisuals.d.ts.map +1 -0
- package/dist/panelGraphVisuals.js +330 -0
- package/dist/panelGraphVisuals.js.map +1 -0
- package/dist/panelIcons.d.ts +4 -0
- package/dist/panelIcons.d.ts.map +1 -0
- package/dist/panelIcons.js +43 -0
- package/dist/panelIcons.js.map +1 -0
- package/dist/panelInspectorView.d.ts +33 -0
- package/dist/panelInspectorView.d.ts.map +1 -0
- package/dist/panelInspectorView.js +357 -0
- package/dist/panelInspectorView.js.map +1 -0
- package/dist/panelMemoryView.d.ts +21 -0
- package/dist/panelMemoryView.d.ts.map +1 -0
- package/dist/panelMemoryView.js +134 -0
- package/dist/panelMemoryView.js.map +1 -0
- package/dist/panelPassesView.d.ts +31 -0
- package/dist/panelPassesView.d.ts.map +1 -0
- package/dist/panelPassesView.js +183 -0
- package/dist/panelPassesView.js.map +1 -0
- package/dist/panelResourcesView.d.ts +19 -0
- package/dist/panelResourcesView.d.ts.map +1 -0
- package/dist/panelResourcesView.js +89 -0
- package/dist/panelResourcesView.js.map +1 -0
- package/dist/panelSelection.d.ts +5 -0
- package/dist/panelSelection.d.ts.map +1 -0
- package/dist/panelSelection.js +88 -0
- package/dist/panelSelection.js.map +1 -0
- package/dist/panelTypes.d.ts +40 -0
- package/dist/panelTypes.d.ts.map +1 -0
- package/dist/panelTypes.js +2 -0
- package/dist/panelTypes.js.map +1 -0
- package/dist/panelVisualTheme.d.ts +76 -0
- package/dist/panelVisualTheme.d.ts.map +1 -0
- package/dist/panelVisualTheme.js +39 -0
- package/dist/panelVisualTheme.js.map +1 -0
- package/dist/panelWorkbenchHelpers.d.ts +34 -0
- package/dist/panelWorkbenchHelpers.d.ts.map +1 -0
- package/dist/panelWorkbenchHelpers.js +162 -0
- package/dist/panelWorkbenchHelpers.js.map +1 -0
- package/dist/panelWorkbenchView.d.ts +77 -0
- package/dist/panelWorkbenchView.d.ts.map +1 -0
- package/dist/panelWorkbenchView.js +409 -0
- package/dist/panelWorkbenchView.js.map +1 -0
- package/dist/query.d.ts +11 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +21 -0
- package/dist/query.js.map +1 -0
- package/dist/styles.d.ts +8 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +1073 -0
- package/dist/styles.js.map +1 -0
- package/package.json +29 -0
- package/src/FrameGraphInspector.ts +632 -0
- package/src/debugCaptureModel.ts +583 -0
- package/src/index.ts +21 -0
- package/src/panelAliasAnalysis.ts +164 -0
- package/src/panelCytoscapeGraphRenderer.ts +617 -0
- package/src/panelDiagnosticsView.ts +160 -0
- package/src/panelDomHelpers.ts +151 -0
- package/src/panelGraphLayout.ts +281 -0
- package/src/panelGraphRenderer.ts +21 -0
- package/src/panelGraphScene.ts +787 -0
- package/src/panelGraphView.ts +108 -0
- package/src/panelGraphVisuals.ts +364 -0
- package/src/panelIcons.ts +56 -0
- package/src/panelInspectorView.ts +377 -0
- package/src/panelMemoryView.ts +154 -0
- package/src/panelPassesView.ts +222 -0
- package/src/panelResourcesView.ts +115 -0
- package/src/panelSelection.ts +99 -0
- package/src/panelTypes.ts +28 -0
- package/src/panelVisualTheme.ts +39 -0
- package/src/panelWorkbenchHelpers.ts +216 -0
- package/src/panelWorkbenchView.ts +465 -0
- package/src/query.ts +22 -0
- package/src/styles.ts +1076 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { formatGpuDuration, labelNode, labelResource } from "./panelDomHelpers.js";
|
|
2
|
+
export function createGraphScene(snapshot, options) {
|
|
3
|
+
return options.mode === 'resources'
|
|
4
|
+
? createResourceGraphScene(snapshot)
|
|
5
|
+
: createPassGraphScene(snapshot, options.groupsEnabled, options.expandedGroupPaths);
|
|
6
|
+
}
|
|
7
|
+
export function graphGroupElementId(pathKey) {
|
|
8
|
+
return `group:${pathKey}`;
|
|
9
|
+
}
|
|
10
|
+
export function selectionKey(selection) {
|
|
11
|
+
switch (selection.kind) {
|
|
12
|
+
case 'node':
|
|
13
|
+
return `node:${selection.id}`;
|
|
14
|
+
case 'group':
|
|
15
|
+
return `group:${selection.pathKey}`;
|
|
16
|
+
case 'resource':
|
|
17
|
+
return `resource:${selection.id}`;
|
|
18
|
+
case 'allocation':
|
|
19
|
+
return `allocation:${selection.id}`;
|
|
20
|
+
case 'root':
|
|
21
|
+
return `root:${selection.index}`;
|
|
22
|
+
case 'culled':
|
|
23
|
+
return `culled:${selection.index}`;
|
|
24
|
+
case 'segment':
|
|
25
|
+
return `segment:${selection.index}`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
29
|
+
const interaction = createMutableInteractionIndex();
|
|
30
|
+
const segmentByNodeId = executionSegmentByNodeId(snapshot);
|
|
31
|
+
const groupsById = new Map(snapshot.debugGroups.map((group) => [group.id, group]));
|
|
32
|
+
const useGroups = groupsEnabled && snapshot.debugGroups.length > 0;
|
|
33
|
+
const isExpanded = (group) => expandedGroupPaths.has(group.pathKey);
|
|
34
|
+
const hasVisibleAncestors = (group) => group.ancestorIds
|
|
35
|
+
.slice(0, -1)
|
|
36
|
+
.every((id) => isExpanded(groupsById.get(id)));
|
|
37
|
+
const includedGroups = useGroups
|
|
38
|
+
? snapshot.debugGroups.filter((group) => group.summary.retainedNodeCount > 0 && hasVisibleAncestors(group))
|
|
39
|
+
: [];
|
|
40
|
+
const includedGroupIds = new Set(includedGroups.map((group) => group.id));
|
|
41
|
+
const representedNodeIdsByGroupId = new Map();
|
|
42
|
+
for (const node of snapshot.nodes) {
|
|
43
|
+
const group = node.debugGroupId === undefined ? undefined : groupsById.get(node.debugGroupId);
|
|
44
|
+
for (const groupId of group?.ancestorIds ?? []) {
|
|
45
|
+
const represented = representedNodeIdsByGroupId.get(groupId) ?? [];
|
|
46
|
+
represented.push(node.id);
|
|
47
|
+
representedNodeIdsByGroupId.set(groupId, represented);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const visiblePasses = snapshot.nodes.filter((node) => {
|
|
51
|
+
if (!useGroups || node.debugGroupId === undefined)
|
|
52
|
+
return true;
|
|
53
|
+
return groupsById.get(node.debugGroupId).ancestorIds.every((id) => isExpanded(groupsById.get(id)));
|
|
54
|
+
});
|
|
55
|
+
const visiblePassIds = new Set(visiblePasses.map((node) => node.id));
|
|
56
|
+
const includedGroupsByParentId = indexByOptionalId(includedGroups, (group) => group.parentId);
|
|
57
|
+
const visiblePassesByGroupId = indexByOptionalId(visiblePasses, (node) => node.debugGroupId);
|
|
58
|
+
const groupNodes = includedGroups.map((group) => {
|
|
59
|
+
const id = graphGroupElementId(group.pathKey);
|
|
60
|
+
const collapsed = !isExpanded(group);
|
|
61
|
+
const representedNodeIds = representedNodeIdsByGroupId.get(group.id) ?? [];
|
|
62
|
+
const childNodeIds = collapsed ? [] : [
|
|
63
|
+
...(includedGroupsByParentId.get(group.id) ?? [])
|
|
64
|
+
.map((child) => graphGroupElementId(child.pathKey)),
|
|
65
|
+
...(visiblePassesByGroupId.get(group.id) ?? [])
|
|
66
|
+
.map((node) => passElementId(node.id)),
|
|
67
|
+
];
|
|
68
|
+
const node = {
|
|
69
|
+
id,
|
|
70
|
+
kind: 'group',
|
|
71
|
+
groupId: group.id,
|
|
72
|
+
groupPathKey: group.pathKey,
|
|
73
|
+
label: createGroupLabel(group, collapsed),
|
|
74
|
+
overviewLabel: `${collapsed ? '▸' : '▾'} ${group.label}`,
|
|
75
|
+
title: createGroupTitle(group),
|
|
76
|
+
parentId: group.parentId !== undefined && includedGroupIds.has(group.parentId)
|
|
77
|
+
? graphGroupElementId(groupsById.get(group.parentId).pathKey)
|
|
78
|
+
: undefined,
|
|
79
|
+
collapsed,
|
|
80
|
+
depthBand: group.ancestorIds.length % 2,
|
|
81
|
+
childNodeIds,
|
|
82
|
+
representedNodeIds,
|
|
83
|
+
retainedNodeCount: group.summary.retainedNodeCount,
|
|
84
|
+
culledNodeCount: group.summary.culledNodeCount,
|
|
85
|
+
gpuWorkDurationMicros: group.summary.gpuWorkDurationMicros,
|
|
86
|
+
timedNodeCount: group.summary.timedNodeCount,
|
|
87
|
+
timingEligibleNodeCount: group.summary.timingEligibleNodeCount,
|
|
88
|
+
};
|
|
89
|
+
const selection = { kind: 'group', pathKey: group.pathKey };
|
|
90
|
+
registerElement(interaction, node.id, selection, {
|
|
91
|
+
nodeIds: representedNodeIds,
|
|
92
|
+
groupPathKeys: [group.pathKey],
|
|
93
|
+
resourceIds: [
|
|
94
|
+
...group.summary.inputResources.map((resource) => resource.id),
|
|
95
|
+
...group.summary.outputResources.map((resource) => resource.id),
|
|
96
|
+
],
|
|
97
|
+
accessIds: [],
|
|
98
|
+
dependencies: [],
|
|
99
|
+
});
|
|
100
|
+
return node;
|
|
101
|
+
});
|
|
102
|
+
const passNodes = visiblePasses.map((node) => {
|
|
103
|
+
const segment = segmentByNodeId.get(node.id);
|
|
104
|
+
const sceneNode = {
|
|
105
|
+
id: passElementId(node.id),
|
|
106
|
+
kind: 'pass',
|
|
107
|
+
nodeId: node.id,
|
|
108
|
+
passKind: node.kind,
|
|
109
|
+
executionSegmentIndex: segment?.index,
|
|
110
|
+
gpuDurationMicros: node.gpuDurationMicros,
|
|
111
|
+
label: formatGraphNodeLabel(`${segmentLabel(segment)} ${labelNode(node)}`),
|
|
112
|
+
overviewLabel: shortGraphLabel(labelNode(node)),
|
|
113
|
+
title: createNodeTitle(node, segment, snapshot),
|
|
114
|
+
parentId: useGroups && node.debugGroupId !== undefined
|
|
115
|
+
? graphGroupElementId(groupsById.get(node.debugGroupId).pathKey)
|
|
116
|
+
: undefined,
|
|
117
|
+
};
|
|
118
|
+
const selection = { kind: 'node', id: node.id };
|
|
119
|
+
registerElement(interaction, sceneNode.id, selection, emptyReferences({ nodeIds: [node.id] }));
|
|
120
|
+
return sceneNode;
|
|
121
|
+
});
|
|
122
|
+
const representativeByNodeId = new Map();
|
|
123
|
+
for (const node of snapshot.nodes) {
|
|
124
|
+
if (visiblePassIds.has(node.id)) {
|
|
125
|
+
representativeByNodeId.set(node.id, passElementId(node.id));
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const group = groupsById.get(node.debugGroupId);
|
|
129
|
+
const collapsedAncestorId = group.ancestorIds.find((id) => !isExpanded(groupsById.get(id)));
|
|
130
|
+
representativeByNodeId.set(node.id, graphGroupElementId(groupsById.get(collapsedAncestorId).pathKey));
|
|
131
|
+
}
|
|
132
|
+
for (const node of snapshot.nodes) {
|
|
133
|
+
const representative = representativeByNodeId.get(node.id);
|
|
134
|
+
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative], [representative]);
|
|
135
|
+
}
|
|
136
|
+
if (useGroups) {
|
|
137
|
+
for (const group of snapshot.debugGroups) {
|
|
138
|
+
if (includedGroupIds.has(group.id))
|
|
139
|
+
continue;
|
|
140
|
+
const collapsedAncestorId = group.ancestorIds.find((id) => !isExpanded(groupsById.get(id)));
|
|
141
|
+
if (collapsedAncestorId !== undefined) {
|
|
142
|
+
const representative = graphGroupElementId(groupsById.get(collapsedAncestorId).pathKey);
|
|
143
|
+
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative], [representative]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const dependencyEdges = useGroups
|
|
148
|
+
? createFoldedDependencyEdges(snapshot.edges, representativeByNodeId)
|
|
149
|
+
: snapshot.edges.map((edge, index) => createDependencySceneEdge(edge, passElementId(edge.fromNodeId), passElementId(edge.toNodeId), [edge], index));
|
|
150
|
+
for (const edge of dependencyEdges) {
|
|
151
|
+
const selection = { kind: 'resource', id: edge.resourceId };
|
|
152
|
+
interaction.selectionByElementId.set(edge.id, selection);
|
|
153
|
+
appendSelectionElement(interaction.primaryElementIdsBySelection, selection, edge.id);
|
|
154
|
+
appendSelectionElement(interaction.relatedElementIdsBySelection, selection, edge.id);
|
|
155
|
+
interaction.semanticReferencesByElementId.set(edge.id, {
|
|
156
|
+
nodeIds: unique(edge.underlyingDependencies.flatMap((dependency) => [dependency.fromNodeId, dependency.toNodeId])),
|
|
157
|
+
groupPathKeys: [],
|
|
158
|
+
resourceIds: [edge.resourceId],
|
|
159
|
+
accessIds: [],
|
|
160
|
+
dependencies: edge.underlyingDependencies,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return finalizeScene('passes', [...groupNodes, ...passNodes], dependencyEdges, interaction);
|
|
164
|
+
}
|
|
165
|
+
function createResourceGraphScene(snapshot) {
|
|
166
|
+
const interaction = createMutableInteractionIndex();
|
|
167
|
+
const segmentByNodeId = executionSegmentByNodeId(snapshot);
|
|
168
|
+
const accessesByResourceId = indexAccessesByResourceId(snapshot.accessEdges);
|
|
169
|
+
const passNodes = snapshot.nodes.map((node) => {
|
|
170
|
+
const segment = segmentByNodeId.get(node.id);
|
|
171
|
+
const sceneNode = {
|
|
172
|
+
id: passElementId(node.id),
|
|
173
|
+
kind: 'pass',
|
|
174
|
+
nodeId: node.id,
|
|
175
|
+
passKind: node.kind,
|
|
176
|
+
executionSegmentIndex: segment?.index,
|
|
177
|
+
gpuDurationMicros: node.gpuDurationMicros,
|
|
178
|
+
label: formatGraphNodeLabel(`${segmentLabel(segment)} ${labelNode(node)}`),
|
|
179
|
+
overviewLabel: shortGraphLabel(labelNode(node)),
|
|
180
|
+
title: createNodeTitle(node, segment, snapshot),
|
|
181
|
+
};
|
|
182
|
+
registerElement(interaction, sceneNode.id, { kind: 'node', id: node.id }, emptyReferences({ nodeIds: [node.id] }));
|
|
183
|
+
return sceneNode;
|
|
184
|
+
});
|
|
185
|
+
const culledNodes = snapshot.culledNodes.map((culled, index) => {
|
|
186
|
+
const sceneNode = {
|
|
187
|
+
id: culledPassElementId(index, culled.node.id),
|
|
188
|
+
kind: 'culled-pass',
|
|
189
|
+
culledIndex: index,
|
|
190
|
+
nodeId: culled.node.id,
|
|
191
|
+
passKind: culled.node.kind,
|
|
192
|
+
reason: culled.reason,
|
|
193
|
+
label: `${formatGraphNodeLabel(labelNode(culled.node))}\n(culled)`,
|
|
194
|
+
overviewLabel: `${shortGraphLabel(labelNode(culled.node))} (culled)`,
|
|
195
|
+
title: createCulledNodeTitle(culled),
|
|
196
|
+
};
|
|
197
|
+
registerElement(interaction, sceneNode.id, { kind: 'culled', index }, emptyReferences({ nodeIds: [culled.node.id] }));
|
|
198
|
+
return sceneNode;
|
|
199
|
+
});
|
|
200
|
+
const resourceNodes = snapshot.resources.map((resource) => {
|
|
201
|
+
const sceneNode = {
|
|
202
|
+
id: resourceElementId(resource.id),
|
|
203
|
+
kind: 'resource',
|
|
204
|
+
resourceId: resource.id,
|
|
205
|
+
resourceKind: resource.kind,
|
|
206
|
+
label: formatGraphResourceLabel(labelResource(resource)),
|
|
207
|
+
overviewLabel: shortGraphLabel(labelResource(resource)),
|
|
208
|
+
title: createResourceTitle(resource, snapshot, accessesByResourceId.get(resource.id) ?? []),
|
|
209
|
+
};
|
|
210
|
+
registerElement(interaction, sceneNode.id, { kind: 'resource', id: resource.id }, emptyReferences({ resourceIds: [resource.id] }));
|
|
211
|
+
return sceneNode;
|
|
212
|
+
});
|
|
213
|
+
const culledNodeIds = new Map(snapshot.culledNodes.map((culled, index) => [
|
|
214
|
+
culled.node.id,
|
|
215
|
+
culledPassElementId(index, culled.node.id),
|
|
216
|
+
]));
|
|
217
|
+
const accessEdges = snapshot.accessEdges.map((access) => {
|
|
218
|
+
const passId = culledNodeIds.get(access.nodeId) ?? passElementId(access.nodeId);
|
|
219
|
+
const resourceId = resourceElementId(access.resource.id);
|
|
220
|
+
const edge = {
|
|
221
|
+
id: accessElementId(access.accessId, access.resource.id),
|
|
222
|
+
kind: 'access',
|
|
223
|
+
from: access.mode === 'write' ? passId : resourceId,
|
|
224
|
+
to: access.mode === 'write' ? resourceId : passId,
|
|
225
|
+
label: access.access,
|
|
226
|
+
title: createAccessTitle(access),
|
|
227
|
+
resourceId: access.resource.id,
|
|
228
|
+
accessId: access.accessId,
|
|
229
|
+
accessMode: access.mode,
|
|
230
|
+
access: access.access,
|
|
231
|
+
dashed: culledNodeIds.has(access.nodeId),
|
|
232
|
+
};
|
|
233
|
+
const selection = { kind: 'resource', id: access.resource.id };
|
|
234
|
+
interaction.selectionByElementId.set(edge.id, selection);
|
|
235
|
+
appendSelectionElement(interaction.relatedElementIdsBySelection, selection, edge.id);
|
|
236
|
+
interaction.semanticReferencesByElementId.set(edge.id, {
|
|
237
|
+
nodeIds: [access.nodeId],
|
|
238
|
+
groupPathKeys: [],
|
|
239
|
+
resourceIds: [access.resource.id],
|
|
240
|
+
accessIds: [access.accessId],
|
|
241
|
+
dependencies: [],
|
|
242
|
+
});
|
|
243
|
+
return edge;
|
|
244
|
+
});
|
|
245
|
+
return finalizeScene('resources', [...passNodes, ...culledNodes, ...resourceNodes], accessEdges, interaction);
|
|
246
|
+
}
|
|
247
|
+
export function indexAccessesByResourceId(accesses) {
|
|
248
|
+
const result = new Map();
|
|
249
|
+
for (const access of accesses) {
|
|
250
|
+
const resourceAccesses = result.get(access.resource.id);
|
|
251
|
+
if (resourceAccesses) {
|
|
252
|
+
resourceAccesses.push(access);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
result.set(access.resource.id, [access]);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
function createFoldedDependencyEdges(dependencies, representativeByNodeId) {
|
|
261
|
+
const dependenciesByKey = new Map();
|
|
262
|
+
for (const dependency of dependencies) {
|
|
263
|
+
const from = representativeByNodeId.get(dependency.fromNodeId);
|
|
264
|
+
const to = representativeByNodeId.get(dependency.toNodeId);
|
|
265
|
+
if (from === to)
|
|
266
|
+
continue;
|
|
267
|
+
const key = JSON.stringify([from, to, dependency.resource.id]);
|
|
268
|
+
const existing = dependenciesByKey.get(key);
|
|
269
|
+
if (existing) {
|
|
270
|
+
existing.dependencies.push(dependency);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
dependenciesByKey.set(key, { from, to, resource: dependency.resource, dependencies: [dependency] });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return [...dependenciesByKey.values()].map((entry, index) => createDependencySceneEdge(entry.dependencies.find((dependency) => dependency.kind === 'value') ?? entry.dependencies[0], entry.from, entry.to, entry.dependencies, index));
|
|
277
|
+
}
|
|
278
|
+
function createDependencySceneEdge(dependency, from, to, underlyingDependencies, occurrence) {
|
|
279
|
+
const dependencyKind = underlyingDependencies.some((edge) => edge.kind === 'value') ? 'value' : 'ordering';
|
|
280
|
+
return {
|
|
281
|
+
id: `dependency:${JSON.stringify([from, to, dependency.resource.id, occurrence])}`,
|
|
282
|
+
kind: 'dependency',
|
|
283
|
+
from,
|
|
284
|
+
to,
|
|
285
|
+
title: `${labelResource(dependency.resource)}${underlyingDependencies.length > 1
|
|
286
|
+
? `\n${underlyingDependencies.length} folded dependencies`
|
|
287
|
+
: ''}`,
|
|
288
|
+
resourceId: dependency.resource.id,
|
|
289
|
+
dependencyKind,
|
|
290
|
+
underlyingDependencies,
|
|
291
|
+
underlyingDependencyCount: underlyingDependencies.length,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function finalizeScene(mode, nodes, edges, interaction) {
|
|
295
|
+
const topologyKey = JSON.stringify({
|
|
296
|
+
mode,
|
|
297
|
+
nodes: nodes.map((node) => [node.id, node.kind, node.parentId, node.kind === 'group' ? node.collapsed : undefined]),
|
|
298
|
+
edges: edges.map((edge) => [edge.id, edge.kind, edge.from, edge.to]),
|
|
299
|
+
});
|
|
300
|
+
const contentKey = JSON.stringify({
|
|
301
|
+
mode,
|
|
302
|
+
nodes: nodes.map((node) => [
|
|
303
|
+
node.id,
|
|
304
|
+
node.kind,
|
|
305
|
+
node.parentId,
|
|
306
|
+
node.label,
|
|
307
|
+
node.overviewLabel,
|
|
308
|
+
node.title,
|
|
309
|
+
node.kind === 'pass' || node.kind === 'culled-pass' ? node.passKind : undefined,
|
|
310
|
+
node.kind === 'resource' ? node.resourceKind : undefined,
|
|
311
|
+
node.kind === 'group' ? node.collapsed : undefined,
|
|
312
|
+
node.kind === 'group' ? node.depthBand : undefined,
|
|
313
|
+
node.kind === 'group' ? node.culledNodeCount > 0 : undefined,
|
|
314
|
+
]),
|
|
315
|
+
edges: edges.map((edge) => [
|
|
316
|
+
edge.id,
|
|
317
|
+
edge.kind,
|
|
318
|
+
edge.from,
|
|
319
|
+
edge.to,
|
|
320
|
+
edge.label,
|
|
321
|
+
edge.title,
|
|
322
|
+
edge.kind === 'dependency' ? edge.dependencyKind : undefined,
|
|
323
|
+
edge.kind === 'access' ? edge.accessMode : undefined,
|
|
324
|
+
edge.kind === 'access' ? edge.dashed : undefined,
|
|
325
|
+
]),
|
|
326
|
+
});
|
|
327
|
+
return {
|
|
328
|
+
mode,
|
|
329
|
+
nodes,
|
|
330
|
+
edges,
|
|
331
|
+
topologyKey,
|
|
332
|
+
contentKey,
|
|
333
|
+
interaction,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function createMutableInteractionIndex() {
|
|
337
|
+
return {
|
|
338
|
+
selectionByElementId: new Map(),
|
|
339
|
+
primaryElementIdsBySelection: new Map(),
|
|
340
|
+
relatedElementIdsBySelection: new Map(),
|
|
341
|
+
semanticReferencesByElementId: new Map(),
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
function registerElement(interaction, elementId, selection, references) {
|
|
345
|
+
interaction.selectionByElementId.set(elementId, selection);
|
|
346
|
+
setSelectionElements(interaction, selection, [elementId], [elementId]);
|
|
347
|
+
interaction.semanticReferencesByElementId.set(elementId, references);
|
|
348
|
+
}
|
|
349
|
+
function setSelectionElements(interaction, selection, primary, related) {
|
|
350
|
+
interaction.primaryElementIdsBySelection.set(selectionKey(selection), [...primary]);
|
|
351
|
+
interaction.relatedElementIdsBySelection.set(selectionKey(selection), [...related]);
|
|
352
|
+
}
|
|
353
|
+
function appendSelectionElement(map, selection, elementId) {
|
|
354
|
+
const key = selectionKey(selection);
|
|
355
|
+
const elementIds = map.get(key) ?? [];
|
|
356
|
+
if (!elementIds.includes(elementId))
|
|
357
|
+
elementIds.push(elementId);
|
|
358
|
+
map.set(key, elementIds);
|
|
359
|
+
}
|
|
360
|
+
function emptyReferences(overrides) {
|
|
361
|
+
return {
|
|
362
|
+
nodeIds: [],
|
|
363
|
+
groupPathKeys: [],
|
|
364
|
+
resourceIds: [],
|
|
365
|
+
accessIds: [],
|
|
366
|
+
dependencies: [],
|
|
367
|
+
...overrides,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function passElementId(nodeId) {
|
|
371
|
+
return `pass:${nodeId}`;
|
|
372
|
+
}
|
|
373
|
+
function culledPassElementId(index, nodeId) {
|
|
374
|
+
return `culled-pass:${index}:${nodeId}`;
|
|
375
|
+
}
|
|
376
|
+
function resourceElementId(resourceId) {
|
|
377
|
+
return `resource:${resourceId}`;
|
|
378
|
+
}
|
|
379
|
+
function accessElementId(accessId, resourceId) {
|
|
380
|
+
return `access:${accessId}:${resourceId}`;
|
|
381
|
+
}
|
|
382
|
+
function formatGraphNodeLabel(label) {
|
|
383
|
+
return formatGraphLabel(label, 20, 3);
|
|
384
|
+
}
|
|
385
|
+
function formatGraphResourceLabel(label) {
|
|
386
|
+
return formatGraphLabel(label, 18, 3);
|
|
387
|
+
}
|
|
388
|
+
function formatGraphLabel(label, maxLineLength, maxLines) {
|
|
389
|
+
if (label.length <= maxLineLength)
|
|
390
|
+
return label;
|
|
391
|
+
const chunks = label.match(/[^.]+\.?/g) ?? [label];
|
|
392
|
+
const lines = [];
|
|
393
|
+
let current = '';
|
|
394
|
+
let truncated = false;
|
|
395
|
+
for (const chunk of chunks) {
|
|
396
|
+
const next = current ? `${current}${chunk}` : chunk;
|
|
397
|
+
if (current && next.length > maxLineLength) {
|
|
398
|
+
lines.push(current);
|
|
399
|
+
current = chunk;
|
|
400
|
+
if (lines.length === maxLines) {
|
|
401
|
+
truncated = true;
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
if (!current && chunk.length > maxLineLength) {
|
|
407
|
+
const wrapped = splitLongGraphLabelChunk(chunk, maxLineLength);
|
|
408
|
+
for (const line of wrapped) {
|
|
409
|
+
if (lines.length === maxLines) {
|
|
410
|
+
truncated = true;
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
lines.push(line);
|
|
414
|
+
}
|
|
415
|
+
current = '';
|
|
416
|
+
if (truncated)
|
|
417
|
+
break;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
current = next;
|
|
421
|
+
}
|
|
422
|
+
if (current && lines.length < maxLines)
|
|
423
|
+
lines.push(current);
|
|
424
|
+
else if (current)
|
|
425
|
+
truncated = true;
|
|
426
|
+
if (lines.length > maxLines) {
|
|
427
|
+
lines.length = maxLines;
|
|
428
|
+
truncated = true;
|
|
429
|
+
}
|
|
430
|
+
if (truncated && lines.length > 0) {
|
|
431
|
+
const line = lines[lines.length - 1];
|
|
432
|
+
lines[lines.length - 1] = line.length < maxLineLength
|
|
433
|
+
? `${line}…`
|
|
434
|
+
: `${line.slice(0, Math.max(1, maxLineLength - 1))}…`;
|
|
435
|
+
}
|
|
436
|
+
return lines.join('\n');
|
|
437
|
+
}
|
|
438
|
+
function splitLongGraphLabelChunk(label, maxLineLength) {
|
|
439
|
+
const chunks = label.match(/[^\s_\-/]+[\s_\-/]*/g) ?? [label];
|
|
440
|
+
const lines = [];
|
|
441
|
+
let current = '';
|
|
442
|
+
for (const chunk of chunks) {
|
|
443
|
+
const next = current ? `${current}${chunk}` : chunk;
|
|
444
|
+
if (current && next.length > maxLineLength) {
|
|
445
|
+
lines.push(current);
|
|
446
|
+
current = chunk;
|
|
447
|
+
}
|
|
448
|
+
else if (!current && chunk.length > maxLineLength) {
|
|
449
|
+
for (let index = 0; index < chunk.length; index += maxLineLength) {
|
|
450
|
+
lines.push(chunk.slice(index, index + maxLineLength));
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
current = next;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (current)
|
|
458
|
+
lines.push(current);
|
|
459
|
+
return lines;
|
|
460
|
+
}
|
|
461
|
+
function shortGraphLabel(label) {
|
|
462
|
+
const parts = label.split(/[./]/).filter(Boolean);
|
|
463
|
+
const last = parts[parts.length - 1] ?? label;
|
|
464
|
+
const short = parts.length > 1 && (/^\d+$/.test(last) || /^view#\d+$/.test(last))
|
|
465
|
+
? `${parts[parts.length - 2]}.${last}`
|
|
466
|
+
: last;
|
|
467
|
+
return short.length <= 18 ? short : `${short.slice(0, 17)}…`;
|
|
468
|
+
}
|
|
469
|
+
function createGroupLabel(group, collapsed) {
|
|
470
|
+
const summary = group.summary;
|
|
471
|
+
const gpu = summary.timingEligibleNodeCount === 0
|
|
472
|
+
? 'no timed passes'
|
|
473
|
+
: `Σ ${(summary.gpuWorkDurationMicros / 1000).toFixed(3)} ms ${summary.timedNodeCount}/${summary.timingEligibleNodeCount}`;
|
|
474
|
+
return `${collapsed ? '▸' : '▾'} ${group.label} · ${summary.retainedNodeCount} retained · ${summary.culledNodeCount} culled · ${gpu}`;
|
|
475
|
+
}
|
|
476
|
+
function createNodeTitle(node, segment, snapshot) {
|
|
477
|
+
return [
|
|
478
|
+
labelNode(node),
|
|
479
|
+
`kind: ${node.kind}`,
|
|
480
|
+
`group: ${debugGroupPathForId(node.debugGroupId, snapshot)}`,
|
|
481
|
+
`segment: ${segment ? `${segment.index}:${segment.kind}` : '-'}`,
|
|
482
|
+
`gpu: ${node.kind === 'external-submission' ? 'opaque' : `${formatGpuDuration(node)} ms`}`,
|
|
483
|
+
`reads: ${node.reads.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
484
|
+
`writes: ${node.writes.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
485
|
+
].join('\n');
|
|
486
|
+
}
|
|
487
|
+
function createGroupTitle(group) {
|
|
488
|
+
const summary = group.summary;
|
|
489
|
+
return [
|
|
490
|
+
group.path.join(' / '),
|
|
491
|
+
`retained: ${summary.retainedNodeCount}`,
|
|
492
|
+
`culled: ${summary.culledNodeCount}`,
|
|
493
|
+
`inputs: ${summary.inputResources.map(labelResource).join(', ') || '-'}`,
|
|
494
|
+
`outputs: ${summary.outputResources.map(labelResource).join(', ') || '-'}`,
|
|
495
|
+
`transient registered/accessed: ${summary.registeredTransientResourceCount}/${summary.accessedTransientResourceCount}`,
|
|
496
|
+
`physical allocations: ${summary.physicalAllocationCount}`,
|
|
497
|
+
`segments: ${summary.executionSegmentCount}`,
|
|
498
|
+
`opaque: ${summary.externalSubmissionCount}`,
|
|
499
|
+
`Σ GPU work: ${(summary.gpuWorkDurationMicros / 1000).toFixed(3)} ms (${summary.timedNodeCount}/${summary.timingEligibleNodeCount})`,
|
|
500
|
+
].join('\n');
|
|
501
|
+
}
|
|
502
|
+
function createCulledNodeTitle(culled) {
|
|
503
|
+
return [
|
|
504
|
+
`${labelNode(culled.node)} (culled)`,
|
|
505
|
+
`kind: ${culled.node.kind}`,
|
|
506
|
+
`reason: ${culled.reason}`,
|
|
507
|
+
`reads: ${culled.node.reads.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
508
|
+
`writes: ${culled.node.writes.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
509
|
+
].join('\n');
|
|
510
|
+
}
|
|
511
|
+
function createResourceTitle(resource, snapshot, accesses) {
|
|
512
|
+
return [
|
|
513
|
+
labelResource(resource),
|
|
514
|
+
`kind: ${resource.kind}`,
|
|
515
|
+
`origin: ${resource.origin}`,
|
|
516
|
+
`registered in: ${debugGroupPathForId(resource.debugGroupId, snapshot)}`,
|
|
517
|
+
`lifetime: ${resource.lifetime ? `${resource.lifetime.firstUse}-${resource.lifetime.lastUse}` : '-'}`,
|
|
518
|
+
`physical: ${resource.physicalResourceId ?? '-'}`,
|
|
519
|
+
`reads: ${accesses.filter((access) => access.mode === 'read').length}`,
|
|
520
|
+
`writes: ${accesses.filter((access) => access.mode === 'write').length}`,
|
|
521
|
+
].join('\n');
|
|
522
|
+
}
|
|
523
|
+
function createAccessTitle(access) {
|
|
524
|
+
return [
|
|
525
|
+
`#${access.accessId} ${access.mode}`,
|
|
526
|
+
labelResource(access.resource),
|
|
527
|
+
`access: ${access.access}`,
|
|
528
|
+
access.mode === 'write' ? `contents: ${access.contents}` : undefined,
|
|
529
|
+
access.mode === 'write' ? `result: ${access.producesValue ? 'produced' : 'discarded'}` : undefined,
|
|
530
|
+
access.textureViewId === undefined ? undefined : `texture view: #${access.textureViewId}`,
|
|
531
|
+
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,
|
|
532
|
+
access.bufferRange ? `buffer: ${access.bufferRange.offset}+${access.bufferRange.size ?? 'end'}` : undefined,
|
|
533
|
+
].filter(Boolean).join('\n');
|
|
534
|
+
}
|
|
535
|
+
function debugGroupPathForId(groupId, snapshot) {
|
|
536
|
+
if (groupId === undefined)
|
|
537
|
+
return '-';
|
|
538
|
+
return snapshot.debugGroups.find((group) => group.id === groupId)?.path.join(' / ') ?? `#${groupId}`;
|
|
539
|
+
}
|
|
540
|
+
function executionSegmentByNodeId(snapshot) {
|
|
541
|
+
return new Map(snapshot.executionSegments.flatMap((segment) => segment.nodeIds.map((nodeId) => [nodeId, segment])));
|
|
542
|
+
}
|
|
543
|
+
function segmentLabel(segment) {
|
|
544
|
+
return segment ? `[S${segment.index}]` : '[S-]';
|
|
545
|
+
}
|
|
546
|
+
function unique(values) {
|
|
547
|
+
return [...new Set(values)];
|
|
548
|
+
}
|
|
549
|
+
function indexByOptionalId(values, getId) {
|
|
550
|
+
const result = new Map();
|
|
551
|
+
for (const value of values) {
|
|
552
|
+
const id = getId(value);
|
|
553
|
+
const indexed = result.get(id) ?? [];
|
|
554
|
+
indexed.push(value);
|
|
555
|
+
result.set(id, indexed);
|
|
556
|
+
}
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
//# sourceMappingURL=panelGraphScene.js.map
|