@zenfg/inspector 0.1.0-beta.3 → 0.1.0
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 +61 -89
- package/THEMING.md +124 -0
- package/dist/FrameGraphInspector.d.ts +23 -4
- package/dist/FrameGraphInspector.d.ts.map +1 -1
- package/dist/FrameGraphInspector.js +137 -29
- 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 +19 -0
- package/dist/debugCaptureModel.d.ts.map +1 -1
- package/dist/debugCaptureModel.js +36 -2
- 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 +15 -2
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -1
- package/dist/panelCytoscapeGraphRenderer.js +145 -23
- 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 +166 -57
- 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 +6 -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 +1 -0
- package/dist/panelGraphScene.d.ts.map +1 -1
- package/dist/panelGraphScene.js +69 -32
- 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 +15 -4
- package/dist/panelGraphView.js.map +1 -1
- package/dist/panelGraphVisuals.d.ts +14 -6
- package/dist/panelGraphVisuals.d.ts.map +1 -1
- package/dist/panelGraphVisuals.js +126 -51
- package/dist/panelGraphVisuals.js.map +1 -1
- package/dist/panelInspectorView.d.ts +8 -0
- package/dist/panelInspectorView.d.ts.map +1 -1
- package/dist/panelInspectorView.js +242 -48
- 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 +34 -4
- 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 +10 -1
- package/dist/panelTypes.d.ts.map +1 -1
- package/dist/panelTypes.js.map +1 -1
- package/dist/panelVisualTheme.d.ts +147 -50
- package/dist/panelVisualTheme.d.ts.map +1 -1
- package/dist/panelVisualTheme.js +34 -44
- 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 +62 -1
- 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 +147 -195
- 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 +130 -31
- package/src/capture.ts +4 -0
- package/src/debugCaptureModel.ts +53 -2
- package/src/index.ts +6 -2
- package/src/panelAliasAnalysis.ts +24 -70
- package/src/panelCytoscapeGraphRenderer.ts +140 -23
- package/src/panelDetailLayout.ts +155 -0
- package/src/panelDetailStyles.ts +56 -0
- package/src/panelDiagnosticsView.ts +166 -85
- package/src/panelDomHelpers.ts +4 -5
- package/src/panelGraphLayout.ts +6 -5
- package/src/panelGraphRenderer.ts +7 -0
- package/src/panelGraphScene.ts +68 -30
- package/src/panelGraphSearch.ts +96 -0
- package/src/panelGraphView.ts +14 -4
- package/src/panelGraphVisuals.ts +122 -52
- package/src/panelInspectorView.ts +227 -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 +37 -5
- package/src/panelTheme.ts +85 -0
- package/src/panelTypes.ts +7 -1
- package/src/panelVisualTheme.ts +38 -49
- package/src/panelWorkbenchHelpers.ts +60 -2
- package/src/panelWorkbenchStyles.ts +100 -0
- package/src/panelWorkbenchView.ts +201 -52
- package/src/query.ts +11 -11
- package/src/styles.ts +148 -195
- package/src/theme.ts +24 -0
- package/src/themeDefinitions.ts +103 -0
- package/src/themePalette.ts +44 -0
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
import { labelNode, labelResource } from "./panelDomHelpers.js";
|
|
2
2
|
import { createPanelIcon } from "./panelIcons.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { RawDetailView } from "./panelRawView.js";
|
|
4
|
+
import { resolveNodeSelection, resolveSelectedCanonicalDetail } from "./panelSelection.js";
|
|
5
|
+
import { createRelationButton, formatEstimatedBytes, formatResourceDescriptor, formatTimingCoverage, enableTabKeyboard, writeClipboardText, selectionKey, groupPath, resourceLabel, } from "./panelWorkbenchHelpers.js";
|
|
5
6
|
function formatAccessFacts(access) {
|
|
6
7
|
return [
|
|
7
8
|
access.mode, access.access,
|
|
8
|
-
...(access.mode === 'write' ? [access.contents,
|
|
9
|
+
...(access.mode === 'write' ? [access.contents, access.producesValue ? 'produces a value' : 'does not produce a value'] : []),
|
|
9
10
|
...(access.bufferRange ? [`bytes ${access.bufferRange.offset}–${access.bufferRange.size === undefined ? 'end' : access.bufferRange.offset + access.bufferRange.size}`] : []),
|
|
10
|
-
...(access.textureRegion ? [
|
|
11
|
+
...(access.textureRegion ? [formatTextureRegion(access.textureRegion)] : []),
|
|
11
12
|
...(access.textureViewId ? [`view ${access.textureViewId}`] : []),
|
|
12
13
|
].join(' · ');
|
|
13
14
|
}
|
|
15
|
+
function formatTextureRegion(region) {
|
|
16
|
+
const end = (start, count) => start === undefined ? 'unknown' : count === undefined ? `${start}–end` : `${start}–${start + count - 1}`;
|
|
17
|
+
return `mip ${end(region.baseMipLevel, region.mipLevelCount)} · ${region.baseDepthSlice === undefined
|
|
18
|
+
? `layers ${end(region.baseArrayLayer, region.arrayLayerCount)}`
|
|
19
|
+
: `depth ${end(region.baseDepthSlice, region.depthSliceCount)}`} · aspect ${region.aspect}`;
|
|
20
|
+
}
|
|
14
21
|
function formatRootRange(root) {
|
|
15
22
|
if (!root.range)
|
|
16
23
|
return 'Range unavailable';
|
|
17
24
|
if (root.range.kind === 'buffer')
|
|
18
25
|
return `bytes ${root.range.offset}–${root.range.offset + root.range.size}`;
|
|
19
|
-
return root.range.regions.map(
|
|
20
|
-
? `layers ${region.baseArrayLayer}+${region.arrayLayerCount}`
|
|
21
|
-
: `depth ${region.baseDepthSlice}+${region.depthSliceCount}`} · ${region.aspect}`).join('; ');
|
|
26
|
+
return root.range.regions.map(formatTextureRegion).join('; ');
|
|
22
27
|
}
|
|
23
28
|
export class InspectorView {
|
|
24
29
|
callbacks;
|
|
@@ -31,8 +36,9 @@ export class InspectorView {
|
|
|
31
36
|
snapshot;
|
|
32
37
|
selected;
|
|
33
38
|
activeTab = 'summary';
|
|
34
|
-
open =
|
|
39
|
+
open = false;
|
|
35
40
|
hoveredLink;
|
|
41
|
+
rawCache;
|
|
36
42
|
constructor(callbacks, onOpenChange, idPrefix) {
|
|
37
43
|
this.callbacks = callbacks;
|
|
38
44
|
this.onOpenChange = onOpenChange;
|
|
@@ -65,6 +71,7 @@ export class InspectorView {
|
|
|
65
71
|
this.tabs.set(tab, button);
|
|
66
72
|
this.tabList.appendChild(button);
|
|
67
73
|
}
|
|
74
|
+
enableTabKeyboard(this.tabList);
|
|
68
75
|
this.content.id = `${idPrefix}-inspector-panel`;
|
|
69
76
|
this.content.className = 'zenfg-inspector-inspector-content';
|
|
70
77
|
this.content.setAttribute('role', 'tabpanel');
|
|
@@ -73,16 +80,24 @@ export class InspectorView {
|
|
|
73
80
|
this.render();
|
|
74
81
|
}
|
|
75
82
|
setSnapshot(snapshot) {
|
|
83
|
+
if (this.snapshot !== snapshot)
|
|
84
|
+
this.rawCache = undefined;
|
|
76
85
|
this.snapshot = snapshot;
|
|
77
86
|
this.render();
|
|
78
87
|
}
|
|
79
88
|
setSelection(selected, reveal = true) {
|
|
89
|
+
if (!selected || !this.selected || selectionKey(selected) !== selectionKey(this.selected))
|
|
90
|
+
this.rawCache = undefined;
|
|
80
91
|
if (selected?.kind === 'resource'
|
|
81
92
|
&& (this.selected?.kind !== 'resource' || this.selected.id !== selected.id))
|
|
82
93
|
this.activeTab = 'summary';
|
|
83
94
|
this.selected = selected;
|
|
84
|
-
if (selected
|
|
95
|
+
if (!selected)
|
|
96
|
+
this.setOpen(false);
|
|
97
|
+
else if (reveal && !this.open) {
|
|
85
98
|
this.setOpen(true);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
86
101
|
this.render();
|
|
87
102
|
}
|
|
88
103
|
setOpen(open) {
|
|
@@ -92,6 +107,8 @@ export class InspectorView {
|
|
|
92
107
|
this.clearLinkHover();
|
|
93
108
|
this.open = open;
|
|
94
109
|
this.updateOpenState();
|
|
110
|
+
if (open)
|
|
111
|
+
this.render();
|
|
95
112
|
this.onOpenChange(open);
|
|
96
113
|
}
|
|
97
114
|
get isOpen() {
|
|
@@ -102,11 +119,13 @@ export class InspectorView {
|
|
|
102
119
|
this.root.classList.toggle('open', this.open);
|
|
103
120
|
}
|
|
104
121
|
render() {
|
|
122
|
+
const restoreFocus = this.content.contains(document.activeElement);
|
|
105
123
|
this.clearLinkHover();
|
|
106
124
|
for (const [tab, button] of this.tabs) {
|
|
107
125
|
const active = tab === this.activeTab;
|
|
108
126
|
button.classList.toggle('active', active);
|
|
109
127
|
button.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
128
|
+
button.tabIndex = active ? 0 : -1;
|
|
110
129
|
if (active)
|
|
111
130
|
this.content.setAttribute('aria-labelledby', button.id);
|
|
112
131
|
}
|
|
@@ -122,33 +141,59 @@ export class InspectorView {
|
|
|
122
141
|
return;
|
|
123
142
|
}
|
|
124
143
|
this.title.textContent = this.selectionTitle(snapshot, selected);
|
|
144
|
+
if (!this.open)
|
|
145
|
+
return;
|
|
125
146
|
if (this.activeTab === 'raw') {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
147
|
+
const key = selectionKey(selected);
|
|
148
|
+
if (this.rawCache?.snapshot !== snapshot || this.rawCache.key !== key) {
|
|
149
|
+
const detail = resolveSelectedCanonicalDetail(snapshot, selected);
|
|
150
|
+
this.rawCache = detail ? { snapshot, key, view: new RawDetailView(detail, Boolean(snapshot.protocol.capture.migration)) } : undefined;
|
|
151
|
+
}
|
|
152
|
+
if (this.rawCache)
|
|
153
|
+
this.content.appendChild(this.rawCache.view.root);
|
|
154
|
+
else
|
|
155
|
+
this.content.textContent = 'Canonical object unavailable in this capture.';
|
|
130
156
|
}
|
|
131
157
|
else if (this.activeTab === 'relations') {
|
|
132
158
|
this.content.appendChild(this.createRelations(snapshot, selected));
|
|
133
159
|
}
|
|
134
160
|
else {
|
|
135
|
-
this.content.
|
|
161
|
+
this.content.append(this.createLocationActions(snapshot, selected), this.createSummary(snapshot, selected));
|
|
162
|
+
const identity = this.selectionId(snapshot, selected);
|
|
163
|
+
if (identity)
|
|
164
|
+
this.content.appendChild(this.summary([['ID', this.copyId(identity)]]));
|
|
165
|
+
const diagnostics = this.createDiagnostics(snapshot, selected);
|
|
166
|
+
if (diagnostics)
|
|
167
|
+
this.content.appendChild(diagnostics);
|
|
168
|
+
if (selected.kind === 'node' || selected.kind === 'culled') {
|
|
169
|
+
const node = snapshot.nodeById.get(selected.id) ?? snapshot.culledById.get(selected.id)?.node;
|
|
170
|
+
if (node) {
|
|
171
|
+
const accesses = document.createElement('details');
|
|
172
|
+
const heading = document.createElement('summary');
|
|
173
|
+
heading.textContent = `Access facts · ${node.reads.length + node.writes.length}`;
|
|
174
|
+
accesses.append(heading, this.accessRelations('Reads', node.reads), this.accessRelations('Writes', node.writes));
|
|
175
|
+
this.content.appendChild(accesses);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
136
178
|
}
|
|
179
|
+
// Selecting a relation replaces its button. Keep keyboard focus in the pane.
|
|
180
|
+
if (restoreFocus)
|
|
181
|
+
this.tabs.get(this.activeTab)?.focus();
|
|
137
182
|
}
|
|
138
183
|
selectionTitle(snapshot, selection) {
|
|
139
184
|
switch (selection.kind) {
|
|
140
185
|
case 'node': {
|
|
141
186
|
const node = snapshot.nodeById.get(selection.id);
|
|
142
|
-
return node ? labelNode(node) :
|
|
187
|
+
return node ? labelNode(node) : selection.id;
|
|
143
188
|
}
|
|
144
189
|
case 'group': return snapshot.groupByPathKey.get(selection.pathKey)?.label ?? 'Group';
|
|
145
190
|
case 'resource': {
|
|
146
191
|
const resource = snapshot.resourceById.get(selection.id);
|
|
147
|
-
return resource ? labelResource(resource) :
|
|
192
|
+
return resource ? labelResource(resource) : selection.id;
|
|
148
193
|
}
|
|
149
|
-
case 'allocation': return `Allocation
|
|
194
|
+
case 'allocation': return /^allocation[:#-]/i.test(selection.id) ? selection.id : `Allocation ${selection.id}`;
|
|
150
195
|
case 'root': return 'Output root';
|
|
151
|
-
case 'culled': return
|
|
196
|
+
case 'culled': return `${snapshot.culledById.get(selection.id)?.node.label ?? selection.id} (culled)`;
|
|
152
197
|
case 'segment': return `Segment #${selection.index}`;
|
|
153
198
|
}
|
|
154
199
|
}
|
|
@@ -159,12 +204,21 @@ export class InspectorView {
|
|
|
159
204
|
if (!node)
|
|
160
205
|
return this.summary([]);
|
|
161
206
|
const segment = snapshot.segmentByNodeId.get(node.id);
|
|
207
|
+
const roots = snapshot.roots.filter((root) => root.nodeId === node.id || root.resolution?.producerNodeIds.includes(node.id));
|
|
162
208
|
return this.summary([
|
|
209
|
+
['Compile state', 'Retained · compiler included this pass; execution success is not implied'],
|
|
210
|
+
['Retention', roots.length ? roots.map((root) => root.reason).join(', ') : 'No direct output root recorded; inspect dependencies in Relations'],
|
|
163
211
|
['Kind', node.kind],
|
|
164
|
-
['
|
|
212
|
+
['Execution slot', String(node.order)],
|
|
165
213
|
['Group', groupPath(snapshot, node.debugGroupId)],
|
|
166
214
|
['Segment', segment ? `#${segment.index} ${segment.kind}` : '-'],
|
|
167
|
-
['
|
|
215
|
+
['CPU duration', node.cpuDurationMicros === undefined ? 'Not collected' : (node.cpuDurationMicros / 1000).toFixed(3) + ' ms'],
|
|
216
|
+
['CPU scope', 'Synchronous elapsed time; zero may reflect clock precision.' + (node.kind === 'external-submission' ? ' Includes synchronous external submission.' : '')],
|
|
217
|
+
['GPU duration', node.kind === 'external-submission' ? 'Opaque · external work is not measured'
|
|
218
|
+
: node.kind !== 'render' && node.kind !== 'compute' ? 'Not applicable · this pass kind is not timing eligible'
|
|
219
|
+
: node.gpuDurationMicros === undefined ? `Not collected${snapshot.profiling.status === 'unavailable' ? ` · ${snapshot.profiling.reason}` : ''}`
|
|
220
|
+
: `${(node.gpuDurationMicros / 1000).toFixed(3)} ms`],
|
|
221
|
+
['GPU scope', 'GPU timestamp interval for render/compute passes; excludes external work. Zero may reflect timestamp precision.'],
|
|
168
222
|
['Accesses', `${node.reads.length} reads · ${node.writes.length} writes`],
|
|
169
223
|
['Side effect', node.sideEffect ? 'yes' : 'no'],
|
|
170
224
|
]);
|
|
@@ -177,9 +231,12 @@ export class InspectorView {
|
|
|
177
231
|
['Path', group.path.join(' / ')],
|
|
178
232
|
['Retained', String(group.summary.retainedNodeCount)],
|
|
179
233
|
['Culled', String(group.summary.culledNodeCount)],
|
|
180
|
-
['
|
|
181
|
-
['
|
|
182
|
-
['
|
|
234
|
+
['CPU coverage', formatTimingCoverage(group.summary.cpuTimedNodeCount, group.summary.retainedNodeCount)],
|
|
235
|
+
['CPU pass sum', group.summary.cpuTimedNodeCount ? (group.summary.cpuWorkDurationMicros / 1000).toFixed(3) + ' ms' : 'Not collected'],
|
|
236
|
+
['GPU coverage', formatTimingCoverage(group.summary.timedNodeCount, group.summary.timingEligibleNodeCount)],
|
|
237
|
+
['GPU pass sum', group.summary.timingEligibleNodeCount === 0 ? 'Not applicable' : group.summary.timedNodeCount === 0 ? 'Not collected' : (group.summary.gpuWorkDurationMicros / 1000).toFixed(3) + ' ms'],
|
|
238
|
+
['Opaque passes', `${group.summary.externalSubmissionCount} · excluded from GPU pass sum`],
|
|
239
|
+
['Allocations', snapshot.protocol.memory.allocationReport.status === 'available' ? String(group.summary.physicalAllocationCount) : 'Unknown · allocation report unavailable'],
|
|
183
240
|
['Segments', String(group.summary.executionSegmentCount)],
|
|
184
241
|
]);
|
|
185
242
|
}
|
|
@@ -191,9 +248,14 @@ export class InspectorView {
|
|
|
191
248
|
['Kind / origin', `${resource.kind} · ${resource.origin}`],
|
|
192
249
|
['Group', groupPath(snapshot, resource.debugGroupId)],
|
|
193
250
|
['Descriptor', formatResourceDescriptor(resource)],
|
|
194
|
-
['
|
|
195
|
-
['Lifetime', resource.lifetime ?
|
|
196
|
-
['Allocation', resource.physicalResourceId
|
|
251
|
+
['Resource estimate', `${formatEstimatedBytes(resource.estimatedByteSize)} · logical descriptor estimate`],
|
|
252
|
+
['Lifetime', resource.lifetime ? `execution slots ${resource.lifetime.firstUse}–${resource.lifetime.lastUse} (inclusive)` : 'Unavailable'],
|
|
253
|
+
['Allocation', resource.physicalResourceId !== undefined
|
|
254
|
+
? this.createDetailLink(resource.physicalResourceId, { kind: 'allocation', id: resource.physicalResourceId })
|
|
255
|
+
: resource.origin !== 'transient' ? 'Not applicable · externally owned resource'
|
|
256
|
+
: snapshot.protocol.memory.allocationReport.status === 'unavailable' ? 'Unavailable · allocation report not captured'
|
|
257
|
+
: 'Unallocated'],
|
|
258
|
+
['Initial contents', resource.initialContents ?? 'Unknown'],
|
|
197
259
|
['Usage', resource.usageFlags.join(', ') || '-'],
|
|
198
260
|
]);
|
|
199
261
|
}
|
|
@@ -204,7 +266,7 @@ export class InspectorView {
|
|
|
204
266
|
return this.summary([
|
|
205
267
|
['Kind', allocation.kind],
|
|
206
268
|
['Compatibility class', String(allocation.compatibilityClassId)],
|
|
207
|
-
['
|
|
269
|
+
['Physical estimate', `${formatEstimatedBytes(allocation.estimatedByteSize)} · allocation capacity estimate`],
|
|
208
270
|
['Logical resources', String(allocation.resourceIds.length)],
|
|
209
271
|
['Alias', allocation.resourceIds.length > 1 ? `yes · ×${allocation.resourceIds.length}` : 'single'],
|
|
210
272
|
]);
|
|
@@ -213,22 +275,24 @@ export class InspectorView {
|
|
|
213
275
|
const root = snapshot.roots.find((root) => root.key === selection.key);
|
|
214
276
|
return this.summary(root ? [
|
|
215
277
|
['Reason', root.reason],
|
|
216
|
-
['Node', root.nodeId === undefined ? '
|
|
278
|
+
['Node', root.nodeId === undefined ? 'Not applicable' : this.nodeLink(snapshot, root.nodeId)],
|
|
217
279
|
['Resource', root.resource ? this.viewResourceButton(snapshot, root.resource.id) : '-'],
|
|
218
280
|
...(root.reason === 'side-effect' ? [] : [
|
|
219
|
-
['Range', root.range ?
|
|
281
|
+
['Range', root.range ? formatRootRange(root) : 'Unavailable in Legacy capture'],
|
|
220
282
|
['Initial contents', root.resolution ? String(root.resolution.usesInitialContents) : 'Unavailable in Legacy capture'],
|
|
221
283
|
['Producers', root.resolution ? root.resolution.producerNodeIds.join(', ') || 'None' : 'Unavailable in Legacy capture'],
|
|
222
284
|
]),
|
|
223
285
|
] : []);
|
|
224
286
|
}
|
|
225
287
|
case 'culled': {
|
|
226
|
-
const culled = snapshot.
|
|
288
|
+
const culled = snapshot.culledById.get(selection.id);
|
|
227
289
|
return this.summary(culled ? [
|
|
290
|
+
['Compile state', 'Culled · excluded by the compiler'],
|
|
228
291
|
['Node', labelNode(culled.node)],
|
|
229
292
|
['Kind', culled.node.kind],
|
|
230
293
|
['Group', groupPath(snapshot, culled.node.debugGroupId)],
|
|
231
294
|
['Reason', culled.reason],
|
|
295
|
+
['Execution / segment / CPU / GPU', 'Not applicable · culled passes do not execute'],
|
|
232
296
|
['Accesses', `${culled.node.reads.length} reads · ${culled.node.writes.length} writes`],
|
|
233
297
|
] : []);
|
|
234
298
|
}
|
|
@@ -247,6 +311,7 @@ export class InspectorView {
|
|
|
247
311
|
createRelations(snapshot, selection) {
|
|
248
312
|
const host = document.createElement('div');
|
|
249
313
|
host.className = 'zenfg-inspector-inspector-relations';
|
|
314
|
+
host.appendChild(this.createLocationActions(snapshot, selection));
|
|
250
315
|
switch (selection.kind) {
|
|
251
316
|
case 'node': {
|
|
252
317
|
const node = snapshot.nodeById.get(selection.id);
|
|
@@ -257,6 +322,21 @@ export class InspectorView {
|
|
|
257
322
|
host.appendChild(this.relationGroup('Segment', [
|
|
258
323
|
[`#${segment.index} ${segment.kind}`, { kind: 'segment', index: segment.index }],
|
|
259
324
|
]));
|
|
325
|
+
for (const kind of ['value', 'ordering']) {
|
|
326
|
+
const relations = [];
|
|
327
|
+
for (const edge of snapshot.edges) {
|
|
328
|
+
if (edge.kind !== kind || (edge.fromNodeId !== node.id && edge.toNodeId !== node.id))
|
|
329
|
+
continue;
|
|
330
|
+
const upstream = edge.toNodeId === node.id;
|
|
331
|
+
const otherId = upstream ? edge.fromNodeId : edge.toNodeId;
|
|
332
|
+
const other = resolveNodeSelection(snapshot, otherId);
|
|
333
|
+
if (other)
|
|
334
|
+
relations.push([this.accessNodeLabel(snapshot, otherId), other,
|
|
335
|
+
`${upstream ? 'Upstream' : 'Downstream'} · ${resourceLabel(snapshot, edge.resource.id)} · ${kind} dependency`]);
|
|
336
|
+
}
|
|
337
|
+
if (relations.length)
|
|
338
|
+
host.appendChild(this.relationGroup(kind === 'value' ? 'Value dependencies' : 'Ordering dependencies', relations));
|
|
339
|
+
}
|
|
260
340
|
}
|
|
261
341
|
break;
|
|
262
342
|
}
|
|
@@ -264,7 +344,7 @@ export class InspectorView {
|
|
|
264
344
|
const group = snapshot.groupByPathKey.get(selection.pathKey);
|
|
265
345
|
if (group) {
|
|
266
346
|
host.append(this.resourceRelations('Inputs', group.summary.inputResources), this.resourceRelations('Outputs', group.summary.outputResources), this.relationGroup('Output roots', group.summary.outputRoots.map((root) => [
|
|
267
|
-
`${root.resource ? labelResource(root.resource) : '-'} · ${root.reason} · ${
|
|
347
|
+
`${root.resource ? labelResource(root.resource) : '-'} · ${root.reason} · ${formatRootRange(root)}`,
|
|
268
348
|
{ kind: 'root', key: root.key },
|
|
269
349
|
])));
|
|
270
350
|
}
|
|
@@ -291,7 +371,7 @@ export class InspectorView {
|
|
|
291
371
|
])));
|
|
292
372
|
if (resource?.physicalResourceId !== undefined)
|
|
293
373
|
host.appendChild(this.relationGroup('Allocation', [
|
|
294
|
-
[
|
|
374
|
+
[resource.physicalResourceId, { kind: 'allocation', id: resource.physicalResourceId }],
|
|
295
375
|
]));
|
|
296
376
|
break;
|
|
297
377
|
}
|
|
@@ -320,7 +400,7 @@ export class InspectorView {
|
|
|
320
400
|
break;
|
|
321
401
|
}
|
|
322
402
|
case 'culled': {
|
|
323
|
-
const culled = snapshot.
|
|
403
|
+
const culled = snapshot.culledById.get(selection.id);
|
|
324
404
|
if (culled)
|
|
325
405
|
host.append(this.accessRelations('Reads', culled.node.reads), this.accessRelations('Writes', culled.node.writes));
|
|
326
406
|
break;
|
|
@@ -335,7 +415,7 @@ export class InspectorView {
|
|
|
335
415
|
break;
|
|
336
416
|
}
|
|
337
417
|
}
|
|
338
|
-
if (host.childElementCount ===
|
|
418
|
+
if (host.childElementCount === 1) {
|
|
339
419
|
const empty = document.createElement('p');
|
|
340
420
|
empty.className = 'zenfg-inspector-muted';
|
|
341
421
|
empty.textContent = 'No related objects.';
|
|
@@ -362,18 +442,21 @@ export class InspectorView {
|
|
|
362
442
|
section.appendChild(heading);
|
|
363
443
|
for (const [label, selection, description] of relations) {
|
|
364
444
|
const link = this.createDetailLink(label, selection);
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
445
|
+
const entry = document.createElement('div');
|
|
446
|
+
entry.className = 'zenfg-inspector-relation-entry';
|
|
447
|
+
const links = document.createElement('div');
|
|
448
|
+
links.className = 'zenfg-inspector-relation-links';
|
|
449
|
+
links.appendChild(link);
|
|
450
|
+
if (this.callbacks.onReveal)
|
|
451
|
+
links.appendChild(this.revealButton(selection, this.primaryPage(selection)));
|
|
452
|
+
entry.appendChild(links);
|
|
453
|
+
if (description !== undefined) {
|
|
371
454
|
const metadata = document.createElement('span');
|
|
372
455
|
metadata.className = 'zenfg-inspector-muted';
|
|
373
456
|
metadata.textContent = description;
|
|
374
|
-
entry.
|
|
375
|
-
section.appendChild(entry);
|
|
457
|
+
entry.appendChild(metadata);
|
|
376
458
|
}
|
|
459
|
+
section.appendChild(entry);
|
|
377
460
|
}
|
|
378
461
|
if (relations.length === 0) {
|
|
379
462
|
const empty = document.createElement('span');
|
|
@@ -430,14 +513,125 @@ export class InspectorView {
|
|
|
430
513
|
const node = snapshot.nodeById.get(nodeId);
|
|
431
514
|
if (node)
|
|
432
515
|
return labelNode(node);
|
|
433
|
-
const culled = snapshot.
|
|
434
|
-
return culled ? `${labelNode(culled.node)} (culled)` :
|
|
516
|
+
const culled = snapshot.culledById.get(nodeId);
|
|
517
|
+
return culled ? `${labelNode(culled.node)} (culled)` : nodeId;
|
|
435
518
|
}
|
|
436
519
|
accessNodeSelection(snapshot, nodeId) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
520
|
+
return resolveNodeSelection(snapshot, nodeId);
|
|
521
|
+
}
|
|
522
|
+
nodeLink(snapshot, id) {
|
|
523
|
+
const selected = resolveNodeSelection(snapshot, id);
|
|
524
|
+
return selected ? this.createDetailLink(this.accessNodeLabel(snapshot, id), selected) : id;
|
|
525
|
+
}
|
|
526
|
+
primaryPage(selection) {
|
|
527
|
+
switch (selection.kind) {
|
|
528
|
+
case 'node':
|
|
529
|
+
case 'culled':
|
|
530
|
+
case 'group': return 'passes';
|
|
531
|
+
case 'resource': return 'resources';
|
|
532
|
+
case 'allocation': return 'memory';
|
|
533
|
+
case 'root':
|
|
534
|
+
case 'segment': return 'diagnostics';
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
revealButton(selection, page) {
|
|
538
|
+
const button = document.createElement('button');
|
|
539
|
+
button.type = 'button';
|
|
540
|
+
button.className = 'zenfg-inspector-inline-action';
|
|
541
|
+
button.textContent = `Locate in ${page[0].toUpperCase()}${page.slice(1)}`;
|
|
542
|
+
button.addEventListener('click', () => {
|
|
543
|
+
this.clearLinkHover();
|
|
544
|
+
this.callbacks.onReveal?.(selection, page);
|
|
545
|
+
});
|
|
546
|
+
return button;
|
|
547
|
+
}
|
|
548
|
+
createLocationActions(snapshot, selection) {
|
|
549
|
+
const actions = document.createElement('div');
|
|
550
|
+
actions.className = 'zenfg-inspector-detail-actions';
|
|
551
|
+
if (!this.callbacks.onReveal)
|
|
552
|
+
return actions;
|
|
553
|
+
actions.appendChild(this.revealButton(selection, this.primaryPage(selection)));
|
|
554
|
+
if (selection.kind === 'node' || selection.kind === 'group' || selection.kind === 'resource' || selection.kind === 'root') {
|
|
555
|
+
actions.appendChild(this.revealButton(selection, 'graph'));
|
|
556
|
+
}
|
|
557
|
+
if (selection.kind === 'resource' && snapshot.resourceById.get(selection.id)?.origin === 'transient')
|
|
558
|
+
actions.appendChild(this.revealButton(selection, 'memory'));
|
|
559
|
+
if (selection.kind === 'culled') {
|
|
560
|
+
const note = document.createElement('span');
|
|
561
|
+
note.className = 'zenfg-inspector-muted';
|
|
562
|
+
note.textContent = 'Culled passes are absent from Graph. Locate them in Passes.';
|
|
563
|
+
actions.appendChild(note);
|
|
564
|
+
}
|
|
565
|
+
return actions;
|
|
566
|
+
}
|
|
567
|
+
selectionId(snapshot, selection) {
|
|
568
|
+
if ('id' in selection)
|
|
569
|
+
return selection.id;
|
|
570
|
+
if (selection.kind === 'group')
|
|
571
|
+
return snapshot.groupByPathKey.get(selection.pathKey)?.id;
|
|
572
|
+
if (selection.kind === 'segment')
|
|
573
|
+
return snapshot.segmentByIndex.get(selection.index)?.id;
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
576
|
+
copyId(id) {
|
|
577
|
+
const host = document.createElement('span');
|
|
578
|
+
host.className = 'zenfg-inspector-copy-id';
|
|
579
|
+
const code = document.createElement('code');
|
|
580
|
+
code.textContent = id;
|
|
581
|
+
const button = document.createElement('button');
|
|
582
|
+
button.type = 'button';
|
|
583
|
+
button.textContent = 'Copy ID';
|
|
584
|
+
button.setAttribute('aria-label', 'Copy full object ID');
|
|
585
|
+
button.addEventListener('click', () => {
|
|
586
|
+
void writeClipboardText(id).then(() => { button.textContent = 'Copied'; }, () => { button.textContent = 'Copy failed'; });
|
|
587
|
+
});
|
|
588
|
+
host.append(code, button);
|
|
589
|
+
return host;
|
|
590
|
+
}
|
|
591
|
+
createDiagnostics(snapshot, selection) {
|
|
592
|
+
let diagnostics = [];
|
|
593
|
+
if (selection.kind === 'node' || selection.kind === 'culled')
|
|
594
|
+
diagnostics = snapshot.diagnosticsByNodeId.get(selection.id) ?? [];
|
|
595
|
+
else if (selection.kind === 'resource')
|
|
596
|
+
diagnostics = snapshot.diagnosticsByResourceId.get(selection.id) ?? [];
|
|
597
|
+
else if (selection.kind === 'allocation') {
|
|
598
|
+
const resourceIds = new Set(snapshot.allocationById.get(selection.id)?.resourceIds);
|
|
599
|
+
diagnostics = snapshot.protocol.diagnostics.filter((diagnostic) => diagnostic.resourceId !== undefined && resourceIds.has(diagnostic.resourceId));
|
|
600
|
+
}
|
|
601
|
+
else if (selection.kind === 'group') {
|
|
602
|
+
const groupId = snapshot.groupByPathKey.get(selection.pathKey)?.id;
|
|
603
|
+
const groups = new Set(snapshot.debugGroups.filter((group) => groupId !== undefined && group.ancestorIds.includes(groupId)).map((group) => group.id));
|
|
604
|
+
diagnostics = snapshot.protocol.diagnostics.filter((diagnostic) => {
|
|
605
|
+
const nodeGroup = diagnostic.nodeId ? snapshot.canonicalNodeById.get(diagnostic.nodeId)?.groupId : undefined;
|
|
606
|
+
const resourceGroup = diagnostic.resourceId ? snapshot.resourceById.get(diagnostic.resourceId)?.debugGroupId : undefined;
|
|
607
|
+
return (nodeGroup !== undefined && groups.has(nodeGroup)) || (resourceGroup !== undefined && groups.has(resourceGroup));
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
if (!diagnostics.length)
|
|
611
|
+
return undefined;
|
|
612
|
+
const section = document.createElement('section');
|
|
613
|
+
section.className = 'zenfg-inspector-detail-diagnostics';
|
|
614
|
+
const heading = document.createElement('h3');
|
|
615
|
+
heading.textContent = `Related diagnostics ${diagnostics.length}`;
|
|
616
|
+
section.appendChild(heading);
|
|
617
|
+
for (const diagnostic of diagnostics) {
|
|
618
|
+
const entry = document.createElement('article');
|
|
619
|
+
entry.dataset.severity = diagnostic.severity;
|
|
620
|
+
const label = document.createElement('strong');
|
|
621
|
+
label.textContent = `${diagnostic.severity} · ${diagnostic.code}`;
|
|
622
|
+
const message = document.createElement('p');
|
|
623
|
+
message.textContent = diagnostic.message;
|
|
624
|
+
entry.append(label, message);
|
|
625
|
+
if (diagnostic.nodeId) {
|
|
626
|
+
const node = resolveNodeSelection(snapshot, diagnostic.nodeId);
|
|
627
|
+
if (node)
|
|
628
|
+
entry.appendChild(this.createDetailLink(this.accessNodeLabel(snapshot, diagnostic.nodeId), node));
|
|
629
|
+
}
|
|
630
|
+
if (diagnostic.resourceId)
|
|
631
|
+
entry.appendChild(this.viewResourceButton(snapshot, diagnostic.resourceId));
|
|
632
|
+
section.appendChild(entry);
|
|
633
|
+
}
|
|
634
|
+
return section;
|
|
441
635
|
}
|
|
442
636
|
}
|
|
443
637
|
//# sourceMappingURL=panelInspectorView.js.map
|