A GPU with nothing measurable is one line, not half the Processes page
The Mac's Asahi GPU exposes no busy counter, clock, sensor or memory reading, so its box was a graph that could never draw beside a column of dashes. A GPU with nothing to read now gets one line (driver, "nothing measurable" or the stall, and whether a process has it open), its row shrinks to fit, and the Gpu% column and gpu sort are dropped when no GPU on the machine can light them. QoL #3 from bug bash 6 (BUGS.md). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f3ca3d8f73
commit
7d0b87bc45
+26
-7
@@ -348,10 +348,13 @@ export default function Top() {
|
||||
const seg = segOf(g.name), n = all.slice(0, i).filter((o) => segOf(o.name) === seg).length
|
||||
return { g, stem: 'gpu.' + seg + (n ? `_${n + 1}` : '') }
|
||||
})
|
||||
// a GPU with nothing to read can't light a process's Gpu% either: the column, and its sort,
|
||||
// are for a machine where at least one GPU says something
|
||||
const gpuCol = gpus.some(({ g }) => !unmeasured(g))
|
||||
|
||||
return (
|
||||
<Pal.Provider value={pal}>
|
||||
<div className={'top' + (gpus.length ? ' has-gpu' : '')} style={pal.vars as React.CSSProperties}>
|
||||
<div className={'top' + (gpus.length ? ' has-gpu' : '') + (gpus.length && !gpuCol ? ' gpu-slim' : '')} style={pal.vars as React.CSSProperties}>
|
||||
<Box n={1} name="cpu" className="top-cpu" right={<>
|
||||
<span className="t-clock">{clock.toLocaleTimeString([], { hour12: false })}</span>
|
||||
<span className="t-rate">
|
||||
@@ -392,8 +395,8 @@ export default function Top() {
|
||||
{gpus.length > 0 && (
|
||||
<div className="top-gpus">
|
||||
{gpus.map(({ g, stem }, i) => (
|
||||
<Box key={stem} name={`gpu${i}`} className="top-gpu" right={<span className="t-gpu-name" title={[g.name, g.driver, g.bus].filter(Boolean).join(' · ')}>{g.name}</span>}>
|
||||
<GPUBox g={g} values={curves.get(stem + '.usage') ?? []} tick={tick} advance={advance} cap={cap} />
|
||||
<Box key={stem} name={`gpu${i}`} className={'top-gpu' + (unmeasured(g) ? ' slim' : '')} right={<span className="t-gpu-name" title={[g.name, g.driver, g.bus].filter(Boolean).join(' · ')}>{g.name}</span>}>
|
||||
{unmeasured(g) ? <GPULine g={g} /> : <GPUBox g={g} values={curves.get(stem + '.usage') ?? []} tick={tick} advance={advance} cap={cap} />}
|
||||
</Box>
|
||||
))}
|
||||
</div>
|
||||
@@ -461,17 +464,17 @@ export default function Top() {
|
||||
|
||||
<Box n={5} name="proc" className="top-proc" right={<>
|
||||
<span className="t-sorts">
|
||||
{(['pid', 'name', 'mem', ...(gpus.length ? ['gpu' as const] : []), 'cpu'] as const).map((k) => <button key={k} className={sort === k ? 'on' : ''} onClick={() => setSort(k)}>{k}</button>)}
|
||||
{(['pid', 'name', 'mem', ...(gpuCol ? ['gpu' as const] : []), 'cpu'] as const).map((k) => <button key={k} className={sort === k ? 'on' : ''} onClick={() => setSort(k)}>{k}</button>)}
|
||||
<button className={rev ? 'on' : ''} onClick={() => setRev(!rev)}>reverse</button>
|
||||
</span>
|
||||
</>}>
|
||||
<div className={'t-proc' + (gpus.length ? ' has-gpu' : '')}>
|
||||
<div className={'t-proc' + (gpuCol ? ' has-gpu' : '')}>
|
||||
<input className="t-filter" value={filter} onChange={(e) => setFilter(e.target.value)} placeholder="filter" spellCheck={false} autoCapitalize="off" autoCorrect="off" />
|
||||
<div className="t-thead">
|
||||
<span className="c-pid">Pid:</span><span className="c-name">Program:</span><span className="c-cmd">Command:</span>
|
||||
<span className="c-thr">Threads:</span><span className="c-user">User:</span><span className="c-mem">MemB</span><span className="c-g" />{gpus.length > 0 && <span className="c-gpu">Gpu%</span>}<span className="c-cpu">Cpu%</span>
|
||||
<span className="c-thr">Threads:</span><span className="c-user">User:</span><span className="c-mem">MemB</span><span className="c-g" />{gpuCol && <span className="c-gpu">Gpu%</span>}<span className="c-cpu">Cpu%</span>
|
||||
</div>
|
||||
<Rows procs={procs} sel={sel} onSel={setSel} curves={pcurve} gpu={gpus.length > 0} empty={err || 'no process matches'} />
|
||||
<Rows procs={procs} sel={sel} onSel={setSel} curves={pcurve} gpu={gpuCol} empty={err || 'no process matches'} />
|
||||
<div className="t-foot">
|
||||
{sel !== null && procs.find((p) => p.pid === sel) ? (() => { const p = procs.find((x) => x.pid === sel)!; return <>
|
||||
<span className="t-dim">{p.pid} {p.name}</span><span className="spacer" />
|
||||
@@ -569,6 +572,22 @@ function GPUBox({ g, values, tick, advance, cap }: { g: GPU; values: number[]; t
|
||||
)
|
||||
}
|
||||
|
||||
// Nothing on this GPU can be read — no busy counter, engine, temperature, clock, power, memory,
|
||||
// nor a process with it open: the Mac's Asahi, today. Its box would be a graph that can never
|
||||
// draw beside a column of dashes, so it is one line instead. A client opening it brings the box back.
|
||||
const unmeasured = (g: GPU) => !g.usage_src && !g.engines && !(g.temp && g.temp > 0) && !g.mhz && !g.max_mhz && !g.power_w && !g.power_cap_w && !g.fan_rpm && !g.mem_total && !g.mem_used && g.clients.length === 0
|
||||
function GPULine({ g }: { g: GPU }) {
|
||||
return (
|
||||
<div className="t-row t-gpu-line">
|
||||
<span>{g.driver}{g.integrated ? ' · integrated' : ''}</span>
|
||||
{g.stalled
|
||||
? <span className="t-dim" title="a sysfs read of this card stopped answering">readings paused until the console restarts</span>
|
||||
: <span className="t-dim" title="the driver exposes no busy counter, clock, temperature, power or memory reading">nothing measurable</span>}
|
||||
<span className="t-dim t-gpu-who" title={g.clients_ok ? undefined : 'the console has no root here'}>{g.clients_ok ? 'no process has it open' : 'other users’ processes are hidden'}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// One physical drive. `used` is the bytes of the filesystems on it, left out when none can
|
||||
// be attributed to it — a pool member's share of a pool is not a number worth inventing.
|
||||
function Disk({ name, note, size, used, io, temp, mounts, pool }: { name: string; note?: string; size: number; used?: number; io?: number; temp?: number; mounts?: string[]; pool?: string }) {
|
||||
|
||||
+7
-1
@@ -666,6 +666,12 @@ pre.diff .add { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%,
|
||||
.top-gpus { grid-column: 1 / -1; grid-row: 2; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 4px; min-width: 0; }
|
||||
.top.has-gpu .top-mem, .top.has-gpu .top-disks { grid-row: 3; } .top.has-gpu .top-net { grid-row: 4; } .top.has-gpu .top-proc { grid-row: 3 / 5; }
|
||||
.top-gpu { min-height: 210px; }
|
||||
/* a GPU with nothing to measure is one line, and on a machine with no other its row is only as
|
||||
tall as that line. Same weight as .top.has-gpu and after it, so the narrower layouts' own
|
||||
.top.has-gpu, further down, still wins there (their gpu row is already auto). */
|
||||
.top.gpu-slim { grid-template-rows: minmax(200px, 1fr) auto auto minmax(170px, 1fr); }
|
||||
.top-gpu.slim { align-self: start; } /* beside a full gpu box it keeps its one line, not that box's height */
|
||||
.t-row.t-gpu-line { flex-wrap: wrap; gap: 0 14px; } .t-gpu-line > span { white-space: nowrap; } .t-gpu-who { margin-left: auto; } /* on a phone it wraps to its own line, still on the right */
|
||||
.t-gpu { display: flex; gap: 8px; min-width: 0; flex: 1; min-height: 0; }
|
||||
/* the graph and its caption share this box: tall enough for both, so the caption never lands on the stats */
|
||||
.t-gpu-graph { flex: 1; min-width: 0; min-height: 130px; display: flex; flex-direction: column; }
|
||||
@@ -782,7 +788,7 @@ pre.diff .add { color: var(--ok); background: color-mix(in srgb, var(--ok) 12%,
|
||||
@media (max-width: 800px) {
|
||||
.top { display: flex; flex-direction: column; font-size: 12px; }
|
||||
.top > .tbox { flex: 0 0 auto; }
|
||||
.top-gpus { display: flex; flex-direction: column; flex: 0 0 auto; }
|
||||
.top-gpus { display: flex; flex-direction: column; flex: 0 0 auto; } .top-gpu.slim { align-self: stretch; }
|
||||
.t-gpu { flex-direction: column; } .t-gpu-graph { height: 130px; flex: 0 0 130px; } .t-gpu-stats { width: auto; max-height: none; }
|
||||
.t-cpu { flex-direction: column; } .t-cpu-graph { height: 130px; flex: 0 0 130px; } .t-cores { width: auto; max-height: 40vh; }
|
||||
.t-net { flex-direction: column; } .t-net-nums { width: auto; } .t-net-graphs { flex: 0 0 130px; }
|
||||
|
||||
Reference in New Issue
Block a user