Debug Overlay
The engine includes a debug overlay that displays real-time performance information and physics collider wireframes rendered directly on the GPU.
What It Shows
When enabled (press F3), the overlay provides two types of debug visualization:
Text HUD
A semi-transparent dark panel at the top-left corner with three lines of green monospace text:
- FPS — Smoothed frames per second (exponential moving average:
0.95 * old + 0.05 * new) - DT — Delta time in milliseconds
- Entities — Number of active entities in the renderer
Collider Wireframes
Wireframe outlines are drawn around all physics colliders, making it easy to verify collider placement, size, and alignment with visual meshes. Each collider's wireframe color is configurable via the DebugColor field (defaults to green). Supported shapes:
Wireframes follow the entity's transform, so they move and rotate with dynamic physics bodies.
Toggle
Press F3 to toggle the overlay on or off. The overlay is enabled by default when GameConstants.Debug is true.
How It Works
Wireframe Pipeline
Collider wireframes use a dedicated Vulkan graphics pipeline created alongside the 3D scene pipeline:
- Polygon mode —
VK_POLYGON_MODE_LINE(wireframe rendering, requiresfillModeNonSoliddevice feature) - No backface culling — wireframes are visible from all angles
- Depth test — enabled with
LESS_OR_EQUALcompare (draws at same depth as solid geometry) - No depth write — wireframes don't occlude other objects
- Same shaders — reuses
shader.vertandshader.fragfrom the 3D pipeline - Same vertex/index buffers — shares the combined geometry buffer
Debug entities are stored in a separate list (debugEntities_) from regular entities, rendered only when the debug overlay is enabled.
UI Rendering Pipeline
The text HUD uses a dedicated Vulkan graphics pipeline separate from the 3D scene pipeline:
- No depth testing — UI always draws on top
- Alpha blending —
SRC_ALPHA / ONE_MINUS_SRC_ALPHAfor transparency - No backface culling — 2D quads don't need it
- Push constant —
vec2 screenSizeconverts pixel coordinates to NDC
Font Atlas
Text is rendered using a font atlas baked at init time with stb_truetype:
- A TTF font file (
assets/fonts/RobotoMono-Regular.ttf) is loaded - ASCII characters 32-126 are rasterized into a 512x512
R8_UNORMtexture - Each character's glyph metrics (UVs, offsets, advance) are stored for quad generation
- If the font file is missing, a 1x1 white placeholder is used and text rendering is skipped
Shaders
Per-Frame Flow
buildDebugOverlayGeometry()generates UI vertices (background quad + text quads)- Vertices are uploaded to a host-visible, persistently-mapped buffer
recordCommandBuffer()renders: 3D entities → debug wireframes → UI textDebugColliderRenderSystemcreates/syncs wireframe entities matching each collider
Zero Overhead When Disabled
When the overlay is off, no wireframe geometry is created, no UI geometry is built, and no debug draw calls are recorded. Toggling off clears all debug renderer entities.
Systems
DebugOverlaySystem
Runs each frame and handles:
- Edge-detected F3 key press to toggle
GameConstants.Debug - Calling
NativeBridge.SetDebugOverlay()to sync the state to C++
DebugColliderRenderSystem
Runs each frame after DebugOverlaySystem and handles:
- When debug turns on: creates wireframe meshes (colored per
Collider.DebugColor) matching each collider shape and registers them as debug renderer entities - Each frame while debug is on: syncs debug entity transforms to match their collider's position
- When debug turns off: calls
ClearDebugEntities()and clears tracking state - Automatically cleans up debug entities for despawned ECS entities
The system maintains a Dictionary<int, int> mapping ECS entity IDs to debug renderer entity IDs.