@zenfg/inspector 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/FrameGraphInspector.d.ts +153 -0
- package/dist/FrameGraphInspector.d.ts.map +1 -0
- package/dist/FrameGraphInspector.js +580 -0
- package/dist/FrameGraphInspector.js.map +1 -0
- package/dist/debugCaptureModel.d.ts +166 -0
- package/dist/debugCaptureModel.d.ts.map +1 -0
- package/dist/debugCaptureModel.js +364 -0
- package/dist/debugCaptureModel.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/panelAliasAnalysis.d.ts +20 -0
- package/dist/panelAliasAnalysis.d.ts.map +1 -0
- package/dist/panelAliasAnalysis.js +113 -0
- package/dist/panelAliasAnalysis.js.map +1 -0
- package/dist/panelCytoscapeGraphRenderer.d.ts +71 -0
- package/dist/panelCytoscapeGraphRenderer.d.ts.map +1 -0
- package/dist/panelCytoscapeGraphRenderer.js +559 -0
- package/dist/panelCytoscapeGraphRenderer.js.map +1 -0
- package/dist/panelDiagnosticsView.d.ts +22 -0
- package/dist/panelDiagnosticsView.d.ts.map +1 -0
- package/dist/panelDiagnosticsView.js +123 -0
- package/dist/panelDiagnosticsView.js.map +1 -0
- package/dist/panelDomHelpers.d.ts +26 -0
- package/dist/panelDomHelpers.d.ts.map +1 -0
- package/dist/panelDomHelpers.js +129 -0
- package/dist/panelDomHelpers.js.map +1 -0
- package/dist/panelGraphLayout.d.ts +28 -0
- package/dist/panelGraphLayout.d.ts.map +1 -0
- package/dist/panelGraphLayout.js +223 -0
- package/dist/panelGraphLayout.js.map +1 -0
- package/dist/panelGraphRenderer.d.ts +20 -0
- package/dist/panelGraphRenderer.d.ts.map +1 -0
- package/dist/panelGraphRenderer.js +2 -0
- package/dist/panelGraphRenderer.js.map +1 -0
- package/dist/panelGraphScene.d.ts +98 -0
- package/dist/panelGraphScene.d.ts.map +1 -0
- package/dist/panelGraphScene.js +559 -0
- package/dist/panelGraphScene.js.map +1 -0
- package/dist/panelGraphView.d.ts +9 -0
- package/dist/panelGraphView.d.ts.map +1 -0
- package/dist/panelGraphView.js +88 -0
- package/dist/panelGraphView.js.map +1 -0
- package/dist/panelGraphVisuals.d.ts +38 -0
- package/dist/panelGraphVisuals.d.ts.map +1 -0
- package/dist/panelGraphVisuals.js +330 -0
- package/dist/panelGraphVisuals.js.map +1 -0
- package/dist/panelIcons.d.ts +4 -0
- package/dist/panelIcons.d.ts.map +1 -0
- package/dist/panelIcons.js +43 -0
- package/dist/panelIcons.js.map +1 -0
- package/dist/panelInspectorView.d.ts +33 -0
- package/dist/panelInspectorView.d.ts.map +1 -0
- package/dist/panelInspectorView.js +357 -0
- package/dist/panelInspectorView.js.map +1 -0
- package/dist/panelMemoryView.d.ts +21 -0
- package/dist/panelMemoryView.d.ts.map +1 -0
- package/dist/panelMemoryView.js +134 -0
- package/dist/panelMemoryView.js.map +1 -0
- package/dist/panelPassesView.d.ts +31 -0
- package/dist/panelPassesView.d.ts.map +1 -0
- package/dist/panelPassesView.js +183 -0
- package/dist/panelPassesView.js.map +1 -0
- package/dist/panelResourcesView.d.ts +19 -0
- package/dist/panelResourcesView.d.ts.map +1 -0
- package/dist/panelResourcesView.js +89 -0
- package/dist/panelResourcesView.js.map +1 -0
- package/dist/panelSelection.d.ts +5 -0
- package/dist/panelSelection.d.ts.map +1 -0
- package/dist/panelSelection.js +88 -0
- package/dist/panelSelection.js.map +1 -0
- package/dist/panelTypes.d.ts +40 -0
- package/dist/panelTypes.d.ts.map +1 -0
- package/dist/panelTypes.js +2 -0
- package/dist/panelTypes.js.map +1 -0
- package/dist/panelVisualTheme.d.ts +76 -0
- package/dist/panelVisualTheme.d.ts.map +1 -0
- package/dist/panelVisualTheme.js +39 -0
- package/dist/panelVisualTheme.js.map +1 -0
- package/dist/panelWorkbenchHelpers.d.ts +34 -0
- package/dist/panelWorkbenchHelpers.d.ts.map +1 -0
- package/dist/panelWorkbenchHelpers.js +162 -0
- package/dist/panelWorkbenchHelpers.js.map +1 -0
- package/dist/panelWorkbenchView.d.ts +77 -0
- package/dist/panelWorkbenchView.d.ts.map +1 -0
- package/dist/panelWorkbenchView.js +409 -0
- package/dist/panelWorkbenchView.js.map +1 -0
- package/dist/query.d.ts +11 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +21 -0
- package/dist/query.js.map +1 -0
- package/dist/styles.d.ts +8 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +1073 -0
- package/dist/styles.js.map +1 -0
- package/package.json +29 -0
- package/src/FrameGraphInspector.ts +632 -0
- package/src/debugCaptureModel.ts +583 -0
- package/src/index.ts +21 -0
- package/src/panelAliasAnalysis.ts +164 -0
- package/src/panelCytoscapeGraphRenderer.ts +617 -0
- package/src/panelDiagnosticsView.ts +160 -0
- package/src/panelDomHelpers.ts +151 -0
- package/src/panelGraphLayout.ts +281 -0
- package/src/panelGraphRenderer.ts +21 -0
- package/src/panelGraphScene.ts +787 -0
- package/src/panelGraphView.ts +108 -0
- package/src/panelGraphVisuals.ts +364 -0
- package/src/panelIcons.ts +56 -0
- package/src/panelInspectorView.ts +377 -0
- package/src/panelMemoryView.ts +154 -0
- package/src/panelPassesView.ts +222 -0
- package/src/panelResourcesView.ts +115 -0
- package/src/panelSelection.ts +99 -0
- package/src/panelTypes.ts +28 -0
- package/src/panelVisualTheme.ts +39 -0
- package/src/panelWorkbenchHelpers.ts +216 -0
- package/src/panelWorkbenchView.ts +465 -0
- package/src/query.ts +22 -0
- package/src/styles.ts +1076 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { formatBytes, formatGpuFrameDuration, formatPoolHitRate, labelNode, } from "./panelDomHelpers.js";
|
|
2
|
+
import { createPanelIcon, setPanelButtonContent } from "./panelIcons.js";
|
|
3
|
+
import { DiagnosticsView } from "./panelDiagnosticsView.js";
|
|
4
|
+
import { InspectorView } from "./panelInspectorView.js";
|
|
5
|
+
import { MemoryView } from "./panelMemoryView.js";
|
|
6
|
+
import { PassesView } from "./panelPassesView.js";
|
|
7
|
+
import { ResourcesView } from "./panelResourcesView.js";
|
|
8
|
+
import { renderGraphView, resizeGraph } from "./panelGraphView.js";
|
|
9
|
+
import { formatEstimatedBytes } from "./panelWorkbenchHelpers.js";
|
|
10
|
+
export class FrameGraphDebugWorkbench {
|
|
11
|
+
graphView;
|
|
12
|
+
callbacks;
|
|
13
|
+
root = document.createElement('div');
|
|
14
|
+
summary = document.createElement('div');
|
|
15
|
+
commandBar = document.createElement('div');
|
|
16
|
+
brand = document.createElement('div');
|
|
17
|
+
tabList = document.createElement('div');
|
|
18
|
+
commandActions = document.createElement('div');
|
|
19
|
+
commandStatus = document.createElement('span');
|
|
20
|
+
workspace = document.createElement('div');
|
|
21
|
+
main = document.createElement('main');
|
|
22
|
+
emptyHost = document.createElement('div');
|
|
23
|
+
overviewRoot = document.createElement('section');
|
|
24
|
+
graphRoot = document.createElement('section');
|
|
25
|
+
passes;
|
|
26
|
+
resources;
|
|
27
|
+
memory;
|
|
28
|
+
diagnostics;
|
|
29
|
+
inspector;
|
|
30
|
+
inspectorOpenButton = document.createElement('button');
|
|
31
|
+
captureButton = document.createElement('button');
|
|
32
|
+
importButton = document.createElement('button');
|
|
33
|
+
importInput = document.createElement('input');
|
|
34
|
+
exportButton = document.createElement('button');
|
|
35
|
+
exportMenu = document.createElement('div');
|
|
36
|
+
downloadButton = document.createElement('button');
|
|
37
|
+
copyButton = document.createElement('button');
|
|
38
|
+
tabButtons = new Map();
|
|
39
|
+
views = new Map();
|
|
40
|
+
activeTab = 'graph';
|
|
41
|
+
hasSnapshot = false;
|
|
42
|
+
snapshot;
|
|
43
|
+
selected;
|
|
44
|
+
hovered;
|
|
45
|
+
constructor(graphView, callbacks, actions, options) {
|
|
46
|
+
this.graphView = graphView;
|
|
47
|
+
this.callbacks = callbacks;
|
|
48
|
+
this.root.className = 'zenfg-inspector-workbench';
|
|
49
|
+
this.summary.className = 'zenfg-inspector-capture-summary';
|
|
50
|
+
this.commandBar.className = 'zenfg-inspector-workbench-command-bar';
|
|
51
|
+
this.brand.className = 'zenfg-inspector-brand';
|
|
52
|
+
if (options.branding === false)
|
|
53
|
+
this.commandBar.classList.add('branding-hidden');
|
|
54
|
+
else
|
|
55
|
+
this.brand.textContent = options.branding;
|
|
56
|
+
this.tabList.className = 'zenfg-inspector-workbench-tabs';
|
|
57
|
+
this.tabList.setAttribute('role', 'tablist');
|
|
58
|
+
this.tabList.setAttribute('aria-label', 'FrameGraph debug views');
|
|
59
|
+
this.commandActions.className = 'zenfg-inspector-workbench-actions';
|
|
60
|
+
this.commandActions.setAttribute('role', 'toolbar');
|
|
61
|
+
this.commandActions.setAttribute('aria-label', 'FrameGraph commands');
|
|
62
|
+
this.commandStatus.className = 'zenfg-inspector-command-status';
|
|
63
|
+
this.commandStatus.setAttribute('role', 'status');
|
|
64
|
+
this.commandStatus.setAttribute('aria-live', 'polite');
|
|
65
|
+
this.workspace.className = 'zenfg-inspector-workspace inspector-open';
|
|
66
|
+
this.main.className = 'zenfg-inspector-main';
|
|
67
|
+
this.emptyHost.className = 'zenfg-inspector-workbench-empty';
|
|
68
|
+
this.emptyHost.setAttribute('role', 'status');
|
|
69
|
+
this.emptyHost.setAttribute('aria-live', 'polite');
|
|
70
|
+
this.overviewRoot.className = 'zenfg-inspector-view zenfg-inspector-overview-view';
|
|
71
|
+
this.overviewRoot.id = `${options.idPrefix}-view-overview`;
|
|
72
|
+
this.overviewRoot.setAttribute('role', 'tabpanel');
|
|
73
|
+
this.overviewRoot.append(this.summary);
|
|
74
|
+
this.graphRoot.className = 'zenfg-inspector-view zenfg-inspector-graph-view';
|
|
75
|
+
this.graphRoot.id = `${options.idPrefix}-view-graph`;
|
|
76
|
+
this.graphRoot.setAttribute('role', 'tabpanel');
|
|
77
|
+
this.graphRoot.append(this.graphView.toolbar, this.graphView.host);
|
|
78
|
+
this.passes = new PassesView(callbacks, options.idPrefix);
|
|
79
|
+
this.resources = new ResourcesView(callbacks, options.idPrefix);
|
|
80
|
+
this.memory = new MemoryView(callbacks, options.idPrefix);
|
|
81
|
+
this.diagnostics = new DiagnosticsView(callbacks, options.idPrefix);
|
|
82
|
+
this.inspector = new InspectorView(callbacks, (open) => this.handleInspectorOpenChange(open), options.idPrefix);
|
|
83
|
+
this.views.set('overview', this.overviewRoot);
|
|
84
|
+
this.views.set('graph', this.graphRoot);
|
|
85
|
+
this.views.set('passes', this.passes.root);
|
|
86
|
+
this.views.set('resources', this.resources.root);
|
|
87
|
+
this.views.set('memory', this.memory.root);
|
|
88
|
+
this.views.set('diagnostics', this.diagnostics.root);
|
|
89
|
+
for (const [tab, label] of [
|
|
90
|
+
['overview', 'Overview'],
|
|
91
|
+
['graph', 'Graph'],
|
|
92
|
+
['passes', 'Passes'],
|
|
93
|
+
['resources', 'Resources'],
|
|
94
|
+
['memory', 'Memory'],
|
|
95
|
+
['diagnostics', 'Diagnostics'],
|
|
96
|
+
]) {
|
|
97
|
+
const button = document.createElement('button');
|
|
98
|
+
button.type = 'button';
|
|
99
|
+
button.id = `${options.idPrefix}-tab-${tab}`;
|
|
100
|
+
button.textContent = label;
|
|
101
|
+
button.setAttribute('role', 'tab');
|
|
102
|
+
button.setAttribute('aria-controls', `${options.idPrefix}-view-${tab}`);
|
|
103
|
+
button.addEventListener('click', () => this.setActiveTab(tab));
|
|
104
|
+
this.tabButtons.set(tab, button);
|
|
105
|
+
this.tabList.appendChild(button);
|
|
106
|
+
}
|
|
107
|
+
this.inspectorOpenButton.type = 'button';
|
|
108
|
+
this.inspectorOpenButton.hidden = true;
|
|
109
|
+
this.inspectorOpenButton.className = 'zenfg-inspector-open-inspector';
|
|
110
|
+
setPanelButtonContent(this.inspectorOpenButton, 'inspector', 'Inspector');
|
|
111
|
+
this.inspectorOpenButton.title = 'Open selection inspector';
|
|
112
|
+
this.inspectorOpenButton.addEventListener('click', () => this.inspector.setOpen(true));
|
|
113
|
+
this.captureButton.type = 'button';
|
|
114
|
+
this.captureButton.className = 'zenfg-inspector-capture-action';
|
|
115
|
+
this.captureButton.addEventListener('click', actions.onCapture);
|
|
116
|
+
this.importButton.type = 'button';
|
|
117
|
+
this.importButton.textContent = 'Import';
|
|
118
|
+
this.importButton.addEventListener('click', () => this.importInput.click());
|
|
119
|
+
this.importInput.type = 'file';
|
|
120
|
+
this.importInput.accept = '.fgsnapshot.json,.json,application/json';
|
|
121
|
+
this.importInput.hidden = true;
|
|
122
|
+
this.importInput.addEventListener('change', () => {
|
|
123
|
+
const file = this.importInput.files?.[0];
|
|
124
|
+
this.importInput.value = '';
|
|
125
|
+
if (file)
|
|
126
|
+
actions.onImport(file);
|
|
127
|
+
});
|
|
128
|
+
this.exportButton.type = 'button';
|
|
129
|
+
this.exportButton.textContent = 'Export';
|
|
130
|
+
this.exportButton.setAttribute('aria-haspopup', 'menu');
|
|
131
|
+
this.exportButton.setAttribute('aria-expanded', 'false');
|
|
132
|
+
this.exportButton.setAttribute('aria-controls', `${options.idPrefix}-export-menu`);
|
|
133
|
+
this.exportButton.addEventListener('click', () => {
|
|
134
|
+
this.setExportMenuOpen(this.exportMenu.hidden !== false);
|
|
135
|
+
});
|
|
136
|
+
this.exportMenu.className = 'zenfg-inspector-export-menu';
|
|
137
|
+
this.exportMenu.id = `${options.idPrefix}-export-menu`;
|
|
138
|
+
this.exportMenu.setAttribute('role', 'menu');
|
|
139
|
+
this.exportMenu.hidden = true;
|
|
140
|
+
this.downloadButton.type = 'button';
|
|
141
|
+
this.downloadButton.textContent = 'Download JSON';
|
|
142
|
+
this.downloadButton.setAttribute('role', 'menuitem');
|
|
143
|
+
this.downloadButton.addEventListener('click', () => {
|
|
144
|
+
this.setExportMenuOpen(false);
|
|
145
|
+
actions.onDownload();
|
|
146
|
+
});
|
|
147
|
+
this.copyButton.type = 'button';
|
|
148
|
+
this.copyButton.className = 'zenfg-inspector-copy-action';
|
|
149
|
+
this.copyButton.textContent = 'Copy JSON';
|
|
150
|
+
this.copyButton.setAttribute('role', 'menuitem');
|
|
151
|
+
this.copyButton.addEventListener('click', () => {
|
|
152
|
+
this.setExportMenuOpen(false);
|
|
153
|
+
actions.onCopyJson();
|
|
154
|
+
});
|
|
155
|
+
this.exportMenu.append(this.downloadButton, this.copyButton);
|
|
156
|
+
this.commandActions.append(this.commandStatus, this.inspectorOpenButton, this.captureButton, this.importButton, this.exportButton, this.importInput);
|
|
157
|
+
if (options.branding === false)
|
|
158
|
+
this.commandBar.append(this.tabList, this.commandActions, this.exportMenu);
|
|
159
|
+
else
|
|
160
|
+
this.commandBar.append(this.brand, this.tabList, this.commandActions, this.exportMenu);
|
|
161
|
+
this.root.addEventListener('click', (event) => {
|
|
162
|
+
const target = event.target;
|
|
163
|
+
if (!target || typeof target.nodeType !== 'number' || this.exportMenu.hidden)
|
|
164
|
+
return;
|
|
165
|
+
const targetNode = target;
|
|
166
|
+
if (!this.exportButton.contains(targetNode) && !this.exportMenu.contains(targetNode))
|
|
167
|
+
this.setExportMenuOpen(false);
|
|
168
|
+
});
|
|
169
|
+
this.root.addEventListener('keydown', (event) => {
|
|
170
|
+
if (event.key !== 'Escape' || this.exportMenu.hidden)
|
|
171
|
+
return;
|
|
172
|
+
this.setExportMenuOpen(false);
|
|
173
|
+
this.exportButton.focus();
|
|
174
|
+
});
|
|
175
|
+
this.main.append(this.emptyHost, ...this.views.values());
|
|
176
|
+
this.workspace.append(this.main, this.inspector.root);
|
|
177
|
+
this.root.append(this.commandBar, this.workspace);
|
|
178
|
+
this.showEmptyState('empty', 'Drop a ZenFG Snapshot here or choose Import.', 'Files are processed locally in your browser.');
|
|
179
|
+
this.setSnapshotActionState({
|
|
180
|
+
providerAvailable: false,
|
|
181
|
+
hasCapture: false,
|
|
182
|
+
capturing: false,
|
|
183
|
+
importing: false,
|
|
184
|
+
copying: false,
|
|
185
|
+
copied: false,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
setSnapshot(snapshot, selected) {
|
|
189
|
+
this.hasSnapshot = true;
|
|
190
|
+
this.snapshot = snapshot;
|
|
191
|
+
this.selected = selected;
|
|
192
|
+
this.hovered = undefined;
|
|
193
|
+
this.renderSummary(snapshot);
|
|
194
|
+
this.passes.setSnapshot(snapshot);
|
|
195
|
+
this.resources.setSnapshot(snapshot);
|
|
196
|
+
this.memory.setSnapshot(snapshot);
|
|
197
|
+
this.diagnostics.setSnapshot(snapshot);
|
|
198
|
+
this.inspector.setSnapshot(snapshot);
|
|
199
|
+
this.passes.setSelection(selected);
|
|
200
|
+
this.resources.setSelection(selected);
|
|
201
|
+
this.memory.setSelection(selected);
|
|
202
|
+
this.diagnostics.setSelection(selected);
|
|
203
|
+
this.inspector.setSelection(selected, false);
|
|
204
|
+
this.emptyHost.hidden = true;
|
|
205
|
+
this.updateActiveTab();
|
|
206
|
+
this.updateWorkspaceState();
|
|
207
|
+
if (this.activeTab === 'graph')
|
|
208
|
+
this.renderGraph();
|
|
209
|
+
}
|
|
210
|
+
showEmptyState(kind, message, detail) {
|
|
211
|
+
this.hasSnapshot = false;
|
|
212
|
+
this.snapshot = undefined;
|
|
213
|
+
this.selected = undefined;
|
|
214
|
+
this.hovered = undefined;
|
|
215
|
+
this.emptyHost.hidden = false;
|
|
216
|
+
this.emptyHost.dataset.state = kind;
|
|
217
|
+
const icon = createPanelIcon(kind === 'capturing' ? 'spinner' : kind === 'error' ? 'error' : kind === 'waiting' ? 'waiting' : 'empty');
|
|
218
|
+
const label = document.createElement('span');
|
|
219
|
+
label.className = 'zenfg-inspector-empty-message';
|
|
220
|
+
label.textContent = message;
|
|
221
|
+
const detailLabel = document.createElement('span');
|
|
222
|
+
detailLabel.className = 'zenfg-inspector-empty-detail';
|
|
223
|
+
detailLabel.textContent = detail ?? '';
|
|
224
|
+
this.emptyHost.replaceChildren(icon, label, ...(detail ? [detailLabel] : []));
|
|
225
|
+
this.updateActiveTab();
|
|
226
|
+
this.updateWorkspaceState();
|
|
227
|
+
}
|
|
228
|
+
setSnapshotActionState(state) {
|
|
229
|
+
const captureLabel = state.capturing ? 'Capturing…' : 'Capture';
|
|
230
|
+
setPanelButtonContent(this.captureButton, state.capturing ? 'spinner' : 'capture', captureLabel);
|
|
231
|
+
this.captureButton.disabled = state.capturing || !state.providerAvailable;
|
|
232
|
+
this.captureButton.hidden = !state.providerAvailable;
|
|
233
|
+
this.captureButton.classList.toggle('active', state.capturing);
|
|
234
|
+
this.captureButton.setAttribute('aria-busy', state.capturing ? 'true' : 'false');
|
|
235
|
+
this.captureButton.dataset.tone = state.capturing ? 'pending' : 'accent';
|
|
236
|
+
this.captureButton.title = !state.providerAvailable
|
|
237
|
+
? 'Waiting for a FrameGraph capture source.'
|
|
238
|
+
: state.capturing
|
|
239
|
+
? 'Capturing the next rendered frame.'
|
|
240
|
+
: 'Capture and display the next rendered frame.';
|
|
241
|
+
this.importButton.textContent = state.importing ? 'Importing…' : 'Import';
|
|
242
|
+
this.importButton.disabled = state.importing;
|
|
243
|
+
this.importButton.setAttribute('aria-busy', state.importing ? 'true' : 'false');
|
|
244
|
+
this.exportButton.disabled = !state.hasCapture;
|
|
245
|
+
this.downloadButton.disabled = !state.hasCapture;
|
|
246
|
+
if (!state.hasCapture)
|
|
247
|
+
this.setExportMenuOpen(false);
|
|
248
|
+
const copyLabel = state.copying ? 'Copying…' : state.copied ? 'Copied' : 'Copy JSON';
|
|
249
|
+
const copyIcon = state.copying ? 'spinner' : state.copied ? 'check' : 'copy';
|
|
250
|
+
setPanelButtonContent(this.copyButton, copyIcon, copyLabel);
|
|
251
|
+
this.copyButton.disabled = !state.hasCapture || state.copying;
|
|
252
|
+
this.copyButton.classList.toggle('active', state.copying || state.copied);
|
|
253
|
+
this.copyButton.setAttribute('aria-busy', state.copying ? 'true' : 'false');
|
|
254
|
+
this.copyButton.dataset.tone = state.copied ? 'success' : state.copying ? 'pending' : 'neutral';
|
|
255
|
+
this.copyButton.title = state.hasCapture
|
|
256
|
+
? 'Copy the current canonical FrameGraph Snapshot JSON.'
|
|
257
|
+
: 'Load a snapshot before exporting.';
|
|
258
|
+
this.commandStatus.textContent = state.message ?? '';
|
|
259
|
+
this.commandStatus.hidden = !state.message;
|
|
260
|
+
this.commandStatus.dataset.tone = state.message ? state.messageTone ?? 'error' : 'neutral';
|
|
261
|
+
if (state.message)
|
|
262
|
+
this.commandStatus.title = state.message;
|
|
263
|
+
else
|
|
264
|
+
this.commandStatus.removeAttribute('title');
|
|
265
|
+
}
|
|
266
|
+
setSelection(selected) {
|
|
267
|
+
this.selected = selected;
|
|
268
|
+
this.passes.setSelection(selected);
|
|
269
|
+
this.resources.setSelection(selected);
|
|
270
|
+
this.memory.setSelection(selected);
|
|
271
|
+
this.diagnostics.setSelection(selected);
|
|
272
|
+
this.inspector.setSelection(selected);
|
|
273
|
+
if (this.activeTab === 'graph')
|
|
274
|
+
this.renderGraph();
|
|
275
|
+
}
|
|
276
|
+
setHovered(hovered) {
|
|
277
|
+
this.hovered = hovered;
|
|
278
|
+
if (this.activeTab === 'graph')
|
|
279
|
+
this.renderGraph();
|
|
280
|
+
}
|
|
281
|
+
setActiveTab(tab) {
|
|
282
|
+
if (this.activeTab === tab)
|
|
283
|
+
return;
|
|
284
|
+
this.activeTab = tab;
|
|
285
|
+
this.updateActiveTab();
|
|
286
|
+
if (tab === 'graph') {
|
|
287
|
+
window.requestAnimationFrame(() => {
|
|
288
|
+
resizeGraph(this.graphView);
|
|
289
|
+
this.renderGraph();
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
refreshGraphStructure() {
|
|
294
|
+
if (!this.snapshot)
|
|
295
|
+
return;
|
|
296
|
+
this.passes.setSnapshot(this.snapshot);
|
|
297
|
+
if (this.activeTab === 'graph')
|
|
298
|
+
this.renderGraph();
|
|
299
|
+
}
|
|
300
|
+
resizeGraph(fit = false) {
|
|
301
|
+
if (fit)
|
|
302
|
+
this.graphView.fitOnNextRender = true;
|
|
303
|
+
window.requestAnimationFrame(() => {
|
|
304
|
+
resizeGraph(this.graphView);
|
|
305
|
+
if (this.activeTab === 'graph')
|
|
306
|
+
this.renderGraph();
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
updateActiveTab() {
|
|
310
|
+
for (const [tab, button] of this.tabButtons) {
|
|
311
|
+
const active = tab === this.activeTab;
|
|
312
|
+
button.disabled = !this.hasSnapshot;
|
|
313
|
+
button.classList.toggle('active', active);
|
|
314
|
+
button.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
315
|
+
const view = this.views.get(tab);
|
|
316
|
+
view.hidden = !this.hasSnapshot || !active;
|
|
317
|
+
view.setAttribute('aria-labelledby', button.id);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
renderGraph() {
|
|
321
|
+
if (!this.snapshot)
|
|
322
|
+
return;
|
|
323
|
+
renderGraphView(this.graphView, this.snapshot, this.selected, this.hovered, this.callbacks.onSelect, this.callbacks.onHover, this.callbacks.onGroupToggle);
|
|
324
|
+
}
|
|
325
|
+
renderSummary(snapshot) {
|
|
326
|
+
const frameGraphSegments = snapshot.executionSegments.filter((segment) => segment.kind === 'frame-graph').length;
|
|
327
|
+
const opaqueIntervals = snapshot.executionSegments.length - frameGraphSegments;
|
|
328
|
+
const metrics = snapshot.metrics;
|
|
329
|
+
const coverage = metrics.timingEligibleNodeCount === 0
|
|
330
|
+
? 'No eligible passes'
|
|
331
|
+
: `${metrics.timedNodeCount}/${metrics.timingEligibleNodeCount} timed`;
|
|
332
|
+
const slowest = metrics.slowestNode
|
|
333
|
+
? `${labelNode(metrics.slowestNode)} · ${(metrics.slowestNode.gpuDurationMicros / 1000).toFixed(3)} ms`
|
|
334
|
+
: 'Unknown';
|
|
335
|
+
const protocol = snapshot.protocol;
|
|
336
|
+
const runtime = protocol.producer.runtime;
|
|
337
|
+
const producer = [
|
|
338
|
+
protocol.producer.name,
|
|
339
|
+
protocol.producer.version,
|
|
340
|
+
protocol.producer.language,
|
|
341
|
+
].filter(Boolean).join(' · ');
|
|
342
|
+
const runtimeLabel = runtime
|
|
343
|
+
? [runtime.implementation, runtime.graphicsApi, runtime.backend].filter(Boolean).join(' · ') || 'Unknown'
|
|
344
|
+
: 'Unknown';
|
|
345
|
+
const poolRetained = snapshot.resourcePool.status === 'available'
|
|
346
|
+
? `${snapshot.resourcePool.estimatedRetainedBytes === undefined ? 'Unknown' : formatBytes(snapshot.resourcePool.estimatedRetainedBytes)} · ${snapshot.resourcePool.retainedCount} allocations`
|
|
347
|
+
: 'Unavailable';
|
|
348
|
+
this.summary.replaceChildren(this.createSummaryGroup('Capture', [
|
|
349
|
+
['Source', snapshot.source.label],
|
|
350
|
+
['Schema', `${protocol.format} v${protocol.version.major}.${protocol.version.minor}`],
|
|
351
|
+
['Producer', producer],
|
|
352
|
+
['Runtime', runtimeLabel],
|
|
353
|
+
['Frame', String(snapshot.frameIndex)],
|
|
354
|
+
...(snapshot.source.migratedFromLegacy ? [['Migration', 'Legacy V0 → V1']] : []),
|
|
355
|
+
['Timing', snapshot.profiling.status === 'available' ? 'Available' : snapshot.profiling.reason],
|
|
356
|
+
]), this.createSummaryGroup('GPU', [
|
|
357
|
+
['GPU Span', snapshot.profiling.status === 'available' ? `${formatGpuFrameDuration(snapshot)} ms` : 'Unknown'],
|
|
358
|
+
['Coverage', coverage],
|
|
359
|
+
['Slowest', slowest],
|
|
360
|
+
]), this.createSummaryGroup('Work', [
|
|
361
|
+
['Nodes', `${snapshot.nodes.length} retained · ${snapshot.culledNodes.length} culled`],
|
|
362
|
+
['Segments', `${frameGraphSegments} FG · ${opaqueIntervals} opaque`],
|
|
363
|
+
['Groups', snapshot.availability.groups ? String(snapshot.debugGroups.length) : 'Unknown'],
|
|
364
|
+
['Recording order', snapshot.availability.recordingOrder ? 'Available' : 'Unknown'],
|
|
365
|
+
]), this.createSummaryGroup('Resources', [
|
|
366
|
+
['Logical / physical', `${snapshot.resources.length} / ${snapshot.physicalAllocations.length}`],
|
|
367
|
+
['Texture views', snapshot.availability.textureViews ? String(snapshot.textureViewById.size) : 'Unknown'],
|
|
368
|
+
['Access regions', snapshot.availability.accessRegions ? 'Available' : 'Unknown'],
|
|
369
|
+
['Transient estimate', formatEstimatedBytes(metrics.transientEstimatedByteSize)],
|
|
370
|
+
]), this.createSummaryGroup('Pool', [
|
|
371
|
+
['Retained', poolRetained],
|
|
372
|
+
['Reuse', formatPoolHitRate(snapshot)],
|
|
373
|
+
]));
|
|
374
|
+
}
|
|
375
|
+
createSummaryGroup(title, rows) {
|
|
376
|
+
const group = document.createElement('section');
|
|
377
|
+
const heading = document.createElement('h2');
|
|
378
|
+
heading.textContent = title;
|
|
379
|
+
group.appendChild(heading);
|
|
380
|
+
for (const [label, value] of rows) {
|
|
381
|
+
const row = document.createElement('div');
|
|
382
|
+
const term = document.createElement('span');
|
|
383
|
+
term.textContent = label;
|
|
384
|
+
const description = document.createElement('strong');
|
|
385
|
+
description.textContent = value;
|
|
386
|
+
description.title = value;
|
|
387
|
+
row.append(term, description);
|
|
388
|
+
group.appendChild(row);
|
|
389
|
+
}
|
|
390
|
+
return group;
|
|
391
|
+
}
|
|
392
|
+
setExportMenuOpen(open) {
|
|
393
|
+
this.exportMenu.hidden = !open;
|
|
394
|
+
this.exportButton.setAttribute('aria-expanded', open ? 'true' : 'false');
|
|
395
|
+
}
|
|
396
|
+
handleInspectorOpenChange(_open) {
|
|
397
|
+
this.updateWorkspaceState();
|
|
398
|
+
if (this.activeTab === 'graph')
|
|
399
|
+
this.resizeGraph(false);
|
|
400
|
+
}
|
|
401
|
+
updateWorkspaceState() {
|
|
402
|
+
const inspectorOpen = this.hasSnapshot && this.inspector.isOpen;
|
|
403
|
+
this.workspace.classList.toggle('has-capture', this.hasSnapshot);
|
|
404
|
+
this.workspace.classList.toggle('inspector-open', inspectorOpen);
|
|
405
|
+
this.inspector.root.classList.toggle('unavailable', !this.hasSnapshot);
|
|
406
|
+
this.inspectorOpenButton.hidden = !this.hasSnapshot || inspectorOpen;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
//# sourceMappingURL=panelWorkbenchView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panelWorkbenchView.js","sourceRoot":"","sources":["../src/panelWorkbenchView.ts"],"names":[],"mappings":"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","sourcesContent":["import type { FrameGraphDebugViewModel } from './debugCaptureModel.ts';\nimport {\n\tformatBytes,\n\tformatGpuFrameDuration,\n\tformatPoolHitRate,\n\tlabelNode,\n} from './panelDomHelpers.ts';\nimport { createPanelIcon, setPanelButtonContent } from './panelIcons.ts';\nimport { DiagnosticsView } from './panelDiagnosticsView.ts';\nimport { InspectorView } from './panelInspectorView.ts';\nimport { MemoryView } from './panelMemoryView.ts';\nimport { PassesView } from './panelPassesView.ts';\nimport { ResourcesView } from './panelResourcesView.ts';\nimport { renderGraphView, resizeGraph } from './panelGraphView.ts';\nimport type { GraphViewState, Selection, WorkbenchTab } from './panelTypes.ts';\nimport { formatEstimatedBytes, type WorkbenchCallbacks } from './panelWorkbenchHelpers.ts';\n\nexport type FrameGraphDebugWorkbenchActions = {\n\tonCapture(): void;\n\tonImport(file: File): void;\n\tonDownload(): void;\n\tonCopyJson(): void;\n};\nexport type FrameGraphDebugWorkbenchOptions = {\n\tbranding: string | false;\n\tidPrefix: string;\n};\nexport type FrameGraphDebugEmptyStateKind = 'waiting' | 'capturing' | 'empty' | 'error';\n\n\nexport type FrameGraphDebugSnapshotActionState = {\n\tproviderAvailable: boolean;\n\thasCapture: boolean;\n\tcapturing: boolean;\n\timporting: boolean;\n\tcopying: boolean;\n\tcopied: boolean;\n\tmessage?: string;\n\tmessageTone?: 'neutral' | 'error';\n};\n\nexport class FrameGraphDebugWorkbench {\n\treadonly root = document.createElement('div');\n\tprivate readonly summary = document.createElement('div');\n\tprivate readonly commandBar = document.createElement('div');\n\tprivate readonly brand = document.createElement('div');\n\tprivate readonly tabList = document.createElement('div');\n\tprivate readonly commandActions = document.createElement('div');\n\tprivate readonly commandStatus = document.createElement('span');\n\tprivate readonly workspace = document.createElement('div');\n\tprivate readonly main = document.createElement('main');\n\tprivate readonly emptyHost = document.createElement('div');\n\tprivate readonly overviewRoot = document.createElement('section');\n\tprivate readonly graphRoot = document.createElement('section');\n\tprivate readonly passes: PassesView;\n\tprivate readonly resources: ResourcesView;\n\tprivate readonly memory: MemoryView;\n\tprivate readonly diagnostics: DiagnosticsView;\n\tprivate readonly inspector: InspectorView;\n\tprivate readonly inspectorOpenButton = document.createElement('button');\n\tprivate readonly captureButton = document.createElement('button');\n\tprivate readonly importButton = document.createElement('button');\n\tprivate readonly importInput = document.createElement('input');\n\tprivate readonly exportButton = document.createElement('button');\n\tprivate readonly exportMenu = document.createElement('div');\n\tprivate readonly downloadButton = document.createElement('button');\n\tprivate readonly copyButton = document.createElement('button');\n\tprivate readonly tabButtons = new Map<WorkbenchTab, HTMLButtonElement>();\n\tprivate readonly views = new Map<WorkbenchTab, HTMLElement>();\n\tprivate activeTab: WorkbenchTab = 'graph';\n\tprivate hasSnapshot = false;\n\tprivate snapshot: FrameGraphDebugViewModel | undefined;\n\tprivate selected: Selection | undefined;\n\tprivate hovered: Selection | undefined;\n\n\tconstructor(\n\t\tprivate readonly graphView: GraphViewState,\n\t\tprivate readonly callbacks: WorkbenchCallbacks,\n\t\tactions: FrameGraphDebugWorkbenchActions,\n\t\toptions: FrameGraphDebugWorkbenchOptions,\n\t) {\n\t\tthis.root.className = 'zenfg-inspector-workbench';\n\t\tthis.summary.className = 'zenfg-inspector-capture-summary';\n\t\tthis.commandBar.className = 'zenfg-inspector-workbench-command-bar';\n\t\tthis.brand.className = 'zenfg-inspector-brand';\n\t\tif (options.branding === false) this.commandBar.classList.add('branding-hidden');\n\t\telse this.brand.textContent = options.branding;\n\t\tthis.tabList.className = 'zenfg-inspector-workbench-tabs';\n\t\tthis.tabList.setAttribute('role', 'tablist');\n\t\tthis.tabList.setAttribute('aria-label', 'FrameGraph debug views');\n\t\tthis.commandActions.className = 'zenfg-inspector-workbench-actions';\n\t\tthis.commandActions.setAttribute('role', 'toolbar');\n\t\tthis.commandActions.setAttribute('aria-label', 'FrameGraph commands');\n\t\tthis.commandStatus.className = 'zenfg-inspector-command-status';\n\t\tthis.commandStatus.setAttribute('role', 'status');\n\t\tthis.commandStatus.setAttribute('aria-live', 'polite');\n\t\tthis.workspace.className = 'zenfg-inspector-workspace inspector-open';\n\t\tthis.main.className = 'zenfg-inspector-main';\n\t\tthis.emptyHost.className = 'zenfg-inspector-workbench-empty';\n\t\tthis.emptyHost.setAttribute('role', 'status');\n\t\tthis.emptyHost.setAttribute('aria-live', 'polite');\n\n\t\tthis.overviewRoot.className = 'zenfg-inspector-view zenfg-inspector-overview-view';\n\t\tthis.overviewRoot.id = `${options.idPrefix}-view-overview`;\n\t\tthis.overviewRoot.setAttribute('role', 'tabpanel');\n\t\tthis.overviewRoot.append(this.summary);\n\t\tthis.graphRoot.className = 'zenfg-inspector-view zenfg-inspector-graph-view';\n\t\tthis.graphRoot.id = `${options.idPrefix}-view-graph`;\n\t\tthis.graphRoot.setAttribute('role', 'tabpanel');\n\t\tthis.graphRoot.append(this.graphView.toolbar, this.graphView.host);\n\t\tthis.passes = new PassesView(callbacks, options.idPrefix);\n\t\tthis.resources = new ResourcesView(callbacks, options.idPrefix);\n\t\tthis.memory = new MemoryView(callbacks, options.idPrefix);\n\t\tthis.diagnostics = new DiagnosticsView(callbacks, options.idPrefix);\n\t\tthis.inspector = new InspectorView(callbacks, (open) => this.handleInspectorOpenChange(open), options.idPrefix);\n\n\t\tthis.views.set('overview', this.overviewRoot);\n\t\tthis.views.set('graph', this.graphRoot);\n\t\tthis.views.set('passes', this.passes.root);\n\t\tthis.views.set('resources', this.resources.root);\n\t\tthis.views.set('memory', this.memory.root);\n\t\tthis.views.set('diagnostics', this.diagnostics.root);\n\t\tfor (const [tab, label] of [\n\t\t\t['overview', 'Overview'],\n\t\t\t['graph', 'Graph'],\n\t\t\t['passes', 'Passes'],\n\t\t\t['resources', 'Resources'],\n\t\t\t['memory', 'Memory'],\n\t\t\t['diagnostics', 'Diagnostics'],\n\t\t] as const) {\n\t\t\tconst button = document.createElement('button');\n\t\t\tbutton.type = 'button';\n\t\t\tbutton.id = `${options.idPrefix}-tab-${tab}`;\n\t\t\tbutton.textContent = label;\n\t\t\tbutton.setAttribute('role', 'tab');\n\t\t\tbutton.setAttribute('aria-controls', `${options.idPrefix}-view-${tab}`);\n\t\t\tbutton.addEventListener('click', () => this.setActiveTab(tab));\n\t\t\tthis.tabButtons.set(tab, button);\n\t\t\tthis.tabList.appendChild(button);\n\t\t}\n\n\t\tthis.inspectorOpenButton.type = 'button';\n\t\tthis.inspectorOpenButton.hidden = true;\n\t\tthis.inspectorOpenButton.className = 'zenfg-inspector-open-inspector';\n\t\tsetPanelButtonContent(this.inspectorOpenButton, 'inspector', 'Inspector');\n\t\tthis.inspectorOpenButton.title = 'Open selection inspector';\n\t\tthis.inspectorOpenButton.addEventListener('click', () => this.inspector.setOpen(true));\n\n\t\tthis.captureButton.type = 'button';\n\t\tthis.captureButton.className = 'zenfg-inspector-capture-action';\n\t\tthis.captureButton.addEventListener('click', actions.onCapture);\n\t\tthis.importButton.type = 'button';\n\t\tthis.importButton.textContent = 'Import';\n\t\tthis.importButton.addEventListener('click', () => this.importInput.click());\n\t\tthis.importInput.type = 'file';\n\t\tthis.importInput.accept = '.fgsnapshot.json,.json,application/json';\n\t\tthis.importInput.hidden = true;\n\t\tthis.importInput.addEventListener('change', () => {\n\t\t\tconst file = this.importInput.files?.[0];\n\t\t\tthis.importInput.value = '';\n\t\t\tif (file) actions.onImport(file);\n\t\t});\n\t\tthis.exportButton.type = 'button';\n\t\tthis.exportButton.textContent = 'Export';\n\t\tthis.exportButton.setAttribute('aria-haspopup', 'menu');\n\t\tthis.exportButton.setAttribute('aria-expanded', 'false');\n\t\tthis.exportButton.setAttribute('aria-controls', `${options.idPrefix}-export-menu`);\n\t\tthis.exportButton.addEventListener('click', () => {\n\t\t\tthis.setExportMenuOpen(this.exportMenu.hidden !== false);\n\t\t});\n\t\tthis.exportMenu.className = 'zenfg-inspector-export-menu';\n\t\tthis.exportMenu.id = `${options.idPrefix}-export-menu`;\n\t\tthis.exportMenu.setAttribute('role', 'menu');\n\t\tthis.exportMenu.hidden = true;\n\t\tthis.downloadButton.type = 'button';\n\t\tthis.downloadButton.textContent = 'Download JSON';\n\t\tthis.downloadButton.setAttribute('role', 'menuitem');\n\t\tthis.downloadButton.addEventListener('click', () => {\n\t\t\tthis.setExportMenuOpen(false);\n\t\t\tactions.onDownload();\n\t\t});\n\t\tthis.copyButton.type = 'button';\n\t\tthis.copyButton.className = 'zenfg-inspector-copy-action';\n\t\tthis.copyButton.textContent = 'Copy JSON';\n\t\tthis.copyButton.setAttribute('role', 'menuitem');\n\t\tthis.copyButton.addEventListener('click', () => {\n\t\t\tthis.setExportMenuOpen(false);\n\t\t\tactions.onCopyJson();\n\t\t});\n\t\tthis.exportMenu.append(this.downloadButton, this.copyButton);\n\n\t\tthis.commandActions.append(\n\t\t\tthis.commandStatus,\n\t\t\tthis.inspectorOpenButton,\n\t\t\tthis.captureButton,\n\t\t\tthis.importButton,\n\t\t\tthis.exportButton,\n\t\t\tthis.importInput,\n\t\t);\n\t\tif (options.branding === false) this.commandBar.append(this.tabList, this.commandActions, this.exportMenu);\n\t\telse this.commandBar.append(this.brand, this.tabList, this.commandActions, this.exportMenu);\n\t\tthis.root.addEventListener('click', (event) => {\n\t\t\tconst target = event.target;\n\t\t\tif (!target || typeof (target as Node).nodeType !== 'number' || this.exportMenu.hidden) return;\n\t\t\tconst targetNode = target as Node;\n\t\t\tif (!this.exportButton.contains(targetNode) && !this.exportMenu.contains(targetNode)) this.setExportMenuOpen(false);\n\t\t});\n\t\tthis.root.addEventListener('keydown', (event) => {\n\t\t\tif (event.key !== 'Escape' || this.exportMenu.hidden) return;\n\t\t\tthis.setExportMenuOpen(false);\n\t\t\tthis.exportButton.focus();\n\t\t});\n\t\tthis.main.append(this.emptyHost, ...this.views.values());\n\t\tthis.workspace.append(this.main, this.inspector.root);\n\t\tthis.root.append(this.commandBar, this.workspace);\n\t\tthis.showEmptyState('empty', 'Drop a ZenFG Snapshot here or choose Import.', 'Files are processed locally in your browser.');\n\t\tthis.setSnapshotActionState({\n\t\t\tproviderAvailable: false,\n\t\t\thasCapture: false,\n\t\t\tcapturing: false,\n\t\t\timporting: false,\n\t\t\tcopying: false,\n\t\t\tcopied: false,\n\t\t});\n\t}\n\n\tsetSnapshot(snapshot: FrameGraphDebugViewModel, selected: Selection | undefined): void {\n\t\tthis.hasSnapshot = true;\n\t\tthis.snapshot = snapshot;\n\t\tthis.selected = selected;\n\t\tthis.hovered = undefined;\n\t\tthis.renderSummary(snapshot);\n\t\tthis.passes.setSnapshot(snapshot);\n\t\tthis.resources.setSnapshot(snapshot);\n\t\tthis.memory.setSnapshot(snapshot);\n\t\tthis.diagnostics.setSnapshot(snapshot);\n\t\tthis.inspector.setSnapshot(snapshot);\n\t\tthis.passes.setSelection(selected);\n\t\tthis.resources.setSelection(selected);\n\t\tthis.memory.setSelection(selected);\n\t\tthis.diagnostics.setSelection(selected);\n\t\tthis.inspector.setSelection(selected, false);\n\t\tthis.emptyHost.hidden = true;\n\t\tthis.updateActiveTab();\n\t\tthis.updateWorkspaceState();\n\t\tif (this.activeTab === 'graph') this.renderGraph();\n\t}\n\n\tshowEmptyState(kind: FrameGraphDebugEmptyStateKind, message: string, detail?: string): void {\n\t\tthis.hasSnapshot = false;\n\t\tthis.snapshot = undefined;\n\t\tthis.selected = undefined;\n\t\tthis.hovered = undefined;\n\t\tthis.emptyHost.hidden = false;\n\t\tthis.emptyHost.dataset.state = kind;\n\t\tconst icon = createPanelIcon(kind === 'capturing' ? 'spinner' : kind === 'error' ? 'error' : kind === 'waiting' ? 'waiting' : 'empty');\n\t\tconst label = document.createElement('span');\n\t\tlabel.className = 'zenfg-inspector-empty-message';\n\t\tlabel.textContent = message;\n\t\tconst detailLabel = document.createElement('span');\n\t\tdetailLabel.className = 'zenfg-inspector-empty-detail';\n\t\tdetailLabel.textContent = detail ?? '';\n\t\tthis.emptyHost.replaceChildren(icon, label, ...(detail ? [detailLabel] : []));\n\t\tthis.updateActiveTab();\n\t\tthis.updateWorkspaceState();\n\t}\n\n\tsetSnapshotActionState(state: FrameGraphDebugSnapshotActionState): void {\n\t\tconst captureLabel = state.capturing ? 'Capturing…' : 'Capture';\n\t\tsetPanelButtonContent(this.captureButton, state.capturing ? 'spinner' : 'capture', captureLabel);\n\t\tthis.captureButton.disabled = state.capturing || !state.providerAvailable;\n\t\tthis.captureButton.hidden = !state.providerAvailable;\n\t\tthis.captureButton.classList.toggle('active', state.capturing);\n\t\tthis.captureButton.setAttribute('aria-busy', state.capturing ? 'true' : 'false');\n\t\tthis.captureButton.dataset.tone = state.capturing ? 'pending' : 'accent';\n\t\tthis.captureButton.title = !state.providerAvailable\n\t\t\t? 'Waiting for a FrameGraph capture source.'\n\t\t\t: state.capturing\n\t\t\t\t? 'Capturing the next rendered frame.'\n\t\t\t\t: 'Capture and display the next rendered frame.';\n\n\t\tthis.importButton.textContent = state.importing ? 'Importing…' : 'Import';\n\t\tthis.importButton.disabled = state.importing;\n\t\tthis.importButton.setAttribute('aria-busy', state.importing ? 'true' : 'false');\n\t\tthis.exportButton.disabled = !state.hasCapture;\n\t\tthis.downloadButton.disabled = !state.hasCapture;\n\t\tif (!state.hasCapture) this.setExportMenuOpen(false);\n\n\t\tconst copyLabel = state.copying ? 'Copying…' : state.copied ? 'Copied' : 'Copy JSON';\n\t\tconst copyIcon = state.copying ? 'spinner' : state.copied ? 'check' : 'copy';\n\t\tsetPanelButtonContent(this.copyButton, copyIcon, copyLabel);\n\t\tthis.copyButton.disabled = !state.hasCapture || state.copying;\n\t\tthis.copyButton.classList.toggle('active', state.copying || state.copied);\n\t\tthis.copyButton.setAttribute('aria-busy', state.copying ? 'true' : 'false');\n\t\tthis.copyButton.dataset.tone = state.copied ? 'success' : state.copying ? 'pending' : 'neutral';\n\t\tthis.copyButton.title = state.hasCapture\n\t\t\t? 'Copy the current canonical FrameGraph Snapshot JSON.'\n\t\t\t: 'Load a snapshot before exporting.';\n\n\t\tthis.commandStatus.textContent = state.message ?? '';\n\t\tthis.commandStatus.hidden = !state.message;\n\t\tthis.commandStatus.dataset.tone = state.message ? state.messageTone ?? 'error' : 'neutral';\n\t\tif (state.message) this.commandStatus.title = state.message;\n\t\telse this.commandStatus.removeAttribute('title');\n\t}\r\n\n\tsetSelection(selected: Selection | undefined): void {\n\t\tthis.selected = selected;\n\t\tthis.passes.setSelection(selected);\n\t\tthis.resources.setSelection(selected);\n\t\tthis.memory.setSelection(selected);\n\t\tthis.diagnostics.setSelection(selected);\n\t\tthis.inspector.setSelection(selected);\n\t\tif (this.activeTab === 'graph') this.renderGraph();\n\t}\n\n\tsetHovered(hovered: Selection | undefined): void {\n\t\tthis.hovered = hovered;\n\t\tif (this.activeTab === 'graph') this.renderGraph();\n\t}\n\n\tsetActiveTab(tab: WorkbenchTab): void {\n\t\tif (this.activeTab === tab) return;\n\t\tthis.activeTab = tab;\n\t\tthis.updateActiveTab();\n\t\tif (tab === 'graph') {\n\t\t\twindow.requestAnimationFrame(() => {\n\t\t\t\tresizeGraph(this.graphView);\n\t\t\t\tthis.renderGraph();\n\t\t\t});\n\t\t}\n\t}\n\n\trefreshGraphStructure(): void {\n\t\tif (!this.snapshot) return;\n\t\tthis.passes.setSnapshot(this.snapshot);\n\t\tif (this.activeTab === 'graph') this.renderGraph();\n\t}\n\n\tresizeGraph(fit = false): void {\n\t\tif (fit) this.graphView.fitOnNextRender = true;\n\t\twindow.requestAnimationFrame(() => {\n\t\t\tresizeGraph(this.graphView);\n\t\t\tif (this.activeTab === 'graph') this.renderGraph();\n\t\t});\n\t}\n\n\tprivate updateActiveTab(): void {\n\t\tfor (const [tab, button] of this.tabButtons) {\n\t\t\tconst active = tab === this.activeTab;\n\t\t\tbutton.disabled = !this.hasSnapshot;\n\t\t\tbutton.classList.toggle('active', active);\n\t\t\tbutton.setAttribute('aria-selected', active ? 'true' : 'false');\n\t\t\tconst view = this.views.get(tab)!;\n\t\t\tview.hidden = !this.hasSnapshot || !active;\n\t\t\tview.setAttribute('aria-labelledby', button.id);\n\t\t}\n\t}\n\n\tprivate renderGraph(): void {\n\t\tif (!this.snapshot) return;\n\t\trenderGraphView(\n\t\t\tthis.graphView,\n\t\t\tthis.snapshot,\n\t\t\tthis.selected,\n\t\t\tthis.hovered,\n\t\t\tthis.callbacks.onSelect,\n\t\t\tthis.callbacks.onHover,\n\t\t\tthis.callbacks.onGroupToggle,\n\t\t);\n\t}\n\n\tprivate renderSummary(snapshot: FrameGraphDebugViewModel): void {\n\t\tconst frameGraphSegments = snapshot.executionSegments.filter((segment) => segment.kind === 'frame-graph').length;\n\t\tconst opaqueIntervals = snapshot.executionSegments.length - frameGraphSegments;\n\t\tconst metrics = snapshot.metrics;\n\t\tconst coverage = metrics.timingEligibleNodeCount === 0\n\t\t\t? 'No eligible passes'\n\t\t\t: `${metrics.timedNodeCount}/${metrics.timingEligibleNodeCount} timed`;\n\t\tconst slowest = metrics.slowestNode\n\t\t\t? `${labelNode(metrics.slowestNode)} · ${(metrics.slowestNode.gpuDurationMicros! / 1000).toFixed(3)} ms`\n\t\t\t: 'Unknown';\n\t\tconst protocol = snapshot.protocol;\n\t\tconst runtime = protocol.producer.runtime;\n\t\tconst producer = [\n\t\t\tprotocol.producer.name,\n\t\t\tprotocol.producer.version,\n\t\t\tprotocol.producer.language,\n\t\t].filter(Boolean).join(' · ');\n\t\tconst runtimeLabel = runtime\n\t\t\t? [runtime.implementation, runtime.graphicsApi, runtime.backend].filter(Boolean).join(' · ') || 'Unknown'\n\t\t\t: 'Unknown';\n\t\tconst poolRetained = snapshot.resourcePool.status === 'available'\n\t\t\t? `${snapshot.resourcePool.estimatedRetainedBytes === undefined ? 'Unknown' : formatBytes(snapshot.resourcePool.estimatedRetainedBytes)} · ${snapshot.resourcePool.retainedCount} allocations`\n\t\t\t: 'Unavailable';\n\t\tthis.summary.replaceChildren(\n\t\t\tthis.createSummaryGroup('Capture', [\n\t\t\t\t['Source', snapshot.source.label],\n\t\t\t\t['Schema', `${protocol.format} v${protocol.version.major}.${protocol.version.minor}`],\n\t\t\t\t['Producer', producer],\n\t\t\t\t['Runtime', runtimeLabel],\n\t\t\t\t['Frame', String(snapshot.frameIndex)],\n\t\t\t\t...(snapshot.source.migratedFromLegacy ? [['Migration', 'Legacy V0 → V1'] as const] : []),\n\t\t\t\t['Timing', snapshot.profiling.status === 'available' ? 'Available' : snapshot.profiling.reason],\n\t\t\t]),\n\t\t\tthis.createSummaryGroup('GPU', [\n\t\t\t\t['GPU Span', snapshot.profiling.status === 'available' ? `${formatGpuFrameDuration(snapshot)} ms` : 'Unknown'],\n\t\t\t\t['Coverage', coverage],\n\t\t\t\t['Slowest', slowest],\n\t\t\t]),\n\t\t\tthis.createSummaryGroup('Work', [\n\t\t\t\t['Nodes', `${snapshot.nodes.length} retained · ${snapshot.culledNodes.length} culled`],\n\t\t\t\t['Segments', `${frameGraphSegments} FG · ${opaqueIntervals} opaque`],\n\t\t\t\t['Groups', snapshot.availability.groups ? String(snapshot.debugGroups.length) : 'Unknown'],\n\t\t\t\t['Recording order', snapshot.availability.recordingOrder ? 'Available' : 'Unknown'],\n\t\t\t]),\n\t\t\tthis.createSummaryGroup('Resources', [\n\t\t\t\t['Logical / physical', `${snapshot.resources.length} / ${snapshot.physicalAllocations.length}`],\n\t\t\t\t['Texture views', snapshot.availability.textureViews ? String(snapshot.textureViewById.size) : 'Unknown'],\n\t\t\t\t['Access regions', snapshot.availability.accessRegions ? 'Available' : 'Unknown'],\n\t\t\t\t['Transient estimate', formatEstimatedBytes(metrics.transientEstimatedByteSize)],\n\t\t\t]),\n\t\t\tthis.createSummaryGroup('Pool', [\n\t\t\t\t['Retained', poolRetained],\n\t\t\t\t['Reuse', formatPoolHitRate(snapshot)],\n\t\t\t]),\n\t\t);\n\t}\n\n\tprivate createSummaryGroup(title: string, rows: readonly (readonly [string, string])[]): HTMLElement {\n\t\tconst group = document.createElement('section');\n\t\tconst heading = document.createElement('h2');\n\t\theading.textContent = title;\n\t\tgroup.appendChild(heading);\n\t\tfor (const [label, value] of rows) {\n\t\t\tconst row = document.createElement('div');\n\t\t\tconst term = document.createElement('span');\n\t\t\tterm.textContent = label;\n\t\t\tconst description = document.createElement('strong');\n\t\t\tdescription.textContent = value;\n\t\t\tdescription.title = value;\n\t\t\trow.append(term, description);\n\t\t\tgroup.appendChild(row);\n\t\t}\n\t\treturn group;\n\t}\n\n\tprivate setExportMenuOpen(open: boolean): void {\n\t\tthis.exportMenu.hidden = !open;\n\t\tthis.exportButton.setAttribute('aria-expanded', open ? 'true' : 'false');\n\t}\n\n\tprivate handleInspectorOpenChange(_open: boolean): void {\n\t\tthis.updateWorkspaceState();\n\t\tif (this.activeTab === 'graph') this.resizeGraph(false);\n\t}\n\n\tprivate updateWorkspaceState(): void {\n\t\tconst inspectorOpen = this.hasSnapshot && this.inspector.isOpen;\n\t\tthis.workspace.classList.toggle('has-capture', this.hasSnapshot);\n\t\tthis.workspace.classList.toggle('inspector-open', inspectorOpen);\n\t\tthis.inspector.root.classList.toggle('unavailable', !this.hasSnapshot);\n\t\tthis.inspectorOpenButton.hidden = !this.hasSnapshot || inspectorOpen;\n\t}\n}\n"]}
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Query-string flag recognized by {@link isZenFGInspectorRequested}. */
|
|
2
|
+
export declare const ZENFG_INSPECTOR_QUERY_PARAM = "fg";
|
|
3
|
+
/**
|
|
4
|
+
* Reports whether a URL contains the `fg` inspector opt-in parameter.
|
|
5
|
+
*
|
|
6
|
+
* @param href - Absolute URL accepted by `URL`; defaults to the current page
|
|
7
|
+
* URL when a browser location is available.
|
|
8
|
+
* @returns `false` when no URL is available or the supplied URL is invalid.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isZenFGInspectorRequested(href?: string): boolean;
|
|
11
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,SAA4B,GAAG,OAAO,CAWnF"}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Query-string flag recognized by {@link isZenFGInspectorRequested}. */
|
|
2
|
+
export const ZENFG_INSPECTOR_QUERY_PARAM = 'fg';
|
|
3
|
+
/**
|
|
4
|
+
* Reports whether a URL contains the `fg` inspector opt-in parameter.
|
|
5
|
+
*
|
|
6
|
+
* @param href - Absolute URL accepted by `URL`; defaults to the current page
|
|
7
|
+
* URL when a browser location is available.
|
|
8
|
+
* @returns `false` when no URL is available or the supplied URL is invalid.
|
|
9
|
+
*/
|
|
10
|
+
export function isZenFGInspectorRequested(href = globalThis.location?.href) {
|
|
11
|
+
if (!href) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return new URL(href).searchParams.has(ZENFG_INSPECTOR_QUERY_PARAM);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI;IACtE,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACD,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,CAAC;QACH,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC","sourcesContent":["/** Query-string flag recognized by {@link isZenFGInspectorRequested}. */\nexport const ZENFG_INSPECTOR_QUERY_PARAM = 'fg';\n\n/**\n * Reports whether a URL contains the `fg` inspector opt-in parameter.\n *\n * @param href - Absolute URL accepted by `URL`; defaults to the current page\n * URL when a browser location is available.\n * @returns `false` when no URL is available or the supplied URL is invalid.\n */\nexport function isZenFGInspectorRequested(href = globalThis.location?.href): boolean {\n if (!href) {\r\n return false;\r\n }\r\n\r\n try {\r\n return new URL(href).searchParams.has(ZENFG_INSPECTOR_QUERY_PARAM);\r\n }\r\n catch {\r\n return false;\r\n }\r\n}\r\n"]}
|
package/dist/styles.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installs the inspector's shared stylesheet in the current document once.
|
|
3
|
+
*
|
|
4
|
+
* @remarks This is safe to call repeatedly and is a no-op during server-side
|
|
5
|
+
* rendering. `FrameGraphInspector` calls it automatically.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ensureFrameGraphInspectorStyles(): void;
|
|
8
|
+
//# sourceMappingURL=styles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"AAoiCA;;;;;GAKG;AACH,wBAAgB,+BAA+B,IAAI,IAAI,CAStD"}
|