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:
@@ -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.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**
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user