Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture Overview

RDPE consists of several layers that work together to run particle simulations on the GPU.

High-Level Flow

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  User's Rust    │────▶│  RDPE Compile    │────▶│   GPU Runtime   │
│  Particle +     │     │  Time (Derive    │     │   (wgpu +       │
│  Rules          │     │  Macro + Shader  │     │   Compute       │
│                 │     │  Generation)     │     │   Shaders)      │
└─────────────────┘     └──────────────────┘     └─────────────────┘

Components

1. Particle Derive Macro (rdpe-derive)

The #[derive(Particle)] macro transforms your Rust struct into GPU-compatible form:

#![allow(unused)]
fn main() {
#[derive(Particle, Clone)]
struct Boid {
    position: Vec3,
    velocity: Vec3,
    #[color]
    tint: Vec3,
    particle_type: u32,
}
}

The macro generates:

  • A BoidGpu struct with correct memory alignment (16-byte for GPU arrays)
  • WGSL struct definition for shaders
  • to_gpu() conversion method
  • Tracking of color field offset for rendering

2. Simulation Builder (simulation.rs)

The builder pattern configures everything before running:

#![allow(unused)]
fn main() {
Simulation::<MyParticle>::new()
    .with_particle_count(10_000)
    .with_bounds(1.0)
    .with_spatial_config(0.1, 32)  // For neighbor rules
    .with_spawner(|i, count| { ... })
    .with_rule(Rule::Gravity(9.8))
    .with_rule(Rule::Separate { radius: 0.1, strength: 1.0 })
    .run();
}

At .run() time, the simulation:

  1. Detects if any rules need neighbor queries
  2. Generates appropriate WGSL compute shaders
  3. Generates render shaders
  4. Spawns particles using your spawner function
  5. Initializes GPU state and runs the event loop

3. Shader Generation

Rules are translated to WGSL code:

RuleGenerated Code Location
Gravity, Drag, BounceWallsMain compute shader body
Separate, Cohere, CollideInside neighbor iteration loop
Typed { ... }Wraps inner rule with type checks
Convert { ... }Inside neighbor loop with probability
Custom(code)Inserted directly

4. GPU State (gpu/mod.rs)

Manages all GPU resources:

  • Particle buffer - Storage buffer with all particle data
  • Uniform buffer - View/projection matrix, time, delta_time
  • Compute pipeline - Runs the physics simulation
  • Render pipeline - Draws particles as billboarded quads
  • Spatial hashing (optional) - For neighbor queries

5. Spatial Hashing (gpu/spatial_gpu.rs)

When rules need neighbor queries, RDPE builds a spatial hash:

  1. Morton encoding - Convert 3D position to 1D cell index
  2. Radix sort - Sort particles by cell (dynamic passes based on grid resolution)
  3. Cell table - Build start/end indices for each cell

This accelerates neighbor queries from O(n²) to O(n × average_neighbors).

Render Loop

Each frame:

1. Update uniforms (time, camera)
2. [If spatial] Run spatial hashing passes
3. Run compute shader (apply all rules, integrate velocity)
4. Run render pass (draw particles as quads)
5. Present frame

Memory Layout

The derive macro ensures GPU-compatible alignment:

Particle in Rust          GPU Memory (16-byte aligned)
┌──────────────┐          ┌──────────────────────────────┐
│ position: Vec3│    ──▶   │ position: vec3<f32> (12 bytes)│
│ velocity: Vec3│          │ _pad0: f32 (4 bytes)          │
└──────────────┘          │ velocity: vec3<f32> (12 bytes)│
                          │ particle_type: u32 (4 bytes)  │
                          └──────────────────────────────┘

The particle_type field is auto-added if not present.