Sizing MaximumAmount

Maximum Amount on a ParticleEmitter sets the upper bound on the particles that emitter can hold at once. The engine allocates a GPU particle pool of that size at load. The engine can grow the pool at runtime, but doing so reallocates the backing buffer and is best avoided on a hot path: pick a size at authoring time that covers your steady state. Picking a good value is the difference between smooth animation and wasted memory (or, on the other side, clipped emission).

How the pool is used

Each live particle occupies one slot in the pool. When a particle dies its slot becomes available for a newly-spawned particle the next frame. MaximumAmount therefore caps the simultaneously live particle count, not the total number emitted over time.

If the emission-count material asks to spawn more particles than the pool has free slots, the surplus spawns are silently dropped that frame. The emitter does not stall or queue.

Size to steady-state, not to peaks

The right target is particles_per_second × average_lifetime, plus headroom for variance:

MaximumAmount ≈ (Lifetime + LifetimeVariance) × AmountPerSecond

Binding that expression directly on the emitter keeps the cap in step with rate and lifetime changes. Each stock emission-count material has a recommended binding: see the emission-count materials table in the editor reference.

For bursty sources (EmissionCount_Burst, EmissionCount_Periodic), size to the largest burst plus the steady-state residue from any overlapping bursts. EmissionCount_Burst with a single shot can use BurstCount directly; periodic bursts typically need BurstCount × ceil(Lifetime / BurstInterval).

Why over-sizing is costly

  • Memory. Every slot in the pool holds the full ParticleData record (position, velocity, lifetime, colour, and so on) plus index-buffer entries for sorting, and sort scratch buffers (radix counters, local offsets) are allocated to cover the whole pool. Wasted slots are pure memory overhead, and a sorted emitter allocates additional scratch proportional to MaximumAmount.

  • Dispatch cost. The per-frame affector, collision, and sort dispatches are indirect: their workgroup counts are derived from the active particle count each frame, not MaximumAmount. Oversizing by itself does not inflate per-frame compute. The cost of oversizing is memory (and the resulting cache / GPU-memory pressure), not wasted work over empty slots.

Under-sizing symptoms

If the pool is too small you’ll see particles “skip” (new spawn bursts won’t all appear) or effects look visibly thinner than designed. If the emission rate is bursty and the cap is sized to the average, the first frame of each burst eats the slack.

Practical heuristics

  • Hot-loop effects (fire, smoke, sparks): bind MaximumAmount to the material’s steady-state expression; revisit only if you observe skipping.

  • One-shot bursts: match MaximumAmount to the burst size; accept the wasted capacity when idle rather than oversizing a sibling emitter.

  • Subemitters: MaximumAmount on the subemitter caps the secondary particle pool. A good starting point is SourceEmitter.MaximumAmount × Emission.ParticlesPerEvent for per-event spawns; tune down if the effect clearly peaks lower.

  • Screen-coverage heavy effects: 2D particles cost less than 3D meshes: you can afford a larger MaximumAmount when using the 2D billboarded path.

See also