Module Sarek_vulkan.Vulkan_api_batch

Recording, submitting and completing launch batches (specs/vulkan-async-launches.md). Every function that takes a stream runs with stream.device.batch_lock held, except try_release_idle, which takes it with try_lock.

module Device = Vulkan_api_device
val default_batch : int

Dispatches one command buffer collects before it is submitted, from SPOC_VULKAN_BATCH (a positive integer; anything else gives the default). 1 submits and waits at every launch: the behaviour before batching, and the fallback if batching is suspected (Sarek #504, cause unconfirmed).

val batch_limit : int Stdlib.ref
val max_deferred_frees : int

Queued buffer releases (count, bytes) past which the next launch first waits for all launches and runs them. A bound acted on at the next launch, not a hard cap: a program that queues releases and never launches again keeps them until a drain point or an allocation failure (Vulkan_api_release.with_release_retry).

val max_deferred_bytes : int Stdlib.ref
val is_device_lost : BE.t -> bool

The device reported that it is lost: the work of every submission is over.

val failure_of_error : BE.t -> string * string

Kernels and reason of a batch failure, from the error it raised.

val force_idle : Vulkan_api_slot.slot -> unit

The slot's fields back to idle; allocation-free, so no asynchronous exception can interrupt it. Pool counters may be stale afterwards.

val retire_robust : int -> Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unit

retire, retried when an exception interrupts it (asynchronous ones can land at any allocation); after attempts failures only the fields are reset.

Abandon a slot whose work is over or can never be observed.

val await_slot : Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unit

Wait for the slot's submission, if any, and retire it. A failing wait raises the batch error naming its kernels. Only a lost device ends the slot's work: the slot is then abandoned and later waits do not raise the error again. Any other failure (a timeout under SPOC_VULKAN_FENCE_TIMEOUT_S, out of memory) does not say the work is over, so the slot stays in flight and the next wait tries again.

val raise_deferred : Vulkan_api_slot.stream -> unit
val keep_wait_error : Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unit

Like await_slot, but a failure is kept as the stream's deferred error (the first one wins) instead of being raised.

val submit_info_of : Vulkan_api_slot.slot -> (Vulkan_types.vk_submit_info, [ `Struct ]) Ctypes.structured * Vulkan_types.vk_command_buffer Ctypes.structure Ctypes_static.ptr Ctypes.ptr
val submit_slot : Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unit

End the slot's command buffer and submit it with its fence. The fence is reset first and armed set right after: an exception that escapes while armed may have come after the queue accepted the batch.

val reset_command_buffer : Vulkan_api_slot.slot -> unit

Put the slot's command buffer back in the initial state, whatever state an interrupted end-of-batch step left it in. A device error is ignored: the buffer is then unusable anyway.

val wait_queue_idle : Device.t -> unit
val recover_from_exception : int -> Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unit

An exception that is not a Vulkan failure (an asynchronous one: Sys.Break, Out_of_memory, a raising finaliser or memprof callback) escaped submit_slot. Before the end-of-batch step the batch is intact and stays open. After it began, the command buffer may be recording, executable or submitted, and the batch may or may not run: if the fence was armed, wait for the queue to be idle, reset the command buffer so that the next begin is on an initial-state buffer, then abandon the slot (its launches may or may not have run). Another exception can land in this recovery itself, so every step is retried, nothing is allocated outside the protected region, and attempts bounds the retries. The caller re-raises the original exception as it is.

val submit_open : defer:bool -> Vulkan_api_slot.stream -> unit

Submit the open batch of stream, without waiting for it. A Vulkan failure retires the batch; it is raised now when defer is false, and otherwise kept for the next wait of the stream (the first failure wins). Any other exception propagates unchanged after recover_from_exception.

val drain_stream : Vulkan_api_slot.stream -> unit

Submit and wait for everything launched on stream; raises the first failure.

val on_all_streams : Sarek_vulkan.Vulkan_api_slot.Device.t -> (Vulkan_api_slot.stream -> unit) -> unit

Run f on every stream of device, then re-raise the first exception: one failing stream must not leave the others undrained.

val release_deferred : Device.t -> unit

Run the queued buffer releases. Caller holds batch_lock and every launch recorded so far has completed.

val prune_awaited : Vulkan_api_slot.stream -> unit

Forget the awaited events of stream whose work has completed (their fence is no longer pending or reads signalled), so a stream that waits on many events does not keep them all. An event whose lock another domain holds is kept: its fence is being recorded or waited and must not be queried. Caller holds batch_lock.

val drain_device : Device.t -> unit

Wait for every launch of device, then run the queued releases, without consuming any stream's deferred error: it belongs to the wait of that stream, not to the launch that happens to trigger this. A failing wait is kept as that stream's deferred error; the releases run only if every slot ended up idle.

Retire the launch of slot if its fence has already signalled, without waiting. A lost device ends the work: the error is kept once as the stream's deferred error and the slot is abandoned. Other status errors are left to the next wait, which reports them.

val try_release_idle : Device.t -> unit

The free path of Vulkan_api_release: never submits, records, waits or consumes an error, and never blocks on the batch lock (a lock held, even by this domain, leaves the release queued; so does re-entry on the registry lock, which raises and is caught by the caller). Queued releases run only if no stream has an open batch or a launch in flight, so a free is prompt when the device is idle and otherwise waits for the next drain point. The destroyed flag is re-checked under the lock, which Device.destroy holds from its last release to vkDestroyDevice.

val flush_device : Device.t -> unit

Submit the open batch of every stream of device without waiting.

val begin_on_open_buffer : int Stdlib.Atomic.t

Test instrumentation: begins attempted on a command buffer still recording.

Make the active slot of stream ready to record: wait for its previous submission if it is still in flight, then begin the command buffer.

val record_dispatch : Vulkan_api_slot.stream -> kernel:string -> layout:Vulkan_types.vk_descriptor_set_layout -> descriptors:int -> record: (Vulkan_types.vk_command_buffer Ctypes.structure Ctypes.ptr -> Vulkan_types.vk_descriptor_set -> unit) -> unit

Record one dispatch of kernel on stream. record command_buffer set issues the bind, push-constant and dispatch commands (after the barrier recorded here) with set, a descriptor set of layout that it must fill. Nothing is recorded when the set cannot be allocated.

Record that stream waits for event. The event is not added twice, and events whose work has completed are dropped first, so the list stays bounded by the events actually outstanding. synchronize waits on the event's fence as it is then: an event re-recorded after the wait was issued is waited for too, which over-waits and is safe.

val await_events : Vulkan_api_slot.stream -> Vulkan_api_slot.awaited_event list -> unit

Wait for the events stream awaits, then forget them. Every wait is attempted; the first failure is re-raised after the others. Takes the events' locks, so it runs without batch_lock (Vulkan_api_event.record takes an event's lock after releasing it, never the reverse).

val synchronize : Vulkan_api_slot.stream -> unit

Complete everything launched on stream and the events it awaits; also reap the other streams, so a device that is now idle runs its queued releases.

val pending_launches : Device.t -> int

Launches recorded on device that have not completed: open batches and batches in flight. Zero after any synchronisation point.