simvx.graphics.renderer.particle_compute

GPU compute shader particle simulation.

Dispatches a compute shader to update particle positions, velocities, lifetimes, and visual properties entirely on the GPU: avoiding per-frame CPU-to-GPU uploads for particle data.

Mirrors the web backend’s GPUParticlePass (gpu_particle_pass.js): each GPUParticles3D / GPUParticles2D emitter owns a persistent SSBO keyed by a stable emitter_id (the truncated id(node) minted in scene_adapter.py). Multiple emitters in the same scene render independently; an emitter that leaves the tree has its SSBO and descriptor pool released via :meth:prune_inactive.

Module Contents

Classes

ParticleCompute

GPU-based particle simulation via Vulkan compute shader.

Data

API

simvx.graphics.renderer.particle_compute.__all__

[‘ParticleCompute’]

simvx.graphics.renderer.particle_compute.log

‘getLogger(…)’

class simvx.graphics.renderer.particle_compute.ParticleCompute(engine: Any)[source]

GPU-based particle simulation via Vulkan compute shader.

Owns one compute pipeline (shared across emitters) and N per-emitter SSBOs. Mirrors the web GPUParticlePass lifecycle: dispatch() lazy-creates a slot for an unseen emitter_id; prune_inactive() releases slots whose ids are no longer in the scene tree.

Initialization

setup() None[source]

Create the shared compute pipeline.

Per-emitter SSBOs are allocated lazily on first dispatch() for each unique emitter_id.

begin_frame() None[source]

Bump the per-frame stamp used to track active emitters.

prune_inactive(active_ids: set[int]) None[source]

Free GPU resources for emitters not in active_ids.

Called once per frame after every emitter has been submitted, so slots whose GPUParticles* node has left the tree get cleaned up. Mirrors :func:GPUParticlePass.pruneInactive on the web side.

dispatch(cmd: Any, dt: float, emitter_id: int, emitter_config: dict) None[source]

Dispatch the compute shader for one emitter.

Args: cmd: Active command buffer (must be outside a render pass). dt: Delta time in seconds. emitter_id: Stable per-node identifier (id(node) & 0xFFFFFFFF). emitter_config: See :meth:GPUParticles3D.emitter_config.

render(cmd: Any, particle_pass: Any, view_proj: numpy.ndarray, camera_right: numpy.ndarray, camera_up: numpy.ndarray, extent: tuple[int, int]) None[source]

Draw every active emitter’s particles using the shared billboard pipeline.

Iterates the slots whose last_active_frame matches the current frame counter (i.e. were dispatched this frame). The compute-owned SSBO is bound through a per-slot descriptor set built against ParticlePass._ssbo_layout: no per-frame upload.

get_particle_ssbo(emitter_id: int) Any[source]

Return the SSBO for emitter_id (or None if no slot exists).

upload_initial_particles(emitter_id: int, particles: numpy.ndarray) None[source]

Seed a specific emitter’s GPU buffer with CPU-generated particle data.

Used by tests and tools that want a deterministic starting state. len(particles) must not exceed the slot’s max_particles.

emitter_count() int[source]

Number of active per-emitter slots (for tests / debugging).

has_emitter(emitter_id: int) bool[source]

True if the given emitter has an allocated slot.

property ready: bool[source]
cleanup() None[source]

Destroy every per-emitter slot and the shared compute pipeline.