@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/dist/panelGraphScene.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { formatGpuDuration, labelNode, labelResource } from "./panelDomHelpers.js";
|
|
2
|
+
import { declarationEntrances } from "./debugCaptureModel.js";
|
|
3
|
+
import { formatMeasuredGpuWork } from "./panelWorkbenchHelpers.js";
|
|
2
4
|
export function createGraphScene(snapshot, options) {
|
|
3
|
-
return options.
|
|
4
|
-
? createResourceGraphScene(snapshot)
|
|
5
|
-
: createPassGraphScene(snapshot, options.groupsEnabled, options.expandedGroupPaths);
|
|
5
|
+
return createFrameFlowScene(snapshot, options.groupsEnabled, options.expandedGroupPaths, options.showResourceDeclarations ?? true);
|
|
6
6
|
}
|
|
7
7
|
export function graphGroupElementId(pathKey) {
|
|
8
8
|
return `group:${pathKey}`;
|
|
@@ -18,14 +18,14 @@ export function selectionKey(selection) {
|
|
|
18
18
|
case 'allocation':
|
|
19
19
|
return `allocation:${selection.id}`;
|
|
20
20
|
case 'root':
|
|
21
|
-
return `root:${selection.
|
|
21
|
+
return `root:${selection.key}`;
|
|
22
22
|
case 'culled':
|
|
23
|
-
return `culled:${selection.
|
|
23
|
+
return `culled:${selection.id}`;
|
|
24
24
|
case 'segment':
|
|
25
25
|
return `segment:${selection.index}`;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
function
|
|
28
|
+
function createFrameFlowScene(snapshot, groupsEnabled, expandedGroupPaths, showResourceDeclarations) {
|
|
29
29
|
const interaction = createMutableInteractionIndex();
|
|
30
30
|
const segmentByNodeId = executionSegmentByNodeId(snapshot);
|
|
31
31
|
const groupsById = new Map(snapshot.debugGroups.map((group) => [group.id, group]));
|
|
@@ -34,8 +34,21 @@ function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
|
34
34
|
const hasVisibleAncestors = (group) => group.ancestorIds
|
|
35
35
|
.slice(0, -1)
|
|
36
36
|
.every((id) => isExpanded(groupsById.get(id)));
|
|
37
|
+
const retainedIds = new Set(snapshot.nodes.map((node) => node.id));
|
|
38
|
+
const usedResourceIds = new Set([
|
|
39
|
+
...snapshot.accessEdges.filter((access) => retainedIds.has(access.nodeId)).map((access) => access.resource.id),
|
|
40
|
+
...snapshot.roots.flatMap((root) => root.resource ? [root.resource.id] : []),
|
|
41
|
+
]);
|
|
42
|
+
const resources = showResourceDeclarations ? snapshot.resources.filter((resource) => usedResourceIds.has(resource.id)) : [];
|
|
43
|
+
const populatedGroupIds = new Set();
|
|
44
|
+
for (const item of [...snapshot.nodes, ...resources]) {
|
|
45
|
+
for (const id of groupsById.get(item.debugGroupId ?? '')?.ancestorIds ?? [])
|
|
46
|
+
populatedGroupIds.add(id);
|
|
47
|
+
}
|
|
48
|
+
const visibleResources = resources.filter((resource) => !useGroups || resource.debugGroupId === undefined
|
|
49
|
+
|| groupsById.get(resource.debugGroupId).ancestorIds.every((id) => isExpanded(groupsById.get(id))));
|
|
37
50
|
const includedGroups = useGroups
|
|
38
|
-
? snapshot.debugGroups.filter((group) => group.
|
|
51
|
+
? snapshot.debugGroups.filter((group) => populatedGroupIds.has(group.id) && hasVisibleAncestors(group))
|
|
39
52
|
: [];
|
|
40
53
|
const includedGroupIds = new Set(includedGroups.map((group) => group.id));
|
|
41
54
|
const representedNodeIdsByGroupId = new Map();
|
|
@@ -64,6 +77,7 @@ function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
|
64
77
|
.map((child) => graphGroupElementId(child.pathKey)),
|
|
65
78
|
...(visiblePassesByGroupId.get(group.id) ?? [])
|
|
66
79
|
.map((node) => passElementId(node.id)),
|
|
80
|
+
...visibleResources.filter((resource) => resource.debugGroupId === group.id).map((resource) => resourceElementId(resource.id)),
|
|
67
81
|
];
|
|
68
82
|
const node = {
|
|
69
83
|
id,
|
|
@@ -71,8 +85,8 @@ function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
|
71
85
|
groupId: group.id,
|
|
72
86
|
groupPathKey: group.pathKey,
|
|
73
87
|
label: createGroupLabel(group, collapsed),
|
|
74
|
-
overviewLabel: `${collapsed ? '▸' : '▾'} ${group.label}`,
|
|
75
|
-
title: createGroupTitle(group),
|
|
88
|
+
overviewLabel: `${collapsed ? '▸' : '▾'} ${group.label.replace(/\s+/g, ' ').trim()}`,
|
|
89
|
+
title: createGroupTitle(group, snapshot),
|
|
76
90
|
parentId: group.parentId !== undefined && includedGroupIds.has(group.parentId)
|
|
77
91
|
? graphGroupElementId(groupsById.get(group.parentId).pathKey)
|
|
78
92
|
: undefined,
|
|
@@ -131,174 +145,132 @@ function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
|
131
145
|
}
|
|
132
146
|
for (const node of snapshot.nodes) {
|
|
133
147
|
const representative = representativeByNodeId.get(node.id);
|
|
134
|
-
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative]
|
|
148
|
+
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative]);
|
|
135
149
|
}
|
|
136
150
|
if (useGroups) {
|
|
137
151
|
for (const group of snapshot.debugGroups) {
|
|
138
|
-
if (includedGroupIds.has(group.id))
|
|
152
|
+
if (includedGroupIds.has(group.id) || !populatedGroupIds.has(group.id))
|
|
139
153
|
continue;
|
|
140
154
|
const collapsedAncestorId = group.ancestorIds.find((id) => !isExpanded(groupsById.get(id)));
|
|
141
155
|
if (collapsedAncestorId !== undefined) {
|
|
142
156
|
const representative = graphGroupElementId(groupsById.get(collapsedAncestorId).pathKey);
|
|
143
|
-
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative]
|
|
157
|
+
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative]);
|
|
144
158
|
}
|
|
145
159
|
}
|
|
146
160
|
}
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
});
|
|
161
|
+
const representativeByResourceId = new Map();
|
|
162
|
+
for (const resource of resources) {
|
|
163
|
+
const collapsed = useGroups ? groupsById.get(resource.debugGroupId ?? '')?.ancestorIds.find((id) => !isExpanded(groupsById.get(id))) : undefined;
|
|
164
|
+
const representative = collapsed ? graphGroupElementId(groupsById.get(collapsed).pathKey) : resourceElementId(resource.id);
|
|
165
|
+
representativeByResourceId.set(resource.id, representative);
|
|
162
166
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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),
|
|
167
|
+
const resourceNodes = visibleResources.map((resource) => {
|
|
168
|
+
const origin = resource.origin === 'transient' ? 'Created' : resource.origin === 'imported' ? 'Imported' : 'Surface';
|
|
169
|
+
const node = {
|
|
170
|
+
id: resourceElementId(resource.id), kind: 'resource', resourceId: resource.id, resourceKind: resource.kind,
|
|
171
|
+
label: origin + ' · ' + (resource.kind === 'buffer' ? 'Buffer' : 'Texture') + '\n' + formatGraphResourceLabel(labelResource(resource)),
|
|
172
|
+
overviewLabel: origin + '\n' + formatGraphResourceLabel(shortGraphLabel(labelResource(resource)), 1),
|
|
173
|
+
title: createResourceTitle(resource, snapshot, snapshot.accessesByResourceId.get(resource.id) ?? [])
|
|
174
|
+
+ '\nDeclaration entrance, not a complete initial-content provenance graph.',
|
|
175
|
+
parentId: useGroups && resource.debugGroupId ? graphGroupElementId(groupsById.get(resource.debugGroupId).pathKey) : undefined,
|
|
232
176
|
};
|
|
233
|
-
|
|
234
|
-
interaction.
|
|
235
|
-
|
|
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;
|
|
177
|
+
registerElement(interaction, node.id, { kind: 'resource', id: resource.id }, emptyReferences({ resourceIds: [resource.id] }));
|
|
178
|
+
interaction.resourceElementIdsByResourceId.set(resource.id, [node.id]);
|
|
179
|
+
return node;
|
|
244
180
|
});
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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);
|
|
181
|
+
const rootNodes = [];
|
|
182
|
+
const aggregates = new Map();
|
|
183
|
+
const addRelation = (from, to, resourceId, relation) => {
|
|
265
184
|
if (from === to)
|
|
185
|
+
return;
|
|
186
|
+
const key = JSON.stringify([from, to, resourceId]);
|
|
187
|
+
const entry = aggregates.get(key) ?? { from, to, resourceId, relations: [] };
|
|
188
|
+
entry.relations.push(relation);
|
|
189
|
+
aggregates.set(key, entry);
|
|
190
|
+
};
|
|
191
|
+
for (const dependency of snapshot.edges) {
|
|
192
|
+
addRelation(representativeByNodeId.get(dependency.fromNodeId), representativeByNodeId.get(dependency.toNodeId), dependency.resource.id, { role: dependency.kind, nodeIds: [dependency.fromNodeId, dependency.toNodeId], dependency });
|
|
193
|
+
}
|
|
194
|
+
for (const access of showResourceDeclarations ? declarationEntrances(snapshot.nodes, snapshot.accessEdges, snapshot.edges) : []) {
|
|
195
|
+
addRelation(representativeByResourceId.get(access.resource.id), representativeByNodeId.get(access.nodeId), access.resource.id, { role: 'declaration', nodeIds: [access.nodeId] });
|
|
196
|
+
}
|
|
197
|
+
const rootOccurrences = new Map();
|
|
198
|
+
const rangesByRootFamily = new Map();
|
|
199
|
+
for (const root of snapshot.roots) {
|
|
200
|
+
if (!root.resource)
|
|
201
|
+
continue;
|
|
202
|
+
const family = JSON.stringify([root.resource.id, root.reason]);
|
|
203
|
+
const ranges = rangesByRootFamily.get(family) ?? new Set();
|
|
204
|
+
ranges.add(root.key);
|
|
205
|
+
rangesByRootFamily.set(family, ranges);
|
|
206
|
+
}
|
|
207
|
+
for (const root of snapshot.roots) {
|
|
208
|
+
if (!root.resource) {
|
|
209
|
+
if (root.nodeId) {
|
|
210
|
+
const representative = representativeByNodeId.get(root.nodeId);
|
|
211
|
+
setSelectionElements(interaction, { kind: 'root', key: root.key }, [representative]);
|
|
212
|
+
}
|
|
266
213
|
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
214
|
}
|
|
272
|
-
|
|
273
|
-
|
|
215
|
+
const occurrence = rootOccurrences.get(root.key) ?? 0;
|
|
216
|
+
rootOccurrences.set(root.key, occurrence + 1);
|
|
217
|
+
const id = 'root:' + root.key + (occurrence ? ':' + occurrence : '');
|
|
218
|
+
const resource = snapshot.resourceById.get(root.resource.id);
|
|
219
|
+
const rangeLabel = root.range?.kind === 'buffer' ? `bytes ${root.range.offset}–${root.range.offset + root.range.size}`
|
|
220
|
+
: 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';
|
|
221
|
+
const rangeSummary = root.range?.kind === 'texture'
|
|
222
|
+
? 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('; ')
|
|
223
|
+
: rangeLabel;
|
|
224
|
+
const reasonLabel = root.reason.split('-').map((word) => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
225
|
+
const disambiguate = rangesByRootFamily.get(JSON.stringify([resource.id, root.reason])).size > 1;
|
|
226
|
+
const initialLabel = !showResourceDeclarations && root.resolution?.usesInitialContents
|
|
227
|
+
? '\n' + (root.resolution.producerNodeIds.length ? 'With initial contents' : 'Initial contents only') : '';
|
|
228
|
+
rootNodes.push({
|
|
229
|
+
id, kind: 'root', rootKey: root.key, resourceId: resource.id, resourceKind: resource.kind,
|
|
230
|
+
label: reasonLabel + '\n' + formatGraphResourceLabel(labelResource(resource))
|
|
231
|
+
+ (disambiguate ? '\n' + formatGraphResourceLabel(rangeSummary, 1) : '') + initialLabel,
|
|
232
|
+
overviewLabel: reasonLabel + '\n' + formatGraphResourceLabel(shortGraphLabel(labelResource(resource)), 1) + initialLabel,
|
|
233
|
+
title: labelResource(resource) + '\n' + root.reason + '\n' + rangeLabel
|
|
234
|
+
+ (root.resolution ? '\nProducers: ' + (root.resolution.producerNodeIds.join(', ') || 'none') + '\nInitial contents: ' + root.resolution.usesInitialContents : '\nOutput sources unavailable in Legacy capture.'),
|
|
235
|
+
});
|
|
236
|
+
registerElement(interaction, id, { kind: 'root', key: root.key }, emptyReferences({ resourceIds: [resource.id], nodeIds: root.resolution?.producerNodeIds ?? [] }));
|
|
237
|
+
for (const nodeId of root.resolution?.producerNodeIds ?? []) {
|
|
238
|
+
addRelation(representativeByNodeId.get(nodeId), id, resource.id, { role: 'output-producer', nodeIds: [nodeId], rootKey: root.key });
|
|
274
239
|
}
|
|
240
|
+
if (showResourceDeclarations && root.resolution?.usesInitialContents)
|
|
241
|
+
addRelation(representativeByResourceId.get(resource.id), id, resource.id, { role: 'output-initial', nodeIds: [], rootKey: root.key });
|
|
275
242
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
243
|
+
const edges = [...aggregates.entries()].map(([key, entry]) => {
|
|
244
|
+
const underlyingDependencies = entry.relations.flatMap((relation) => relation.dependency ? [relation.dependency] : []);
|
|
245
|
+
const edge = {
|
|
246
|
+
id: 'flow:' + key, ...entry,
|
|
247
|
+
kind: entry.relations.every((relation) => relation.role === 'ordering') ? 'ordering' : 'flow',
|
|
248
|
+
title: labelResource(snapshot.resourceById.get(entry.resourceId)) + '\n'
|
|
249
|
+
+ unique(entry.relations.map((relation) => relation.role)).join(' · '),
|
|
250
|
+
underlyingDependencies, underlyingDependencyCount: underlyingDependencies.length,
|
|
251
|
+
};
|
|
252
|
+
const selection = { kind: 'resource', id: entry.resourceId };
|
|
253
|
+
registerElement(interaction, edge.id, selection, emptyReferences({
|
|
254
|
+
nodeIds: unique(entry.relations.flatMap((relation) => relation.nodeIds)), resourceIds: [entry.resourceId], dependencies: underlyingDependencies,
|
|
255
|
+
}));
|
|
256
|
+
const resourceElements = interaction.resourceElementIdsByResourceId.get(entry.resourceId) ?? [];
|
|
257
|
+
resourceElements.push(edge.id);
|
|
258
|
+
interaction.resourceElementIdsByResourceId.set(entry.resourceId, resourceElements);
|
|
259
|
+
return edge;
|
|
260
|
+
});
|
|
261
|
+
for (const [resourceId, elements] of interaction.resourceElementIdsByResourceId) {
|
|
262
|
+
const selection = { kind: 'resource', id: resourceId };
|
|
263
|
+
setSelectionElements(interaction, selection, elements);
|
|
264
|
+
interaction.hoverElementIdsBySelection.set(selectionKey(selection), [...elements]);
|
|
265
|
+
}
|
|
266
|
+
return finalizeScene([...groupNodes, ...passNodes, ...resourceNodes, ...rootNodes], edges, interaction);
|
|
293
267
|
}
|
|
294
|
-
function finalizeScene(
|
|
268
|
+
function finalizeScene(nodes, edges, interaction) {
|
|
295
269
|
const topologyKey = JSON.stringify({
|
|
296
|
-
mode,
|
|
297
270
|
nodes: nodes.map((node) => [node.id, node.kind, node.parentId, node.kind === 'group' ? node.collapsed : undefined]),
|
|
298
271
|
edges: edges.map((edge) => [edge.id, edge.kind, edge.from, edge.to]),
|
|
299
272
|
});
|
|
300
273
|
const contentKey = JSON.stringify({
|
|
301
|
-
mode,
|
|
302
274
|
nodes: nodes.map((node) => [
|
|
303
275
|
node.id,
|
|
304
276
|
node.kind,
|
|
@@ -306,8 +278,8 @@ function finalizeScene(mode, nodes, edges, interaction) {
|
|
|
306
278
|
node.label,
|
|
307
279
|
node.overviewLabel,
|
|
308
280
|
node.title,
|
|
309
|
-
node.kind === 'pass'
|
|
310
|
-
node.kind === 'resource' ? node.resourceKind : undefined,
|
|
281
|
+
node.kind === 'pass' ? node.passKind : undefined,
|
|
282
|
+
(node.kind === 'resource' || node.kind === 'root') ? node.resourceKind : undefined,
|
|
311
283
|
node.kind === 'group' ? node.collapsed : undefined,
|
|
312
284
|
node.kind === 'group' ? node.depthBand : undefined,
|
|
313
285
|
node.kind === 'group' ? node.culledNodeCount > 0 : undefined,
|
|
@@ -317,15 +289,11 @@ function finalizeScene(mode, nodes, edges, interaction) {
|
|
|
317
289
|
edge.kind,
|
|
318
290
|
edge.from,
|
|
319
291
|
edge.to,
|
|
320
|
-
edge.label,
|
|
321
292
|
edge.title,
|
|
322
|
-
edge.
|
|
323
|
-
edge.kind === 'access' ? edge.accessMode : undefined,
|
|
324
|
-
edge.kind === 'access' ? edge.dashed : undefined,
|
|
293
|
+
edge.relations,
|
|
325
294
|
]),
|
|
326
295
|
});
|
|
327
296
|
return {
|
|
328
|
-
mode,
|
|
329
297
|
nodes,
|
|
330
298
|
edges,
|
|
331
299
|
topologyKey,
|
|
@@ -337,25 +305,19 @@ function createMutableInteractionIndex() {
|
|
|
337
305
|
return {
|
|
338
306
|
selectionByElementId: new Map(),
|
|
339
307
|
primaryElementIdsBySelection: new Map(),
|
|
340
|
-
|
|
308
|
+
hoverElementIdsBySelection: new Map(),
|
|
309
|
+
resourceElementIdsByResourceId: new Map(),
|
|
341
310
|
semanticReferencesByElementId: new Map(),
|
|
342
311
|
};
|
|
343
312
|
}
|
|
344
313
|
function registerElement(interaction, elementId, selection, references) {
|
|
345
314
|
interaction.selectionByElementId.set(elementId, selection);
|
|
346
|
-
setSelectionElements(interaction, selection, [elementId]
|
|
315
|
+
setSelectionElements(interaction, selection, [elementId]);
|
|
316
|
+
interaction.hoverElementIdsBySelection.set(selectionKey(selection), [elementId]);
|
|
347
317
|
interaction.semanticReferencesByElementId.set(elementId, references);
|
|
348
318
|
}
|
|
349
|
-
function setSelectionElements(interaction, selection, primary
|
|
319
|
+
function setSelectionElements(interaction, selection, primary) {
|
|
350
320
|
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
321
|
}
|
|
360
322
|
function emptyReferences(overrides) {
|
|
361
323
|
return {
|
|
@@ -370,20 +332,56 @@ function emptyReferences(overrides) {
|
|
|
370
332
|
function passElementId(nodeId) {
|
|
371
333
|
return `pass:${nodeId}`;
|
|
372
334
|
}
|
|
373
|
-
function culledPassElementId(index, nodeId) {
|
|
374
|
-
return `culled-pass:${index}:${nodeId}`;
|
|
375
|
-
}
|
|
376
335
|
function resourceElementId(resourceId) {
|
|
377
336
|
return `resource:${resourceId}`;
|
|
378
337
|
}
|
|
379
|
-
function accessElementId(accessId, resourceId) {
|
|
380
|
-
return `access:${accessId}:${resourceId}`;
|
|
381
|
-
}
|
|
382
338
|
function formatGraphNodeLabel(label) {
|
|
383
339
|
return formatGraphLabel(label, 20, 3);
|
|
384
340
|
}
|
|
385
|
-
function formatGraphResourceLabel(label) {
|
|
386
|
-
|
|
341
|
+
function formatGraphResourceLabel(label, maxLines = 2) {
|
|
342
|
+
let remaining = [...label.replace(/\s+/g, ' ').trim()];
|
|
343
|
+
// Reserve conservative monospace cells for CJK/emoji fallback glyphs as well.
|
|
344
|
+
const cells = (glyph) => /\p{Mark}/u.test(glyph) ? 0 : glyph.codePointAt(0) > 255 ? 2 : 1;
|
|
345
|
+
const take = (limit) => {
|
|
346
|
+
let width = 0;
|
|
347
|
+
let count = 0;
|
|
348
|
+
for (const glyph of remaining) {
|
|
349
|
+
if (width + cells(glyph) > limit)
|
|
350
|
+
break;
|
|
351
|
+
width += cells(glyph);
|
|
352
|
+
count++;
|
|
353
|
+
}
|
|
354
|
+
return count;
|
|
355
|
+
};
|
|
356
|
+
const lines = [];
|
|
357
|
+
while (remaining.length) {
|
|
358
|
+
let count = take(18);
|
|
359
|
+
if (count === remaining.length) {
|
|
360
|
+
lines.push(remaining.join(''));
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
if (lines.length === maxLines - 1) {
|
|
364
|
+
lines.push(remaining.slice(0, take(17)).join('').trimEnd() + '…');
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
// Prefer a readable namespace/path boundary over splitting a word.
|
|
368
|
+
let boundary = 0;
|
|
369
|
+
for (const separators of [/[./]/u, /[_\-\s]/u]) {
|
|
370
|
+
for (let index = count - 1; index > 0; index--) {
|
|
371
|
+
if (separators.test(remaining[index])) {
|
|
372
|
+
boundary = index + 1;
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (boundary)
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
if (boundary)
|
|
380
|
+
count = boundary;
|
|
381
|
+
lines.push(remaining.slice(0, count).join('').trimEnd());
|
|
382
|
+
remaining = remaining.slice(count);
|
|
383
|
+
}
|
|
384
|
+
return lines.join('\n');
|
|
387
385
|
}
|
|
388
386
|
function formatGraphLabel(label, maxLineLength, maxLines) {
|
|
389
387
|
if (label.length <= maxLineLength)
|
|
@@ -468,23 +466,28 @@ function shortGraphLabel(label) {
|
|
|
468
466
|
}
|
|
469
467
|
function createGroupLabel(group, collapsed) {
|
|
470
468
|
const summary = group.summary;
|
|
471
|
-
const
|
|
472
|
-
? '
|
|
473
|
-
|
|
474
|
-
|
|
469
|
+
const parts = [
|
|
470
|
+
`${collapsed ? '▸' : '▾'} ${group.label.replace(/\s+/g, ' ').trim()}`,
|
|
471
|
+
`${summary.retainedNodeCount} nodes` + (summary.culledNodeCount ? ` · ${summary.culledNodeCount} culled` : ''),
|
|
472
|
+
];
|
|
473
|
+
return parts.join(collapsed ? '\n' : ' · ');
|
|
475
474
|
}
|
|
476
475
|
function createNodeTitle(node, segment, snapshot) {
|
|
476
|
+
const gpu = node.kind === 'external-submission' ? 'Opaque'
|
|
477
|
+
: node.kind !== 'render' && node.kind !== 'compute' ? 'Not applicable'
|
|
478
|
+
: node.gpuDurationMicros === undefined ? 'Not collected' : `${formatGpuDuration(node)} ms`;
|
|
477
479
|
return [
|
|
478
480
|
labelNode(node),
|
|
479
481
|
`kind: ${node.kind}`,
|
|
480
482
|
`group: ${debugGroupPathForId(node.debugGroupId, snapshot)}`,
|
|
481
483
|
`segment: ${segment ? `${segment.index}:${segment.kind}` : '-'}`,
|
|
482
|
-
`
|
|
484
|
+
`CPU duration: ${node.cpuDurationMicros === undefined ? 'Not collected' : (node.cpuDurationMicros / 1000).toFixed(3) + ' ms'}`,
|
|
485
|
+
`GPU duration: ${gpu}`,
|
|
483
486
|
`reads: ${node.reads.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
484
487
|
`writes: ${node.writes.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
485
488
|
].join('\n');
|
|
486
489
|
}
|
|
487
|
-
function createGroupTitle(group) {
|
|
490
|
+
function createGroupTitle(group, snapshot) {
|
|
488
491
|
const summary = group.summary;
|
|
489
492
|
return [
|
|
490
493
|
group.path.join(' / '),
|
|
@@ -493,19 +496,11 @@ function createGroupTitle(group) {
|
|
|
493
496
|
`inputs: ${summary.inputResources.map(labelResource).join(', ') || '-'}`,
|
|
494
497
|
`outputs: ${summary.outputResources.map(labelResource).join(', ') || '-'}`,
|
|
495
498
|
`transient registered/accessed: ${summary.registeredTransientResourceCount}/${summary.accessedTransientResourceCount}`,
|
|
496
|
-
`physical allocations: ${summary.physicalAllocationCount}`,
|
|
499
|
+
`physical allocations: ${snapshot.protocol.memory.allocationReport.status === 'available' ? summary.physicalAllocationCount : 'Not collected'}`,
|
|
497
500
|
`segments: ${summary.executionSegmentCount}`,
|
|
498
501
|
`opaque: ${summary.externalSubmissionCount}`,
|
|
499
|
-
|
|
500
|
-
|
|
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(', ') || '-'}`,
|
|
502
|
+
`CPU pass sum: ${summary.cpuTimedNodeCount ? (summary.cpuWorkDurationMicros / 1000).toFixed(3) + ' ms' : 'Not collected'} (${summary.cpuTimedNodeCount}/${summary.retainedNodeCount})`,
|
|
503
|
+
`GPU pass sum: ${formatMeasuredGpuWork(summary.gpuWorkDurationMicros, summary.timedNodeCount, summary.timingEligibleNodeCount)}`,
|
|
509
504
|
].join('\n');
|
|
510
505
|
}
|
|
511
506
|
function createResourceTitle(resource, snapshot, accesses) {
|
|
@@ -520,18 +515,6 @@ function createResourceTitle(resource, snapshot, accesses) {
|
|
|
520
515
|
`writes: ${accesses.filter((access) => access.mode === 'write').length}`,
|
|
521
516
|
].join('\n');
|
|
522
517
|
}
|
|
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
518
|
function debugGroupPathForId(groupId, snapshot) {
|
|
536
519
|
if (groupId === undefined)
|
|
537
520
|
return '-';
|