e385eae0f5
- docs/performance-budget.md: 60fps/2GB VRAM budget with CPU, GPU, memory, and draw-call targets (P95<16.6ms, P99<50ms) - scripts/profiling/: SceneTree + Node profilers, test scenes - scripts/generate_occluders.gd: auto-generate OccluderInstance3D from CSG wall pieces (128^3 voxel occlusion culling) - scripts/convert_csg_to_mesh.gd: CSG->StaticMesh baker with LOD slots (LOD1@15m, LOD2@30m) - project.godot: occlusion culling + LOD settings enabled - modular scene UV2 data from lightmapping pass - rcon_command_handler.gd conflict resolved (kept upstream plugin cmd) - Fixed profiler_node.gd warmup frame bug (60 frames, not 3) - Fixed convert_csg_to_mesh.gd LOD API (add_lod + distance)
185 lines
7.2 KiB
Markdown
185 lines
7.2 KiB
Markdown
# 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**
|