@zenfg/inspector 0.1.0-beta.2 → 0.1.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GUIDE.md +182 -0
- package/README.md +60 -26
- package/THEMING.md +124 -0
- package/dist/FrameGraphInspector.d.ts +24 -8
- package/dist/FrameGraphInspector.d.ts.map +1 -1
- package/dist/FrameGraphInspector.js +151 -65
- package/dist/FrameGraphInspector.js.map +1 -1
- package/dist/capture.d.ts +5 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +2 -0
- package/dist/capture.js.map +1 -0
- package/dist/debugCaptureModel.d.ts +28 -4
- package/dist/debugCaptureModel.d.ts.map +1 -1
- package/dist/debugCaptureModel.js +72 -16
- package/dist/debugCaptureModel.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/panelAliasAnalysis.d.ts +1 -0
- package/dist/panelAliasAnalysis.d.ts.map +1 -1
- package/dist/panelAliasAnalysis.js +23 -54
- package/dist/panelAliasAnalysis.js.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts +18 -3
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.js +183 -41
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -1
- package/dist/panelDetailLayout.d.ts +27 -0
- package/dist/panelDetailLayout.d.ts.map +1 -0
- package/dist/panelDetailLayout.js +174 -0
- package/dist/panelDetailLayout.js.map +1 -0
- package/dist/panelDetailStyles.d.ts +2 -0
- package/dist/panelDetailStyles.d.ts.map +1 -0
- package/dist/panelDetailStyles.js +57 -0
- package/dist/panelDetailStyles.js.map +1 -0
- package/dist/panelDiagnosticsView.d.ts +10 -1
- package/dist/panelDiagnosticsView.d.ts.map +1 -1
- package/dist/panelDiagnosticsView.js +167 -58
- package/dist/panelDiagnosticsView.js.map +1 -1
- package/dist/panelDomHelpers.d.ts.map +1 -1
- package/dist/panelDomHelpers.js +6 -5
- package/dist/panelDomHelpers.js.map +1 -1
- package/dist/panelGraphLayout.d.ts +3 -2
- package/dist/panelGraphLayout.d.ts.map +1 -1
- package/dist/panelGraphLayout.js +20 -5
- package/dist/panelGraphLayout.js.map +1 -1
- package/dist/panelGraphRenderer.d.ts +10 -0
- package/dist/panelGraphRenderer.d.ts.map +1 -1
- package/dist/panelGraphRenderer.js.map +1 -1
- package/dist/panelGraphScene.d.ts +15 -26
- package/dist/panelGraphScene.d.ts.map +1 -1
- package/dist/panelGraphScene.js +191 -208
- package/dist/panelGraphScene.js.map +1 -1
- package/dist/panelGraphSearch.d.ts +16 -0
- package/dist/panelGraphSearch.d.ts.map +1 -0
- package/dist/panelGraphSearch.js +93 -0
- package/dist/panelGraphSearch.js.map +1 -0
- package/dist/panelGraphView.d.ts +82 -0
- package/dist/panelGraphView.d.ts.map +1 -1
- package/dist/panelGraphView.js +42 -9
- package/dist/panelGraphView.js.map +1 -1
- package/dist/panelGraphVisuals.d.ts +18 -6
- package/dist/panelGraphVisuals.d.ts.map +1 -1
- package/dist/panelGraphVisuals.js +156 -116
- package/dist/panelGraphVisuals.js.map +1 -1
- package/dist/panelInspectorView.d.ts +13 -0
- package/dist/panelInspectorView.d.ts.map +1 -1
- package/dist/panelInspectorView.js +326 -46
- package/dist/panelInspectorView.js.map +1 -1
- package/dist/panelListStyles.d.ts +3 -0
- package/dist/panelListStyles.d.ts.map +1 -0
- package/dist/panelListStyles.js +155 -0
- package/dist/panelListStyles.js.map +1 -0
- package/dist/panelMemoryStyles.d.ts +3 -0
- package/dist/panelMemoryStyles.d.ts.map +1 -0
- package/dist/panelMemoryStyles.js +42 -0
- package/dist/panelMemoryStyles.js.map +1 -0
- package/dist/panelMemoryView.d.ts +13 -1
- package/dist/panelMemoryView.d.ts.map +1 -1
- package/dist/panelMemoryView.js +161 -41
- package/dist/panelMemoryView.js.map +1 -1
- package/dist/panelPassesView.d.ts +15 -0
- package/dist/panelPassesView.d.ts.map +1 -1
- package/dist/panelPassesView.js +202 -72
- package/dist/panelPassesView.js.map +1 -1
- package/dist/panelRawView.d.ts +15 -0
- package/dist/panelRawView.d.ts.map +1 -0
- package/dist/panelRawView.js +105 -0
- package/dist/panelRawView.js.map +1 -0
- package/dist/panelResourcesView.d.ts +8 -0
- package/dist/panelResourcesView.d.ts.map +1 -1
- package/dist/panelResourcesView.js +62 -31
- package/dist/panelResourcesView.js.map +1 -1
- package/dist/panelSelection.d.ts +9 -1
- package/dist/panelSelection.d.ts.map +1 -1
- package/dist/panelSelection.js +36 -6
- package/dist/panelSelection.js.map +1 -1
- package/dist/panelTheme.d.ts +13 -0
- package/dist/panelTheme.d.ts.map +1 -0
- package/dist/panelTheme.js +104 -0
- package/dist/panelTheme.js.map +1 -0
- package/dist/panelTypes.d.ts +18 -4
- package/dist/panelTypes.d.ts.map +1 -1
- package/dist/panelTypes.js.map +1 -1
- package/dist/panelVisualTheme.d.ts +176 -71
- package/dist/panelVisualTheme.d.ts.map +1 -1
- package/dist/panelVisualTheme.js +34 -36
- package/dist/panelVisualTheme.js.map +1 -1
- package/dist/panelWorkbenchHelpers.d.ts +12 -1
- package/dist/panelWorkbenchHelpers.d.ts.map +1 -1
- package/dist/panelWorkbenchHelpers.js +63 -2
- package/dist/panelWorkbenchHelpers.js.map +1 -1
- package/dist/panelWorkbenchStyles.d.ts +2 -0
- package/dist/panelWorkbenchStyles.d.ts.map +1 -0
- package/dist/panelWorkbenchStyles.js +101 -0
- package/dist/panelWorkbenchStyles.js.map +1 -0
- package/dist/panelWorkbenchView.d.ts +16 -0
- package/dist/panelWorkbenchView.d.ts.map +1 -1
- package/dist/panelWorkbenchView.js +228 -50
- package/dist/panelWorkbenchView.js.map +1 -1
- package/dist/query.js.map +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +161 -205
- package/dist/styles.js.map +1 -1
- package/dist/theme.d.ts +22 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +10 -0
- package/dist/theme.js.map +1 -0
- package/dist/themeDefinitions.d.ts +251 -0
- package/dist/themeDefinitions.d.ts.map +1 -0
- package/dist/themeDefinitions.js +96 -0
- package/dist/themeDefinitions.js.map +1 -0
- package/dist/themePalette.d.ts +66 -0
- package/dist/themePalette.d.ts.map +1 -0
- package/dist/themePalette.js +37 -0
- package/dist/themePalette.js.map +1 -0
- package/dist/themes.css +165 -0
- package/package.json +44 -10
- package/src/FrameGraphInspector.ts +145 -68
- package/src/capture.ts +4 -0
- package/src/debugCaptureModel.ts +97 -21
- package/src/index.ts +6 -2
- package/src/panelAliasAnalysis.ts +24 -70
- package/src/panelCytoscapeGraphRenderer.ts +177 -40
- package/src/panelDetailLayout.ts +155 -0
- package/src/panelDetailStyles.ts +56 -0
- package/src/panelDiagnosticsView.ts +167 -86
- package/src/panelDomHelpers.ts +4 -5
- package/src/panelGraphLayout.ts +20 -5
- package/src/panelGraphRenderer.ts +7 -0
- package/src/panelGraphScene.ts +204 -292
- package/src/panelGraphSearch.ts +96 -0
- package/src/panelGraphView.ts +41 -9
- package/src/panelGraphVisuals.ts +152 -118
- package/src/panelInspectorView.ts +315 -48
- package/src/panelListStyles.ts +154 -0
- package/src/panelMemoryStyles.ts +41 -0
- package/src/panelMemoryView.ts +178 -53
- package/src/panelPassesView.ts +190 -137
- package/src/panelRawView.ts +99 -0
- package/src/panelResourcesView.ts +69 -56
- package/src/panelSelection.ts +39 -7
- package/src/panelTheme.ts +85 -0
- package/src/panelTypes.ts +16 -4
- package/src/panelVisualTheme.ts +38 -38
- package/src/panelWorkbenchHelpers.ts +62 -4
- package/src/panelWorkbenchStyles.ts +100 -0
- package/src/panelWorkbenchView.ts +201 -52
- package/src/query.ts +11 -11
- package/src/styles.ts +162 -205
- package/src/theme.ts +24 -0
- package/src/themeDefinitions.ts +103 -0
- package/src/themePalette.ts +44 -0
package/src/panelGraphScene.ts
CHANGED
|
@@ -4,11 +4,12 @@ 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
|
+
import { formatMeasuredGpuWork } from './panelWorkbenchHelpers.ts';
|
|
12
13
|
|
|
13
14
|
export type GraphSceneElementId = string;
|
|
14
15
|
|
|
@@ -28,14 +29,6 @@ export type PassSceneNode = GraphSceneNodeBase & {
|
|
|
28
29
|
readonly gpuDurationMicros?: number;
|
|
29
30
|
};
|
|
30
31
|
|
|
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
32
|
export type GroupSceneNode = GraphSceneNodeBase & {
|
|
40
33
|
readonly kind: 'group';
|
|
41
34
|
readonly groupId: string;
|
|
@@ -57,34 +50,29 @@ export type ResourceSceneNode = GraphSceneNodeBase & {
|
|
|
57
50
|
readonly resourceKind: FrameGraphDebugResource['kind'];
|
|
58
51
|
};
|
|
59
52
|
|
|
60
|
-
export type
|
|
53
|
+
export type RootSceneNode = GraphSceneNodeBase & {
|
|
54
|
+
readonly kind: 'root';
|
|
55
|
+
readonly rootKey: string;
|
|
56
|
+
readonly resourceId: string;
|
|
57
|
+
readonly resourceKind: FrameGraphDebugResource['kind'];
|
|
58
|
+
};
|
|
59
|
+
export type GraphSceneNode = PassSceneNode | GroupSceneNode | ResourceSceneNode | RootSceneNode;
|
|
61
60
|
|
|
62
61
|
type GraphSceneEdgeBase = {
|
|
63
62
|
readonly id: GraphSceneElementId;
|
|
64
63
|
readonly from: GraphSceneElementId;
|
|
65
64
|
readonly to: GraphSceneElementId;
|
|
66
|
-
readonly label?: string;
|
|
67
65
|
readonly title: string;
|
|
68
66
|
readonly resourceId: string;
|
|
69
67
|
};
|
|
70
68
|
|
|
71
|
-
export type
|
|
72
|
-
readonly kind: '
|
|
73
|
-
readonly
|
|
69
|
+
export type GraphSceneEdge = GraphSceneEdgeBase & {
|
|
70
|
+
readonly kind: 'flow' | 'ordering';
|
|
71
|
+
readonly relations: readonly GraphFlowRelation[];
|
|
74
72
|
readonly underlyingDependencies: readonly FrameGraphDebugEdge[];
|
|
75
73
|
readonly underlyingDependencyCount: number;
|
|
76
74
|
};
|
|
77
75
|
|
|
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
76
|
export type GraphSemanticReferences = {
|
|
89
77
|
readonly nodeIds: readonly string[];
|
|
90
78
|
readonly groupPathKeys: readonly string[];
|
|
@@ -96,12 +84,12 @@ export type GraphSemanticReferences = {
|
|
|
96
84
|
export type GraphSceneInteractionIndex = {
|
|
97
85
|
readonly selectionByElementId: ReadonlyMap<GraphSceneElementId, Selection>;
|
|
98
86
|
readonly primaryElementIdsBySelection: ReadonlyMap<string, readonly GraphSceneElementId[]>;
|
|
99
|
-
readonly
|
|
87
|
+
readonly hoverElementIdsBySelection: ReadonlyMap<string, readonly GraphSceneElementId[]>;
|
|
88
|
+
readonly resourceElementIdsByResourceId: ReadonlyMap<string, readonly GraphSceneElementId[]>;
|
|
100
89
|
readonly semanticReferencesByElementId: ReadonlyMap<GraphSceneElementId, GraphSemanticReferences>;
|
|
101
90
|
};
|
|
102
91
|
|
|
103
92
|
export type GraphScene = {
|
|
104
|
-
readonly mode: GraphViewMode;
|
|
105
93
|
readonly nodes: readonly GraphSceneNode[];
|
|
106
94
|
readonly edges: readonly GraphSceneEdge[];
|
|
107
95
|
readonly topologyKey: string;
|
|
@@ -110,15 +98,16 @@ export type GraphScene = {
|
|
|
110
98
|
};
|
|
111
99
|
|
|
112
100
|
export type CreateGraphSceneOptions = {
|
|
113
|
-
readonly mode: GraphViewMode;
|
|
114
101
|
readonly groupsEnabled: boolean;
|
|
102
|
+
readonly showResourceDeclarations?: boolean;
|
|
115
103
|
readonly expandedGroupPaths: ReadonlySet<string>;
|
|
116
104
|
};
|
|
117
105
|
|
|
118
106
|
type MutableInteractionIndex = {
|
|
119
107
|
readonly selectionByElementId: Map<GraphSceneElementId, Selection>;
|
|
120
108
|
readonly primaryElementIdsBySelection: Map<string, GraphSceneElementId[]>;
|
|
121
|
-
readonly
|
|
109
|
+
readonly hoverElementIdsBySelection: Map<string, GraphSceneElementId[]>;
|
|
110
|
+
readonly resourceElementIdsByResourceId: Map<string, GraphSceneElementId[]>;
|
|
122
111
|
readonly semanticReferencesByElementId: Map<GraphSceneElementId, GraphSemanticReferences>;
|
|
123
112
|
};
|
|
124
113
|
|
|
@@ -126,9 +115,7 @@ export function createGraphScene(
|
|
|
126
115
|
snapshot: FrameGraphDebugViewModel,
|
|
127
116
|
options: CreateGraphSceneOptions,
|
|
128
117
|
): GraphScene {
|
|
129
|
-
return options.
|
|
130
|
-
? createResourceGraphScene(snapshot)
|
|
131
|
-
: createPassGraphScene(snapshot, options.groupsEnabled, options.expandedGroupPaths);
|
|
118
|
+
return createFrameFlowScene(snapshot, options.groupsEnabled, options.expandedGroupPaths, options.showResourceDeclarations ?? true);
|
|
132
119
|
}
|
|
133
120
|
|
|
134
121
|
export function graphGroupElementId(pathKey: string): GraphSceneElementId {
|
|
@@ -146,18 +133,19 @@ export function selectionKey(selection: Selection): string {
|
|
|
146
133
|
case 'allocation':
|
|
147
134
|
return `allocation:${selection.id}`;
|
|
148
135
|
case 'root':
|
|
149
|
-
return `root:${selection.
|
|
136
|
+
return `root:${selection.key}`;
|
|
150
137
|
case 'culled':
|
|
151
|
-
return `culled:${selection.
|
|
138
|
+
return `culled:${selection.id}`;
|
|
152
139
|
case 'segment':
|
|
153
140
|
return `segment:${selection.index}`;
|
|
154
141
|
}
|
|
155
142
|
}
|
|
156
143
|
|
|
157
|
-
function
|
|
144
|
+
function createFrameFlowScene(
|
|
158
145
|
snapshot: FrameGraphDebugViewModel,
|
|
159
146
|
groupsEnabled: boolean,
|
|
160
147
|
expandedGroupPaths: ReadonlySet<string>,
|
|
148
|
+
showResourceDeclarations: boolean,
|
|
161
149
|
): GraphScene {
|
|
162
150
|
const interaction = createMutableInteractionIndex();
|
|
163
151
|
const segmentByNodeId = executionSegmentByNodeId(snapshot);
|
|
@@ -167,8 +155,20 @@ function createPassGraphScene(
|
|
|
167
155
|
const hasVisibleAncestors = (group: FrameGraphDebugGroup) => group.ancestorIds
|
|
168
156
|
.slice(0, -1)
|
|
169
157
|
.every((id) => isExpanded(groupsById.get(id)!));
|
|
158
|
+
const retainedIds = new Set(snapshot.nodes.map((node) => node.id));
|
|
159
|
+
const usedResourceIds = new Set([
|
|
160
|
+
...snapshot.accessEdges.filter((access) => retainedIds.has(access.nodeId)).map((access) => access.resource.id),
|
|
161
|
+
...snapshot.roots.flatMap((root) => root.resource ? [root.resource.id] : []),
|
|
162
|
+
]);
|
|
163
|
+
const resources = showResourceDeclarations ? snapshot.resources.filter((resource) => usedResourceIds.has(resource.id)) : [];
|
|
164
|
+
const populatedGroupIds = new Set<string>();
|
|
165
|
+
for (const item of [...snapshot.nodes, ...resources]) {
|
|
166
|
+
for (const id of groupsById.get(item.debugGroupId ?? '')?.ancestorIds ?? []) populatedGroupIds.add(id);
|
|
167
|
+
}
|
|
168
|
+
const visibleResources = resources.filter((resource) => !useGroups || resource.debugGroupId === undefined
|
|
169
|
+
|| groupsById.get(resource.debugGroupId)!.ancestorIds.every((id) => isExpanded(groupsById.get(id)!)));
|
|
170
170
|
const includedGroups = useGroups
|
|
171
|
-
? snapshot.debugGroups.filter((group) => group.
|
|
171
|
+
? snapshot.debugGroups.filter((group) => populatedGroupIds.has(group.id) && hasVisibleAncestors(group))
|
|
172
172
|
: [];
|
|
173
173
|
const includedGroupIds = new Set(includedGroups.map((group) => group.id));
|
|
174
174
|
const representedNodeIdsByGroupId = new Map<string, string[]>();
|
|
@@ -197,6 +197,7 @@ function createPassGraphScene(
|
|
|
197
197
|
.map((child) => graphGroupElementId(child.pathKey)),
|
|
198
198
|
...(visiblePassesByGroupId.get(group.id) ?? [])
|
|
199
199
|
.map((node) => passElementId(node.id)),
|
|
200
|
+
...visibleResources.filter((resource) => resource.debugGroupId === group.id).map((resource) => resourceElementId(resource.id)),
|
|
200
201
|
];
|
|
201
202
|
const node: GroupSceneNode = {
|
|
202
203
|
id,
|
|
@@ -204,8 +205,8 @@ function createPassGraphScene(
|
|
|
204
205
|
groupId: group.id,
|
|
205
206
|
groupPathKey: group.pathKey,
|
|
206
207
|
label: createGroupLabel(group, collapsed),
|
|
207
|
-
overviewLabel: `${collapsed ? '▸' : '▾'} ${group.label}`,
|
|
208
|
-
title: createGroupTitle(group),
|
|
208
|
+
overviewLabel: `${collapsed ? '▸' : '▾'} ${group.label.replace(/\s+/g, ' ').trim()}`,
|
|
209
|
+
title: createGroupTitle(group, snapshot),
|
|
209
210
|
parentId: group.parentId !== undefined && includedGroupIds.has(group.parentId)
|
|
210
211
|
? graphGroupElementId(groupsById.get(group.parentId)!.pathKey)
|
|
211
212
|
: undefined,
|
|
@@ -267,226 +268,138 @@ function createPassGraphScene(
|
|
|
267
268
|
|
|
268
269
|
for (const node of snapshot.nodes) {
|
|
269
270
|
const representative = representativeByNodeId.get(node.id)!;
|
|
270
|
-
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative]
|
|
271
|
+
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative]);
|
|
271
272
|
}
|
|
272
273
|
if (useGroups) {
|
|
273
274
|
for (const group of snapshot.debugGroups) {
|
|
274
|
-
if (includedGroupIds.has(group.id)) continue;
|
|
275
|
+
if (includedGroupIds.has(group.id) || !populatedGroupIds.has(group.id)) continue;
|
|
275
276
|
const collapsedAncestorId = group.ancestorIds.find((id) => !isExpanded(groupsById.get(id)!));
|
|
276
277
|
if (collapsedAncestorId !== undefined) {
|
|
277
278
|
const representative = graphGroupElementId(groupsById.get(collapsedAncestorId)!.pathKey);
|
|
278
|
-
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative]
|
|
279
|
+
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative]);
|
|
279
280
|
}
|
|
280
281
|
}
|
|
281
282
|
}
|
|
282
283
|
|
|
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
|
-
});
|
|
284
|
+
const representativeByResourceId = new Map<string, string>();
|
|
285
|
+
for (const resource of resources) {
|
|
286
|
+
const collapsed = useGroups ? groupsById.get(resource.debugGroupId ?? '')?.ancestorIds.find((id) => !isExpanded(groupsById.get(id)!)) : undefined;
|
|
287
|
+
const representative = collapsed ? graphGroupElementId(groupsById.get(collapsed)!.pathKey) : resourceElementId(resource.id);
|
|
288
|
+
representativeByResourceId.set(resource.id, representative);
|
|
304
289
|
}
|
|
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),
|
|
345
|
-
};
|
|
346
|
-
registerElement(
|
|
347
|
-
interaction,
|
|
348
|
-
sceneNode.id,
|
|
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) ?? []),
|
|
290
|
+
const resourceNodes: ResourceSceneNode[] = visibleResources.map((resource) => {
|
|
291
|
+
const origin = resource.origin === 'transient' ? 'Created' : resource.origin === 'imported' ? 'Imported' : 'Surface';
|
|
292
|
+
const node: ResourceSceneNode = {
|
|
293
|
+
id: resourceElementId(resource.id), kind: 'resource', resourceId: resource.id, resourceKind: resource.kind,
|
|
294
|
+
label: origin + ' · ' + (resource.kind === 'buffer' ? 'Buffer' : 'Texture') + '\n' + formatGraphResourceLabel(labelResource(resource)),
|
|
295
|
+
overviewLabel: origin + '\n' + formatGraphResourceLabel(shortGraphLabel(labelResource(resource)), 1),
|
|
296
|
+
title: createResourceTitle(resource, snapshot, snapshot.accessesByResourceId.get(resource.id) ?? [])
|
|
297
|
+
+ '\nDeclaration entrance, not a complete initial-content provenance graph.',
|
|
298
|
+
parentId: useGroups && resource.debugGroupId ? graphGroupElementId(groupsById.get(resource.debugGroupId)!.pathKey) : undefined,
|
|
363
299
|
};
|
|
364
|
-
registerElement(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
{ kind: 'resource', id: resource.id },
|
|
368
|
-
emptyReferences({ resourceIds: [resource.id] }),
|
|
369
|
-
);
|
|
370
|
-
return sceneNode;
|
|
300
|
+
registerElement(interaction, node.id, { kind: 'resource', id: resource.id }, emptyReferences({ resourceIds: [resource.id] }));
|
|
301
|
+
interaction.resourceElementIdsByResourceId.set(resource.id, [node.id]);
|
|
302
|
+
return node;
|
|
371
303
|
});
|
|
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
|
-
|
|
304
|
+
const rootNodes: RootSceneNode[] = [];
|
|
305
|
+
const aggregates = new Map<string, { from: string; to: string; resourceId: string; relations: GraphFlowRelation[] }>();
|
|
306
|
+
const addRelation = (from: string, to: string, resourceId: string, relation: GraphFlowRelation) => {
|
|
307
|
+
if (from === to) return;
|
|
308
|
+
const key = JSON.stringify([from, to, resourceId]);
|
|
309
|
+
const entry = aggregates.get(key) ?? { from, to, resourceId, relations: [] };
|
|
310
|
+
entry.relations.push(relation);
|
|
311
|
+
aggregates.set(key, entry);
|
|
312
|
+
};
|
|
313
|
+
for (const dependency of snapshot.edges) {
|
|
314
|
+
addRelation(representativeByNodeId.get(dependency.fromNodeId)!, representativeByNodeId.get(dependency.toNodeId)!, dependency.resource.id,
|
|
315
|
+
{ role: dependency.kind, nodeIds: [dependency.fromNodeId, dependency.toNodeId], dependency });
|
|
316
|
+
}
|
|
317
|
+
for (const access of showResourceDeclarations ? declarationEntrances(snapshot.nodes, snapshot.accessEdges, snapshot.edges) : []) {
|
|
318
|
+
addRelation(representativeByResourceId.get(access.resource.id)!, representativeByNodeId.get(access.nodeId)!, access.resource.id,
|
|
319
|
+
{ role: 'declaration', nodeIds: [access.nodeId] });
|
|
320
|
+
}
|
|
321
|
+
const rootOccurrences = new Map<string, number>();
|
|
322
|
+
const rangesByRootFamily = new Map<string, Set<string>>();
|
|
323
|
+
for (const root of snapshot.roots) {
|
|
324
|
+
if (!root.resource) continue;
|
|
325
|
+
const family = JSON.stringify([root.resource.id, root.reason]);
|
|
326
|
+
const ranges = rangesByRootFamily.get(family) ?? new Set<string>();
|
|
327
|
+
ranges.add(root.key);
|
|
328
|
+
rangesByRootFamily.set(family, ranges);
|
|
329
|
+
}
|
|
330
|
+
for (const root of snapshot.roots) {
|
|
331
|
+
if (!root.resource) {
|
|
332
|
+
if (root.nodeId) {
|
|
333
|
+
const representative = representativeByNodeId.get(root.nodeId)!;
|
|
334
|
+
setSelectionElements(interaction, { kind: 'root', key: root.key }, [representative]);
|
|
335
|
+
}
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
const occurrence = rootOccurrences.get(root.key) ?? 0;
|
|
339
|
+
rootOccurrences.set(root.key, occurrence + 1);
|
|
340
|
+
const id = 'root:' + root.key + (occurrence ? ':' + occurrence : '');
|
|
341
|
+
const resource = snapshot.resourceById.get(root.resource.id)!;
|
|
342
|
+
const rangeLabel = root.range?.kind === 'buffer' ? `bytes ${root.range.offset}–${root.range.offset + root.range.size}`
|
|
343
|
+
: 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';
|
|
344
|
+
const rangeSummary = root.range?.kind === 'texture'
|
|
345
|
+
? 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('; ')
|
|
346
|
+
: rangeLabel;
|
|
347
|
+
const reasonLabel = root.reason.split('-').map((word) => word[0]!.toUpperCase() + word.slice(1)).join(' ');
|
|
348
|
+
const disambiguate = rangesByRootFamily.get(JSON.stringify([resource.id, root.reason]))!.size > 1;
|
|
349
|
+
const initialLabel = !showResourceDeclarations && root.resolution?.usesInitialContents
|
|
350
|
+
? '\n' + (root.resolution.producerNodeIds.length ? 'With initial contents' : 'Initial contents only') : '';
|
|
351
|
+
rootNodes.push({
|
|
352
|
+
id, kind: 'root', rootKey: root.key, resourceId: resource.id, resourceKind: resource.kind,
|
|
353
|
+
label: reasonLabel + '\n' + formatGraphResourceLabel(labelResource(resource))
|
|
354
|
+
+ (disambiguate ? '\n' + formatGraphResourceLabel(rangeSummary, 1) : '') + initialLabel,
|
|
355
|
+
overviewLabel: reasonLabel + '\n' + formatGraphResourceLabel(shortGraphLabel(labelResource(resource)), 1) + initialLabel,
|
|
356
|
+
title: labelResource(resource) + '\n' + root.reason + '\n' + rangeLabel
|
|
357
|
+
+ (root.resolution ? '\nProducers: ' + (root.resolution.producerNodeIds.join(', ') || 'none') + '\nInitial contents: ' + root.resolution.usesInitialContents : '\nOutput sources unavailable in Legacy capture.'),
|
|
401
358
|
});
|
|
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]);
|
|
359
|
+
registerElement(interaction, id, { kind: 'root', key: root.key },
|
|
360
|
+
emptyReferences({ resourceIds: [resource.id], nodeIds: root.resolution?.producerNodeIds ?? [] }));
|
|
361
|
+
for (const nodeId of root.resolution?.producerNodeIds ?? []) {
|
|
362
|
+
addRelation(representativeByNodeId.get(nodeId)!, id, resource.id, { role: 'output-producer', nodeIds: [nodeId], rootKey: root.key });
|
|
418
363
|
}
|
|
364
|
+
if (showResourceDeclarations && root.resolution?.usesInitialContents) addRelation(representativeByResourceId.get(resource.id)!, id, resource.id,
|
|
365
|
+
{ role: 'output-initial', nodeIds: [], rootKey: root.key });
|
|
419
366
|
}
|
|
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
|
-
}
|
|
367
|
+
const edges: GraphSceneEdge[] = [...aggregates.entries()].map(([key, entry]) => {
|
|
368
|
+
const underlyingDependencies = entry.relations.flatMap((relation) => relation.dependency ? [relation.dependency] : []);
|
|
369
|
+
const edge: GraphSceneEdge = {
|
|
370
|
+
id: 'flow:' + key, ...entry,
|
|
371
|
+
kind: entry.relations.every((relation) => relation.role === 'ordering') ? 'ordering' : 'flow',
|
|
372
|
+
title: labelResource(snapshot.resourceById.get(entry.resourceId)!) + '\n'
|
|
373
|
+
+ unique(entry.relations.map((relation) => relation.role)).join(' · '),
|
|
374
|
+
underlyingDependencies, underlyingDependencyCount: underlyingDependencies.length,
|
|
375
|
+
};
|
|
376
|
+
const selection: Selection = { kind: 'resource', id: entry.resourceId };
|
|
377
|
+
registerElement(interaction, edge.id, selection, emptyReferences({
|
|
378
|
+
nodeIds: unique(entry.relations.flatMap((relation) => relation.nodeIds)), resourceIds: [entry.resourceId], dependencies: underlyingDependencies,
|
|
379
|
+
}));
|
|
380
|
+
const resourceElements = interaction.resourceElementIdsByResourceId.get(entry.resourceId) ?? [];
|
|
381
|
+
resourceElements.push(edge.id);
|
|
382
|
+
interaction.resourceElementIdsByResourceId.set(entry.resourceId, resourceElements);
|
|
383
|
+
return edge;
|
|
384
|
+
});
|
|
385
|
+
for (const [resourceId, elements] of interaction.resourceElementIdsByResourceId) {
|
|
386
|
+
const selection: Selection = { kind: 'resource', id: resourceId };
|
|
387
|
+
setSelectionElements(interaction, selection, elements);
|
|
388
|
+
interaction.hoverElementIdsBySelection.set(selectionKey(selection), [...elements]);
|
|
444
389
|
}
|
|
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
|
-
};
|
|
390
|
+
return finalizeScene([...groupNodes, ...passNodes, ...resourceNodes, ...rootNodes], edges, interaction);
|
|
475
391
|
}
|
|
476
392
|
|
|
477
393
|
function finalizeScene(
|
|
478
|
-
mode: GraphViewMode,
|
|
479
394
|
nodes: readonly GraphSceneNode[],
|
|
480
395
|
edges: readonly GraphSceneEdge[],
|
|
481
396
|
interaction: MutableInteractionIndex,
|
|
482
397
|
): GraphScene {
|
|
483
398
|
const topologyKey = JSON.stringify({
|
|
484
|
-
mode,
|
|
485
399
|
nodes: nodes.map((node) => [node.id, node.kind, node.parentId, node.kind === 'group' ? node.collapsed : undefined]),
|
|
486
400
|
edges: edges.map((edge) => [edge.id, edge.kind, edge.from, edge.to]),
|
|
487
401
|
});
|
|
488
402
|
const contentKey = JSON.stringify({
|
|
489
|
-
mode,
|
|
490
403
|
nodes: nodes.map((node) => [
|
|
491
404
|
node.id,
|
|
492
405
|
node.kind,
|
|
@@ -494,8 +407,8 @@ function finalizeScene(
|
|
|
494
407
|
node.label,
|
|
495
408
|
node.overviewLabel,
|
|
496
409
|
node.title,
|
|
497
|
-
node.kind === 'pass'
|
|
498
|
-
node.kind === 'resource' ? node.resourceKind : undefined,
|
|
410
|
+
node.kind === 'pass' ? node.passKind : undefined,
|
|
411
|
+
(node.kind === 'resource' || node.kind === 'root') ? node.resourceKind : undefined,
|
|
499
412
|
node.kind === 'group' ? node.collapsed : undefined,
|
|
500
413
|
node.kind === 'group' ? node.depthBand : undefined,
|
|
501
414
|
node.kind === 'group' ? node.culledNodeCount > 0 : undefined,
|
|
@@ -505,15 +418,11 @@ function finalizeScene(
|
|
|
505
418
|
edge.kind,
|
|
506
419
|
edge.from,
|
|
507
420
|
edge.to,
|
|
508
|
-
edge.label,
|
|
509
421
|
edge.title,
|
|
510
|
-
edge.
|
|
511
|
-
edge.kind === 'access' ? edge.accessMode : undefined,
|
|
512
|
-
edge.kind === 'access' ? edge.dashed : undefined,
|
|
422
|
+
edge.relations,
|
|
513
423
|
]),
|
|
514
424
|
});
|
|
515
425
|
return {
|
|
516
|
-
mode,
|
|
517
426
|
nodes,
|
|
518
427
|
edges,
|
|
519
428
|
topologyKey,
|
|
@@ -526,7 +435,8 @@ function createMutableInteractionIndex(): MutableInteractionIndex {
|
|
|
526
435
|
return {
|
|
527
436
|
selectionByElementId: new Map(),
|
|
528
437
|
primaryElementIdsBySelection: new Map(),
|
|
529
|
-
|
|
438
|
+
hoverElementIdsBySelection: new Map(),
|
|
439
|
+
resourceElementIdsByResourceId: new Map(),
|
|
530
440
|
semanticReferencesByElementId: new Map(),
|
|
531
441
|
};
|
|
532
442
|
}
|
|
@@ -538,7 +448,8 @@ function registerElement(
|
|
|
538
448
|
references: GraphSemanticReferences,
|
|
539
449
|
): void {
|
|
540
450
|
interaction.selectionByElementId.set(elementId, selection);
|
|
541
|
-
setSelectionElements(interaction, selection, [elementId]
|
|
451
|
+
setSelectionElements(interaction, selection, [elementId]);
|
|
452
|
+
interaction.hoverElementIdsBySelection.set(selectionKey(selection), [elementId]);
|
|
542
453
|
interaction.semanticReferencesByElementId.set(elementId, references);
|
|
543
454
|
}
|
|
544
455
|
|
|
@@ -546,21 +457,8 @@ function setSelectionElements(
|
|
|
546
457
|
interaction: MutableInteractionIndex,
|
|
547
458
|
selection: Selection,
|
|
548
459
|
primary: readonly GraphSceneElementId[],
|
|
549
|
-
related: readonly GraphSceneElementId[],
|
|
550
460
|
): void {
|
|
551
461
|
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
462
|
}
|
|
565
463
|
|
|
566
464
|
function emptyReferences(overrides: Partial<GraphSemanticReferences>): GraphSemanticReferences {
|
|
@@ -578,24 +476,55 @@ function passElementId(nodeId: string): GraphSceneElementId {
|
|
|
578
476
|
return `pass:${nodeId}`;
|
|
579
477
|
}
|
|
580
478
|
|
|
581
|
-
function culledPassElementId(index: number, nodeId: string): GraphSceneElementId {
|
|
582
|
-
return `culled-pass:${index}:${nodeId}`;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
479
|
function resourceElementId(resourceId: string): GraphSceneElementId {
|
|
586
480
|
return `resource:${resourceId}`;
|
|
587
481
|
}
|
|
588
482
|
|
|
589
|
-
function accessElementId(accessId: string, resourceId: string): GraphSceneElementId {
|
|
590
|
-
return `access:${accessId}:${resourceId}`;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
483
|
function formatGraphNodeLabel(label: string): string {
|
|
594
484
|
return formatGraphLabel(label, 20, 3);
|
|
595
485
|
}
|
|
596
486
|
|
|
597
|
-
function formatGraphResourceLabel(label: string): string {
|
|
598
|
-
|
|
487
|
+
function formatGraphResourceLabel(label: string, maxLines = 2): string {
|
|
488
|
+
let remaining = [...label.replace(/\s+/g, ' ').trim()];
|
|
489
|
+
// Reserve conservative monospace cells for CJK/emoji fallback glyphs as well.
|
|
490
|
+
const cells = (glyph: string) => /\p{Mark}/u.test(glyph) ? 0 : glyph.codePointAt(0)! > 255 ? 2 : 1;
|
|
491
|
+
const take = (limit: number) => {
|
|
492
|
+
let width = 0;
|
|
493
|
+
let count = 0;
|
|
494
|
+
for (const glyph of remaining) {
|
|
495
|
+
if (width + cells(glyph) > limit) break;
|
|
496
|
+
width += cells(glyph);
|
|
497
|
+
count++;
|
|
498
|
+
}
|
|
499
|
+
return count;
|
|
500
|
+
};
|
|
501
|
+
const lines: string[] = [];
|
|
502
|
+
while (remaining.length) {
|
|
503
|
+
let count = take(18);
|
|
504
|
+
if (count === remaining.length) {
|
|
505
|
+
lines.push(remaining.join(''));
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
if (lines.length === maxLines - 1) {
|
|
509
|
+
lines.push(remaining.slice(0, take(17)).join('').trimEnd() + '…');
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
// Prefer a readable namespace/path boundary over splitting a word.
|
|
513
|
+
let boundary = 0;
|
|
514
|
+
for (const separators of [/[./]/u, /[_\-\s]/u]) {
|
|
515
|
+
for (let index = count - 1; index > 0; index--) {
|
|
516
|
+
if (separators.test(remaining[index]!)) {
|
|
517
|
+
boundary = index + 1;
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
if (boundary) break;
|
|
522
|
+
}
|
|
523
|
+
if (boundary) count = boundary;
|
|
524
|
+
lines.push(remaining.slice(0, count).join('').trimEnd());
|
|
525
|
+
remaining = remaining.slice(count);
|
|
526
|
+
}
|
|
527
|
+
return lines.join('\n');
|
|
599
528
|
}
|
|
600
529
|
|
|
601
530
|
function formatGraphLabel(label: string, maxLineLength: number, maxLines: number): string {
|
|
@@ -677,10 +606,11 @@ function shortGraphLabel(label: string): string {
|
|
|
677
606
|
|
|
678
607
|
function createGroupLabel(group: FrameGraphDebugGroup, collapsed: boolean): string {
|
|
679
608
|
const summary = group.summary;
|
|
680
|
-
const
|
|
681
|
-
? '
|
|
682
|
-
|
|
683
|
-
|
|
609
|
+
const parts = [
|
|
610
|
+
`${collapsed ? '▸' : '▾'} ${group.label.replace(/\s+/g, ' ').trim()}`,
|
|
611
|
+
`${summary.retainedNodeCount} nodes` + (summary.culledNodeCount ? ` · ${summary.culledNodeCount} culled` : ''),
|
|
612
|
+
];
|
|
613
|
+
return parts.join(collapsed ? '\n' : ' · ');
|
|
684
614
|
}
|
|
685
615
|
|
|
686
616
|
function createNodeTitle(
|
|
@@ -688,18 +618,22 @@ function createNodeTitle(
|
|
|
688
618
|
segment: FrameGraphDebugViewModel['executionSegments'][number] | undefined,
|
|
689
619
|
snapshot: FrameGraphDebugViewModel,
|
|
690
620
|
): string {
|
|
621
|
+
const gpu = node.kind === 'external-submission' ? 'Opaque'
|
|
622
|
+
: node.kind !== 'render' && node.kind !== 'compute' ? 'Not applicable'
|
|
623
|
+
: node.gpuDurationMicros === undefined ? 'Not collected' : `${formatGpuDuration(node)} ms`;
|
|
691
624
|
return [
|
|
692
625
|
labelNode(node),
|
|
693
626
|
`kind: ${node.kind}`,
|
|
694
627
|
`group: ${debugGroupPathForId(node.debugGroupId, snapshot)}`,
|
|
695
628
|
`segment: ${segment ? `${segment.index}:${segment.kind}` : '-'}`,
|
|
696
|
-
`
|
|
629
|
+
`CPU duration: ${node.cpuDurationMicros === undefined ? 'Not collected' : (node.cpuDurationMicros / 1000).toFixed(3) + ' ms'}`,
|
|
630
|
+
`GPU duration: ${gpu}`,
|
|
697
631
|
`reads: ${node.reads.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
698
632
|
`writes: ${node.writes.map((access) => labelResource(access.resource)).join(', ') || '-'}`,
|
|
699
633
|
].join('\n');
|
|
700
634
|
}
|
|
701
635
|
|
|
702
|
-
function createGroupTitle(group: FrameGraphDebugGroup): string {
|
|
636
|
+
function createGroupTitle(group: FrameGraphDebugGroup, snapshot: FrameGraphDebugViewModel): string {
|
|
703
637
|
const summary = group.summary;
|
|
704
638
|
return [
|
|
705
639
|
group.path.join(' / '),
|
|
@@ -708,20 +642,11 @@ function createGroupTitle(group: FrameGraphDebugGroup): string {
|
|
|
708
642
|
`inputs: ${summary.inputResources.map(labelResource).join(', ') || '-'}`,
|
|
709
643
|
`outputs: ${summary.outputResources.map(labelResource).join(', ') || '-'}`,
|
|
710
644
|
`transient registered/accessed: ${summary.registeredTransientResourceCount}/${summary.accessedTransientResourceCount}`,
|
|
711
|
-
`physical allocations: ${summary.physicalAllocationCount}`,
|
|
645
|
+
`physical allocations: ${snapshot.protocol.memory.allocationReport.status === 'available' ? summary.physicalAllocationCount : 'Not collected'}`,
|
|
712
646
|
`segments: ${summary.executionSegmentCount}`,
|
|
713
647
|
`opaque: ${summary.externalSubmissionCount}`,
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
|
|
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(', ') || '-'}`,
|
|
648
|
+
`CPU pass sum: ${summary.cpuTimedNodeCount ? (summary.cpuWorkDurationMicros / 1000).toFixed(3) + ' ms' : 'Not collected'} (${summary.cpuTimedNodeCount}/${summary.retainedNodeCount})`,
|
|
649
|
+
`GPU pass sum: ${formatMeasuredGpuWork(summary.gpuWorkDurationMicros, summary.timedNodeCount, summary.timingEligibleNodeCount)}`,
|
|
725
650
|
].join('\n');
|
|
726
651
|
}
|
|
727
652
|
|
|
@@ -742,19 +667,6 @@ function createResourceTitle(
|
|
|
742
667
|
].join('\n');
|
|
743
668
|
}
|
|
744
669
|
|
|
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
670
|
function debugGroupPathForId(groupId: string | undefined, snapshot: FrameGraphDebugViewModel): string {
|
|
759
671
|
if (groupId === undefined) return '-';
|
|
760
672
|
return snapshot.debugGroups.find((group) => group.id === groupId)?.path.join(' / ') ?? `#${groupId}`;
|