Performance Architecture¶
Rhythm games require consistent frame timing. A dropped frame means a missed beat, which breaks the player's flow and ruins the experience.
This document outlines the strategies AeroBeat uses to ensure a locked 60 FPS (Mobile), 90 FPS (VR), and 120+ FPS (PC).
1. The "Zero Stutter" Mandate¶
We prioritize Frame Pacing over Graphical Fidelity.
- Rule: No resource loading, shader compilation, or heavy garbage collection is allowed during the
Gameplaystate. - Enforcement: The
AeroPerformanceMonitortracks frame times. If a spike > 16ms occurs during gameplay, it is logged as a "Performance Strike" against the active UGC.
2. Shader Pre-Warming¶
Godot 4.x (Vulkan) compiles shaders asynchronously, but pipeline creation can still cause hitches on the main thread the first time a material appears on screen.
The Strategy¶
We use a Pre-Warm Phase during the "Loading..." screen.
- Scan: The
ModLoaderidentifies all uniqueMaterialsandMeshesrequired by the selected Song, Environment, Skins, and Avatars. - Instantiate: The
AeroShaderCachesingleton spawns a hiddenSubViewport. - Render: It instances every unique mesh+material combination into this viewport.
- Wait: The game waits for 3 frames (to ensure the GPU pipeline is fully built).
- Cleanup: The instances are removed, but the driver cache remains hot.
- Start: The song begins.
Note: This applies to Particles as well. All particle systems must emit at least one particle during pre-warm.
3. Draw Call Budgets¶
To support mobile VR (Quest) and Phones, we enforce strict draw call limits.
| Platform | Target Draw Calls | Max Polycount (Scene) |
|---|---|---|
| Mobile / Quest | < 150 | 100k |
| PC / Console | < 1000 | 500k |
Mitigation Strategies¶
- GPU Instancing: The Core Engine automatically uses
MultiMeshInstance3Dfor all gameplay targets (Notes, Obstacles). - Texture Atlasing: The Cloud Baker attempts to merge textures for static environment geometry.
- Avatar Limits: Avatars are limited to 3 Materials max.
4. Level of Detail (LOD)¶
We rely on Godot's automatic LOD system, but we enforce generation at the Cloud Baker level.
Cloud Baker Pipeline¶
When a creator uploads a high-poly mesh (e.g., a 20k poly statue):
- The Baker generates LOD1 (50%) and LOD2 (20%) meshes using the mesh optimizer.
- These are packed into the
.pck.
Runtime Selection¶
- PC: Uses
LOD0(Original) by default. - Mobile: Forces
LOD Biasto preferLOD1immediately, saving vertex processing power.
5. Memory Management (VRAM)¶
Rhythm games often load hundreds of songs. We cannot keep everything in memory.
The "Jukebox" Lifecycle¶
- Menu: Only the "Preview Audio" (30s clip) and "Thumbnail" (512px) are loaded.
- Selection: When a song is picked, the full
.oggand Environment.pckare loaded. - Gameplay: All assets are pinned in VRAM.
- End Screen: Assets remain loaded (for "Replay").
- Exit to Menu: Aggressive Unload. All gameplay resources are freed.
GC.collect()is forced.
Texture Compression¶
- Mobile: All textures are converted to ASTC 6x6 by the Cloud Baker.
- PC: Textures use S3TC (BC7).
- UI: Uses WebP (Lossless) to save disk space while keeping sharpness.
6. Object Pooling¶
We do not instantiate() notes during gameplay.
- Pools:
AeroTargetPool,AeroObstaclePool,AeroEffectPool. - Size: Pools are initialized during the Loading Screen based on the song's note count (plus a 20% buffer).
- Behavior: When a note is hit, it is
reparentedorhidden, notqueue_free()'d.