Quiet the debug-noise that was lagging the browser

You're right — the telemetry path itself was contributing to perceived
browser lag, not just the GPU work. Two specific sources removed:

render/mod.rs::rebuild_chunk:
  - Per-chunk log::info!("rebuild_chunk {coord}: {ms}ms ...") fired
    ~289 times during world init, plus on every edit. Chrome's console
    buffer accumulates these and DevTools recalcs on each new line.
    Removed entirely. The bench toggles + FPS HUD already give us the
    measurement signal; per-chunk timing was noise once verified.

web/main.js setupFpsHud:
  - Was a 5 Hz setInterval that did `el.textContent = ...` and two
    `classList.toggle` calls each tick (15 DOM writes/sec). Now:
      * Polls at 1 Hz (the EMA already smooths)
      * Only writes textContent when the displayed string actually
        changes (skip is the common case once stabilized)
      * Only writes class when the tier (green/warn/bad) crosses
        a threshold
    Same readout, ~15× fewer DOM writes.

web/main.js setupStatusLoop (HP bar):
  - Same dedupe pattern. 10 Hz polling, but DOM writes only when
    hp or alive changes from the last sample. Was firing 3 style
    writes per tick unconditionally; now zero in steady state.

No functionality changes — every telemetry getter, every bench
toggle, every test-harness export still works (just verified via
the 18-check functional contract suite).

Wasm release built; tests 63/63 pass.
This commit is contained in:
Maximus Gorog 2026-05-24 17:22:30 -06:00
parent d622cdb330
commit a01d5c1fa9
2 changed files with 38 additions and 25 deletions

View file

@ -507,20 +507,12 @@ impl Renderer {
// immutable &World into build_chunk_mesh while still owning // immutable &World into build_chunk_mesh while still owning
// &mut self below. // &mut self below.
let chunk_clone = chunk.clone(); let chunk_clone = chunk.clone();
let t0 = browser_now(); // Mesh build no longer logs per chunk — the per-frame spam
// (~289 entries during init) was filling the browser console
// and contributing to perceived browser lag, especially with
// DevTools open. Use the FPS HUD / bench toggles for runtime
// perf measurement instead.
let mesh = build_chunk_mesh(world, &chunk_clone); let mesh = build_chunk_mesh(world, &chunk_clone);
let mesh_ms = browser_now() - t0;
// Lowered threshold so the heightmap-fast path also surfaces
// in telemetry. Tune up later if it gets noisy.
if mesh_ms > 0.5 {
log::info!(
"rebuild_chunk {:?}: {:.1}ms ({} verts, {} idx)",
coord,
mesh_ms,
mesh.vertices.len(),
mesh.indices.len()
);
}
if mesh.indices.is_empty() { if mesh.indices.is_empty() {
self.chunk_buffers.remove(&coord); self.chunk_buffers.remove(&coord);
return; return;

View file

@ -202,12 +202,24 @@ function setupMenu() {
function setupStatusLoop() { function setupStatusLoop() {
const hpFill = document.getElementById("hp-fill"); const hpFill = document.getElementById("hp-fill");
const hpLabel = document.getElementById("hp-label"); const hpLabel = document.getElementById("hp-label");
let lastHp = -1;
let lastAlive = null;
setInterval(() => { setInterval(() => {
const hp = wasm.get_hp(); const hp = wasm.get_hp();
const alive = wasm.is_alive(); const alive = wasm.is_alive();
hpFill.style.width = (hp / 20 * 100) + "%"; // Dedupe: only touch the DOM when something actually changed.
hpLabel.textContent = `${hp} / 20`; // Pre-change the loop ran 10× per second and fired three style
document.body.classList.toggle("dead", !alive); // writes each time — every frame paying a recalc cost even when
// HP held steady. Worth-only-noticeable when the value changes.
if (hp !== lastHp) {
hpFill.style.width = (hp / 20 * 100) + "%";
hpLabel.textContent = `${hp} / 20`;
lastHp = hp;
}
if (alive !== lastAlive) {
document.body.classList.toggle("dead", !alive);
lastAlive = alive;
}
}, 100); }, 100);
} }
@ -678,22 +690,31 @@ function cycleHotbar(delta) {
wasm.select_block(b); wasm.select_block(b);
} }
// FPS HUD — reads the EMA-smoothed dt published by App::publish_telemetry // FPS HUD — reads the EMA-smoothed dt from the game and writes it
// each frame. Polled every 200ms (5Hz) which is plenty for a readable // once a second. The previous version polled at 5 Hz with DOM writes
// number without adding measurable overhead. Coloring: green ≤ 18ms, // every poll, which on weak hardware contributed to perceived browser
// amber 18-33ms, red beyond. // lag. 1 Hz is more than enough to read the number; the EMA already
// smooths anything faster anyway. Also skips DOM writes when the
// reported value hasn't changed enough to display differently.
function setupFpsHud() { function setupFpsHud() {
const el = document.getElementById("fps"); const el = document.getElementById("fps");
if (!el) return; if (!el) return;
let lastText = "";
let lastTier = "";
setInterval(() => { setInterval(() => {
const dt = wasm.get_frame_dt_ms ? wasm.get_frame_dt_ms() : 0; const dt = wasm.get_frame_dt_ms ? wasm.get_frame_dt_ms() : 0;
if (!dt || dt <= 0) { if (!dt || dt <= 0) {
el.textContent = "— fps"; if (lastText !== "— fps") { el.textContent = "— fps"; lastText = "— fps"; }
return; return;
} }
const fps = 1000 / dt; const fps = 1000 / dt;
el.textContent = `${fps.toFixed(0)} fps (${dt.toFixed(1)}ms)`; const next = `${fps.toFixed(0)} fps (${dt.toFixed(1)}ms)`;
el.classList.toggle("warn", dt > 18 && dt <= 33); if (next !== lastText) { el.textContent = next; lastText = next; }
el.classList.toggle("bad", dt > 33); const tier = dt > 33 ? "bad" : dt > 18 ? "warn" : "";
}, 200); if (tier !== lastTier) {
el.classList.toggle("warn", tier === "warn");
el.classList.toggle("bad", tier === "bad");
lastTier = tier;
}
}, 1000);
} }