Commit graph

5 commits

Author SHA1 Message Date
Maximus Gorog
a01d5c1fa9 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.
2026-05-24 17:22:30 -06:00
Maximus Gorog
bb006839cc Runtime perf: cheap fog, sky overdraw kill, fewer cloud octaves, in-game FPS HUD
The features added in Rounds A–D were correct but expensive. The hot
path per frame was sky_color() called from apply_fog for EVERY distant
pixel — 4-octave cloud fbm + star hash + sun/moon disc per fragment,
hundreds of thousands of pixels per frame. Profile-driven cuts that
keep all features but stop paying for them in the wrong places:

1. apply_fog now mixes terrain toward sky_dome (cheap gradient) not
   sky_color (gradient + clouds + sun + moon + stars). Distant terrain
   still fades to the right-direction sky color at every time of day;
   the per-pixel cost drops by ~80%. Full sky_color still runs for
   the SKY BACKGROUND pass where it's actually paid for.

2. Sky pipeline draws AFTER terrain with depth_compare = LessEqual.
   The full-screen sky was previously written first then over-painted
   by terrain — sky's expensive fragment shader ran on every screen
   pixel. Now it only runs on pixels with no terrain in front of them
   (depth = 1.0 cleared), which on most views is 30–60% of the screen
   instead of 100%.

3. fbm2 reduced from 4 → 3 octaves. Negligible visual change at the
   scales we sample, ~25% cheaper per cloud-pixel.

4. Cloud branch skips entirely when day_strength < 0.05 (full night).
   Clouds invisible at night anyway, fbm + smoothstep + mix skipped.

5. In-game FPS HUD (top-right corner):
   - Telemetry struct gains frame_dt_ms (EMA-smoothed in app.rs
     with coefficient 0.85 so the number is readable, not flickery).
   - wasm bridge: get_frame_dt_ms().
   - main.js setupFpsHud() polls it at 5Hz, color-coded:
       green ≤ 18ms (≥55fps), amber 18-33ms, red beyond.
   - Reads what THE GAME measures, not the browser's
     requestAnimationFrame which gets throttled to 1 Hz on
     unfocused windows.

No features removed. God rays, FXAA, ACES tonemap, bounce baking,
specular materials, leaf translucency — all still there. Tests:
63 passing. Wasm release clean.
2026-05-24 15:44:14 -06:00
Maximus Gorog
c0589d0dfc Tick/toc instrumentation across build + test + mesh phases
run.sh:
  - phase() wrapper logs elapsed seconds per build step.
  - Tracks total build+startup at the end.
  - Output is "==> phase / [Ns] phase" so the slow steps are obvious.

test/run.py:
  - Per-step time.perf_counter() around each scenario step.
  - "slowest steps" summary printed at the end so the worst
    offenders are immediately visible.
  - Total wall-clock time at scenario end.

src/render/mod.rs:
  - browser_now() helper: web_sys::performance().now() on wasm,
    Instant-based on native. Monotonic ms timestamps for tick/toc.
  - Renderer::rebuild_chunk wraps build_chunk_mesh in a t0/t1
    measurement and logs anything over 5ms with vertex/index counts.
    Surfaces sky_visibility cost in the browser console.

web/main.js:
  - Exposes window.voxel_game = wasm after init so the test
    harness can drive scenarios declaratively (set_scene_time,
    teleport, look_at, get_position, etc.).

src/shader.wgsl:
  - Fix duplicate `let to_eye` declaration introduced in Round D
    (specular's normalized to_eye conflicted with fog's raw version).
    Renamed fog's local to_eye_raw. The test harness caught this
    immediately — first WGSL compile error, first scenario run.

Findings from running scenarios/lighting-times-of-day.yaml:
  - 289 chunks × ~100ms avg = ~29s mesh-build on main thread.
  - Page-ready latency dominated by this. window.voxel_game appears
    almost immediately (init resolves before chunks build), but
    the world is invisible until meshes are uploaded.
  - sky_visibility (8 cosine rays × HashMap voxel lookups) is the
    hot path inside build_chunk_mesh.

Next: make chunk-mesh build progressive (one or two chunks per tick
instead of all up-front), so the world becomes visible immediately
and pops in over a few seconds.
2026-05-24 11:49:08 -06:00
Maximus Gorog
b52c1927cf Render + UI polish since pre-alpha-0.0.1
- Greedy meshing now bakes per-vertex AO with 4-corner sampling and an
  anisotropic-diagonal split when corner AO disagrees.
- WGSL: extracted sky_dome() for hemisphere ambient sampling so vertical
  faces match the sun-side sky tint at day; ambient_strength mixed by
  day strength instead of a flat constant.
- Step-1 post pipeline: render scene into an offscreen color texture,
  pass-through to the surface. Foundation for FXAA/shafts that will
  follow.
- Input bug: merge_held() now recomputes per tick from sticky keyboard +
  live touch bridge, so releasing the joystick actually stops the
  player (previous OR-into-self bug ate playtests).
- Touch UI hit-zones reordered (menu/hotbar above the joystick z-index);
  hotbar widened to 10 slots with tap-to-select on mobile.
- find_safe_spawn anchors on natural_surface_y so spawn is deterministic
  from noise — towers built at spawn no longer climb the spawn point.
- move_axis is sub-stepped (0.45-block max) so high-velocity falls can't
  teleport the player inside terrain.
2026-05-23 18:44:56 -06:00
Maximus Gorog
3a4ae970b2 pre-alpha 0.0.1 — initial multiplayer voxel sandbox
Web/wasm Rust voxel game with:
- wgpu 23 client (WebGPU when available, WebGL2 fallback)
- Chunked terrain (17x17 chunks, deterministic value-noise generator)
- Greedy meshing with frustum + distance culling
- Sky shader, leaf wind shader, distance fog
- Player physics: substepped AABB collision, gravity, fall damage,
  natural-surface respawn
- Touch UI (MCPE-style joystick + jump/break/place/sprint),
  gamepad polling with axis calibration, mouse+keyboard
- HP / death-screen / respawn flow
- 10-slot hotbar with mouse-wheel + hotkey + tap cycling
- Settings menu (mouse sens, FOV, render distance, input mode toggle)
- Axum multiplayer server: WebSocket protocol, edit log,
  10Hz player broadcasts
- 31 unit tests covering spawn invariants, collision sweeps,
  raycast hit/miss, greedy mesh winding, fall damage, oriented
  box rotation, hotbar block roundtrip, and the input-merge
  regression that latched movement after touch release

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 23:33:47 -06:00