feat: performance budget profiling setup

- 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)
This commit is contained in:
2026-07-01 19:01:03 -04:00
parent 34507f9043
commit e385eae0f5
23 changed files with 1114 additions and 22 deletions
+184
View File
@@ -0,0 +1,184 @@
# 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.07.5 | > 7.5 |
| CPU frame ms | < 10.0 | 10.013.0 | > 13.0 |
| Draw calls | < 1200 | 12001800 | > 2000 |
| Triangles / frame | < 300K | 300K600K | > 1M |
| VRAM used | < 1.2 GB | 1.21.7 GB | > 1.8 GB |
| Main RAM used | < 2.0 GB | 2.03.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 | 600800 | Per-object, one per visible piece |
| Shadows | 200400 | Single directional cascade |
| Transparent | 50100 | Reflection probes, lightmap |
| UI | 100200 | HUD, minimap, scoreboard |
| **Total** | **9501500** | Well within 2000 target |
### VRAM Budget
| Category | Size | Notes |
|----------------------|----------|-------|
| Texture pool (1K × 7 mats) | 75 MB | 28 PBR maps |
| Lightmap atlas | 64128 MB| 2048 × 2048, DXT5 |
| Shadow map | 16 MB | 2048 × 2048 atlas |
| Mesh geometry | 2040 MB | LOD0LOD2 |
| 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 | 015 m | 100% | Full detail, panel gaps |
| 1 | 1540 m | 50% | Collapse bevels, remove small holes |
| 2 | 4080 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: **3050% 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 4555 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: ~2030
- Estimated triangles: ~8K12K
- 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: ~150250
- Estimated triangles: ~40K60K
- VRAM: ~200 MB (materials + lightmap + probes)
- **Expected: 90120 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: ~200K400K
- VRAM: ~500 MB
- **Target: 60 fps on GTX 1050**
+35
View File
@@ -44,6 +44,41 @@ Art pass, baked lighting, performance profiling, LOD + occlusion culling for the
| VRAM | 2GB texture pool |
| LOD0 tris/wall | 500-2000 |
## Deliverables (t_p3_profile — Performance Budget & Profiling)
### Performance Budget Document
- **docs/performance-budget.md** — full frame budget breakdown, LOD thresholds, occlusion culling specs, profiling methodology, and pass/fail criteria
### Visibility Ranges (CSG LOD equivalent)
All 11 modular kit scenes now have distance-based visibility ranges:
- Walls/endcaps: visible up to 50m (fade over 5m)
- Floors: visible up to 60m (fade over 10m)
- Pillars/beams: visible up to 30m (fade over 3m)
- Accent panels: visible up to 20m (fade over 2m)
- Doorway/window walls: visible up to 40m (fade over 5m)
These are the immediate optimization for CSG-based geometry. When maps are built from the modular kit, ~30-50% of distant pieces are automatically culled.
### Occlusion Culling
- **client/scripts/generate_occluders.gd** — `@tool` script that auto-generates OccluderInstance3D nodes from wall/pillar CSG geometry
- **Project settings** enable occlusion culling at 128^3 voxel resolution
- Expected benefit: 30-50% reduction in visible geometry on structured indoor maps
### Profiling Tools
- **client/scripts/profiling/profile_scene.gd** — standalone headless profiler that captures per-frame metrics (frame time, GPU time, draw calls, tris, VRAM) and evaluates against budget; produces CSV output + summary stats (P50/P95/P99)
- Usage: `godot --headless --script scripts/profiling/profile_scene.gd --scene res://tools/test_map.tscn`
### CSG → Mesh Baking Pipeline
- **client/scripts/convert_csg_to_mesh.gd** — `@tool` script that converts CSG nodes to MeshInstance3D with LOD slot framework, enabling traditional mesh LOD for the final optimization pass
- Collision shape preservation, visibility range inheritance
### Project Configuration
- Forward+ renderer explicitly configured
- Occlusion culling enabled (128^3 voxels, 6 rays/octant)
- LOD settings tuned for competitive FPS scales
- All costly post-processing disabled (SSAO, SSR, glow, volumetric fog, TAA)
- MSAA x2 + debanding for clean image at minimal GPU cost
## LOD Strategy
- 3 LOD levels per modular asset