bvle-voxels/CLAUDE.md
Samuel Bouchet 57ac08f231 Refactor: extract VoxelRTManager, DeferredGPUBuffer, decompose VoxelRenderPath
- Extract DeferredGPUBuffer utility (staging→dirty→capacity GPU buffer pattern)
- Extract VoxelRTManager from VoxelRenderer (~500 lines: BLAS/TLAS, RT shadows+AO)
- Decompose VoxelRenderPath into CameraController, AnimationState, VoxelProfiler
- Replace toping std::sort with O(n) counting sort by (type, variant)
- Update CLAUDE.md architecture docs to reflect new file structure
2026-03-31 13:46:35 +02:00

10 KiB
Raw Blame History

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 (CameraController, AnimationState, VoxelProfiler)
│   │   ├── VoxelRTManager.h/.cpp # Ray tracing: BLAS/TLAS lifecycle, shadows+AO dispatches
│   │   ├── DeferredGPUBuffer.h # Utilitaire staging→dirty→capacity GPU buffer upload
│   │   └── 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

# 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/*.hlslengine/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 : VoxelProfiler (21 ProfileAccum, moyennes toutes les 5s)
  • DeferredGPUBuffer : utilitaire pour buffers GPU avec staging CPU, dirty flag, capacity-based growth (25% headroom)
  • VoxelRTManager (VoxelRTManager.h/.cpp) : gère BLAS/TLAS, dispatches RT shadows+AO, isolé du renderer
  • VoxelRenderPath décomposé en : CameraController (mouvement/souris), AnimationState (tick terrain), VoxelProfiler
  • Toping sort : counting sort O(n) par (type, variant) au lieu de std::sort

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