-
Notifications
You must be signed in to change notification settings - Fork 1
GPU Particles
SparkEngine provides a GPU-accelerated particle system that runs emission, simulation, sorting, and rendering entirely on the GPU via D3D11 compute shaders. Each emitter supports up to 1 million particles with bitonic sort for correct alpha blending.
Source: SparkEngine/Source/Graphics/GPUParticleSystem.h, SparkEngine/Source/Graphics/GPUParticleTypes.h
Namespace: Global (classes prefixed with GPU)
Tests: Tests/TestGPUParticleSystem.cpp (11 test cases)
- Overview
- Pipeline
- Emitter Configuration
- GPU Data Layout
- API Reference
- Usage Example
- Performance
- Integration
- See Also
The GPU particle system offloads all particle work to compute shaders, freeing the CPU for gameplay logic. The CPU only manages emitter configuration and issues dispatch calls.
┌─────────────────────────────────────────────────────────────┐
│ CPU Side │
│ CreateEmitter() → configure desc → Update(dt) → Render() │
├─────────────────────────────────────────────────────────────┤
│ GPU Side │
│ ┌──────────────┐ ┌────────────────┐ ┌─────────────────┐ │
│ │ ParticleEmit │→│ParticleSimulate│→│ParticleBitonic │ │
│ │ .hlsl │ │ .hlsl │ │ Sort.hlsl │ │
│ │ │ │ │ │ │ │
│ │ Pop dead │ │ Integrate vel │ │ Sort alive by │ │
│ │ Init new │ │ Kill expired │ │ camera distance │ │
│ │ particles │ │ Compact alive │ │ │ │
│ └──────────────┘ └────────────────┘ └─────────────────┘ │
│ | │
│ v │
│ DrawInstancedIndirect │
│ (alive list + particle pool SRVs) │
└─────────────────────────────────────────────────────────────┘
| Constant | Value | Description |
|---|---|---|
kMaxGPUParticlesPerEmitter |
1,048,576 (1M) | Hard maximum particles per emitter |
kDefaultGPUParticleCapacity |
65,536 (64K) | Default capacity if not specified |
kParticleThreadGroupSize |
256 | Compute shader thread group size |
Each frame, Update(deltaTime) dispatches three compute shader passes per emitter:
-
Emit (
ParticleEmit.hlsl): Pops indices from the dead list, initializes new particles with position, velocity, lifetime, color, and rotation based on emitter settings -
Simulate (
ParticleSimulate.hlsl): Integrates velocity with gravity and drag, ages particles, kills expired ones (push to dead list), and compacts the alive list -
Bitonic Sort (
ParticleBitonicSort.hlsl): Sorts the alive list by camera distance for correct back-to-front alpha blending - Update indirect args: Writes the alive count into the indirect draw argument buffer
Render(view, projection) then binds the alive list SRV and particle pool SRV and issues DrawInstancedIndirect.
The ParticleEmitterDesc (defined in GPUParticleTypes.h) configures emitter behavior:
| Field | Type | Default | Description |
|---|---|---|---|
name |
string |
"" |
Debug identifier |
emissionRate |
float |
100.0 | Particles emitted per second |
emitterShape |
EmitterShape |
Point | Point, Sphere, Cone, Box |
shapeRadius |
float |
1.0 | Radius for Sphere/Cone shapes |
coneAngle |
float |
45.0 | Half-angle in degrees for Cone |
gravity |
XMFLOAT3 |
{0,-9.8,0} | Gravity acceleration |
drag |
float |
0.0 | Velocity damping per second |
gravityMultiplier |
float |
1.0 | Scale factor for gravity |
lifetimeRange |
x=min, y=max | 1.0-3.0 | Random lifetime range (seconds) |
sizeRange |
x=min, y=max | 0.1-0.5 | Random particle size range |
rotSpeedRange |
x=min, y=max | 0.0-0.0 | Random rotation speed (rad/s) |
The per-particle structured buffer layout (16-byte aligned):
struct alignas(16) GPUParticleData
{
XMFLOAT3 position;
float age;
XMFLOAT3 velocity;
float lifetime;
XMFLOAT4 color;
float size;
float rotation;
float rotationSpeed;
uint32_t alive; // 1 = alive, 0 = dead
};Per-emitter constant buffer sent to compute shaders each frame:
struct alignas(16) GPUParticleEmitterCB
{
XMFLOAT3 emitterPosition;
float deltaTime;
XMFLOAT3 gravity;
float drag;
float emissionRate;
float shapeRadius;
float coneAngle;
uint32_t maxParticles;
XMFLOAT4 lifetimeRange; // x=min, y=max, z=speedMin, w=speedMax
XMFLOAT4 sizeRange; // x=sizeMin, y=sizeMax, z=rotMin, w=rotMax
XMFLOAT4 rotSpeedRange; // x=min, y=max
uint32_t emitCount; // particles to emit this frame
uint32_t emitterShape;
float gravityMultiplier;
float totalTime;
};Each emitter owns seven GPU buffers:
| Buffer | Type | Description |
|---|---|---|
particleBuffer |
RW Structured | Pool of all particle data |
deadListBuffer |
Append/Consume | Available particle indices |
aliveListBuffer |
RW Structured | This frame's alive particle indices |
aliveListSwapBuffer |
RW Structured | Previous frame's alive indices (double-buffered) |
sortKeysBuffer |
RW Structured | Camera distance keys for bitonic sort |
indirectArgsBuffer |
Indirect |
DrawInstancedIndirect arguments |
emitterCBBuffer |
Constant | Per-emitter constants |
auto& gpu = GPUParticleSystem::GetInstance();
// Windows (D3D11)
HRESULT hr = gpu.Initialize(device, context);
// Non-Windows (stub)
bool ok = gpu.Initialize();// Create with custom capacity
uint32_t id = gpu.CreateEmitter(desc, 100000); // 100K particles
// Destroy single emitter
gpu.DestroyEmitter(id);
// Destroy all emitters
gpu.DestroyAllEmitters();// Each frame
gpu.Update(deltaTime);
gpu.Render(viewMatrix, projMatrix);gpu.SetEmitterPosition(id, {10.0f, 5.0f, 0.0f});
gpu.SetEmitterPlaying(id, false); // pause emission
uint32_t count = gpu.GetEmitterCount();
std::string status = gpu.GetStatus();auto& gpuParticles = GPUParticleSystem::GetInstance();
gpuParticles.Initialize(device, context);
ParticleEmitterDesc desc;
desc.name = "campfire";
desc.emissionRate = 500.0f;
desc.gravity = {0.0f, 2.0f, 0.0f}; // upward drift
desc.drag = 0.5f;
desc.shapeRadius = 0.3f;
uint32_t fireId = gpuParticles.CreateEmitter(desc, 50000);
gpuParticles.SetEmitterPosition(fireId, {5.0f, 0.0f, 10.0f});
// In render loop
gpuParticles.Update(deltaTime);
gpuParticles.Render(camera.GetViewMatrix(), camera.GetProjectionMatrix());-
Thread group size: 256 threads per group (must match HLSL
[numthreads]) -
Dispatch count:
ceil(maxParticles / 256)groups per emitter per compute pass - Sorting: Bitonic sort is O(n log^2 n) but runs entirely on GPU with high parallelism
- Memory: Each particle is 64 bytes (GPUParticleData). 1M particles = ~64 MB per emitter
- Indirect draw: Zero CPU readback during normal rendering — the GPU writes draw args directly
-
GraphicsEngine: Calls
Update()andRender()as part of the main render pipeline -
CPU ParticleSystem: The existing
ParticleSystemclass handles CPU-side particles; GPU particles are a separate, higher-performance path for large counts - Platform: Requires D3D11 compute shader support. Non-Windows platforms get a no-op stub
- Rendering and Graphics — Overall graphics pipeline
- GPU-Driven Rendering — Related GPU compute pipeline for geometry culling
- Shader Pipeline — Compute shader compilation
Published from 936a8401bf15. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Golden Image Testing
- Utilities
- Testing
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers