Add game profiling system and rendering optimizations#14
Merged
Conversation
Adds a zero-overhead profiling system behind the `profiling` feature flag. When enabled, RAII scope guards measure per-phase timing in both the server tick loop and client render loop, with deep sub-phase breakdown for the platformer game. A browser overlay (F3) shows FPS, frame time, and a color-coded phase breakdown bar. DevTools performance marks are emitted for each scope. Server exposes GET /api/v1/profile for live tick stats. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The profiling module's WASM timing path needs web_sys::Performance. Add web-sys as an optional dependency of breakpoint-core, activated by the profiling feature flag. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Profiler data showed 94% of each server tick spent in serialization (497us) and encoding (379us), not game logic (43us). Four optimizations: 1. Remove 23K-tile course from per-tick state — send via separate CourseUpdate messages only when breakable walls are destroyed. PlatformerNetState (without course) used for wire format. 2. Eliminate buffer clone + double MessagePack pass — encode_game_state_fast() writes [type_byte|tick_le32|state_data] in one allocation. 3. Throttle client JS bridge from 60Hz to 10Hz (force-push on state changes). 4. Incremental scene updates — cache static tiles via mark_static()/ clear_dynamic(), only rebuild ~110 dynamic objects per frame instead of ~485. Expected: serialize_state 497us→~50us, encode_broadcast 379us→~20us, network payload ~5KB→~500B per tick. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The incremental scene update (Optimization 4) cached tiles as "static" but tile culling is camera-dependent — as the player scrolls, different tiles must be visible. This caused tiles to disappear during movement. Reverted to full scene.clear() + tile re-render per frame. The other 3 optimizations (course removal, fast encoding, bridge throttle) remain and address the actual bottleneck (94% serialization overhead). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tion Add as_any() to BreakpointGame trait for zero-copy downcast to concrete game types. Platformer render, camera, HUD, events, and weather now access PlatformRacer::state() directly instead of serialize+deserialize round-trips via read_game_state(). - Eliminate 2x serialize_state() per network tick (prev_state + cache) - Eliminate 4x read_game_state() per frame in platformer hot path - Remove dead ActiveGame fields: prev_state, interp_alpha, cached_state_bytes - Add minimap cache (rooms/connections by course_version, ~600 tile queries saved) - Golf/LaserTag/Tron unchanged (small states, still use read_game_state) Expected: ~3-5ms/frame savings → 38 FPS to ~45-50 FPS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e atlas - Cache sprite atlas in app.rs (use OnceLock instead of rebuilding 500+ entry HashMap per frame) - Add outline support to sprite_batch shader (per-vertex attribute), allowing 12 enemy/player sprites per frame to be batched instead of drawn individually - Add pre-built batch buffers to Scene (add_batch_sprite bypasses RenderObject creation, frustum culling, and sorting for ~600 tile sprites) - Reuse per-blend-mode vertex Vecs across frames (eliminate 3 heap allocations) - Relax batch eligibility: only dissolve sprites need individual draw calls Estimated ~4-6ms/frame savings (22-25ms → 16-21ms, targeting 60 FPS). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The server's encode_game_state_fast writes [0x10][4-byte LE tick][raw state] instead of msgpack, causing the test protocol decoder to silently fail on all GAME_STATE messages (0 received in Tron tests). Fix: detect GameState type byte in decode() and parse the fast binary format with readUInt32LE + subarray instead of msgpack unpack. Also add missing message types (CourseUpdate, RequestGameStart, AddBot, RemoveBot) to the test protocol constants. Update CLAUDE.md and README.md: - Test count: 484 -> 699, Playwright specs: 12 -> 16 - Document profiling system, fast game state protocol, CourseUpdate messages - Correct release profile (opt-level=3 server, "z" client) - Update platformer description (Castlevania-style labyrinth) - Add new key file paths (profiling, protocol, browser tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
--features profiling): server-side game loop timing, client-side frame timing, browser overlay viaprofiler.js-- zero overhead when disabledGameStatetrait, eliminate per-framedecode_server_messagePerformance Impact
Target: 60 FPS (16.67ms budget) for the platformer game.
Test plan
cargo fmt --all -- --checkcargo clippy --workspace --all-targets -- -D warningscargo clippy --workspace --all-targets --features profiling -- -D warningscargo test --workspace-- all 484 tests passwasm-pack buildwith profiling feature succeedsGenerated with Claude Code