Performance Budget & Profiling Methodology
Target hardware: GTX 1050 / RX 560 class (2 GB VRAM)
Engine: Godot 4.7 Forward+ renderer
Frame target: 60 fps (16.6 ms per frame)
1. Frame Budget (16.6 ms)
| Phase |
Budget |
Notes |
| GPU total |
8.0 ms |
Forward+ opaque pass, transparent, sky, tonemap |
| CPU — physics |
1.5 ms |
128-tick, capsule sweep, raycasts |
| CPU — render |
1.5 ms |
Culling, sorting, draw-call submission |
| CPU — gameplay |
3.0 ms |
Input, animation, network interpolation |
| Headroom |
2.6 ms |
Buffer for frametime variance (≈15%) |
Bottleneck detection thresholds
| Metric |
Green |
Yellow |
Red |
| GPU frame ms |
< 5.0 |
5.0–7.5 |
> 7.5 |
| CPU frame ms |
< 10.0 |
10.0–13.0 |
> 13.0 |
| Draw calls |
< 1200 |
1200–1800 |
> 2000 |
| Triangles / frame |
< 300K |
300K–600K |
> 1M |
| VRAM used |
< 1.2 GB |
1.2–1.7 GB |
> 1.8 GB |
| Main RAM used |
< 2.0 GB |
2.0–3.0 GB |
> 3.5 GB |
2. Rendering Budget
Triangle Count
| Asset type |
LOD0 tris |
LOD1 tris (50%) |
LOD2 tris (25%) |
Instances/map |
| Wall panel |
800 |
400 |
200 |
~600 |
| Floor slab |
800 |
400 |
200 |
~400 |
| Pillar |
400 |
200 |
100 |
~80 |
| Doorway / window |
1200 |
600 |
300 |
~60 |
| Accent panel |
400 |
200 |
100 |
~40 |
Budget calculation (max visible):
- Walls ≈ 50 visible × 800 = 40K (LOD0)
- Floors ≈ 20 visible × 800 = 16K (LOD0)
- Details ≈ 30 visible × 400 = 12K (LOD0)
- Total LOD0 tris ≈ 68K — well within budget
- With LOD + occlusion, visible tris on target GPU < 200K
Draw Calls
| Pass |
Draw calls |
Notes |
| Opaque solids |
600–800 |
Per-object, one per visible piece |
| Shadows |
200–400 |
Single directional cascade |
| Transparent |
50–100 |
Reflection probes, lightmap |
| UI |
100–200 |
HUD, minimap, scoreboard |
| Total |
950–1500 |
Well within 2000 target |
VRAM Budget
| Category |
Size |
Notes |
| Texture pool (1K × 7 mats) |
75 MB |
28 PBR maps |
| Lightmap atlas |
64–128 MB |
2048 × 2048, DXT5 |
| Shadow map |
16 MB |
2048 × 2048 atlas |
| Mesh geometry |
20–40 MB |
LOD0–LOD2 |
| Reflection probe cubemaps |
8 MB |
8 × 512 cubemaps |
| Audio + misc |
50 MB |
|
| Total base |
~300 MB |
|
| Headroom |
1.7 GB |
For gameplay assets, streaming |
3. LOD Strategy
CSG-based pieces (current)
Since the modular kit uses CSG nodes (procedural geometry), traditional mesh LOD is not directly supported. The equivalent optimizations are:
| Technique |
Benefit |
How |
| Visibility ranges |
Skip rendering past cutoff |
visibility_range_end on each CSGBox3D/Combiner |
| Occlusion culling |
Skip behind-occluder geometry |
OccluderInstance3D on major walls |
| CSG → Mesh baking |
Replace procedural with static mesh |
convert_csg_to_mesh.gd tool script |
| Mesh LOD after baking |
Traditional LOD on static meshes |
Godot ImporterMesh LOD |
Visibility Range Thresholds (CSG phase)
| Asset |
Hide beyond |
Rationale |
| Wall panels |
50 m |
OC sightlines rarely exceed 40 m |
| Floor slabs |
60 m |
Visible as ground plane further |
| Pillars |
30 m |
Thin silhouette, cull earlier |
| Beams |
30 m |
Ceiling detail, only visible indoors |
| Accent panels |
20 m |
Small team-color decals |
| Doorway / window |
40 m |
Opening shapes visible at medium range |
Mesh LOD Targets (baked mesh phase)
| LOD |
Distance |
Tris % |
Notes |
| 0 |
0–15 m |
100% |
Full detail, panel gaps |
| 1 |
15–40 m |
50% |
Collapse bevels, remove small holes |
| 2 |
40–80 m |
25% |
Planar collapse, aggressive decimate |
| 3 |
80 m+ |
CULL |
Not visible at competitive sightlines |
4. Occlusion Culling
Implementation
- OccluderInstance3D nodes auto-generated from wall CSGBox3D geometry
- Voxel resolution: 128³ (good balance of accuracy vs memory)
- Generation script:
client/scripts/generate_occluders.gd — @tool script
- Activation: Scene tree → Add OccluderInstance3D → Set occluder shape to box matching wall extents
Occluder Coverage
| Occluder origin |
Covers |
Shape |
| Exterior walls |
Block view of outdoor |
Box |
| Interior dividers |
Lane separation |
Box |
| Pillar clusters |
Mid-range detail |
Box |
Expected culling benefit: 30–50% reduction in visible geometry on a 3-lane map with interior dividers.
5. Profiling Methodology
Toolchain
| Tool |
Use |
| Godot Editor Debugger |
Real-time frame profiler, monitor, 3D scene debug |
profile_scene.gd |
Headless frame-time capture |
| GPU vendor tools (NVIDIA NSight / Radeon GPA) |
GPU-bound profiling |
| Frame debugger (Godot built-in) |
Draw-call inspection |
Profiling Workflow
- CPU baseline: Run
profile_scene.gd --headless on target hardware
- GPU baseline: Capture 1000+ frames with editor profiler, record p50/p95/p99
- Draw call audit: Use frame debugger to count and classify draw calls
- Occlusion test: Toggle OccluderInstance3D visibility, measure FPS delta
- LOD validation: Check LOD transitions with debug visualization
- Thermal test: 30-minute continuous gameplay, log frame-time variance
Pass/Fail Criteria
- PASS: holds 60 fps on target hardware for 95% of frames across all 3 maps
- ACCEPTABLE: drops to 45–55 fps during intense firefights but recovers within 2 s
- FAIL: sustained < 45 fps or > 100 ms frame-time spike
6. Reference Frames
kit_demo.tscn (indoor single-room, 5.12 × 5.12 m)
- ~11 CSG pieces, 8 materials
- Estimated draw calls: ~20–30
- Estimated triangles: ~8K–12K
- VRAM: ~80 MB (materials + lightmap)
- Expected: > 200 fps on GTX 1050
template_map.tscn (3-lane showroom, 20 × 16 m)
- ~25 CSG pieces + prefabs
- Estimated draw calls: ~150–250
- Estimated triangles: ~40K–60K
- VRAM: ~200 MB (materials + lightmap + probes)
- Expected: 90–120 fps on GTX 1050
Full competitive map (target, ~80 × 60 m)
- ~800 CSG pieces (when built from modular kit)
- Estimated draw calls: ~1500 (with LOD + occlusion)
- Estimated triangles: ~200K–400K
- VRAM: ~500 MB
- Target: 60 fps on GTX 1050