From a01d5c1fa9f9aec62ee03b8e72e99bc49eb4c9f6 Mon Sep 17 00:00:00 2001 From: Maximus Gorog Date: Sun, 24 May 2026 17:22:30 -0600 Subject: [PATCH] Quiet the debug-noise that was lagging the browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/render/mod.rs | 18 +++++------------- web/main.js | 45 +++++++++++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/render/mod.rs b/src/render/mod.rs index 34fac83..d615b2c 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -507,20 +507,12 @@ impl Renderer { // immutable &World into build_chunk_mesh while still owning // &mut self below. 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_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() { self.chunk_buffers.remove(&coord); return; diff --git a/web/main.js b/web/main.js index 2a466a0..2b1e9ed 100644 --- a/web/main.js +++ b/web/main.js @@ -202,12 +202,24 @@ function setupMenu() { function setupStatusLoop() { const hpFill = document.getElementById("hp-fill"); const hpLabel = document.getElementById("hp-label"); + let lastHp = -1; + let lastAlive = null; setInterval(() => { const hp = wasm.get_hp(); const alive = wasm.is_alive(); - hpFill.style.width = (hp / 20 * 100) + "%"; - hpLabel.textContent = `${hp} / 20`; - document.body.classList.toggle("dead", !alive); + // Dedupe: only touch the DOM when something actually changed. + // Pre-change the loop ran 10× per second and fired three style + // 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); } @@ -678,22 +690,31 @@ function cycleHotbar(delta) { wasm.select_block(b); } -// FPS HUD — reads the EMA-smoothed dt published by App::publish_telemetry -// each frame. Polled every 200ms (5Hz) which is plenty for a readable -// number without adding measurable overhead. Coloring: green ≤ 18ms, -// amber 18-33ms, red beyond. +// FPS HUD — reads the EMA-smoothed dt from the game and writes it +// once a second. The previous version polled at 5 Hz with DOM writes +// every poll, which on weak hardware contributed to perceived browser +// 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() { const el = document.getElementById("fps"); if (!el) return; + let lastText = ""; + let lastTier = ""; setInterval(() => { const dt = wasm.get_frame_dt_ms ? wasm.get_frame_dt_ms() : 0; if (!dt || dt <= 0) { - el.textContent = "— fps"; + if (lastText !== "— fps") { el.textContent = "— fps"; lastText = "— fps"; } return; } const fps = 1000 / dt; - el.textContent = `${fps.toFixed(0)} fps (${dt.toFixed(1)}ms)`; - el.classList.toggle("warn", dt > 18 && dt <= 33); - el.classList.toggle("bad", dt > 33); - }, 200); + const next = `${fps.toFixed(0)} fps (${dt.toFixed(1)}ms)`; + if (next !== lastText) { el.textContent = next; lastText = next; } + const tier = dt > 33 ? "bad" : dt > 18 ? "warn" : ""; + if (tier !== lastTier) { + el.classList.toggle("warn", tier === "warn"); + el.classList.toggle("bad", tier === "bad"); + lastTier = tier; + } + }, 1000); }