Sarek_vulkan.Vulkan_api_batchRecording, 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_devicemodule BE = Sarek_backend_error.Backend_errorDispatches 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).
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 is_device_lost : BE.t -> boolThe device reported that it is lost: the work of every submission is over.
val failure_of_error : BE.t -> string * stringKernels and reason of a batch failure, from the error it raised.
val force_idle : Vulkan_api_slot.slot -> unitThe 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 ->
unitretire, retried when an exception interrupts it (asynchronous ones can land at any allocation); after attempts failures only the fields are reset.
val abandon : Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unitAbandon a slot whose work is over or can never be observed.
val await_slot : Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unitWait 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 -> unitval keep_wait_error : Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unitLike 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.ptrval submit_slot : Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unitEnd 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 -> unitPut 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 -> unitval recover_from_exception :
int ->
Vulkan_api_slot.stream ->
Vulkan_api_slot.slot ->
unitAn 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 -> unitSubmit 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 -> unitSubmit 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) ->
unitRun 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 -> unitRun the queued buffer releases. Caller holds batch_lock and every launch recorded so far has completed.
val prune_awaited : Vulkan_api_slot.stream -> unitForget 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_all : Sarek_vulkan.Vulkan_api_slot.Device.t -> unitval drain_device : Device.t -> unitval settle : Sarek_vulkan.Vulkan_api_slot.Device.t -> unitWait 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.
val reap : Vulkan_api_slot.stream -> Vulkan_api_slot.slot -> unitRetire 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 reap_all : Sarek_vulkan.Vulkan_api_slot.Device.t -> unitval try_release_idle : Device.t -> unitThe 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 -> unitSubmit the open batch of every stream of device without waiting.
Test instrumentation: begins attempted on a command buffer still recording.
val open_active : Vulkan_api_slot.stream -> Vulkan_api_slot.slotMake 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) ->
unitRecord 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.
val add_awaited :
Vulkan_api_slot.stream ->
Vulkan_api_slot.awaited_event ->
unitRecord 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 ->
unitWait 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 -> unitComplete 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 -> intLaunches recorded on device that have not completed: open batches and batches in flight. Zero after any synchronisation point.