# 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

1. **CPU baseline**: Run `profile_scene.gd --headless` on target hardware
2. **GPU baseline**: Capture 1000+ frames with editor profiler, record p50/p95/p99
3. **Draw call audit**: Use frame debugger to count and classify draw calls
4. **Occlusion test**: Toggle OccluderInstance3D visibility, measure FPS delta
5. **LOD validation**: Check LOD transitions with debug visualization
6. **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**
