@three-flatland/devtools 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +82 -0
- package/dist/buffers-modal.cjs +491 -0
- package/dist/buffers-modal.cjs.map +1 -0
- package/dist/buffers-modal.d.cts +62 -0
- package/dist/buffers-modal.d.ts +62 -0
- package/dist/buffers-modal.js +467 -0
- package/dist/buffers-modal.js.map +1 -0
- package/dist/buffers-view.cjs +269 -0
- package/dist/buffers-view.cjs.map +1 -0
- package/dist/buffers-view.d.cts +39 -0
- package/dist/buffers-view.d.ts +39 -0
- package/dist/buffers-view.js +245 -0
- package/dist/buffers-view.js.map +1 -0
- package/dist/create-pane.cjs +203 -0
- package/dist/create-pane.cjs.map +1 -0
- package/dist/create-pane.d.cts +66 -0
- package/dist/create-pane.d.ts +66 -0
- package/dist/create-pane.js +178 -0
- package/dist/create-pane.js.map +1 -0
- package/dist/dashboard/app.tsx +42 -0
- package/dist/dashboard/client.ts +21 -0
- package/dist/dashboard/export.ts +84 -0
- package/dist/dashboard/hooks.ts +75 -0
- package/dist/dashboard/index.html +1149 -0
- package/dist/dashboard/index.tsx +18 -0
- package/dist/dashboard/panels/batches.tsx +282 -0
- package/dist/dashboard/panels/buffers.tsx +425 -0
- package/dist/dashboard/panels/env.tsx +85 -0
- package/dist/dashboard/panels/header-stats.tsx +30 -0
- package/dist/dashboard/panels/producer-select.tsx +81 -0
- package/dist/dashboard/panels/protocol-log.tsx +743 -0
- package/dist/dashboard/panels/registry.tsx +642 -0
- package/dist/dashboard/panels/stats.tsx +431 -0
- package/dist/dashboard/protocol-store.ts +312 -0
- package/dist/dashboard/tsconfig.json +19 -0
- package/dist/dashboard/vendor/hooks.module.js +1 -0
- package/dist/dashboard/vendor/jsx-runtime.js +22 -0
- package/dist/dashboard/vendor/preact.module.js +12 -0
- package/dist/devtools-client.cjs +870 -0
- package/dist/devtools-client.cjs.map +1 -0
- package/dist/devtools-client.d.cts +362 -0
- package/dist/devtools-client.d.ts +362 -0
- package/dist/devtools-client.js +852 -0
- package/dist/devtools-client.js.map +1 -0
- package/dist/index.cjs +42 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/perf-trace.cjs +103 -0
- package/dist/perf-trace.cjs.map +1 -0
- package/dist/perf-trace.d.cts +27 -0
- package/dist/perf-trace.d.ts +27 -0
- package/dist/perf-trace.js +79 -0
- package/dist/perf-trace.js.map +1 -0
- package/dist/plugins.cjs +44 -0
- package/dist/plugins.cjs.map +1 -0
- package/dist/plugins.d.cts +7 -0
- package/dist/plugins.d.ts +7 -0
- package/dist/plugins.js +9 -0
- package/dist/plugins.js.map +1 -0
- package/dist/provider-switcher.cjs +95 -0
- package/dist/provider-switcher.cjs.map +1 -0
- package/dist/provider-switcher.d.cts +18 -0
- package/dist/provider-switcher.d.ts +18 -0
- package/dist/provider-switcher.js +71 -0
- package/dist/provider-switcher.js.map +1 -0
- package/dist/react/devtools-provider.cjs +59 -0
- package/dist/react/devtools-provider.cjs.map +1 -0
- package/dist/react/devtools-provider.d.cts +39 -0
- package/dist/react/devtools-provider.d.ts +39 -0
- package/dist/react/devtools-provider.js +38 -0
- package/dist/react/devtools-provider.js.map +1 -0
- package/dist/react/use-fps-graph.cjs +49 -0
- package/dist/react/use-fps-graph.cjs.map +1 -0
- package/dist/react/use-fps-graph.d.cts +15 -0
- package/dist/react/use-fps-graph.d.ts +15 -0
- package/dist/react/use-fps-graph.js +25 -0
- package/dist/react/use-fps-graph.js.map +1 -0
- package/dist/react/use-pane-button.cjs +58 -0
- package/dist/react/use-pane-button.cjs.map +1 -0
- package/dist/react/use-pane-button.d.cts +10 -0
- package/dist/react/use-pane-button.d.ts +10 -0
- package/dist/react/use-pane-button.js +34 -0
- package/dist/react/use-pane-button.js.map +1 -0
- package/dist/react/use-pane-folder.cjs +48 -0
- package/dist/react/use-pane-folder.cjs.map +1 -0
- package/dist/react/use-pane-folder.d.cts +15 -0
- package/dist/react/use-pane-folder.d.ts +15 -0
- package/dist/react/use-pane-folder.js +24 -0
- package/dist/react/use-pane-folder.js.map +1 -0
- package/dist/react/use-pane-input.cjs +70 -0
- package/dist/react/use-pane-input.cjs.map +1 -0
- package/dist/react/use-pane-input.d.cts +44 -0
- package/dist/react/use-pane-input.d.ts +44 -0
- package/dist/react/use-pane-input.js +46 -0
- package/dist/react/use-pane-input.js.map +1 -0
- package/dist/react/use-pane-radio-grid.cjs +78 -0
- package/dist/react/use-pane-radio-grid.cjs.map +1 -0
- package/dist/react/use-pane-radio-grid.d.cts +35 -0
- package/dist/react/use-pane-radio-grid.d.ts +35 -0
- package/dist/react/use-pane-radio-grid.js +54 -0
- package/dist/react/use-pane-radio-grid.js.map +1 -0
- package/dist/react/use-pane.cjs +45 -0
- package/dist/react/use-pane.cjs.map +1 -0
- package/dist/react/use-pane.d.cts +40 -0
- package/dist/react/use-pane.d.ts +40 -0
- package/dist/react/use-pane.js +21 -0
- package/dist/react/use-pane.js.map +1 -0
- package/dist/react.cjs +50 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +12 -0
- package/dist/react.d.ts +12 -0
- package/dist/react.js +19 -0
- package/dist/react.js.map +1 -0
- package/dist/registry-view.cjs +371 -0
- package/dist/registry-view.cjs.map +1 -0
- package/dist/registry-view.d.cts +24 -0
- package/dist/registry-view.d.ts +24 -0
- package/dist/registry-view.js +347 -0
- package/dist/registry-view.js.map +1 -0
- package/dist/stats-graph.cjs +297 -0
- package/dist/stats-graph.cjs.map +1 -0
- package/dist/stats-graph.d.cts +40 -0
- package/dist/stats-graph.d.ts +40 -0
- package/dist/stats-graph.js +273 -0
- package/dist/stats-graph.js.map +1 -0
- package/dist/stats-row.cjs +153 -0
- package/dist/stats-row.cjs.map +1 -0
- package/dist/stats-row.d.cts +24 -0
- package/dist/stats-row.d.ts +24 -0
- package/dist/stats-row.js +129 -0
- package/dist/stats-row.js.map +1 -0
- package/dist/theme.cjs +260 -0
- package/dist/theme.cjs.map +1 -0
- package/dist/theme.d.cts +10 -0
- package/dist/theme.d.ts +10 -0
- package/dist/theme.js +235 -0
- package/dist/theme.js.map +1 -0
- package/dist/vite-plugin.cjs +113 -0
- package/dist/vite-plugin.cjs.map +1 -0
- package/dist/vite-plugin.d.cts +9 -0
- package/dist/vite-plugin.d.ts +9 -0
- package/dist/vite-plugin.js +88 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +124 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
/**
|
|
3
|
+
* Devtools dashboard — standalone full-page inspector.
|
|
4
|
+
*
|
|
5
|
+
* Mounted by the Vite plugin at `/__three-flatland__/devtools`. Shares the
|
|
6
|
+
* same `BroadcastChannel` discovery as the Tweakpane client, so any producer
|
|
7
|
+
* the app creates is visible here automatically. No dependency on Tweakpane.
|
|
8
|
+
*
|
|
9
|
+
* Rendered with vendored Preact (see `./vendor/`). No runtime dep leaks into
|
|
10
|
+
* the published package — the plugin only serves this entry when the user
|
|
11
|
+
* opts in.
|
|
12
|
+
*/
|
|
13
|
+
import { render } from 'preact'
|
|
14
|
+
import { App } from './app'
|
|
15
|
+
|
|
16
|
+
const root = document.getElementById('root')
|
|
17
|
+
if (root === null) throw new Error('[devtools-dashboard] #root missing')
|
|
18
|
+
render(<App />, root)
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/** @jsxImportSource preact */
|
|
2
|
+
/**
|
|
3
|
+
* Batch inspector — renders the two arrays in
|
|
4
|
+
* `DevtoolsState.batches`:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Active batches** (first) — the current contents of every batch
|
|
7
|
+
* source (ECS `BatchRegistry` + engine-owned `InstancedMesh`es like
|
|
8
|
+
* tilemap chunks) grouped by `(kind, runKey)`. Each run header
|
|
9
|
+
* shows a kind badge + material label + total instance count; each
|
|
10
|
+
* row shows the batch index or label and its live instance count.
|
|
11
|
+
*
|
|
12
|
+
* 2. **Passes** — a flat, indented list of render passes that fired
|
|
13
|
+
* this frame. Each parent pass (e.g. `frame`, `sdf`) has a caret
|
|
14
|
+
* that collapses its subtree. Collapse state is keyed by label
|
|
15
|
+
* (not index) so it survives frame-to-frame churn. Totals row at
|
|
16
|
+
* the bottom sums root-level entries.
|
|
17
|
+
*
|
|
18
|
+
* Sections themselves are not collapsible; the panel is always two
|
|
19
|
+
* tables. The whole panel is a single scroll container — inner lists
|
|
20
|
+
* don't create their own scrollbars.
|
|
21
|
+
*/
|
|
22
|
+
import { useMemo, useState } from 'preact/hooks'
|
|
23
|
+
import { useDevtoolsState } from '../hooks.js'
|
|
24
|
+
import type { BatchPassSnapshot } from '../../devtools-client.js'
|
|
25
|
+
|
|
26
|
+
interface GroupedRun {
|
|
27
|
+
runKey: number
|
|
28
|
+
layer: number
|
|
29
|
+
materialId: number
|
|
30
|
+
materialName: string
|
|
31
|
+
kind: string
|
|
32
|
+
totalSprites: number
|
|
33
|
+
batches: Array<{ batchIdx: number; spriteCount: number; label: string }>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface PassNode {
|
|
37
|
+
pass: BatchPassSnapshot
|
|
38
|
+
index: number
|
|
39
|
+
children: PassNode[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function BatchesPanel() {
|
|
43
|
+
const state = useDevtoolsState()
|
|
44
|
+
const batches = state.batches
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Collapse state for pass subtrees. Keyed by pass label — labels
|
|
48
|
+
* (`frame`, `sdf`, `sdf.seed`, etc.) are stable string constants
|
|
49
|
+
* so the set survives per-frame rebuilds. Collapse state for leaf
|
|
50
|
+
* passes is irrelevant (no children to hide) so this set only ever
|
|
51
|
+
* contains parent labels.
|
|
52
|
+
*/
|
|
53
|
+
const [collapsedPasses, setCollapsedPasses] = useState<Set<string>>(() => new Set())
|
|
54
|
+
const toggleCollapsed = (label: string) => {
|
|
55
|
+
setCollapsedPasses((prev) => {
|
|
56
|
+
const next = new Set(prev)
|
|
57
|
+
if (next.has(label)) next.delete(label)
|
|
58
|
+
else next.add(label)
|
|
59
|
+
return next
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const passTotals = useMemo(() => {
|
|
64
|
+
let calls = 0
|
|
65
|
+
let tris = 0
|
|
66
|
+
let cpuMs = 0
|
|
67
|
+
for (const p of batches.passes) {
|
|
68
|
+
if (p.parent === -1) {
|
|
69
|
+
calls += p.calls
|
|
70
|
+
tris += p.triangles
|
|
71
|
+
cpuMs += p.cpuMs
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { calls, tris, cpuMs }
|
|
75
|
+
}, [batches.frame])
|
|
76
|
+
|
|
77
|
+
const runs = useMemo(() => {
|
|
78
|
+
const m = new Map<string, GroupedRun>()
|
|
79
|
+
for (const b of batches.batches) {
|
|
80
|
+
const groupKey = `${b.kind}:${b.runKey}`
|
|
81
|
+
let run = m.get(groupKey)
|
|
82
|
+
if (run === undefined) {
|
|
83
|
+
run = {
|
|
84
|
+
runKey: b.runKey,
|
|
85
|
+
layer: b.layer,
|
|
86
|
+
materialId: b.materialId,
|
|
87
|
+
materialName: b.materialName,
|
|
88
|
+
kind: b.kind,
|
|
89
|
+
totalSprites: 0,
|
|
90
|
+
batches: [],
|
|
91
|
+
}
|
|
92
|
+
m.set(groupKey, run)
|
|
93
|
+
}
|
|
94
|
+
run.totalSprites += b.spriteCount
|
|
95
|
+
run.batches.push({ batchIdx: b.batchIdx, spriteCount: b.spriteCount, label: b.label })
|
|
96
|
+
}
|
|
97
|
+
const arr = Array.from(m.values())
|
|
98
|
+
arr.sort((a, b) =>
|
|
99
|
+
a.kind.localeCompare(b.kind) || a.layer - b.layer || a.materialId - b.materialId,
|
|
100
|
+
)
|
|
101
|
+
return arr
|
|
102
|
+
}, [batches.frame])
|
|
103
|
+
|
|
104
|
+
const totalSprites = useMemo(() => {
|
|
105
|
+
let n = 0
|
|
106
|
+
for (const r of runs) n += r.totalSprites
|
|
107
|
+
return n
|
|
108
|
+
}, [runs])
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Rebuild the pass tree from the flat array. Parents come before
|
|
112
|
+
* children in the producer's emission order (frame first, then its
|
|
113
|
+
* children, etc.) so one linear pass + index lookup suffices.
|
|
114
|
+
*/
|
|
115
|
+
const passRoots = useMemo(() => {
|
|
116
|
+
const nodes: PassNode[] = batches.passes.map((p, i) => ({
|
|
117
|
+
pass: p,
|
|
118
|
+
index: i,
|
|
119
|
+
children: [],
|
|
120
|
+
}))
|
|
121
|
+
const roots: PassNode[] = []
|
|
122
|
+
for (const n of nodes) {
|
|
123
|
+
if (n.pass.parent === -1) roots.push(n)
|
|
124
|
+
else nodes[n.pass.parent]?.children.push(n)
|
|
125
|
+
}
|
|
126
|
+
return roots
|
|
127
|
+
}, [batches.frame])
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<section class="panel batches-panel">
|
|
131
|
+
<header class="panel-header batches-header">
|
|
132
|
+
<span>Batches</span>
|
|
133
|
+
<span class="batches-header-meta">
|
|
134
|
+
frame {batches.frame} · {passTotals.calls} draws · {runs.length} runs · {batches.batches.length} batches · {totalSprites} sprites
|
|
135
|
+
</span>
|
|
136
|
+
</header>
|
|
137
|
+
|
|
138
|
+
<div class="batches-scroll">
|
|
139
|
+
<div class="batches-section">
|
|
140
|
+
<div class="batches-section-title">
|
|
141
|
+
<span>Active batches</span>
|
|
142
|
+
<span class="batches-section-count">{batches.batches.length}</span>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="batches-table-head batches-table-head--runs">
|
|
145
|
+
<span class="batches-col-label">run / batch</span>
|
|
146
|
+
<span class="batches-col-num">layer</span>
|
|
147
|
+
<span class="batches-col-num">sprites</span>
|
|
148
|
+
</div>
|
|
149
|
+
<ul class="batches-run-list">
|
|
150
|
+
{runs.length === 0 ? (
|
|
151
|
+
<li class="batches-empty">no active batches</li>
|
|
152
|
+
) : (
|
|
153
|
+
runs.map((r) => (
|
|
154
|
+
<li key={`${r.kind}:${r.runKey}`} class="batches-run">
|
|
155
|
+
<div class="batches-run-head">
|
|
156
|
+
<span class="batches-col-label batches-run-label">
|
|
157
|
+
<span class={`batches-kind batches-kind--${r.kind}`}>{r.kind}</span>
|
|
158
|
+
<span class="batches-mat">{r.materialName}</span>
|
|
159
|
+
<span class="batches-run-sub">mat#{r.materialId}</span>
|
|
160
|
+
</span>
|
|
161
|
+
<span class="batches-col-num">{r.layer}</span>
|
|
162
|
+
<span class="batches-col-num">{r.totalSprites}</span>
|
|
163
|
+
</div>
|
|
164
|
+
{r.batches.length > 1 && (
|
|
165
|
+
<ul class="batches-run-children">
|
|
166
|
+
{r.batches.map((b) => (
|
|
167
|
+
<li key={b.batchIdx} class="batches-batch-row">
|
|
168
|
+
<span class="batches-col-label batches-batch-label">
|
|
169
|
+
{b.label.length > 0 ? b.label : `batch #${b.batchIdx}`}
|
|
170
|
+
</span>
|
|
171
|
+
<span class="batches-col-num">—</span>
|
|
172
|
+
<span class="batches-col-num">{b.spriteCount}</span>
|
|
173
|
+
</li>
|
|
174
|
+
))}
|
|
175
|
+
</ul>
|
|
176
|
+
)}
|
|
177
|
+
</li>
|
|
178
|
+
))
|
|
179
|
+
)}
|
|
180
|
+
</ul>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div class="batches-section">
|
|
184
|
+
<div class="batches-section-title">
|
|
185
|
+
<span>Passes</span>
|
|
186
|
+
<span class="batches-section-count">{batches.passes.length}</span>
|
|
187
|
+
</div>
|
|
188
|
+
<div class="batches-table-head batches-table-head--passes">
|
|
189
|
+
<span class="batches-col-label">pass</span>
|
|
190
|
+
<span class="batches-col-num">calls</span>
|
|
191
|
+
<span class="batches-col-num">tris</span>
|
|
192
|
+
<span class="batches-col-num">ms</span>
|
|
193
|
+
</div>
|
|
194
|
+
<ul class="batches-pass-list">
|
|
195
|
+
{passRoots.length === 0 ? (
|
|
196
|
+
<li class="batches-empty">no passes captured</li>
|
|
197
|
+
) : (
|
|
198
|
+
passRoots.map((root) => (
|
|
199
|
+
<PassRow
|
|
200
|
+
key={root.index}
|
|
201
|
+
node={root}
|
|
202
|
+
collapsed={collapsedPasses}
|
|
203
|
+
onToggle={toggleCollapsed}
|
|
204
|
+
/>
|
|
205
|
+
))
|
|
206
|
+
)}
|
|
207
|
+
</ul>
|
|
208
|
+
<div class="batches-total-row">
|
|
209
|
+
<span class="batches-col-label">total (root)</span>
|
|
210
|
+
<span class="batches-col-num">{passTotals.calls}</span>
|
|
211
|
+
<span class="batches-col-num">{formatTris(passTotals.tris)}</span>
|
|
212
|
+
<span class="batches-col-num">{passTotals.cpuMs.toFixed(2)}</span>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
</section>
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Recursive row renderer for the pass tree. A pass is "expandable"
|
|
222
|
+
* when it has children; clicking its caret toggles the subtree.
|
|
223
|
+
* Leaf passes render without a caret. Indentation scales with depth
|
|
224
|
+
* so the tree structure is obvious even without explicit tree lines.
|
|
225
|
+
*/
|
|
226
|
+
function PassRow({
|
|
227
|
+
node,
|
|
228
|
+
collapsed,
|
|
229
|
+
onToggle,
|
|
230
|
+
}: {
|
|
231
|
+
node: PassNode
|
|
232
|
+
collapsed: Set<string>
|
|
233
|
+
onToggle: (label: string) => void
|
|
234
|
+
}) {
|
|
235
|
+
const { pass, children } = node
|
|
236
|
+
const hasChildren = children.length > 0
|
|
237
|
+
const isCollapsed = collapsed.has(pass.label)
|
|
238
|
+
const pad = 8 + pass.depth * 12
|
|
239
|
+
|
|
240
|
+
return (
|
|
241
|
+
<>
|
|
242
|
+
<li
|
|
243
|
+
class={`batches-pass-row${hasChildren ? ' batches-pass-row--parent' : ''}`}
|
|
244
|
+
style={{ paddingLeft: `${pad}px` }}
|
|
245
|
+
>
|
|
246
|
+
<span class="batches-col-label batches-pass-label">
|
|
247
|
+
{hasChildren ? (
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
class="batches-tree-caret"
|
|
251
|
+
aria-expanded={!isCollapsed}
|
|
252
|
+
onClick={() => onToggle(pass.label)}
|
|
253
|
+
>
|
|
254
|
+
{isCollapsed ? '▸' : '▾'}
|
|
255
|
+
</button>
|
|
256
|
+
) : (
|
|
257
|
+
<span class="batches-tree-caret batches-tree-caret--leaf" />
|
|
258
|
+
)}
|
|
259
|
+
<span>{pass.label}</span>
|
|
260
|
+
</span>
|
|
261
|
+
<span class="batches-col-num">{pass.calls}</span>
|
|
262
|
+
<span class="batches-col-num">{formatTris(pass.triangles)}</span>
|
|
263
|
+
<span class="batches-col-num">{pass.cpuMs.toFixed(2)}</span>
|
|
264
|
+
</li>
|
|
265
|
+
{hasChildren && !isCollapsed
|
|
266
|
+
? children.map((c) => (
|
|
267
|
+
<PassRow
|
|
268
|
+
key={c.index}
|
|
269
|
+
node={c}
|
|
270
|
+
collapsed={collapsed}
|
|
271
|
+
onToggle={onToggle}
|
|
272
|
+
/>
|
|
273
|
+
))
|
|
274
|
+
: null}
|
|
275
|
+
</>
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function formatTris(n: number): string {
|
|
280
|
+
if (n >= 10_000) return `${(n / 1000).toFixed(1)}k`
|
|
281
|
+
return n.toString()
|
|
282
|
+
}
|