bvle-voxels/CLAUDE.md
Samuel Bouchet afb86446cd Fix VRAM leak: capacity-based BLAS/TLAS + deferred toping BLAS upload
Per-frame CreateRaytracingAccelerationStructure calls during F3 animation
caused VRAM explosion (especially toping BLAS at ~23M vertices). Now all
3 BLASes use capacity-based allocation with 25% headroom — only recreated
when vertex count exceeds capacity, otherwise just BuildRaytracingAS with
updated desc.vertex_count. TLAS only recreated when instance count changes.

Also adds deferred toping BLAS position upload via UpdateBuffer in Render()
(topingBLASDirty_ flag), enabling toping shadows to update during animation.

Split CLAUDE.md into CLAUDE.md + TROUBLESHOOTING.md for maintainability.
2026-03-30 21:37:39 +02:00

195 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# BVLE Voxels - Prototype de Moteur Voxel Hybride
## Vue d'ensemble
Prototype de moteur voxel basé sur **Wicked Engine** (MIT, C++17, DX12/Vulkan) pour valider les performances de rendu sur GPU moderne (AMD RDNA 2+ / Nvidia RTX 3060+). Le document de spécification complet est dans `voxel_engine_spec.md` à la racine du projet.
Cible : 60+ fps en 1440p, monde de 512x512x256 voxels visibles.
## Architecture
```
bvle-voxels/
├── CMakeLists.txt # Build CMake racine
├── engine/ # Wicked Engine (clone --depth 1, branche main)
│ └── WickedEngine/shaders/voxel/ # Nos shaders copiés ici pour compilation DXC
├── src/
│ ├── voxel/ # Bibliothèque VoxelEngine (static lib)
│ │ ├── VoxelTypes.h # Types fondamentaux (VoxelData, PackedQuad, MaterialDesc, ChunkPos)
│ │ ├── VoxelWorld.h/.cpp # Monde voxel (hashmap de chunks, génération procédurale)
│ │ ├── VoxelMesher.h/.cpp # Binary Greedy Mesher CPU + SmoothMesher (Naive Surface Nets)
│ │ ├── VoxelRenderer.h/.cpp# Renderer + VoxelRenderPath (sous-classe RenderPath3D)
│ │ └── TopingSystem.h/.cpp # Système de topings (biseaux décoratifs sur faces +Y)
│ └── app/
│ └── main.cpp # Point d'entrée Win32 + crash handler SEH
├── shaders/ # Sources HLSL des shaders voxel (copiés dans engine/ au build)
│ ├── voxelCommon.hlsli # Root signature et CB partagés (inclus par tous les shaders)
│ ├── voxelVS.hlsl # Vertex shader (vertex pulling, triple-mode: CPU/MDI/GPU mesh)
│ ├── voxelPS.hlsl # Pixel shader (triplanar + lighting)
│ ├── voxelCullCS.hlsl # Compute shader frustum+backface cull (Phase 2.3)
│ ├── voxelMeshCS.hlsl # Compute shader GPU mesher 1×1 (Phase 2.4-2.5)
│ ├── voxelTopingVS.hlsl # Vertex shader topings (instanced vertex pulling, t4/t5)
│ ├── voxelTopingPS.hlsl # Pixel shader topings (triplanar + directional lighting)
│ ├── voxelSmoothVS.hlsl # Vertex shader smooth Surface Nets (vertex pulling, t6)
│ ├── voxelSmoothPS.hlsl # Pixel shader smooth (triplanar + material blending)
│ ├── voxelBLASExtractCS.hlsl # Compute shader BLAS position extraction (Phase 6.1)
│ ├── voxelShadowCS.hlsl # Compute shader RT shadows + raw AO (inline ray queries, Phase 6.2+6.3)
│ ├── voxelAOBlurCS.hlsl # Compute shader bilateral AO blur (separable H/V, Phase 6.3)
│ └── voxelAOApplyCS.hlsl # Compute shader AO apply + tone mapping + saturation (Phase 6.3 + 7)
├── CLAUDE.md
└── TROUBLESHOOTING.md # Pièges techniques, debugging, APIs Wicked
```
## Build
### Prérequis
- CMake 3.19+ (`winget install Kitware.CMake`)
- Visual Studio 2022 Build Tools (`winget install Microsoft.VisualStudio.2022.BuildTools`)
- Windows SDK 10.0.26100+ (`winget install Microsoft.WindowsSDK.10.0.26100`)
### Commandes
```bash
# Configurer (depuis la racine du projet)
cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_SYSTEM_VERSION=10.0.26100.0
# Compiler
cmake --build build --config Release --target BVLEVoxels --parallel
# Exécutable produit dans build/Release/BVLEVoxels.exe
```
Le SDK 10.0.26100 est requis car les headers DX12 (`d3dx12_check_feature_support.h`) fournis par Wicked Engine ne sont pas compatibles avec le SDK 22621.
### Post-build automatique (CMakeLists.txt)
Le build copie automatiquement :
1. `dxcompiler.dll` → à côté de l'exe (requis pour la compilation runtime des shaders)
2. `shaders/*.hlsl``engine/WickedEngine/shaders/voxel/` (pour que `LoadShader` les trouve via `SHADERSOURCEPATH`)
3. `engine/Content/` → à côté de l'exe (assets Wicked Engine)
## Intégration Wicked Engine
### Backend graphique
Wicked Engine utilise **DX12 par défaut sur Windows**, Vulkan sur Linux. Les shaders sont écrits en **HLSL** et compilés via DXC. Pour forcer Vulkan sur Windows, passer `"vulkan"` en argument de ligne de commande.
### Point d'entrée et architecture de rendu
`VoxelRenderPath` hérite de `wi::RenderPath3D`. Le rendu voxel utilise ses propres render targets (`voxelRT_`, `voxelDepth_`) et est exécuté dans `Render()` sur un **command list dédié**. Le résultat est composité dans `Compose()` via `wi::image::Draw()`.
**NE JAMAIS créer un render pass dans `Compose()`** : cette méthode est appelée à l'intérieur du render pass du swapchain. Imbriquer des render passes est interdit en D3D12.
```
Render() → RenderPath3D::Render() // Wicked rend sa scène
→ device->BeginCommandList() // Nouveau cmd list
→ renderer.render(cmd, ...) // Notre render pass (clear + draw voxels → voxelRT_)
Compose() → RenderPath3D::Compose() // Wicked affiche son résultat
→ wi::image::Draw(voxelRT_) // On overlay nos voxels par-dessus
```
La caméra est gérée manuellement dans `Update()` en écrivant directement `camera->Eye`, `camera->At` (direction LookTo), `camera->Up`.
> See `TROUBLESHOOTING.md` for the detailed Wicked API reference table, shader binding pitfalls, DX12 resource state management, and debugging guides.
## Détails d'implémentation
### VoxelData (16 bits)
```
[15:8] material ID (256 matériaux)
[7:4] flags (smooth, transparent, emissive, custom)
[3:0] metadata (orientation, variant)
```
### PackedQuad (64 bits = 8 octets par quad)
```
[5:0] position X (0-63) [23:18] width (1-32)
[11:6] position Y (0-63) [29:24] height (1-32)
[17:12] position Z (0-63) [32:30] face (0-5)
[40:33] material ID [48:41] blendMatID
[59:49] chunkIndex (11 bits) [63:60] blendEdges (4 bits)
```
### Binary Greedy Mesher (CPU, `VoxelMesher.cpp`)
Masques binaires par axe, face culling par shift/XOR, greedy merge rectangulaire par tranche de profondeur.
### Génération procédurale (`VoxelWorld.cpp`)
Perlin noise 3D, fBm 5 octaves (2 en animation), caves 3D, matériaux par altitude. Chunks Y=0..7. Animation 60 Hz via `regenerateAnimated()` parallélisé avec `wi::jobsystem`.
### Renderer (`VoxelRenderer.cpp`)
- **Triple-mode VS** : CPU path, MDI path, GPU mesh path (défaut)
- **GPU mesh** : compute shader `voxelMeshCS` → barrier UAV→SRV → `DrawInstanced` (readback 1-frame-delay)
- **Vertex pulling** via `SV_VertexID`, pas de vertex buffer classique
- **Per-chunk info** : `StructuredBuffer<GPUChunkInfo>` (80 bytes/chunk)
- **Height-based blending** (Phase 3) : PS lit `voxelDataBuffer` (t3), winner-takes-all heightmap, corner attenuation
- **Render targets propres** : `voxelRT_` (R8G8B8A8) + `voxelDepth_` (D32_FLOAT)
- **CPU profiling** : `ProfileAccum` avec moyennes toutes les 5s
## Phases de développement
### Phase 1 - Setup et meshing de base [FAIT]
Fork Wicked Engine, VoxelWorld procédural, Binary Greedy Mesher CPU (~300K quads), rendu vertex pulling, caméra libre, crash handler SEH.
### Phase 2 - Performance GPU [FAIT]
- **2.1** : Mega-buffer + CPU frustum/backface cull + per-face DrawInstanced
- **2.2** : CPU-filled indirect args + DrawInstancedIndirectCount (MDI)
- **2.3** : GPU compute culling (0.006 ms / 168 chunks)
- **2.4** : GPU compute mesher benchmark (CPU 277ms vs GPU 5.3ms)
- **2.5** : GPU meshing production + CPU optimisations (fused regen+pack, memcpy, dirty cache)
- **Résultat** : 80-110 FPS avec animation 60 Hz, 700+ FPS statique
### Phase 3 - Texture blending [FAIT]
PS-based heightmap blending, winner-takes-all, corner attenuation subtractive. GPU mesh path uniquement.
### Phase 4 - Toping [EN COURS]
- **4.1** [FAIT] : TopingSystem infrastructure, 4-bit adjacency, priority-based, stone wedges + grass tufts
- **4.2** [FAIT] : Shaders dédiés, vertex pulling instancié, half-Lambert + translucency vegetation
- **4.3** [A FAIRE] : Plus de types, LOD, animation vent, compute shader instances
### Phase 5 - Rendu smooth [EN COURS]
- **5.1** [FAIT] : Naive Surface Nets CPU, SDF binaire, cross-chunk connectivity, smooth/blocky boundary
- **5.2** [FAIT] : Smooth vertex normals, geometric normals triplanar, optimisations CPU (560ms → 17ms)
- **5.3** [A FAIRE] : GPU compute Surface Nets
- **5.4** [A FAIRE] : SDF lissé, LOD, pipeline asynchrone
### Phase 6 - Ray tracing hybride [EN COURS]
- **6.1** [FAIT] : Normal RT (MRT), BLAS extraction CS, 3 BLAS (blocky+smooth+topings), TLAS
- **6.2** [FAIT] : RT shadows (3 jittered rays, TMin adaptatif, colored shadows)
- **6.3** [FAIT] : RT AO (4 cosine-weighted rays, IGN + Cranley-Patterson, temporal accumulation, bilateral blur)
- **6.4** [A FAIRE] : Fallback shadow maps + SSAO
### Phase 7 - Stylized Lighting [EN COURS]
- **7.1** [FAIT] : Hemisphere ambient, colored shadows, rim light, tone mapping + saturation, screenshot mode
## Métriques cibles et résultats
| Métrique | Cible | Résultat (Ryzen 7 9800X3D + RX 9070 XT) |
|----------|-------|---------------------------------------|
| FPS 1440p | > 60 fps | 80-110 FPS (anim blocky), 700+ FPS (statique) |
| FPS anim smooth+topings | > 15 fps | 17 FPS (smooth+topings+blocky anim 60Hz) |
| Meshing GPU (blocky) | < 200 us/chunk | ~0.6 us/chunk (0.1ms / 171 chunks) |
| Meshing CPU (smooth) | < 30ms | 17ms (parallele, 648 chunks) |
| Memoire GPU | < 500 Mo | ~30 Mo |
| Draw calls | < 100 | 1 (GPU mesh) ou 1 (MDI) |
## Conventions
- Namespaces : tout le code voxel est dans `namespace voxel`
- Chunks : 32x32x32, configurable via `CHUNK_SIZE`
- Coordonnées : Y = haut, monde infini en X/Z, hashmap sparse
- Matériaux : palette de 256, index 0 = air (vide), 1=grass, 2=dirt, 3=stone (blocky), 4=sand, 5=snow (smooth), 6=smoothstone (smooth)
- Faces : 0=+X, 1=-X, 2=+Y, 3=-Y, 4=+Z, 5=-Z
- Smooth flag : `FLAG_SMOOTH = 0x1` dans VoxelData flags active Surface Nets au lieu du rendu blocky