架构微调

This commit is contained in:
2026-08-11 11:21:54 +08:00
parent 0dcf1fbb35
commit 5710da3acf
3 changed files with 48 additions and 22 deletions
+39 -13
View File
@@ -240,7 +240,13 @@ class GalleryCard {
this.node.querySelector(".perf-pending").textContent = observer.pending_frame_count ?? 0;
this.node.querySelector(".perf-dropped").textContent = observer.dropped_frame_count ?? 0;
this.node.querySelector(".perf-points").textContent = `${Number(dataShape.input_points || 0).toLocaleString()}${Number(dataShape.rendered_elements || 0).toLocaleString()}`;
this.node.querySelector(".perf-limit").textContent = limitNames[limit.current] || limit.current || "N/A";
const limitDurations = {frequency_limited: observer.target_interval_ns, paint_limited: observer.paint_duration_ns, render_limited: observer.render_duration_ns, consumer_limited: observer.consumer_interval_ns};
const limitName = limitNames[limit.current] || limit.current || "N/A";
const limitDuration = limitDurations[limit.current];
const limitValue = limitDuration === undefined ? limitName : `${limitName} · ${formatNanoseconds(limitDuration)}`;
const limitNode = this.node.querySelector(".perf-limit");
limitNode.textContent = limitValue;
limitNode.title = limitDuration === undefined ? limitName : `${limitName} · ${Number(limitDuration || 0).toLocaleString()} ns`;
this.node.querySelector(".perf-event").textContent = observer.last_event || "none";
this.node.querySelector(".perf-timeouts").textContent = this.frameTimeoutCount.toLocaleString();
this.node.querySelector(".perf-pixel-age").textContent = this.lastPixelReceivedAt ? `${Math.max(0, performance.now() - this.lastPixelReceivedAt).toFixed(0)} ms` : "—";
@@ -249,7 +255,7 @@ class GalleryCard {
const setLimitFlag = (selector, enabled, active, duration) => {
const flag = this.node.querySelector(selector);
flag.dataset.active = String(Boolean(active));
flag.querySelector("b").textContent = enabled ? `${active ? "受限" : "未受限"} · ${formatNanoseconds(duration)}` : "已关闭";
flag.querySelector("b").textContent = enabled ? `${active ? "当前瓶颈" : "未受限"} · ${formatNanoseconds(duration)}` : `已关闭 · ${formatNanoseconds(duration)}`;
};
setLimitFlag(".limit-frequency", observer.frequency_limit_enabled !== false, limit.current === "frequency_limited", observer.target_interval_ns);
setLimitFlag(".limit-paint", true, limit.current === "paint_limited", observer.paint_duration_ns);
@@ -514,22 +520,42 @@ function renderControl(item) {
if (item.input === "number") { input.min = item.minimum; input.max = item.maximum; input.step = item.step || "any"; }
}
input.className = "control-input"; input.title = item.description || item.api;
let submitTimer = 0;
const submit = () => {
clearTimeout(submitTimer); submitTimer = 0;
const value = item.input === "boolean" ? input.checked : item.input === "number" ? Number(input.value) : input.value;
if (item.input === "number" && (input.value === "" || !Number.isFinite(value))) return;
const submit = value => {
card.send("gallery_patch", {patch: {[item.id]: value}});
elements.menuStatus.textContent = `提交 ${item.api} · 等待后端回读`;
};
if (item.input === "number") {
input.addEventListener("input", () => {
clearTimeout(submitTimer);
submitTimer = setTimeout(submit, 120);
let committedValue = input.value;
const commit = () => {
const value = input.valueAsNumber;
const minimum = Number(item.minimum);
const maximum = Number(item.maximum);
if (input.value === "" || !Number.isFinite(value) ||
(Number.isFinite(minimum) && value < minimum) ||
(Number.isFinite(maximum) && value > maximum)) {
input.value = committedValue;
return;
}
if (value === Number(committedValue)) {
input.value = committedValue;
return;
}
committedValue = input.value;
item.value = value;
submit(value);
};
input.addEventListener("blur", commit);
input.addEventListener("keydown", event => {
if (event.key === "Enter") {
event.preventDefault();
commit();
} else if (event.key === "Escape") {
event.preventDefault();
event.stopPropagation();
input.value = committedValue;
}
});
input.addEventListener("change", submit);
input.addEventListener("keydown", event => { if (event.key === "Enter") submit(); });
} else input.addEventListener("change", submit);
} else input.addEventListener("change", () => submit(item.input === "boolean" ? input.checked : input.value));
row.append(copy, input); return row;
}
function renderAction(item) {
+1 -1
View File
@@ -89,7 +89,7 @@
<div><dt class="perf-pending">0</dt><dd>Kernel 待处理</dd></div>
<div><dt class="perf-dropped">0</dt><dd>Kernel 丢弃</dd></div>
<div><dt class="perf-points">0→0</dt><dd>输入→绘制</dd></div>
<div><dt class="perf-limit">N/A</dt><dd>当前瓶颈</dd></div>
<div class="perf-limit-cell"><dt class="perf-limit">N/A</dt><dd>当前瓶颈</dd></div>
<div><dt class="perf-event">none</dt><dd>观察事件</dd></div>
<div><dt class="perf-timeouts">0</dt><dd>像素超时</dd></div>
<div><dt class="perf-pixel-age"></dt><dd>最近像素龄</dd></div>
+8 -8
View File
@@ -57,16 +57,16 @@ button:hover { border-color: var(--accent); } button:active { transform: transla
.plot-card[data-mode="manual"] { border-top-color: #ffd166aa; }.plot-card[data-mode="low_latency"] { border-top-color: #45ddbeaa; }.plot-card[data-mode="playback"] { border-top-color: #6aa9ffaa; }.plot-card[hidden] { display:none; }
.card-header, .card-footer { display:flex; align-items:center; justify-content:space-between; gap:15px; padding:13px 15px; }.card-header { border-bottom:1px solid var(--line); }.card-category { display:block; margin-bottom:4px; color:var(--blue); font:700 9px/1 ui-monospace,monospace; letter-spacing:.1em; }.card-title { margin:0; font-size:17px; }
.canvas-shell { position:relative; height:clamp(245px,27vw,350px); outline:none; background:#060a11; cursor:crosshair; }.canvas-shell:focus-visible { box-shadow:inset 0 0 0 2px var(--accent); }.canvas-shell canvas { display:block; width:100%; height:100%; }.canvas-hint { position:absolute; right:9px; bottom:8px; padding:5px 7px; border:1px solid #ffffff1e; border-radius:5px; color:#ffffffa8; background:#04070bc9; font:9px/1 ui-monospace,monospace; pointer-events:none; }.canvas-loading { position:absolute; inset:0; display:grid; place-content:center; justify-items:center; gap:9px; color:var(--muted); background:#080d15; }.plot-card[data-ready="true"] .canvas-loading { display:none; }
.performance-strip { display:grid; grid-template-columns:repeat(11,1fr); gap:1px; margin:0; border-block:1px solid var(--line); background:var(--line); }.performance-strip div { min-width:0; padding:9px 8px; background:#0c1118; }.performance-strip dt { overflow:hidden; color:var(--accent); font:700 11px/1 ui-monospace,monospace; text-overflow:ellipsis; white-space:nowrap; }.performance-strip .perf-event,.performance-strip .perf-limit { font-size:8px; }.performance-strip dd { margin:5px 0 0; color:var(--muted); font-size:8px; }
.limit-flags { display:flex; flex-wrap:wrap; align-items:center; gap:7px; padding:7px 10px; border-bottom:1px solid var(--line); color:var(--muted); background:#090e15; font:9px/1.2 ui-monospace,monospace; }.limit-flags strong { margin-right:auto; color:var(--text); }.limit-flags span { padding:4px 6px; border:1px solid var(--line); border-radius:4px; }.limit-flags span[data-active="true"] { border-color:#ffb84d88; color:#ffcf82; background:#ff9d1712; }.limit-flags b { color:inherit; }.plot-card:not([data-mode="low_latency"]) .limit-flags { display:none; }
.performance-strip { display:grid; grid-template-columns:repeat(auto-fit,minmax(105px,1fr)); gap:1px; margin:0; border-block:1px solid var(--line); background:var(--line); }.performance-strip div { min-width:0; padding:9px 8px; background:#0c1118; }.performance-strip dt { min-width:0; overflow:visible; color:var(--accent); font:700 11px/1.35 ui-monospace,monospace; text-overflow:clip; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }.performance-strip .perf-event,.performance-strip .perf-limit { font-size:10px; }.performance-strip .perf-limit-cell { grid-column:auto; }.performance-strip dd { margin:5px 0 0; color:var(--muted); font-size:8px; line-height:1.35; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }
.limit-flags { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:7px; padding:7px 10px; border-bottom:1px solid var(--line); color:var(--muted); background:#090e15; font:9px/1.35 ui-monospace,monospace; }.limit-flags strong { grid-column:1/-1; color:var(--text); }.limit-flags span { min-width:0; padding:6px 7px; border:1px solid var(--line); border-radius:4px; line-height:1.45; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }.limit-flags span[data-active="true"] { border-color:#ffb84d88; color:#ffcf82; background:#ff9d1712; }.limit-flags b { color:inherit; white-space:normal; overflow-wrap:anywhere; }.plot-card:not([data-mode="low_latency"]) .limit-flags { display:none; }
.kernel-observer-panel { border-bottom:1px solid var(--line); background:#080d14; font-family:ui-monospace,monospace; }
.kernel-observer-panel > header { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:9px 11px; border-bottom:1px solid var(--line); color:var(--muted); font-size:8px; }
.kernel-observer-panel > header div:first-child { display:flex; align-items:center; gap:9px; }.kernel-observer-panel > header span { color:var(--accent); letter-spacing:.08em; }.kernel-observer-panel > header strong,.kernel-observer-panel > header b { color:var(--text); font-weight:700; }
.kernel-observer-panel > header { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:9px 11px; border-bottom:1px solid var(--line); color:var(--muted); font-size:8px; flex-wrap:wrap; }
.kernel-observer-panel > header div:first-child { display:flex; align-items:center; gap:9px; min-width:0; flex-wrap:wrap; }.kernel-observer-panel > header span { color:var(--accent); letter-spacing:.08em; overflow-wrap:anywhere; }.kernel-observer-panel > header strong,.kernel-observer-panel > header b { color:var(--text); font-weight:700; overflow-wrap:anywhere; }
.observer-counters,.latency-summary,.latency-details { display:grid; gap:1px; background:var(--line); }
.observer-counters { grid-template-columns:repeat(8,1fr); }.latency-summary { grid-template-columns:repeat(6,1fr); border-top:1px solid var(--line); }.latency-details { grid-template-columns:repeat(3,1fr); border-top:1px solid var(--line); }
.observer-counters { grid-template-columns:repeat(auto-fit,minmax(90px,1fr)); }.latency-summary { grid-template-columns:repeat(auto-fit,minmax(105px,1fr)); border-top:1px solid var(--line); }.latency-details { grid-template-columns:repeat(auto-fit,minmax(145px,1fr)); border-top:1px solid var(--line); }
.observer-counters div,.latency-summary div,.latency-details div { min-width:0; padding:8px 7px; background:#0b1119; }
.kernel-observer-panel small { display:block; overflow:hidden; margin-bottom:5px; color:var(--muted); font-size:7px; text-overflow:ellipsis; white-space:nowrap; }
.kernel-observer-panel b { display:block; overflow:hidden; color:#b7c7dc; font-size:9px; text-overflow:ellipsis; white-space:nowrap; }
.kernel-observer-panel small { display:block; min-width:0; overflow:visible; margin-bottom:5px; color:var(--muted); font-size:7px; line-height:1.35; text-overflow:clip; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }
.kernel-observer-panel b { display:block; min-width:0; overflow:visible; color:#b7c7dc; font-size:9px; line-height:1.35; text-overflow:clip; white-space:normal; overflow-wrap:anywhere; word-break:break-word; }
.latency-summary b { color:var(--accent); font-size:10px; }.latency-summary .critical { background:#10201e; }.latency-summary .critical b { color:#72f3d9; }
.plot-card:not([data-mode="low_latency"]) .kernel-observer-panel { display:none; }
.card-meta { display:grid; grid-template-columns:1fr auto; gap:17px; padding:13px 15px; }.card-description { margin:0; color:var(--muted); font-size:11px; line-height:1.55; }.card-meta dl { display:flex; margin:0; }.card-meta dl div { min-width:52px; padding-left:11px; border-left:1px solid var(--line); }.card-meta dt { font:700 14px/1 ui-monospace,monospace; }.card-meta dd { margin:5px 0 0; color:var(--muted); font-size:9px; }
@@ -81,4 +81,4 @@ button:hover { border-color: var(--accent); } button:active { transform: transla
.menu-footer { display:flex; justify-content:space-between; gap:14px; padding:10px 14px; border-top:1px solid var(--line); color:var(--muted); font-size:9px; }.menu-footer code { color:var(--accent); }
.toast { position:fixed; z-index:140; left:50%; bottom:22px; max-width:min(560px,calc(100vw - 30px)); padding:11px 15px; border:1px solid var(--strong); border-radius:8px; background:var(--panel2); box-shadow:0 18px 50px #00000073; transform:translateX(-50%); font-size:11px; }.toast[data-error="true"] { border-color:var(--danger); }
@media (max-width:980px) { .hero { grid-template-columns:1fr; }.gallery { grid-template-columns:1fr; }.mode-tabs button { flex-direction:column; align-items:flex-start; }.page-header { align-items:flex-start; flex-direction:column; }.page-description { text-align:left; } }
@media (max-width:650px) { .topbar { align-items:flex-start; }.topbar-actions { align-items:flex-end; flex-direction:column; }.connection-status span { display:none; }.metrics { grid-template-columns:repeat(2,1fr); }.mode-tabs { grid-template-columns:1fr; }.mode-tabs button { flex-direction:row; }.card-meta { grid-template-columns:1fr; }.performance-strip { grid-template-columns:repeat(3,1fr); }.observer-counters { grid-template-columns:repeat(4,1fr); }.latency-summary,.latency-details { grid-template-columns:repeat(2,1fr); }.context-menu { inset:auto 6px 6px!important; width:auto; max-height:calc(100vh - 12px); } }
@media (max-width:650px) { .topbar { align-items:flex-start; }.topbar-actions { align-items:flex-end; flex-direction:column; }.connection-status span { display:none; }.metrics { grid-template-columns:repeat(2,1fr); }.mode-tabs { grid-template-columns:1fr; }.mode-tabs button { flex-direction:row; }.card-meta { grid-template-columns:1fr; }.performance-strip { grid-template-columns:repeat(auto-fit,minmax(92px,1fr)); }.limit-flags { grid-template-columns:1fr; }.observer-counters { grid-template-columns:repeat(auto-fit,minmax(80px,1fr)); }.latency-summary,.latency-details { grid-template-columns:repeat(auto-fit,minmax(100px,1fr)); }.context-menu { inset:auto 6px 6px!important; width:auto; max-height:calc(100vh - 12px); } }