@zenfg/inspector 0.1.0-beta.2 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -1
- package/dist/FrameGraphInspector.d.ts +2 -5
- package/dist/FrameGraphInspector.d.ts.map +1 -1
- package/dist/FrameGraphInspector.js +16 -38
- package/dist/FrameGraphInspector.js.map +1 -1
- package/dist/debugCaptureModel.d.ts +9 -4
- package/dist/debugCaptureModel.d.ts.map +1 -1
- package/dist/debugCaptureModel.js +36 -14
- package/dist/debugCaptureModel.js.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts +3 -1
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.js +39 -19
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -1
- package/dist/panelDiagnosticsView.js +2 -2
- package/dist/panelDiagnosticsView.js.map +1 -1
- package/dist/panelGraphLayout.d.ts.map +1 -1
- package/dist/panelGraphLayout.js +15 -1
- package/dist/panelGraphLayout.js.map +1 -1
- package/dist/panelGraphScene.d.ts +14 -26
- package/dist/panelGraphScene.d.ts.map +1 -1
- package/dist/panelGraphScene.js +141 -195
- package/dist/panelGraphScene.js.map +1 -1
- package/dist/panelGraphView.d.ts.map +1 -1
- package/dist/panelGraphView.js +31 -9
- package/dist/panelGraphView.js.map +1 -1
- package/dist/panelGraphVisuals.d.ts +7 -3
- package/dist/panelGraphVisuals.d.ts.map +1 -1
- package/dist/panelGraphVisuals.js +45 -80
- package/dist/panelGraphVisuals.js.map +1 -1
- package/dist/panelInspectorView.d.ts +5 -0
- package/dist/panelInspectorView.d.ts.map +1 -1
- package/dist/panelInspectorView.js +99 -13
- package/dist/panelInspectorView.js.map +1 -1
- package/dist/panelSelection.js +2 -2
- package/dist/panelSelection.js.map +1 -1
- package/dist/panelTypes.d.ts +8 -3
- package/dist/panelTypes.d.ts.map +1 -1
- package/dist/panelTypes.js.map +1 -1
- package/dist/panelVisualTheme.d.ts +21 -13
- package/dist/panelVisualTheme.d.ts.map +1 -1
- package/dist/panelVisualTheme.js +15 -7
- package/dist/panelVisualTheme.js.map +1 -1
- package/dist/panelWorkbenchHelpers.js +1 -1
- package/dist/panelWorkbenchHelpers.js.map +1 -1
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +16 -12
- package/dist/styles.js.map +1 -1
- package/package.json +2 -2
- package/src/FrameGraphInspector.ts +17 -39
- package/src/debugCaptureModel.ts +44 -19
- package/src/panelCytoscapeGraphRenderer.ts +38 -18
- package/src/panelDiagnosticsView.ts +3 -3
- package/src/panelGraphLayout.ts +15 -1
- package/src/panelGraphScene.ts +153 -279
- package/src/panelGraphView.ts +31 -9
- package/src/panelGraphVisuals.ts +46 -82
- package/src/panelInspectorView.ts +104 -16
- package/src/panelSelection.ts +2 -2
- package/src/panelTypes.ts +9 -3
- package/src/panelVisualTheme.ts +18 -7
- package/src/panelWorkbenchHelpers.ts +2 -2
- package/src/styles.ts +16 -12
package/dist/panelGraphScene.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { formatGpuDuration, labelNode, labelResource } from "./panelDomHelpers.js";
|
|
2
|
+
import { declarationEntrances } from "./debugCaptureModel.js";
|
|
2
3
|
export function createGraphScene(snapshot, options) {
|
|
3
|
-
return options.
|
|
4
|
-
? createResourceGraphScene(snapshot)
|
|
5
|
-
: createPassGraphScene(snapshot, options.groupsEnabled, options.expandedGroupPaths);
|
|
4
|
+
return createFrameFlowScene(snapshot, options.groupsEnabled, options.expandedGroupPaths);
|
|
6
5
|
}
|
|
7
6
|
export function graphGroupElementId(pathKey) {
|
|
8
7
|
return `group:${pathKey}`;
|
|
@@ -18,14 +17,14 @@ export function selectionKey(selection) {
|
|
|
18
17
|
case 'allocation':
|
|
19
18
|
return `allocation:${selection.id}`;
|
|
20
19
|
case 'root':
|
|
21
|
-
return `root:${selection.
|
|
20
|
+
return `root:${selection.key}`;
|
|
22
21
|
case 'culled':
|
|
23
22
|
return `culled:${selection.index}`;
|
|
24
23
|
case 'segment':
|
|
25
24
|
return `segment:${selection.index}`;
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
|
-
function
|
|
27
|
+
function createFrameFlowScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
29
28
|
const interaction = createMutableInteractionIndex();
|
|
30
29
|
const segmentByNodeId = executionSegmentByNodeId(snapshot);
|
|
31
30
|
const groupsById = new Map(snapshot.debugGroups.map((group) => [group.id, group]));
|
|
@@ -34,8 +33,21 @@ function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
|
34
33
|
const hasVisibleAncestors = (group) => group.ancestorIds
|
|
35
34
|
.slice(0, -1)
|
|
36
35
|
.every((id) => isExpanded(groupsById.get(id)));
|
|
36
|
+
const retainedIds = new Set(snapshot.nodes.map((node) => node.id));
|
|
37
|
+
const usedResourceIds = new Set([
|
|
38
|
+
...snapshot.accessEdges.filter((access) => retainedIds.has(access.nodeId)).map((access) => access.resource.id),
|
|
39
|
+
...snapshot.roots.flatMap((root) => root.resource ? [root.resource.id] : []),
|
|
40
|
+
]);
|
|
41
|
+
const resources = snapshot.resources.filter((resource) => usedResourceIds.has(resource.id));
|
|
42
|
+
const populatedGroupIds = new Set();
|
|
43
|
+
for (const item of [...snapshot.nodes, ...resources]) {
|
|
44
|
+
for (const id of groupsById.get(item.debugGroupId ?? '')?.ancestorIds ?? [])
|
|
45
|
+
populatedGroupIds.add(id);
|
|
46
|
+
}
|
|
47
|
+
const visibleResources = resources.filter((resource) => !useGroups || resource.debugGroupId === undefined
|
|
48
|
+
|| groupsById.get(resource.debugGroupId).ancestorIds.every((id) => isExpanded(groupsById.get(id))));
|
|
37
49
|
const includedGroups = useGroups
|
|
38
|
-
? snapshot.debugGroups.filter((group) => group.
|
|
50
|
+
? snapshot.debugGroups.filter((group) => populatedGroupIds.has(group.id) && hasVisibleAncestors(group))
|
|
39
51
|
: [];
|
|
40
52
|
const includedGroupIds = new Set(includedGroups.map((group) => group.id));
|
|
41
53
|
const representedNodeIdsByGroupId = new Map();
|
|
@@ -64,6 +76,7 @@ function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
|
64
76
|
.map((child) => graphGroupElementId(child.pathKey)),
|
|
65
77
|
...(visiblePassesByGroupId.get(group.id) ?? [])
|
|
66
78
|
.map((node) => passElementId(node.id)),
|
|
79
|
+
...visibleResources.filter((resource) => resource.debugGroupId === group.id).map((resource) => resourceElementId(resource.id)),
|
|
67
80
|
];
|
|
68
81
|
const node = {
|
|
69
82
|
id,
|
|
@@ -131,7 +144,7 @@ function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
|
131
144
|
}
|
|
132
145
|
for (const node of snapshot.nodes) {
|
|
133
146
|
const representative = representativeByNodeId.get(node.id);
|
|
134
|
-
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative]
|
|
147
|
+
setSelectionElements(interaction, { kind: 'node', id: node.id }, [representative]);
|
|
135
148
|
}
|
|
136
149
|
if (useGroups) {
|
|
137
150
|
for (const group of snapshot.debugGroups) {
|
|
@@ -140,165 +153,121 @@ function createPassGraphScene(snapshot, groupsEnabled, expandedGroupPaths) {
|
|
|
140
153
|
const collapsedAncestorId = group.ancestorIds.find((id) => !isExpanded(groupsById.get(id)));
|
|
141
154
|
if (collapsedAncestorId !== undefined) {
|
|
142
155
|
const representative = graphGroupElementId(groupsById.get(collapsedAncestorId).pathKey);
|
|
143
|
-
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative]
|
|
156
|
+
setSelectionElements(interaction, { kind: 'group', pathKey: group.pathKey }, [representative]);
|
|
144
157
|
}
|
|
145
158
|
}
|
|
146
159
|
}
|
|
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
|
-
});
|
|
160
|
+
const representativeByResourceId = new Map();
|
|
161
|
+
for (const resource of resources) {
|
|
162
|
+
const collapsed = useGroups ? groupsById.get(resource.debugGroupId ?? '')?.ancestorIds.find((id) => !isExpanded(groupsById.get(id))) : undefined;
|
|
163
|
+
const representative = collapsed ? graphGroupElementId(groupsById.get(collapsed).pathKey) : resourceElementId(resource.id);
|
|
164
|
+
representativeByResourceId.set(resource.id, representative);
|
|
162
165
|
}
|
|
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),
|
|
166
|
+
const resourceNodes = visibleResources.map((resource) => {
|
|
167
|
+
const origin = resource.origin === 'transient' ? 'Created' : resource.origin === 'imported' ? 'Imported' : 'Surface';
|
|
168
|
+
const node = {
|
|
169
|
+
id: resourceElementId(resource.id), kind: 'resource', resourceId: resource.id, resourceKind: resource.kind,
|
|
170
|
+
label: origin + ' · ' + (resource.kind === 'buffer' ? 'Buffer' : 'Texture') + '\n' + formatGraphResourceLabel(labelResource(resource)),
|
|
171
|
+
overviewLabel: origin + '\n' + formatGraphResourceLabel(shortGraphLabel(labelResource(resource))),
|
|
172
|
+
title: createResourceTitle(resource, snapshot, snapshot.accessesByResourceId.get(resource.id) ?? [])
|
|
173
|
+
+ '\nDeclaration entrance, not a complete initial-content provenance graph.',
|
|
174
|
+
parentId: useGroups && resource.debugGroupId ? graphGroupElementId(groupsById.get(resource.debugGroupId).pathKey) : undefined,
|
|
232
175
|
};
|
|
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;
|
|
176
|
+
registerElement(interaction, node.id, { kind: 'resource', id: resource.id }, emptyReferences({ resourceIds: [resource.id] }));
|
|
177
|
+
interaction.resourceElementIdsByResourceId.set(resource.id, [node.id]);
|
|
178
|
+
return node;
|
|
244
179
|
});
|
|
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);
|
|
180
|
+
const rootNodes = [];
|
|
181
|
+
const aggregates = new Map();
|
|
182
|
+
const addRelation = (from, to, resourceId, relation) => {
|
|
265
183
|
if (from === to)
|
|
184
|
+
return;
|
|
185
|
+
const key = JSON.stringify([from, to, resourceId]);
|
|
186
|
+
const entry = aggregates.get(key) ?? { from, to, resourceId, relations: [] };
|
|
187
|
+
entry.relations.push(relation);
|
|
188
|
+
aggregates.set(key, entry);
|
|
189
|
+
};
|
|
190
|
+
for (const dependency of snapshot.edges) {
|
|
191
|
+
addRelation(representativeByNodeId.get(dependency.fromNodeId), representativeByNodeId.get(dependency.toNodeId), dependency.resource.id, { role: dependency.kind, nodeIds: [dependency.fromNodeId, dependency.toNodeId], dependency });
|
|
192
|
+
}
|
|
193
|
+
for (const access of declarationEntrances(snapshot.nodes, snapshot.accessEdges, snapshot.edges)) {
|
|
194
|
+
addRelation(representativeByResourceId.get(access.resource.id), representativeByNodeId.get(access.nodeId), access.resource.id, { role: 'declaration', nodeIds: [access.nodeId] });
|
|
195
|
+
}
|
|
196
|
+
const rootOccurrences = new Map();
|
|
197
|
+
const rangesByRootFamily = new Map();
|
|
198
|
+
for (const root of snapshot.roots) {
|
|
199
|
+
if (!root.resource)
|
|
200
|
+
continue;
|
|
201
|
+
const family = JSON.stringify([root.resource.id, root.reason]);
|
|
202
|
+
const ranges = rangesByRootFamily.get(family) ?? new Set();
|
|
203
|
+
ranges.add(root.key);
|
|
204
|
+
rangesByRootFamily.set(family, ranges);
|
|
205
|
+
}
|
|
206
|
+
for (const root of snapshot.roots) {
|
|
207
|
+
if (!root.resource) {
|
|
208
|
+
if (root.nodeId) {
|
|
209
|
+
const representative = representativeByNodeId.get(root.nodeId);
|
|
210
|
+
setSelectionElements(interaction, { kind: 'root', key: root.key }, [representative]);
|
|
211
|
+
}
|
|
266
212
|
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
213
|
}
|
|
272
|
-
|
|
273
|
-
|
|
214
|
+
const occurrence = rootOccurrences.get(root.key) ?? 0;
|
|
215
|
+
rootOccurrences.set(root.key, occurrence + 1);
|
|
216
|
+
const id = 'root:' + root.key + (occurrence ? ':' + occurrence : '');
|
|
217
|
+
const resource = snapshot.resourceById.get(root.resource.id);
|
|
218
|
+
const rangeLabel = root.range?.kind === 'buffer' ? `bytes ${root.range.offset}–${root.range.offset + root.range.size}`
|
|
219
|
+
: 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';
|
|
220
|
+
const rangeSummary = root.range?.kind === 'texture'
|
|
221
|
+
? 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('; ')
|
|
222
|
+
: rangeLabel;
|
|
223
|
+
const reasonLabel = root.reason.split('-').map((word) => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
224
|
+
const disambiguate = rangesByRootFamily.get(JSON.stringify([resource.id, root.reason])).size > 1;
|
|
225
|
+
rootNodes.push({
|
|
226
|
+
id, kind: 'root', rootKey: root.key, resourceId: resource.id, resourceKind: resource.kind,
|
|
227
|
+
label: reasonLabel + '\n' + formatGraphResourceLabel(labelResource(resource))
|
|
228
|
+
+ (disambiguate ? '\n' + formatGraphResourceLabel(rangeSummary) : ''),
|
|
229
|
+
overviewLabel: reasonLabel + '\n' + formatGraphResourceLabel(shortGraphLabel(labelResource(resource))),
|
|
230
|
+
title: labelResource(resource) + '\n' + root.reason + '\n' + rangeLabel
|
|
231
|
+
+ (root.resolution ? '\nProducers: ' + (root.resolution.producerNodeIds.join(', ') || 'none') + '\nInitial contents: ' + root.resolution.usesInitialContents : '\nOutput sources unavailable in Legacy capture.'),
|
|
232
|
+
});
|
|
233
|
+
registerElement(interaction, id, { kind: 'root', key: root.key }, emptyReferences({ resourceIds: [resource.id], nodeIds: root.resolution?.producerNodeIds ?? [] }));
|
|
234
|
+
for (const nodeId of root.resolution?.producerNodeIds ?? []) {
|
|
235
|
+
addRelation(representativeByNodeId.get(nodeId), id, resource.id, { role: 'output-producer', nodeIds: [nodeId], rootKey: root.key });
|
|
274
236
|
}
|
|
237
|
+
if (root.resolution?.usesInitialContents)
|
|
238
|
+
addRelation(representativeByResourceId.get(resource.id), id, resource.id, { role: 'output-initial', nodeIds: [], rootKey: root.key });
|
|
275
239
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
240
|
+
const edges = [...aggregates.entries()].map(([key, entry]) => {
|
|
241
|
+
const underlyingDependencies = entry.relations.flatMap((relation) => relation.dependency ? [relation.dependency] : []);
|
|
242
|
+
const edge = {
|
|
243
|
+
id: 'flow:' + key, ...entry,
|
|
244
|
+
kind: entry.relations.every((relation) => relation.role === 'ordering') ? 'ordering' : 'flow',
|
|
245
|
+
title: labelResource(snapshot.resourceById.get(entry.resourceId)) + '\n'
|
|
246
|
+
+ unique(entry.relations.map((relation) => relation.role)).join(' · '),
|
|
247
|
+
underlyingDependencies, underlyingDependencyCount: underlyingDependencies.length,
|
|
248
|
+
};
|
|
249
|
+
const selection = { kind: 'resource', id: entry.resourceId };
|
|
250
|
+
registerElement(interaction, edge.id, selection, emptyReferences({
|
|
251
|
+
nodeIds: unique(entry.relations.flatMap((relation) => relation.nodeIds)), resourceIds: [entry.resourceId], dependencies: underlyingDependencies,
|
|
252
|
+
}));
|
|
253
|
+
const resourceElements = interaction.resourceElementIdsByResourceId.get(entry.resourceId) ?? [];
|
|
254
|
+
resourceElements.push(edge.id);
|
|
255
|
+
interaction.resourceElementIdsByResourceId.set(entry.resourceId, resourceElements);
|
|
256
|
+
return edge;
|
|
257
|
+
});
|
|
258
|
+
for (const [resourceId, elements] of interaction.resourceElementIdsByResourceId) {
|
|
259
|
+
const selection = { kind: 'resource', id: resourceId };
|
|
260
|
+
setSelectionElements(interaction, selection, elements);
|
|
261
|
+
interaction.hoverElementIdsBySelection.set(selectionKey(selection), [...elements]);
|
|
262
|
+
}
|
|
263
|
+
return finalizeScene([...groupNodes, ...passNodes, ...resourceNodes, ...rootNodes], edges, interaction);
|
|
293
264
|
}
|
|
294
|
-
function finalizeScene(
|
|
265
|
+
function finalizeScene(nodes, edges, interaction) {
|
|
295
266
|
const topologyKey = JSON.stringify({
|
|
296
|
-
mode,
|
|
297
267
|
nodes: nodes.map((node) => [node.id, node.kind, node.parentId, node.kind === 'group' ? node.collapsed : undefined]),
|
|
298
268
|
edges: edges.map((edge) => [edge.id, edge.kind, edge.from, edge.to]),
|
|
299
269
|
});
|
|
300
270
|
const contentKey = JSON.stringify({
|
|
301
|
-
mode,
|
|
302
271
|
nodes: nodes.map((node) => [
|
|
303
272
|
node.id,
|
|
304
273
|
node.kind,
|
|
@@ -306,8 +275,8 @@ function finalizeScene(mode, nodes, edges, interaction) {
|
|
|
306
275
|
node.label,
|
|
307
276
|
node.overviewLabel,
|
|
308
277
|
node.title,
|
|
309
|
-
node.kind === 'pass'
|
|
310
|
-
node.kind === 'resource' ? node.resourceKind : undefined,
|
|
278
|
+
node.kind === 'pass' ? node.passKind : undefined,
|
|
279
|
+
(node.kind === 'resource' || node.kind === 'root') ? node.resourceKind : undefined,
|
|
311
280
|
node.kind === 'group' ? node.collapsed : undefined,
|
|
312
281
|
node.kind === 'group' ? node.depthBand : undefined,
|
|
313
282
|
node.kind === 'group' ? node.culledNodeCount > 0 : undefined,
|
|
@@ -317,15 +286,11 @@ function finalizeScene(mode, nodes, edges, interaction) {
|
|
|
317
286
|
edge.kind,
|
|
318
287
|
edge.from,
|
|
319
288
|
edge.to,
|
|
320
|
-
edge.label,
|
|
321
289
|
edge.title,
|
|
322
|
-
edge.
|
|
323
|
-
edge.kind === 'access' ? edge.accessMode : undefined,
|
|
324
|
-
edge.kind === 'access' ? edge.dashed : undefined,
|
|
290
|
+
edge.relations,
|
|
325
291
|
]),
|
|
326
292
|
});
|
|
327
293
|
return {
|
|
328
|
-
mode,
|
|
329
294
|
nodes,
|
|
330
295
|
edges,
|
|
331
296
|
topologyKey,
|
|
@@ -337,25 +302,19 @@ function createMutableInteractionIndex() {
|
|
|
337
302
|
return {
|
|
338
303
|
selectionByElementId: new Map(),
|
|
339
304
|
primaryElementIdsBySelection: new Map(),
|
|
340
|
-
|
|
305
|
+
hoverElementIdsBySelection: new Map(),
|
|
306
|
+
resourceElementIdsByResourceId: new Map(),
|
|
341
307
|
semanticReferencesByElementId: new Map(),
|
|
342
308
|
};
|
|
343
309
|
}
|
|
344
310
|
function registerElement(interaction, elementId, selection, references) {
|
|
345
311
|
interaction.selectionByElementId.set(elementId, selection);
|
|
346
|
-
setSelectionElements(interaction, selection, [elementId]
|
|
312
|
+
setSelectionElements(interaction, selection, [elementId]);
|
|
313
|
+
interaction.hoverElementIdsBySelection.set(selectionKey(selection), [elementId]);
|
|
347
314
|
interaction.semanticReferencesByElementId.set(elementId, references);
|
|
348
315
|
}
|
|
349
|
-
function setSelectionElements(interaction, selection, primary
|
|
316
|
+
function setSelectionElements(interaction, selection, primary) {
|
|
350
317
|
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
318
|
}
|
|
360
319
|
function emptyReferences(overrides) {
|
|
361
320
|
return {
|
|
@@ -370,20 +329,28 @@ function emptyReferences(overrides) {
|
|
|
370
329
|
function passElementId(nodeId) {
|
|
371
330
|
return `pass:${nodeId}`;
|
|
372
331
|
}
|
|
373
|
-
function culledPassElementId(index, nodeId) {
|
|
374
|
-
return `culled-pass:${index}:${nodeId}`;
|
|
375
|
-
}
|
|
376
332
|
function resourceElementId(resourceId) {
|
|
377
333
|
return `resource:${resourceId}`;
|
|
378
334
|
}
|
|
379
|
-
function accessElementId(accessId, resourceId) {
|
|
380
|
-
return `access:${accessId}:${resourceId}`;
|
|
381
|
-
}
|
|
382
335
|
function formatGraphNodeLabel(label) {
|
|
383
336
|
return formatGraphLabel(label, 20, 3);
|
|
384
337
|
}
|
|
385
338
|
function formatGraphResourceLabel(label) {
|
|
386
|
-
|
|
339
|
+
const singleLine = label.replace(/\s+/g, ' ').trim();
|
|
340
|
+
// Reserve conservative monospace cells for CJK/emoji fallback glyphs as well.
|
|
341
|
+
const glyphs = [...singleLine];
|
|
342
|
+
const cells = (glyph) => /\p{Mark}/u.test(glyph) ? 0 : glyph.codePointAt(0) > 255 ? 2 : 1;
|
|
343
|
+
if (glyphs.reduce((sum, glyph) => sum + cells(glyph), 0) <= 18)
|
|
344
|
+
return singleLine;
|
|
345
|
+
let result = '';
|
|
346
|
+
let width = 0;
|
|
347
|
+
for (const glyph of glyphs) {
|
|
348
|
+
if (width + cells(glyph) > 17)
|
|
349
|
+
break;
|
|
350
|
+
result += glyph;
|
|
351
|
+
width += cells(glyph);
|
|
352
|
+
}
|
|
353
|
+
return result + '…';
|
|
387
354
|
}
|
|
388
355
|
function formatGraphLabel(label, maxLineLength, maxLines) {
|
|
389
356
|
if (label.length <= maxLineLength)
|
|
@@ -499,15 +466,6 @@ function createGroupTitle(group) {
|
|
|
499
466
|
`Σ GPU work: ${(summary.gpuWorkDurationMicros / 1000).toFixed(3)} ms (${summary.timedNodeCount}/${summary.timingEligibleNodeCount})`,
|
|
500
467
|
].join('\n');
|
|
501
468
|
}
|
|
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
469
|
function createResourceTitle(resource, snapshot, accesses) {
|
|
512
470
|
return [
|
|
513
471
|
labelResource(resource),
|
|
@@ -520,18 +478,6 @@ function createResourceTitle(resource, snapshot, accesses) {
|
|
|
520
478
|
`writes: ${accesses.filter((access) => access.mode === 'write').length}`,
|
|
521
479
|
].join('\n');
|
|
522
480
|
}
|
|
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
481
|
function debugGroupPathForId(groupId, snapshot) {
|
|
536
482
|
if (groupId === undefined)
|
|
537
483
|
return '-';
|