simvx.graphics.renderer.async_compute

Async-compute scheduler: route compute passes to a dedicated queue when one exists.

The renderer records several compute passes per frame (occlusion cull, Hi-Z build, particle simulation, light cull). On a GPU with a dedicated compute queue family (COMPUTE without GRAPHICS), those passes can run on a separate queue and overlap graphics work; the graphics submit then waits on a compute-done semaphore before its consumers (draw-indirect / vertex / depth sample) read the compute results.

AsyncComputeScheduler is the single seam that hides this. Two modes, selected once at construction from :pyattr:GPUContext.async_compute:

  • passthrough (no dedicated compute queue, e.g. integrated GPUs / this dev box): :meth:record_compute records straight into the primary graphics command buffer the caller passes in, :meth:submit is a no-op, and

    meth:

    graphics_wait_semaphores is empty. The frame is byte-identical to the pre-async path: the scheduler adds nothing to the command stream.

  • async (dedicated compute queue): :meth:begin_frame opens a per-frame compute command buffer on the compute queue’s pool; :meth:record_compute records into that buffer (ignoring the graphics cmd argument); :meth:submit ends and submits it to the compute queue signalling the frame’s compute-done semaphore; :meth:graphics_wait_semaphores returns that semaphore (with its wait stage) so the graphics submit blocks until compute results are visible.

Cross-queue data visibility for the shared control buffers is handled at buffer-creation time via VK_SHARING_MODE_CONCURRENT (see GPUContext.concurrent_compute_families and memory.create_buffer); this scheduler owns only the execution ordering (command buffer + semaphore).

Routing the actual passes through this scheduler is a separate wave; this module provides the gated abstraction and is a no-op until a pass calls

meth:

record_compute.

Module Contents

Classes

AsyncComputeScheduler

Records + submits compute passes on a dedicated queue when available.

Data

API

simvx.graphics.renderer.async_compute.__all__

[‘AsyncComputeScheduler’]

simvx.graphics.renderer.async_compute.log

‘getLogger(…)’

class simvx.graphics.renderer.async_compute.AsyncComputeScheduler(ctx: simvx.graphics.gpu.context.GPUContext, frames_in_flight: int)[source]

Records + submits compute passes on a dedicated queue when available.

Construct once per Engine. async_enabled reflects the GPU: when False every method degrades to the single-queue passthrough.

Initialization

begin_frame(frame_index: int, graphics_cmd: Any) None[source]

Open recording for frame_index.

graphics_cmd is the primary command buffer for this frame; in passthrough mode compute passes are recorded into it directly. In async mode it is unused (compute records into the dedicated compute cmd) but is still passed for a uniform call site.

record_compute(record_fn: collections.abc.Callable[[Any], None]) None[source]

Record a compute pass.

record_fn(cmd) issues the dispatch(es) and any intra-compute barriers. In async mode cmd is the dedicated compute command buffer; in passthrough mode it is the primary graphics command buffer (so the pass records inline exactly as it does today). Producer->consumer cross-queue ordering is provided by the compute-done semaphore on the async path; the pass’s own intra-queue barriers remain valid in both.

submit() None[source]

Submit the compute command buffer (async) or no-op (passthrough).

In async mode the compute cmd is ended and submitted to the compute queue, signalling this frame’s compute-done semaphore that the graphics submit waits on. With no recorded work the semaphore is still signalled (empty submit) so the graphics wait never deadlocks.

graphics_wait_semaphores() tuple[list[Any], list[int]][source]

Semaphores + per-semaphore wait stages for the graphics submit.

Returns (semaphores, wait_stages) to merge into the frame’s VkSubmitInfo so the graphics queue blocks on compute results before the indirect-draw / vertex / depth-sample stages consume them. Empty in passthrough mode (no extra wait: identical submit to today).

destroy() None[source]