luv

Workshop wiki

gpu.lisp

hal/vulkan/gpu.lisp

system luv · 194 definitions · on GitHub

in-package#:luv
defmacrowith-vulkan-gpu-driver-environment
&bodybody

Run body with the floating-point environment expected by native drivers.

#+sbcl`
sb-int:with-float-traps-masked
:invalid:divide-by-zero:overflow:underflow:inexact
,@body
#+
anddarwin
notsbcl
`(float-features:with-float-traps-masked t ,@body)
#-
orsbcldarwin
`(progn ,@body)
defunwrap-vulkan-gpu-driver-teardown
teardown

Return a durable teardown which restores the Vulkan driver environment.

Queue maintenance already establishes this environment, but process-global finalizer retirement can replay a failed closure from an arbitrary thread.

check-typeteardownfunction
defunretire-vulkan-leaked-native-owner
resource-classlabeldeviceownerteardown

Transfer leaked native ownership before signaling the discipline warning.

unwind-protect

Custody is already durable even if a handler promotes this warning or leaves it by THROW, ERROR, or an interactive debugger restart.

define-conditionvulkan-gpu-error
reason:initarg:reason:readervulkan-gpu-error-reason
details:initarg:details:initformnil:readervulkan-gpu-error-details
:report
lambda
conditionstream
case
vulkan-gpu-error-reasoncondition
:no-physical-device
formatstream"Vulkan found no physical devices."
:no-graphics-queue
formatstream"The Vulkan physical device ~S exposes no graphics queue."
vulkan-gpu-error-detailscondition
:no-compatible-memory
formatstream"The Vulkan device has no compatible memory type for ~S."
vulkan-gpu-error-detailscondition
otherwise
formatstream"Vulkan GPU operation ~S failed: ~S~@[ (~S)~]"
gpu-error-operationcondition
vulkan-gpu-error-reasoncondition
vulkan-gpu-error-detailscondition
defunportable-vulkan-gpu-instance-options

Return optional extensions and flags useful to luv's Vulkan instance.

let*
portability-p
memberportability-extensionavailable:test#'string=
debug-utils-p
memberdebug-utils-extensionavailable:test#'string=
values
append
andportability-p
listportability-extension
anddebug-utils-p
listdebug-utils-extension
ifportability-p'
:enumerate-portability
nil
defclassvulkan-gpu-provider
application-name:initarg:application-name:initform"luv gpu":readervulkan-provider-application-name
debug-callback:initarg:debug-callback:initformnil:readervulkan-provider-debug-callback
debug-severities:initarg:debug-severities:initform'
:warning:error
:readervulkan-provider-debug-severities
debug-types:initarg:debug-types:initform'
:general:validation:performance
:readervulkan-provider-debug-types
defgenericvulkan-provider-instance-options
provider
:documentation

Return Vulkan instance extensions and flags for provider.

defgenericvulkan-gpu-device-extension-names
provider
:documentation

Return device extensions enabled by provider.

defmethodvulkan-gpu-device-extension-names
declare
ignoreprovider
nil

Vulkan is luv's portable default. On Darwin, leave the choice open until the native Metal module has loaded so Apple hosts do not accidentally run through MoltenVK merely because the Vulkan backend appears first in ASDF.

#-darwin
defclassvulkan-gpu-object
handle:initarg:handle:readervulkan-handle
destroyed-p:initformnil:accessorvulkan-object-destroyed-p
retirement-teardown:initformnil:accessorvulkan-object-retirement-teardown:documentation"Progress-tracked native teardown closure, if any."
last-submission:initform0:accessorvulkan-object-last-submission:documentation

Index of the newest queue submission using this object. Zero means it has never been submitted.

defgenericvulkan-native-teardown-closure
object
:method
objectt
nil
:documentation

Return a thunk performing object's native teardown, or NIL when object owns nothing to tear down.

The closure captures only extracted native handles and the device wrapper, never object itself, so it can outlive object as its leak finalizer. Capturing the device wrapper also orders finalization: a device stays reachable until every child's pending finalizer has run.

defmacrowith-vulkan-queue-teardown
device-objectdevice-var
&bodybody

Run native teardown body with DEVICE-VAR bound to device-object's native handle, skipping it entirely once the device is destroyed.

The queue's recursive lock serializes teardown — including teardown on the finalizer thread — against submission and device destruction.

let
object
gensym"OBJECT"
run
gensym"RUN"
queue
gensym"QUEUE"
`
let
,object,device-object
flet
,run
unless
or
vulkan-object-destroyed-p,object
let
,device-var
vulkan-handle,object
declare
ignorable,device-var
,@body
let
,queue
vulkan-device-queue,object
if,queue
sb-thread:with-recursive-lock
vulkan-queue-lock,queue
defgenericvulkan-finalizer-device
object
:method
objectt
declare
ignoreobject
nil
:documentation

Return the live device queue which may durably own object's finalizer.

defmethodinitialize-instance:after

Explicit destroy cancels this finalizer. If the object is instead reclaimed by the collector, the leak is a warned discipline failure and the native resources are freed as a safety net. Anything the queue still retains through a live submission record is reachable, so a collected wrapper is always past the completion frontier.

let
setf
vulkan-object-retirement-teardownobject
closer
whencloser
let*
resource-class
class-name
class-ofobject
label
gpu-object-labelobject
owner
listresource-class:labellabel
sb-ext:finalizeobject
defclassvulkan-gpu-device
instance:initarg:instance:readervulkan-device-instance
instance-extension-names:initarg:instance-extension-names:initformnil:readervulkan-device-instance-extension-names
debug-messenger:initarg:debug-messenger:initformnil:readervulkan-device-debug-messenger
device-extension-names:initarg:device-extension-names:initformnil:readervulkan-device-extension-names
physical-device:initarg:physical-device:readervulkan-device-physical-device
queue-family:initarg:queue-family:readervulkan-device-queue-family
video-queue-family:initarg:video-queue-family:initformnil:readervulkan-device-video-queue-family
queue:initformnil:accessorvulkan-device-queue
retiring-p:initformnil:accessorvulkan-device-retiring-p:documentation"True once the idle-and-ledger admission barrier closes."
native-device-retired-box:initform
listnil
:readervulkan-device-native-retired-box:documentation"Shared phase flag set immediately after vkDestroyDevice succeeds."
destroy-admission:initformnil:accessorvulkan-device-destroy-admission
destroy-teardown:initformnil:accessorvulkan-device-destroy-teardown
finalizer-teardown:initformnil:accessorvulkan-device-finalizer-teardown:documentation"Driver-wrapped fallback sharing DESTROY's native progress sequence."
render-passes:initform
make-hash-table:test#'equal
:readervulkan-device-render-passes
defunvulkan-device-native-retired-p
device
car
vulkan-device-native-retired-boxdevice
defun
valuedevice
setf
car
vulkan-device-native-retired-boxdevice
value
defclassvulkan-gpu-queue
device:initarg:device:readervulkan-queue-device
family:initarg:family:readervulkan-queue-family
timeline:initarg:timeline:readervulkan-queue-timeline:documentation"Timeline semaphore signaled with each submission index."
submission-counter:initform0:accessorvulkan-queue-submission-counter
live-submissions:initform'
:accessorvulkan-queue-live-submissions:documentation

Submission records not yet passed by the frontier, oldest first.

external-semaphore-states:initform'
:accessorvulkan-queue-external-semaphore-states:documentation"Live handle-generation states shared by adopted textures."
retirement-ledger:initform
make-gpu-retirement-ledger
:readervulkan-queue-retirement-ledger:documentation"Native ownership transferred by logical DESTROY."
lock:initform
sb-thread:make-mutex:name"vulkan gpu queue"
:readervulkan-queue-lock:documentation

Guards the counter, live records, deferred destroys, and scheduled texture layouts across the canvas and REPL threads.

defgenericvulkan-admission-closed-p
object
:method
objectt
nil
defmethodvulkan-admission-closed-p
vulkan-device-retiring-pdevice
defmethodvulkan-admission-closed-p
vulkan-device-retiring-p
vulkan-queue-devicequeue
defstructvulkan-gpu-submission

One queue submission awaiting completion, retaining what the GPU may use.

index0:type
unsigned-byte64
command-buffers
:typevector
resources'
:typelist
post-submit-publication
defstructvulkan-external-submission-group

Textures sharing one external timeline semaphore in a submission.

semaphore
current-value0:type
unsigned-byte64
textures'
:typelist
defstructvulkan-external-semaphore-state

Generation-safe shared high-water state for one retained native timeline.

semaphore
value0:type
unsigned-byte64
references0:type
unsigned-byte64
defclassvulkan-gpu-buffer
device:initarg:device:readervulkan-buffer-device
memory:initarg:memory:readervulkan-buffer-memory
mapped:initarg:mapped:readervulkan-buffer-mapped
defclassvulkan-gpu-texture
device:initarg:device:readervulkan-texture-device
memory:initarg:memory:initformnil:readervulkan-texture-memory
owned-p:initarg:owned-p:initformt:readervulkan-texture-owned-p
external-owner:initarg:external-owner:initformnil:readervulkan-texture-external-owner
aspect:initarg:aspect:initformnil:readervulkan-texture-explicit-aspect
external-semaphore:initarg:external-semaphore:initformnil:readervulkan-texture-external-semaphore
external-semaphore-value:initarg:external-semaphore-value:initform0:accessorvulkan-texture-private-external-semaphore-value
external-semaphore-state:initarg:external-semaphore-state:initformnil:readervulkan-texture-external-semaphore-state
external-submitted:initarg:external-submitted:initformnil:readervulkan-texture-external-submitted
vk-format:initarg:vk-format:readervulkan-texture-vk-format
layout:initarg:layout:initform:undefined:accessorvulkan-texture-layout
defclassvulkan-gpu-texture-view
device:initarg:device:readervulkan-texture-view-device
defclassvulkan-gpu-shader-module
device:initarg:device:readervulkan-shader-module-device
defclassvulkan-gpu-sampler
device:initarg:device:readervulkan-sampler-device
defclassvulkan-gpu-bind-group-layout
device:initarg:device:readervulkan-bind-group-layout-device
binding:initarg:binding:initformnil:readervulkan-bind-group-layout-binding
entries:initarg:entries:initformnil:readervulkan-bind-group-layout-entries
defclassvulkan-gpu-compute-pipeline
device:initarg:device:readervulkan-compute-pipeline-device
layout:initarg:layout:readervulkan-compute-pipeline-bind-group-layout
pipeline-layout:initarg:pipeline-layout:readervulkan-compute-pipeline-layout
defclassvulkan-gpu-render-pipeline
device:initarg:device:readervulkan-render-pipeline-device
layout:initarg:layout:readervulkan-render-pipeline-bind-group-layout
pipeline-layout:initarg:pipeline-layout:readervulkan-render-pipeline-layout
render-pass:initarg:render-pass:readervulkan-render-pipeline-render-pass
target-formats:initarg:target-formats:initformnil:readervulkan-render-pipeline-target-formats
vertex-buffers:initarg:vertex-buffers:initformnil:readervulkan-render-pipeline-vertex-buffers
depth-format:initarg:depth-format:initformnil:readervulkan-render-pipeline-depth-format
defclassvulkan-gpu-bind-group
device:initarg:device:readervulkan-bind-group-device
layout:initarg:layout:readervulkan-bind-group-layout
texture-views:initarg:texture-views:initformnil:readervulkan-bind-group-texture-views
samplers:initarg:samplers:initformnil:readervulkan-bind-group-samplers
buffers:initarg:buffers:initformnil:readervulkan-bind-group-buffers
descriptor-pool:initarg:descriptor-pool:readervulkan-bind-group-descriptor-pool
defclassvulkan-gpu-command-encoder
device:initarg:device:readervulkan-command-encoder-device
command-pool:initarg:command-pool:accessorvulkan-command-encoder-command-pool
command-buffer:initarg:command-buffer:readervulkan-command-encoder-command-buffer
initial-texture-layouts:initform
make-hash-table:test#'eq
:readervulkan-command-encoder-initial-texture-layouts
texture-layouts:initform
make-hash-table:test#'eq
:readervulkan-command-encoder-texture-layouts
textures:initform
make-hash-table:test#'eq
:readervulkan-command-encoder-textures
resources:initform
make-hash-table:test#'eq
:readervulkan-command-encoder-resources:documentation

Every GPU object wrapper the recorded commands depend on, including the tracked textures.

native-resource-box:initform
listnil
:readervulkan-command-encoder-native-resource-box:documentation

One-element list holding the tagged native resources, shared with the encoder's leak finalizer so mid-recording growth stays visible to it.

active-pass:initformnil:accessorvulkan-command-encoder-active-pass
retirement-teardown:initformnil:accessorvulkan-command-encoder-retirement-teardown
state:initform:recording:accessorvulkan-command-encoder-state
defclassvulkan-gpu-compute-pass-encoder
encoder:initarg:encoder:readervulkan-compute-pass-command-encoder
pipeline:initformnil:accessorvulkan-compute-pass-pipeline
bind-group:initformnil:accessorvulkan-compute-pass-bind-group
state:initform:recording:accessorvulkan-compute-pass-state
defclassvulkan-gpu-render-pass-encoder
encoder:initarg:encoder:readervulkan-render-pass-command-encoder
framebuffer:initarg:framebuffer:readervulkan-render-pass-framebuffer
targets:initarg:targets:initformnil:readervulkan-render-pass-targets
depth-target:initarg:depth-target:initformnil:readervulkan-render-pass-depth-target
depth-store-op:initarg:depth-store-op:initformnil:readervulkan-render-pass-depth-store-op
pipeline:initformnil:accessorvulkan-render-pass-pipeline
bind-group:initformnil:accessorvulkan-render-pass-bind-group
vertex-buffers:initform
make-hash-table
:readervulkan-render-pass-vertex-buffers
state:initform:recording:accessorvulkan-render-pass-state
defclassvulkan-gpu-command-buffer
device:initarg:device:readervulkan-command-buffer-device
command-pool:initarg:command-pool:readervulkan-command-buffer-command-pool
initial-texture-layouts:initarg:initial-texture-layouts:readervulkan-command-buffer-initial-texture-layouts
final-texture-layouts:initarg:final-texture-layouts:readervulkan-command-buffer-final-texture-layouts
textures:initarg:textures:readervulkan-command-buffer-textures
resources:initarg:resources:initformnil:readervulkan-command-buffer-resources
native-resources:initarg:native-resources:initformnil:readervulkan-command-buffer-native-resources
state:initform:ready:accessorvulkan-command-buffer-state
defmethodvulkan-finalizer-device
vulkan-buffer-deviceobject
defmethodvulkan-finalizer-device
vulkan-texture-deviceobject
defmethodvulkan-finalizer-device
vulkan-texture-view-deviceobject
defmethodvulkan-finalizer-device
vulkan-shader-module-deviceobject
defmethodvulkan-finalizer-device
vulkan-sampler-deviceobject
defmethodvulkan-finalizer-device
vulkan-bind-group-layout-deviceobject
defmethodvulkan-finalizer-device
vulkan-compute-pipeline-deviceobject
defmethodvulkan-finalizer-device
vulkan-render-pipeline-deviceobject
defmethodvulkan-finalizer-device
vulkan-bind-group-deviceobject
defmethodvulkan-finalizer-device
vulkan-command-buffer-deviceobject
defunvulkan-command-encoder-native-resources
encoder
first
vulkan-command-encoder-native-resource-boxencoder
defun
valueencoder
setf
first
vulkan-command-encoder-native-resource-boxencoder
value
defunensure-live-vulkan-object
objectoperation
when
or
vulkan-object-destroyed-pobject
error'gpu-object-destroyed-error:objectobject:operationoperation
object
defuncall-with-live-vulkan-device-queue
deviceoperationthunk

Serialize admitted device-native work against device destruction.

let
queue
vulkan-device-queuedevice
ifqueue
sb-thread:with-recursive-lock
vulkan-queue-lockqueue
funcallthunk
progn
funcallthunk
defmacrowith-live-vulkan-device-queue
deviceoperation
&bodybody
`
call-with-live-vulkan-device-queue,device,operation
lambda
,@body
defunfirst-vulkan-graphics-queue-family
physical-device

Return the first graphics-and-compute queue exposed by physical-device.

or
loopforpropertiesinforindexfrom0when
and
plusp
lvk:queue-family-countproperties
member:graphics
lvk:queue-family-flagsproperties
member:compute
lvk:queue-family-flagsproperties
returnindex
error'vulkan-gpu-error:operation:request-device:reason:no-graphics-queue:detailsphysical-device
defunfirst-vulkan-video-decode-queue-family
physical-device
loopforpropertiesinforindexfrom0when
and
plusp
lvk:queue-family-countproperties
member:video-decode
lvk:queue-family-flagsproperties
returnindex
defparameter*vulkan-video-device-extensions*'
"VK_KHR_video_queue""VK_KHR_video_decode_queue""VK_KHR_video_decode_h264""VK_KHR_video_decode_h265"
"Optional extensions which let FFmpeg decode on luv's VkDevice."
defparameter*vulkan-presentation-timing-device-extensions*"Optional extensions used to observe a swapchain's real display timeline."
defunavailable-vulkan-video-device-extensions
physical-device
let
available
remove-if-not
lambda
name
membernameavailable:test#'string=
*vulkan-video-device-extensions*
defunavailable-vulkan-presentation-timing-device-extensions
physical-device
let
available
remove-if-not
lambda
name
membernameavailable:test#'string=
*vulkan-presentation-timing-device-extensions*
defuninstall-vulkan-device-leak-finalizer
device

Arrange to warn about and reclaim device if it is collected undestroyed.

The finalizer captures the same progress-tracked native teardown as explicit destroy, but not the device or queue wrappers. A failed explicit teardown can therefore be abandoned without replaying native calls which already returned.

multiple-value-bind
native-teardownfinalizer-teardown
ensure-vulkan-device-retirement-teardownsdevice
vulkan-device-queuedevice
declare
ignorenative-teardown
let
label
gpu-object-labeldevice
sb-ext:finalizedevice
device
defunmake-vulkan-gpu-device
instancephysical-devicequeue-familydescriptor&keydebug-messengerinstance-extension-namesenabled-extension-namesvideo-queue-family

Create GPU wrappers for an already selected Vulkan device and queue.

let
native-device
lvk:create-devicephysical-devicequeue-family:enabled-extension-namesenabled-extension-names:additional-family-indices
andvideo-queue-family
listvideo-queue-family
timelinenil
handler-case
let*
native-queue
device
make-instance'vulkan-gpu-device:label
gpu-descriptor-labeldescriptor
:handlenative-device:instanceinstance:debug-messengerdebug-messenger:instance-extension-namesinstance-extension-names:device-extension-namesenabled-extension-names:physical-devicephysical-device:queue-familyqueue-family:video-queue-familyvideo-queue-family
queue
progn
setftimeline
make-instance'vulkan-gpu-queue:label"default queue":handlenative-queue:devicedevice:familyqueue-family:timelinetimeline
setf
vulkan-device-queuedevice
queue
device
error
condition
whentimeline
lvk:destroy-semaphorenative-devicetimeline
lvk:destroy-devicenative-device
errorcondition
defunmake-borrowed-vulkan-texture
deviceimagesizeformatvk-format&key
usage'
:copy-dst
owneraspect
layout:undefined
semaphore
semaphore-value0
submitted

Wrap an externally owned Vulkan image as a GPU texture.

let*
queue
vulkan-device-queuedevice
semaphore-state
andsemaphore
retain-vulkan-external-semaphore-statequeuesemaphoresemaphore-value
completed-pnil
unwind-protect
prog1
make-instance'vulkan-gpu-texture:label"borrowed swapchain texture":size
list
firstsize
secondsize
1
:usageusage:dimensions:2d:formatformat:handleimage:devicedevice:vk-formatvk-format:external-ownerowner:aspectaspect:layoutlayout:external-semaphoresemaphore:external-semaphore-valuesemaphore-value:external-semaphore-statesemaphore-state:external-submittedsubmitted:owned-pnil
setfcompleted-pt
unlesscompleted-p
whensemaphore-state
defuncheck-vulkan-device-descriptor
descriptor

Reject WebGPU requirements the initial Vulkan backend cannot honor yet.

unless
typepdescriptor'device-descriptor
error'gpu-request-error:operation:request-device:descriptordescriptor:reason:invalid-descriptor:detailsdescriptor
when
device-descriptor-required-featuresdescriptor
error'gpu-request-error:operation:request-device:descriptordescriptor:reason:unsupported-features:details
device-descriptor-required-featuresdescriptor
when
device-descriptor-required-limitsdescriptor
error'gpu-request-error:operation:request-device:descriptordescriptor:reason:unsupported-limits:details
device-descriptor-required-limitsdescriptor
defunreject-gpu-request
descriptorreason&optionaldetails
error'gpu-request-error:operation:create:descriptordescriptor:reasonreason:detailsdetails
defunvulkan-gpu-format
formatdescriptor
or
cdr
assocformat'
:r16-float.:r16-sfloat
:rgba8-unorm.:r8g8b8a8-unorm
:r8-unorm.:r8-unorm
:rg8-unorm.:r8g8-unorm
:rgba8-unorm-srgb.:r8g8b8a8-srgb
:bgra8-unorm.:b8g8r8a8-unorm
:bgra8-unorm-srgb.:b8g8r8a8-srgb
:rg16-uint.:r16g16-uint
:rg16-float.:r16g16-sfloat
:rgba16-float.:r16g16b16a16-sfloat
:depth32-float.:d32-sfloat
reject-gpu-requestdescriptor:unsupported-texture-formatformat
defunvulkan-texture-format
descriptor
vulkan-gpu-format
texture-descriptor-formatdescriptor
descriptor
defunvulkan-depth-format-p
format
eqformat:depth32-float
defunvulkan-texture-aspect
texture
or
vulkan-texture-explicit-aspecttexture
if
vulkan-depth-format-p
gpu-texture-formattexture
:depth:color
defmethodadopt-native-texture
nativeowner
descriptortexture-descriptor

Wrap one plane view of an AVVkFrame image without taking image ownership.

ensure-live-vulkan-objectdevice:adopt-native-texture
unless
and
listpnative
getfnative:image
owner
reject-gpu-requestdescriptor:invalid-native-texturenative
with-live-vulkan-device-queue
device:adopt-native-texture
make-borrowed-vulkan-texturedevice
getfnative:image
texture-descriptor-sizedescriptor
texture-descriptor-formatdescriptor
or
getfnative:format
:usage
texture-descriptor-usagedescriptor
:ownerowner:aspect
getfnative:aspect
:layout
or
getfnative:layout
:general
:semaphore
getfnative:semaphore
:semaphore-value
or
getfnative:semaphore-value
0
:submitted
getfnative:submitted
defunvulkan-image-usage
usagesformat
mapcar
lambda
ecaseusage
:copy-src:transfer-src
:copy-dst:transfer-dst
:texture-binding:sampled
:render-attachment
if:depth-stencil-attachment:color-attachment
:storage-binding:storage
usages
defunvulkan-buffer-usage
usages
mapcar
lambda
ecaseusage
:uniform:uniform
:storage:storage
:vertex:vertex
:index:index
:copy-dst:transfer-dst
usages
defuncompatible-vulkan-memory-type-p
memory-type-bitsindex
not
zerop
logandmemory-type-bits
ash1index
defunfind-vulkan-texture-memory-type
let
memory-types
lvk:physical-device-memory-types
vulkan-device-physical-devicedevice
memory-type-bits
lvk:image-memory-requirements-memory-type-bitsmemory-requirements
or
loopformemory-typeinmemory-typesforindexfrom0when
and
compatible-vulkan-memory-type-pmemory-type-bitsindex
member:device-local
lvk:physical-memory-type-flagsmemory-type
returnindex
loopformemory-typeinmemory-typesforindexfrom0when
compatible-vulkan-memory-type-pmemory-type-bitsindex
returnindex
error'vulkan-gpu-error:operation:create-texture:reason:no-compatible-memory:detailsmemory-requirements
defunfind-vulkan-upload-memory-type
devicememory-requirements&optional
operation:write-texture
let
memory-types
lvk:physical-device-memory-types
vulkan-device-physical-devicedevice
memory-type-bits
lvk:buffer-memory-requirements-memory-type-bitsmemory-requirements
or
loopformemory-typeinmemory-typesforindexfrom0forflags=
lvk:physical-memory-type-flagsmemory-type
when
and
compatible-vulkan-memory-type-pmemory-type-bitsindex
member:host-visibleflags
member:host-coherentflags
returnindex
error'vulkan-gpu-error:operationoperation:reason:no-compatible-memory:detailsmemory-requirements
defmethodrequest-gpu-device
&optionaldescriptor

Create an owned Vulkan instance, logical device, and graphics queue.

with-vulkan-gpu-driver-environment
let
descriptor
ordescriptor
make-device-descriptor
let
instancenil
debug-messengernil
native-devicenil
completed-pnil
unwind-protect
multiple-value-bind
extensionsflags
setfinstance
lvk:create-instance:application-name
vulkan-provider-application-nameprovider
:flagsflags:enabled-extension-namesextensions

A provider handed an explicit callback insists on the extension; the default messenger only asks for one when the instance already has it, so an ordinary run installs it and simply never hears anything. Nothing speaks through it but a validation layer someone loaded.

let*
requested
vulkan-provider-debug-callbackprovider
available-p
memberlvk:+debug-utils-extension-name+extensions:test#'string=
when
andrequested
notavailable-p
error'vulkan-gpu-error:operation:request-device:reason:missing-debug-utils-extension
when
andcallbackavailable-p
setfdebug-messenger
lvk:install-debug-messengerinstancecallback:severities
vulkan-provider-debug-severitiesprovider
:types
vulkan-provider-debug-typesprovider
let*
physical-device
or
error'vulkan-gpu-error:operation:request-device:reason:no-physical-device
video-queue-family
let
device
make-vulkan-gpu-deviceinstancephysical-devicequeue-familydescriptor:debug-messengerdebug-messenger:instance-extension-namesextensions:video-queue-familyvideo-queue-family:enabled-extension-names
setfnative-device
vulkan-handledevice
completed-pt
device
unlesscompleted-p
unwind-protect
whennative-device
lvk:destroy-devicenative-device
unwind-protect
whendebug-messenger
wheninstance
defmethoddevice-queue
ensure-live-vulkan-objectdevice:device-queue
vulkan-device-queuedevice
defmethodcreate
descriptorbuffer-descriptor

Create one persistently mapped, host-coherent uploadable buffer.

with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-buffer
let*
size
buffer-descriptor-sizedescriptor
usage
buffer-descriptor-usagedescriptor
let
native-device
vulkan-handledevice
buffernil
memorynil
mappednil
completed-pnil
unwind-protect
progn
let*
requirements
memory-type
find-vulkan-upload-memory-typedevicerequirements:create-buffer
setfmemory
lvk:allocate-memorynative-device
lvk:buffer-memory-requirements-sizerequirements
memory-type
lvk:bind-buffer-memorynative-devicebuffermemory
setfmapped
lvk:map-memorynative-devicememorysize
let
object
make-instance'vulkan-gpu-buffer:label
gpu-descriptor-labeldescriptor
:sizesize:usageusage:handlebuffer:devicedevice:memorymemory:mappedmapped
setfcompleted-pt
object
unlesscompleted-p
whenmapped
lvk:unmap-memorynative-devicememory
whenbuffer
lvk:destroy-buffernative-devicebuffer
whenmemory
lvk:free-memorynative-devicememory
defmethodwrite-buffer
data&key
offset0

Copy a one-dimensional numeric array into mapped coherent memory.

data holds single-floats or unsigned 8-, 16-, 32-, or 64-bit integers; offset is aligned to the element size. A storage buffer of packed sites arrives here as its own element type rather than as reinterpreted floats.

with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectbuffer:write-buffer
multiple-value-bind
foreign-typeelement-size
unlessforeign-type
reject-gpu-requestbuffer:unsupported-buffer-datadata
unless
and
typepoffset'
unsigned-byte64
zerop
modoffsetelement-size
<=
+offset
*element-size
lengthdata
gpu-buffer-sizebuffer
reject-gpu-requestbuffer:buffer-write-out-of-bounds
list:offsetoffset:length
*element-size
lengthdata

One memcpy from the pinned storage vector: the element types buffer-data-foreign-type admits are all stored unboxed, so the bytes in the Lisp vector are the bytes the GPU wants.

let
destination
vulkan-buffer-mappedbuffer
sb-kernel:with-array-data
vectordata
start0
end
lengthdata
sb-sys:with-pinned-objects
vector
sb-kernel:system-area-ub8-copy
sb-sys:vector-sapvector
*startelement-size
destinationoffset
*element-size
-endstart
buffer
defmethodread-buffer
&key
offset0
size
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectbuffer:read-buffer
let
size
orsize
-
gpu-buffer-sizebuffer
offset
unless
and
typepoffset'
unsigned-byte64
typepsize'
unsigned-byte64
<=
+offsetsize
gpu-buffer-sizebuffer
reject-gpu-requestbuffer:buffer-read-out-of-bounds
list:offsetoffset:sizesize
submitted-work-done
device-queue
vulkan-buffer-devicebuffer

One memcpy into the pinned result vector, mirroring write-buffer: a byte-at-a-time loop over a whole frame's readback is hundreds of milliseconds; this is the difference between a film and a slideshow.

let
bytes
make-arraysize:element-type'
unsigned-byte8
source
cffi:inc-pointer
vulkan-buffer-mappedbuffer
offset
sb-sys:with-pinned-objects
bytes
sb-kernel:system-area-ub8-copysource0
sb-sys:vector-sapbytes
0size
bytes
defmethodread-buffer-if-ready
&key
offset0
size
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectbuffer:read-buffer-if-ready
let*
size
orsize
-
gpu-buffer-sizebuffer
offset
queue
device-queue
vulkan-buffer-devicebuffer
unless
and
typepoffset'
unsigned-byte64
typepsize'
unsigned-byte64
<=
+offsetsize
gpu-buffer-sizebuffer
reject-gpu-requestbuffer:buffer-read-out-of-bounds
list:offsetoffset:sizesize
sb-thread:with-recursive-lock
vulkan-queue-lockqueue
ensure-live-vulkan-objectbuffer:read-buffer-if-ready
let
when
<=
vulkan-object-last-submissionbuffer
frontier
let
bytes
make-arraysize:element-type'
unsigned-byte8
source
cffi:inc-pointer
vulkan-buffer-mappedbuffer
offset
sb-sys:with-pinned-objects
bytes
sb-kernel:system-area-ub8-copysource0
sb-sys:vector-sapbytes
0size
valuesbytest
defmethodcreate
descriptortexture-descriptor

Create one owned, single-mip Vulkan 2D texture and bind its memory.

with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-texture
let*
size
texture-descriptor-sizedescriptor
usages
texture-descriptor-usagedescriptor
format
native-device
vulkan-handledevice
imagenil
memorynil
completed-pnil
unwind-protect
progn
setfimage
lvk:create-imagenative-device:type:2d:formatformat:width
firstsize
:height
secondsize
:mip-levels1:array-layers1:samples:1:tiling:optimal:usage
vulkan-image-usageusages
texture-descriptor-formatdescriptor
:sharing-mode:exclusive:initial-layout:undefined
let*
setfmemory
lvk:allocate-memorynative-device
lvk:image-memory-requirements-sizerequirements
memory-type
lvk:bind-image-memorynative-deviceimagememory
let
texture
make-instance'vulkan-gpu-texture:label
gpu-descriptor-labeldescriptor
:sizesize:usageusages:dimensions:2d:format
texture-descriptor-formatdescriptor
:handleimage:devicedevice:memorymemory:vk-formatformat
setfcompleted-pt
texture
unlesscompleted-p
whenimage
lvk:destroy-imagenative-deviceimage
whenmemory
lvk:free-memorynative-devicememory
defunensure-vulkan-object-device
objectactual-deviceexpected-deviceoperation
unless
eqactual-deviceexpected-device
error'gpu-device-mismatch-error:objectobject:operationoperation:expected-deviceexpected-device:actual-deviceactual-device
object
defmethodcreate
descriptortexture-view-descriptor
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-texture-view
let
texture
texture-view-descriptor-texturedescriptor
unless
typeptexture'vulkan-gpu-texture
reject-gpu-requestdescriptor:incompatible-texture-backendtexture
ensure-vulkan-object-devicetexture
vulkan-texture-devicetexture
device:create-texture-view
make-instance'vulkan-gpu-texture-view:label
gpu-descriptor-labeldescriptor
:handle
lvk:create-image-view
vulkan-handledevice
vulkan-handletexture
vulkan-texture-vk-formattexture
:aspect
:devicedevice:texturetexture
defmethodcreate
descriptorsampler-descriptor
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-sampler
make-instance'vulkan-gpu-sampler:label
gpu-descriptor-labeldescriptor
:handle
lvk:create-sampler
vulkan-handledevice
:mag-filter
sampler-descriptor-mag-filterdescriptor
:min-filter
sampler-descriptor-min-filterdescriptor
:mipmap-mode
sampler-descriptor-mipmap-filterdescriptor
:address-mode-u
sampler-descriptor-address-mode-udescriptor
:address-mode-v
sampler-descriptor-address-mode-vdescriptor
:address-mode-w
sampler-descriptor-address-mode-wdescriptor
:compare
sampler-descriptor-comparedescriptor
:devicedevice
defmethodcreate
descriptorshader-module-descriptor
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-shader-module
let
code
with-cpu-trace-zone
:vulkan/shader/prepare-spir-v
case
shader-module-descriptor-languagedescriptor
:spir-v
shader-module-descriptor-codedescriptor
:mathematical
let
specification
shader-module-descriptor-codedescriptor
unless
reject-gpu-requestdescriptor:invalid-mathematical-shaderspecification
otherwise
reject-gpu-requestdescriptor:unsupported-shader-language
shader-module-descriptor-languagedescriptor
unless
and
vectorpcode
plusp
lengthcode
every
lambda
word
typepword'
unsigned-byte32
code
reject-gpu-requestdescriptor:invalid-spir-vcode

This forced line separates pure Lisp/SPIR-V preparation from the first driver call if the latter takes the process or device down.

log-event:vulkan"prepared shader module ~A (~:D SPIR-V words)"
or
gpu-descriptor-labeldescriptor
"unlabelled"
lengthcode
make-instance'vulkan-gpu-shader-module:label
gpu-descriptor-labeldescriptor
:handle
with-cpu-trace-zone
:vulkan/shader/create-module
lvk:create-shader-module
vulkan-handledevice
code
:devicedevice
defunstorage-texture-layout-entry
descriptor
let
entries
bind-group-layout-descriptor-entriesdescriptor
unless
and
listpentries
=1
lengthentries
listp
firstentries
eq:storage-texture
getf
firstentries
:type
typep
getf
firstentries
:binding
'
unsigned-byte32
reject-gpu-requestdescriptor:unsupported-bind-group-layoutentries
firstentries
defununiform-buffer-layout-entry
descriptor
let
entries
bind-group-layout-descriptor-entriesdescriptor
unless
and
listpentries
=1
lengthentries
listp
firstentries
eq:uniform-buffer
getf
firstentries
:type
typep
getf
firstentries
:binding
'
unsigned-byte32
reject-gpu-requestdescriptor:unsupported-bind-group-layoutentries
firstentries
defuntexture-sampler-uniform-layout-entries
descriptor
let*
entries
bind-group-layout-descriptor-entriesdescriptor
bindings
mapcar
lambda
entry
getfentry:binding
entries
unless
and
listpentries
plusp
lengthentries
every
lambda
entry
and
listpentry
member
getfentry:type
'
:texture:sampler:uniform-buffer:storage-buffer
typep
getfentry:binding
'
unsigned-byte32
entries
=
lengthbindings
length
remove-duplicatesbindings
reject-gpu-requestdescriptor:unsupported-bind-group-layoutentries
entries
defunvulkan-descriptor-layout-stages
deviceentries

Give each of entries the stages that may read it on device.

A descriptor must name every stage that reads it, and a mesh pipeline reads from stages that did not exist when :VERTEX and :FRAGMENT were the whole graphics vocabulary. Naming the task and mesh stages is only legal once VK_EXT_mesh_shader is enabled, so ask the device first.

let
stages
if
lvk:physical-device-mesh-shader-p
vulkan-device-physical-devicedevice
'
:vertex:fragment:task-ext:mesh-ext
'
:vertex:fragment
mapcar
lambda
entry
if
getfentry:stages
entry
appendentry
list:stagesstages
entries
defmethodcreate
descriptorbind-group-layout-descriptor
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-bind-group-layout
let
entries
bind-group-layout-descriptor-entriesdescriptor
cond
and
=1
lengthentries
eq:storage-texture
getf
firstentries
:type
let*
binding
getfentry:binding
make-instance'vulkan-gpu-bind-group-layout:label
gpu-descriptor-labeldescriptor
:handle
lvk:create-storage-image-descriptor-set-layout
vulkan-handledevice
:bindingbinding
:devicedevice:bindingbinding:entriesentries
and
=1
lengthentries
eq:uniform-buffer
getf
firstentries
:type
let*
binding
getfentry:binding
make-instance'vulkan-gpu-bind-group-layout:label
gpu-descriptor-labeldescriptor
:handle
lvk:create-uniform-buffer-descriptor-set-layout
vulkan-handledevice
:bindingbinding
:devicedevice:entriesentries
t
let
make-instance'vulkan-gpu-bind-group-layout:label
gpu-descriptor-labeldescriptor
:handle:devicedevice:entriesentries
defmethodcreate
descriptorcompute-pipeline-descriptor
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-compute-pipeline
let
module
compute-pipeline-descriptor-moduledescriptor
layout
compute-pipeline-descriptor-layoutdescriptor
entry-point
compute-pipeline-descriptor-entry-pointdescriptor
unless
reject-gpu-requestdescriptor:incompatible-shader-modulemodule
unless
reject-gpu-requestdescriptor:incompatible-bind-group-layoutlayout
ensure-vulkan-object-devicemodule
vulkan-shader-module-devicemodule
device:create-compute-pipeline
ensure-vulkan-object-devicelayout
vulkan-bind-group-layout-devicelayout
device:create-compute-pipeline
unless
reject-gpu-requestdescriptor:invalid-entry-pointentry-point
with-live-vulkan-device-queue
device:create-compute-pipeline
ensure-vulkan-object-devicemodule
vulkan-shader-module-devicemodule
device:create-compute-pipeline
ensure-vulkan-object-devicelayout
vulkan-bind-group-layout-devicelayout
device:create-compute-pipeline
let
pipeline-layout
lvk:create-pipeline-layout
vulkan-handledevice
vector
vulkan-handlelayout
pipelinenil
completed-pnil
unwind-protect
let
wrappernil
setfpipeline
lvk:create-compute-pipeline
vulkan-handledevice
vulkan-handlemodule
pipeline-layout:entry-pointentry-point
wrapper
make-instance'vulkan-gpu-compute-pipeline:label
gpu-descriptor-labeldescriptor
:handlepipeline:devicedevice:layoutlayout:pipeline-layoutpipeline-layout
completed-pt
wrapper
unlesscompleted-p
unwind-protect
whenpipeline
lvk:destroy-pipeline
vulkan-handledevice
pipeline
lvk:destroy-pipeline-layout
vulkan-handledevice
pipeline-layout
defunvulkan-render-pass-for-format
devicegpu-formatsdescriptor&optionaldepth-format
depth-store-op:discard

Return the cached render pass for gpu-formats and optional depth-format.

gpu-formats is a list in fragment-output location order. A single keyword is accepted for callers predating multiple render targets.

with-live-vulkan-device-queue
device:create-render-pass
let*
gpu-formats
if
listpgpu-formats
gpu-formats
andgpu-formats
listgpu-formats
key
listgpu-formatsdepth-formatdepth-store-op
or
gethashkey
vulkan-device-render-passesdevice
setf
gethashkey
vulkan-device-render-passesdevice
ifgpu-formats
lvk:create-color-render-pass
vulkan-handledevice
map'vector
lambda
format
vulkan-gpu-formatformatdescriptor
gpu-formats
:depth-format
anddepth-format
vulkan-gpu-formatdepth-formatdescriptor
:depth-store-opdepth-store-op
lvk:create-depth-render-pass
vulkan-handledevice
vulkan-gpu-formatdepth-formatdescriptor
:depth-store-opdepth-store-op
defunnormalize-vulkan-vertex-buffers
descriptorbuffers
unless
listpbuffers
reject-gpu-requestdescriptor:invalid-vertex-buffersbuffers
loopforbufferinbuffersforbindingfrom0forstride=
getfbuffer:array-stride
forstep-mode=
or
getfbuffer:step-mode
:vertex
forattributes=
getfbuffer:attributes
unless
and
typepstride'
unsigned-byte32
pluspstride
memberstep-mode'
:vertex:instance
listpattributes
attributes
every
lambda
attribute
and
typep
getfattribute:shader-location
'
unsigned-byte32
typep
getfattribute:offset
'
unsigned-byte32
member
getfattribute:format
'
:float32x2:float32x3:float32x4
attributes
do
reject-gpu-requestdescriptor:invalid-vertex-bufferbuffer
collect
list:bindingbinding:array-stridestride:step-modestep-mode:attributes
mapcar
lambda
attribute
list:shader-location
getfattribute:shader-location
:offset
getfattribute:offset
:format
ecase
2:r32g32-sfloat
3:r32g32b32-sfloat
4:r32g32b32a32-sfloat
attributes
defmethodcreate
descriptorrender-pipeline-descriptor
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-render-pipeline
let*
layout
render-pipeline-descriptor-layoutdescriptor
vertex
render-pipeline-descriptor-vertexdescriptor
fragment
render-pipeline-descriptor-fragmentdescriptor
vertex-module
getfvertex:module
vertex-buffers
normalize-vulkan-vertex-buffersdescriptor
or
getfvertex:buffers
nil
fragment-module
getffragment:module
targets
getffragment:targets
formats
mapcar
lambda
target
getftarget:format
targets
blends
mapcar
lambda
target
getftarget:blend
targets
depth-stencil
render-pipeline-descriptor-depth-stencildescriptor
depth-format
anddepth-stencil
getfdepth-stencil:format
depth-compare
anddepth-stencil
getfdepth-stencil:depth-compare
depth-write-enabled
anddepth-stencil
getfdepth-stencil:depth-write-enabled
depth-store-op
anddepth-stencil
or
getfdepth-stencil:depth-store-op
:discard
topology
or
getf
render-pipeline-descriptor-primitivedescriptor
:topology
:triangle-list
unless
and
typepvertex-module'vulkan-gpu-shader-module
or
and
typepfragment-module'vulkan-gpu-shader-module
listptargets
targets
every#'identityformats
every
lambda
blend
memberblend'
nil:premultiplied-alpha
blends
and
nullfragment-module
nulltargets
depth-stencil
or
nulldepth-stencil
and
eqdepth-format:depth32-float
memberdepth-compare'
:never:less:equal:less-or-equal:greater:not-equal:greater-or-equal:always
membertopology'
:triangle-list:triangle-strip
reject-gpu-requestdescriptor:unsupported-render-pipeline
dolist
object
removenil
listlayoutvertex-modulefragment-module
ensure-vulkan-object-deviceobject
etypecaseobject
vulkan-gpu-bind-group-layout
vulkan-bind-group-layout-deviceobject
vulkan-gpu-shader-module
vulkan-shader-module-deviceobject
device:create-render-pipeline
with-live-vulkan-device-queue
device:create-render-pipeline
dolist
object
removenil
listlayoutvertex-modulefragment-module
ensure-vulkan-object-deviceobject
etypecaseobject
vulkan-gpu-bind-group-layout
vulkan-bind-group-layout-deviceobject
vulkan-gpu-shader-module
vulkan-shader-module-deviceobject
device:create-render-pipeline
let*
render-pass
vulkan-render-pass-for-formatdeviceformatsdescriptordepth-format
ordepth-store-op:discard
pipeline-layout
lvk:create-pipeline-layout
vulkan-handledevice
vector
vulkan-handlelayout
pipelinenil
completed-pnil
unwind-protect
let
wrappernil
setfpipeline
lvk:create-graphics-pipeline
vulkan-handledevice
vulkan-handlevertex-module
andfragment-module
vulkan-handlefragment-module
pipeline-layoutrender-pass:vertex-entry-point
or
getfvertex:entry-point
"main"
:fragment-entry-point
or
getffragment:entry-point
"main"
:topologytopology:vertex-buffersvertex-buffers:depth-comparedepth-compare:depth-write-enableddepth-write-enabled:blendsblends
wrapper
make-instance'vulkan-gpu-render-pipeline:label
gpu-descriptor-labeldescriptor
:handlepipeline:devicedevice:layoutlayout:pipeline-layoutpipeline-layout:render-passrender-pass:vertex-buffersvertex-buffers:target-formatsformats:depth-formatdepth-format
completed-pt
wrapper
unlesscompleted-p
unwind-protect
whenpipeline
lvk:destroy-pipeline
vulkan-handledevice
pipeline
lvk:destroy-pipeline-layout
vulkan-handledevice
pipeline-layout
defmethodcreate
descriptormesh-render-pipeline-descriptor

Link task, mesh, and fragment modules into a VK_EXT_mesh_shader pipeline.

The result is an ordinary vulkan-gpu-render-pipeline with no vertex buffers: what distinguishes it is that its draw is a workgroup dispatch, so binding and render-pass compatibility need no separate vocabulary.

with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-mesh-render-pipeline
let*
layout
mesh-render-pipeline-descriptor-layoutdescriptor
task
mesh-render-pipeline-descriptor-taskdescriptor
mesh
mesh-render-pipeline-descriptor-meshdescriptor
fragment
mesh-render-pipeline-descriptor-fragmentdescriptor
task-module
getftask:module
mesh-module
getfmesh:module
fragment-module
getffragment:module
targets
getffragment:targets
formats
mapcar
lambda
target
getftarget:format
targets
blends
mapcar
lambda
target
getftarget:blend
targets
depth-stencil
mesh-render-pipeline-descriptor-depth-stencildescriptor
depth-format
anddepth-stencil
getfdepth-stencil:format
depth-compare
anddepth-stencil
getfdepth-stencil:depth-compare
depth-write-enabled
anddepth-stencil
getfdepth-stencil:depth-write-enabled
depth-store-op
anddepth-stencil
or
getfdepth-stencil:depth-store-op
:discard
unless
lvk:physical-device-mesh-shader-p
vulkan-device-physical-devicedevice
error'gpu-request-error:operation:create-mesh-render-pipeline:descriptordescriptor:reason:vulkan-mesh-shader-runtime-unavailable
unless
and
typepmesh-module'vulkan-gpu-shader-module
or
nulltask-module
typeptask-module'vulkan-gpu-shader-module
or
and
typepfragment-module'vulkan-gpu-shader-module
listptargets
targets
every#'identityformats
every
lambda
blend
memberblend'
nil:premultiplied-alpha
blends
and
nullfragment-module
nulltargets
depth-stencil
or
nulldepth-stencil
and
eqdepth-format:depth32-float
memberdepth-compare'
:never:less:equal:less-or-equal:greater:not-equal:greater-or-equal:always
reject-gpu-requestdescriptor:unsupported-mesh-render-pipeline
dolist
object
removenil
listlayouttask-modulemesh-modulefragment-module
ensure-vulkan-object-deviceobject
etypecaseobject
vulkan-gpu-bind-group-layout
vulkan-bind-group-layout-deviceobject
vulkan-gpu-shader-module
vulkan-shader-module-deviceobject
device:create-mesh-render-pipeline
with-live-vulkan-device-queue
device:create-mesh-render-pipeline
dolist
object
removenil
listlayouttask-modulemesh-modulefragment-module
ensure-vulkan-object-deviceobject
etypecaseobject
vulkan-gpu-bind-group-layout
vulkan-bind-group-layout-deviceobject
vulkan-gpu-shader-module
vulkan-shader-module-deviceobject
device:create-mesh-render-pipeline
let*
render-pass
with-cpu-trace-zone
:vulkan/pipeline/render-pass
vulkan-render-pass-for-formatdeviceformatsdescriptordepth-format
ordepth-store-op:discard
pipeline-layout
with-cpu-trace-zone
:vulkan/pipeline/create-layout
lvk:create-pipeline-layout
vulkan-handledevice
vector
vulkan-handlelayout
pipelinenil
completed-pnil
unwind-protect
let
wrappernil
setfpipeline
with-cpu-trace-zone
:vulkan/pipeline/create-mesh
lvk:create-mesh-graphics-pipeline
vulkan-handledevice
vulkan-handlemesh-module
andfragment-module
vulkan-handlefragment-module
pipeline-layoutrender-pass:task-module
andtask-module
vulkan-handletask-module
:task-entry-point
or
getftask:entry-point
"main"
:mesh-entry-point
or
getfmesh:entry-point
"main"
:fragment-entry-point
or
getffragment:entry-point
"main"
:depth-comparedepth-compare:depth-write-enableddepth-write-enabled:blendsblends
wrapper
make-instance'vulkan-gpu-render-pipeline:label
gpu-descriptor-labeldescriptor
:handlepipeline:devicedevice:layoutlayout:pipeline-layoutpipeline-layout:render-passrender-pass:vertex-buffersnil:target-formatsformats:depth-formatdepth-format
completed-pt
wrapper
unlesscompleted-p
unwind-protect
whenpipeline
lvk:destroy-pipeline
vulkan-handledevice
pipeline
lvk:destroy-pipeline-layout
vulkan-handledevice
pipeline-layout
defunstorage-texture-bind-group-entry
descriptorlayout
let
entries
bind-group-descriptor-entriesdescriptor
unless
and
listpentries
=1
lengthentries
listp
firstentries
=
or
getf
firstentries
:binding
-1
vulkan-bind-group-layout-bindinglayout
typep
getf
firstentries
:resource
'vulkan-gpu-texture-view
reject-gpu-requestdescriptor:unsupported-bind-groupentries
firstentries
defunsampled-texture-sampler-bind-group-entries
descriptorlayout
let*
entries
bind-group-descriptor-entriesdescriptor
layout-entries
vulkan-bind-group-layout-entrieslayout
unless
=
lengthlayout-entries
lengthentries
reject-gpu-requestdescriptor:unsupported-bind-groupentries
loopforlayout-entryinlayout-entriesforentry=
find
getflayout-entry:binding
entries:key
lambda
entry
getfentry:binding
unless
andentry
ecase
getflayout-entry:type
:texture
typep
getfentry:resource
'vulkan-gpu-texture-view
:sampler
typep
getfentry:resource
'vulkan-gpu-sampler
:uniform-buffer:storage-buffer
typep
getfentry:resource
'vulkan-gpu-buffer
do
reject-gpu-requestdescriptor:unsupported-bind-groupentries
collect
listlayout-entryentry
defununiform-buffer-bind-group-entry
descriptorlayout
let*
entries
bind-group-descriptor-entriesdescriptor
layout-entry
first
vulkan-bind-group-layout-entrieslayout
entry
firstentries
unless
and
=1
lengthentries
=
or
getfentry:binding
-1
getflayout-entry:binding
typep
getfentry:resource
'vulkan-gpu-buffer
reject-gpu-requestdescriptor:unsupported-bind-groupentries
entry
defuncreate-vulkan-uniform-bind-group
devicedescriptorlayout
let*
buffer
getfentry:resource
binding
getf
first
vulkan-bind-group-layout-entrieslayout
:binding
ensure-vulkan-object-devicebuffer
vulkan-buffer-devicebuffer
device:create-bind-group
unless
member:uniform
gpu-buffer-usagebuffer
error'gpu-usage-error:objectbuffer:operation:create-bind-group:required-usage:uniform:actual-usage
gpu-buffer-usagebuffer
let
setnil
completed-pnil
unwind-protect
progn
setfset
lvk:allocate-descriptor-set
vulkan-handledevice
pool
vulkan-handlelayout
lvk:update-uniform-buffer-descriptor
vulkan-handledevice
set
vulkan-handlebuffer
gpu-buffer-sizebuffer
:bindingbinding
prog1
make-instance'vulkan-gpu-bind-group:label
gpu-descriptor-labeldescriptor
:handleset:devicedevice:layoutlayout:buffers
listbuffer
:descriptor-poolpool
setfcompleted-pt
unlesscompleted-p
lvk:destroy-descriptor-pool
vulkan-handledevice
pool
defmethodcreate
descriptorbind-group-descriptor
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-bind-group
let
layout
bind-group-descriptor-layoutdescriptor
unless
reject-gpu-requestdescriptor:incompatible-bind-group-layoutlayout
ensure-vulkan-object-devicelayout
vulkan-bind-group-layout-devicelayout
device:create-bind-group
if
vulkan-bind-group-layout-bindinglayout
let*
view
getfentry:resource
texture
gpu-texture-view-textureview
ensure-vulkan-object-deviceview
vulkan-texture-view-deviceview
device:create-bind-group
unless
member:storage-binding
gpu-texture-usagetexture
error'gpu-usage-error:objecttexture:operation:create-bind-group:required-usage:storage-binding:actual-usage
gpu-texture-usagetexture
let
setnil
completed-pnil
unwind-protect
progn
setfset
lvk:allocate-descriptor-set
vulkan-handledevice
pool
vulkan-handlelayout
lvk:update-storage-image-descriptor
vulkan-handledevice
set
vulkan-handleview
:binding
vulkan-bind-group-layout-bindinglayout
prog1
make-instance'vulkan-gpu-bind-group:label
gpu-descriptor-labeldescriptor
:handleset:devicedevice:layoutlayout:texture-views
listview
:descriptor-poolpool
setfcompleted-pt
unlesscompleted-p
lvk:destroy-descriptor-pool
vulkan-handledevice
pool
if
and
=1
length
vulkan-bind-group-layout-entrieslayout
eq:uniform-buffer
getf
first
vulkan-bind-group-layout-entrieslayout
:type
let*
viewsnil
samplersnil
buffersnil
descriptor-entriesnil
dolist
pairpairs
destructuring-bind
layout-entryentry
pair
let
resource
getfentry:resource
ecase
getflayout-entry:type
:texture
ensure-vulkan-object-deviceresource
vulkan-texture-view-deviceresource
device:create-bind-group
let
texture
gpu-texture-view-textureresource
unless
member:texture-binding
gpu-texture-usagetexture
error'gpu-usage-error:objecttexture:operation:create-bind-group:required-usage:texture-binding:actual-usage
gpu-texture-usagetexture
pushresourceviews
push`
:binding,
getflayout-entry:binding
:type:texture:image-view,
vulkan-handleresource
descriptor-entries
:sampler
ensure-vulkan-object-deviceresource
vulkan-sampler-deviceresource
device:create-bind-group
pushresourcesamplers
push`
:binding,
getflayout-entry:binding
:type:sampler:sampler,
vulkan-handleresource
descriptor-entries
:uniform-buffer:storage-buffer
ensure-vulkan-object-deviceresource
vulkan-buffer-deviceresource
device:create-bind-group
let
required
if
eq:storage-buffer
getflayout-entry:type
:storage:uniform
unless
memberrequired
gpu-buffer-usageresource
error'gpu-usage-error:objectresource:operation:create-bind-group:required-usagerequired:actual-usage
gpu-buffer-usageresource
pushresourcebuffers
push`
:binding,
getflayout-entry:binding
:type,
getflayout-entry:type
:buffer,
vulkan-handleresource
:buffer-size,
gpu-buffer-sizeresource
descriptor-entries
setfviews
nreverseviews
samplers
nreversesamplers
buffers
nreversebuffers
descriptor-entries
nreversedescriptor-entries
let
pool
lvk:create-texture-sampler-uniform-descriptor-pool
vulkan-handledevice
descriptor-entries
setnil
completed-pnil
unwind-protect
progn
setfset
lvk:allocate-descriptor-set
vulkan-handledevice
pool
vulkan-handlelayout
lvk:update-texture-sampler-uniform-descriptors
vulkan-handledevice
setdescriptor-entries
prog1
make-instance'vulkan-gpu-bind-group:label
gpu-descriptor-labeldescriptor
:handleset:devicedevice:layoutlayout:texture-viewsviews:samplerssamplers:buffersbuffers:descriptor-poolpool
setfcompleted-pt
unlesscompleted-p
lvk:destroy-descriptor-pool
vulkan-handledevice
pool
defmethodcreate
descriptorcommand-encoder-descriptor

Allocate and begin one Vulkan primary command buffer.

with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectdevice:create-command-encoder
with-live-vulkan-device-queue
device:create-command-encoder
let
command-poolnil
completed-pnil
unwind-protect
progn
setfcommand-pool
lvk:create-command-pool
vulkan-handledevice
vulkan-device-queue-familydevice
:flags'
:transient
let*
command-buffer
lvk:allocate-command-buffer
vulkan-handledevice
command-pool
encodernil
setfencoder
make-instance'vulkan-gpu-command-encoder:label
gpu-descriptor-labeldescriptor
:devicedevice:command-poolcommand-pool:command-buffercommand-buffer
encodercompleted-pt
encoder
unlesscompleted-p
whencommand-pool
lvk:destroy-command-pool
vulkan-handledevice
command-pool
defuninstall-vulkan-encoder-leak-finalizer
encoder

Arrange to warn about and reclaim encoder if it is collected while it still owns its command pool. finish and destroy transfer or release that ownership and cancel this finalizer.

let
device
vulkan-command-encoder-deviceencoder
command-pool
vulkan-command-encoder-command-poolencoder
box
vulkan-command-encoder-native-resource-boxencoder
label
gpu-object-labelencoder
encoder
defunensure-vulkan-command-encoder-state
encoderoperation
unless
eq:recording
vulkan-command-encoder-stateencoder
error'gpu-invalid-state-error:objectencoder:operationoperation:state
vulkan-command-encoder-stateencoder
:expected-state:recording
defunensure-no-active-vulkan-pass
encoderoperation
when
vulkan-command-encoder-active-passencoder
error'gpu-invalid-state-error:objectencoder:operationoperation:state:compute-pass:expected-state:between-passes
defunretain-vulkan-resource
encoderresource

Record resource as a dependency of encoder's future submission.

whenresource
setf
gethashresource
vulkan-command-encoder-resourcesencoder
t
resource
defunensure-vulkan-texture-for-command
encodertexturecommandrequired-usage
unless
typeptexture'vulkan-gpu-texture
reject-gpu-requestcommand:incompatible-texture-backendtexture
unless
eq
vulkan-command-encoder-deviceencoder
vulkan-texture-devicetexture
error'gpu-device-mismatch-error:objecttexture:operation:encode:expected-device
vulkan-command-encoder-deviceencoder
:actual-device
vulkan-texture-devicetexture
unless
memberrequired-usage
gpu-texture-usagetexture
error'gpu-usage-error:objecttexture:operation:encode:required-usagerequired-usage:actual-usage
gpu-texture-usagetexture
setf
gethashtexture
vulkan-command-encoder-texturesencoder
t
texture
defunvulkan-layout-access-and-stage
ecaselayout
:undefined
valuesnil
list:top-of-pipe
:general
values
list:shader-read:shader-write
list:compute-shader
:shader-read-only-optimal
values
list:shader-read
list:vertex-shader:fragment-shader
:color-attachment-optimal
values
list:color-attachment-read:color-attachment-write
list:color-attachment-output
:depth-stencil-attachment-optimal
values
list:depth-stencil-attachment-read:depth-stencil-attachment-write
list:early-fragment-tests:late-fragment-tests
:transfer-src-optimal
values
list:transfer-read
list:transfer
:transfer-dst-optimal
values
list:transfer-write
list:transfer
:present-src-khr
valuesnil
list:bottom-of-pipe
defunvulkan-encoder-texture-layout
encodertexture
multiple-value-bind
layoutpresent-p
gethashtexture
vulkan-command-encoder-texture-layoutsencoder
ifpresent-playout
let
layout
vulkan-texture-layouttexture
setf
gethashtexture
vulkan-command-encoder-initial-texture-layoutsencoder
layout
gethashtexture
vulkan-command-encoder-texture-layoutsencoder
layout
layout
defuntransition-vulkan-texture
encodertexturenew-layout
let
old-layout
unless
eqold-layoutnew-layout
multiple-value-bind
src-accesssrc-stage
multiple-value-bind
dst-accessdst-stage
lvk:cmd-transition-image
vulkan-command-encoder-command-bufferencoder
vulkan-handletexture
old-layoutnew-layoutsrc-accessdst-accesssrc-stagedst-stage:aspect
setf
gethashtexture
vulkan-command-encoder-texture-layoutsencoder
new-layout
texture
defunnormalize-vulkan-clear-color
command
let*
color
gpu-clear-texture-command-colorcommand
components
typecasecolor
listcolor
vector
coercecolor'list
otherwisenil
unless
and
=4
lengthcomponents
every#'realpcomponents
reject-gpu-requestcommand:invalid-clear-colorcolor
map'vector
lambda
component
coercecomponent'single-float
components
defmethodencode
commandgpu-clear-texture-command
with-vulkan-gpu-driver-environment
let
texture
ensure-vulkan-texture-for-commandencoder
gpu-clear-texture-command-texturecommand
command:copy-dst
transition-vulkan-textureencodertexture:transfer-dst-optimal
lvk:cmd-clear-color-image
vulkan-command-encoder-command-bufferencoder
vulkan-handletexture
:transfer-dst-optimalcolor
encoder
defmethodencode
commandgpu-prepare-texture-command
with-vulkan-gpu-driver-environment
let*
usage
gpu-prepare-texture-command-usagecommand
texture
ensure-vulkan-texture-for-commandencoder
gpu-prepare-texture-command-texturecommand
commandusage
ecaseusage
:texture-binding
transition-vulkan-textureencodertexture:shader-read-only-optimal
encoder
defunensure-compatible-vulkan-copy
commandsourcedestination
when
eqsourcedestination
reject-gpu-requestcommand:same-copy-source-and-destinationsource
unless
and
equal
gpu-texture-sizesource
gpu-texture-sizedestination

Vulkan permits image copies between size-compatible color formats. Every format in this initial vocabulary is one four-byte color texel, including rgba storage -> BGRA swapchain copies on Cocoa.

member
gpu-texture-formatsource
'
:rgba8-unorm:rgba8-unorm-srgb:bgra8-unorm:bgra8-unorm-srgb
member
gpu-texture-formatdestination
'
:rgba8-unorm:rgba8-unorm-srgb:bgra8-unorm:bgra8-unorm-srgb
reject-gpu-requestcommand:incompatible-copy
list:source-size
gpu-texture-sizesource
:destination-size
gpu-texture-sizedestination
:source-format
gpu-texture-formatsource
:destination-format
gpu-texture-formatdestination
defmethodencode
commandgpu-copy-texture-command
with-vulkan-gpu-driver-environment
let
source
ensure-vulkan-texture-for-commandencoder
gpu-copy-texture-command-sourcecommand
command:copy-src
destination
ensure-vulkan-texture-for-commandencoder
gpu-copy-texture-command-destinationcommand
command:copy-dst
ensure-compatible-vulkan-copycommandsourcedestination
transition-vulkan-textureencodersource:transfer-src-optimal
transition-vulkan-textureencoderdestination:transfer-dst-optimal
lvk:cmd-copy-image
vulkan-command-encoder-command-bufferencoder
vulkan-handlesource
:transfer-src-optimal
vulkan-handledestination
:transfer-dst-optimal
first
gpu-texture-sizesource
second
gpu-texture-sizesource
encoder
defmethodencode
commandgpu-copy-texture-to-buffer-command
with-vulkan-gpu-driver-environment
let*
source
ensure-vulkan-texture-for-commandencoder
gpu-copy-texture-to-buffer-command-sourcecommand
command:copy-src
destination
gpu-copy-texture-to-buffer-command-destinationcommand
device
vulkan-command-encoder-deviceencoder
size
gpu-texture-sizesource
required-size
*4
firstsize
secondsize
unless
and
typepdestination'vulkan-gpu-buffer
member:copy-dst
gpu-buffer-usagedestination
<=required-size
gpu-buffer-sizedestination
member
gpu-texture-formatsource
'
:rgba8-unorm:rgba8-unorm-srgb:bgra8-unorm:bgra8-unorm-srgb
reject-gpu-requestcommand:unsupported-texture-to-buffer-copy
list:sourcesource:destinationdestination
ensure-vulkan-object-devicedestination
vulkan-buffer-devicedestination
device:copy-texture-to-buffer
transition-vulkan-textureencodersource:transfer-src-optimal
lvk:cmd-copy-image-to-buffer
vulkan-command-encoder-command-bufferencoder
vulkan-handlesource
:transfer-src-optimal
vulkan-handledestination
firstsize
secondsize
retain-vulkan-resourceencoderdestination
encoder
defunreject-texture-write
destinationreason&optionaldetails
error'gpu-request-error:operation:write-texture:descriptordestination:reasonreason:detailsdetails
defuntexture-write-components
valueexpected-lengthdestinationreason
let
components
typecasevalue
listvalue
vector
coercevalue'list
otherwisenil
unless
and
member
lengthcomponents
expected-length
every
lambda
component
typepcomponent'
unsigned-byte32
components
reject-texture-writedestinationreasonvalue
components
defuncheck-vulkan-texture-write
devicecommand
let
destination
gpu-write-texture-command-destinationcommand
data
gpu-write-texture-command-datacommand
data-layout
gpu-write-texture-command-data-layoutcommand
size
gpu-write-texture-command-sizecommand
unless
typepdestination'texture-copy
reject-texture-writedestination:invalid-texture-copydestination
unless
typepdata-layout'texture-data-layout
reject-texture-writedestination:invalid-data-layoutdata-layout
let*
texture
texture-copy-texturedestination
origin
texture-write-components
texture-copy-origindestination
'
23
destination:invalid-origin
extent
texture-write-componentssize'
23
destination:invalid-write-size
when
=2
lengthorigin
setforigin
appendorigin'
0
when
=2
lengthextent
setfextent
appendextent'
1
unless
and
typeptexture'vulkan-gpu-texture
=0
texture-copy-mip-leveldestination
eq:all
texture-copy-aspectdestination
=0
thirdorigin
=1
thirdextent
reject-texture-writedestination:unsupported-texture-copydestination
ensure-live-vulkan-objectdevice:write-texture
ensure-vulkan-object-devicetexture
vulkan-texture-devicetexture
device:write-texture
unless
member:copy-dst
gpu-texture-usagetexture
error'gpu-usage-error:objecttexture:operation:write-texture:required-usage:copy-dst:actual-usage
gpu-texture-usagetexture
unless
member
gpu-texture-formattexture
'
:r16-float:rgba8-unorm:rgba8-unorm-srgb:bgra8-unorm:bgra8-unorm-srgb:rg16-uint:rg16-float:rgba16-float
reject-texture-writedestination:unsupported-texture-format
gpu-texture-formattexture
unless
and
plusp
firstextent
plusp
secondextent
<=
+
firstorigin
firstextent
first
gpu-texture-sizetexture
<=
+
secondorigin
secondextent
second
gpu-texture-sizetexture
reject-texture-writedestination:write-out-of-bounds
list:originorigin:sizeextent
let*
bytes-per-texel
texture-format-bytes-per-texel
gpu-texture-formattexture
element-type
texture-format-upload-element-type
gpu-texture-formattexture
unless
and
arraypdata
=2
array-rankdata
nth-value0
subtypep
array-element-typedata
element-type
>=
array-dimensiondata0
secondextent
>=
array-dimensiondata1
firstextent
reject-texture-writedestination:unsupported-texture-datadata
let*
width
firstextent
offset
texture-data-layout-offsetdata-layout
bytes-per-row
or
texture-data-layout-bytes-per-rowdata-layout
*widthbytes-per-texel
rows-per-image
or
texture-data-layout-rows-per-imagedata-layout
height
unless
and
typepoffset'
unsigned-byte64
zerop
modoffsetbytes-per-texel
typepbytes-per-row'
unsigned-byte32
>=bytes-per-row
*widthbytes-per-texel
zerop
modbytes-per-rowbytes-per-texel
typeprows-per-image'
unsigned-byte32
>=rows-per-imageheight
reject-texture-writedestination:invalid-data-layoutdata-layout
valuestextureoriginextentoffsetbytes-per-rowrows-per-imagebytes-per-texeldatadestination
defuncopy-texture-words-to-mapped-memory
datapointerwidthheightoffsetbytes-per-rowbytes-per-texel
let
foreign-type
ecasebytes-per-texel
2:uint16
4:uint32
8:uint64
dotimes
let
destination
cffi:inc-pointerpointer
+offset
*rowbytes-per-row
dotimes
columnwidth
setf
cffi:mem-arefdestinationforeign-typecolumn
row-major-arefdata
+
*row
array-dimensiondata1
column
defunrecord-vulkan-texture-write
encodercommand

Lower one queue texture write through a private Vulkan command encoder.

with-vulkan-gpu-driver-environment
multiple-value-bind
textureoriginextentoffsetbytes-per-rowrows-per-imagebytes-per-texeldatadestination
check-vulkan-texture-write
vulkan-command-encoder-deviceencoder
command
declare
ignorerows-per-imagedestination
let*
device
vulkan-command-encoder-deviceencoder
native-device
vulkan-handledevice
width
firstextent
data-size
+offset
*bytes-per-row
*widthbytes-per-texel
buffernil
memorynil
mappednil
retained-pnil
unwind-protect
progn
setfbuffer
lvk:create-buffernative-devicedata-size'
:transfer-src
let*
setfmemory
lvk:allocate-memorynative-device
lvk:buffer-memory-requirements-sizerequirements
memory-type
lvk:bind-buffer-memorynative-devicebuffermemory
setfmapped
lvk:map-memorynative-devicememorydata-size
copy-texture-words-to-mapped-memorydatamappedwidthheightoffsetbytes-per-rowbytes-per-texel
lvk:unmap-memorynative-devicememory
setfmappednil
ensure-vulkan-texture-for-commandencodertexturecommand:copy-dst
transition-vulkan-textureencodertexture:transfer-dst-optimal
lvk:cmd-copy-buffer-to-image
vulkan-command-encoder-command-bufferencoder
buffer
vulkan-handletexture
:transfer-dst-optimalwidthheight:buffer-offsetoffset:buffer-row-length
/bytes-per-rowbytes-per-texel
:buffer-image-heightheight:x
firstorigin
:y
secondorigin

Leave the texture where a reader can use it. An upload that stops at :TRANSFER-DST-OPTIMAL makes whoever samples the texture next responsible for the transition, and the place that notices is set-bind-group -- inside a render pass, where a layout transition is not allowed at all. The upload is a whole operation, so it ends in the layout an uploaded texture is for; every other consumer can transition out of this one legally, outside a pass.

transition-vulkan-textureencodertexture:shader-read-only-optimal
push
list:upload-bufferbuffermemory
setfretained-ptbuffernilmemorynil
whenmapped
lvk:unmap-memorynative-devicememory
unlessretained-p
whenbuffer
lvk:destroy-buffernative-devicebuffer
whenmemory
lvk:free-memorynative-devicememory
encoder
defmethodenqueue
commandgpu-write-texture-command

Issue one WebGPU queue write using Vulkan's portable staging path.

The host data is copied into coherent staging memory before returning. The staging buffer is retained by the private command buffer until submission has completed. Vulkan 1.4 host image copies may provide a more direct optional lowering later without changing this queue-level operation.

with-vulkan-gpu-driver-environment
let
encodernil
commandsnil
unwind-protect
progn
setfencoder
create
vulkan-queue-devicequeue
make-command-encoder-descriptor
setfcommands
finishencoder
submitqueuecommands
queue
whencommands
destroycommands
whenencoder
destroyencoder
defunnormalize-render-pass-color
descriptorcolor
let
components
typecasecolor
listcolor
vector
coercecolor'list
otherwisenil
unless
and
=4
lengthcomponents
every#'realpcomponents
reject-gpu-requestdescriptor:invalid-clear-colorcolor
map'vector
lambda
value
coercevalue'single-float
components
defunnormalize-render-pass-depth
descriptordepth
unless
and
realpdepth
<=0depth1
reject-gpu-requestdescriptor:invalid-clear-depthdepth
coercedepth'single-float
defmethodbegin-render-pass
descriptorrender-pass-descriptor
with-vulkan-gpu-driver-environment
ensure-vulkan-command-encoder-stateencoder:begin-render-pass
ensure-no-active-vulkan-passencoder:begin-render-pass
let*
attachments
render-pass-descriptor-color-attachmentsdescriptor
views
mapcar
lambda
attachment
getfattachment:view
attachments
targets
mapcar
lambda
view
and
gpu-texture-view-textureview
views
depth-attachment
render-pass-descriptor-depth-stencil-attachmentdescriptor
depth-view
anddepth-attachment
getfdepth-attachment:view
depth-target
and
typepdepth-view'vulkan-gpu-texture-view
gpu-texture-view-texturedepth-view
clear-colors
mapcar
lambda
attachment
normalize-render-pass-colordescriptor
or
getfattachment:clear-value
0001
attachments
clear-depth
anddepth-attachment
normalize-render-pass-depthdescriptor
or
getfdepth-attachment:depth-clear-value
1.0
depth-store-op
anddepth-attachment
getfdepth-attachment:depth-store-op
unless
and
listpattachments
every
lambda
attachmenttarget
andtarget
eq:clear
getfattachment:load-op
eq:store
getfattachment:store-op
attachmentstargets
or
nulldepth-attachment
anddepth-target
eq:depth32-float
gpu-texture-formatdepth-target
eq:clear
getfdepth-attachment:depth-load-op
memberdepth-store-op'
:discard:store
ortargetsdepth-target
reject-gpu-requestdescriptor:unsupported-render-pass
let*
device
vulkan-command-encoder-deviceencoder
size
gpu-texture-size
or
firsttargets
depth-target
with-live-vulkan-device-queue
device:begin-render-pass
let*
render-pass
vulkan-render-pass-for-formatdevice
mapcar#'gpu-texture-formattargets
descriptor
anddepth-target
gpu-texture-formatdepth-target
ordepth-store-op:discard
framebuffernil
completed-pnil
loopforviewinviewsfortargetintargetsdo
unless
equalsize
gpu-texture-sizetarget
reject-gpu-requestdescriptor:mismatched-color-size
gpu-texture-sizetarget
ensure-vulkan-object-deviceview
vulkan-texture-view-deviceview
device:begin-render-pass
ensure-vulkan-texture-for-commandencodertargetdescriptor:render-attachment
transition-vulkan-textureencodertarget:color-attachment-optimal
whendepth-target
unless
or
nulltargets
equalsize
gpu-texture-sizedepth-target
reject-gpu-requestdescriptor:mismatched-depth-size
gpu-texture-sizedepth-target
ensure-vulkan-object-devicedepth-view
vulkan-texture-view-devicedepth-view
device:begin-render-pass
whendepth-target
retain-vulkan-resourceencoderdepth-view
ensure-vulkan-texture-for-commandencoderdepth-targetdescriptor:render-attachment
transition-vulkan-textureencoderdepth-target:depth-stencil-attachment-optimal
unwind-protect
progn
setfframebuffer
lvk:create-framebuffer
vulkan-handledevice
render-pass
mapcar#'vulkan-handleviews
firstsize
secondsize
:depth-view
anddepth-view
vulkan-handledepth-view
iftargets
lvk:cmd-begin-color-render-pass
vulkan-command-encoder-command-bufferencoder
render-passframebuffer
firstsize
secondsize
clear-colors:depth-clear-valueclear-depth
lvk:cmd-begin-depth-render-pass
vulkan-command-encoder-command-bufferencoder
render-passframebuffer
firstsize
secondsize
clear-depth
lvk:cmd-set-viewport-and-scissor
vulkan-command-encoder-command-bufferencoder
firstsize
secondsize
push
list:framebufferframebuffer
let
pass
make-instance'vulkan-gpu-render-pass-encoder:encoderencoder:framebufferframebuffer:targetstargets:depth-targetdepth-target:depth-store-opdepth-store-op
setf
vulkan-command-encoder-active-passencoder
pass
completed-pt
pass
unlesscompleted-p
whenframebuffer
lvk:destroy-framebuffer
vulkan-handledevice
framebuffer
defunensure-vulkan-render-pass-state
passoperation
unless
eq:recording
vulkan-render-pass-statepass
error'gpu-invalid-state-error:objectpass:operationoperation:state
vulkan-render-pass-statepass
:expected-state:recording
let
encoder
vulkan-render-pass-command-encoderpass
unless
eqpass
vulkan-command-encoder-active-passencoder
error'gpu-invalid-state-error:objectpass:operationoperation:state:detached:expected-state:active
pass
defmethodencode
commandgpu-set-pipeline-command
with-vulkan-gpu-driver-environment
let*
pipeline
gpu-set-pipeline-command-pipelinecommand
encoder
vulkan-render-pass-command-encoderpass
device
vulkan-command-encoder-deviceencoder
unless
reject-gpu-requestcommand:incompatible-render-pipelinepipeline
ensure-vulkan-object-devicepipeline
vulkan-render-pipeline-devicepipeline
device:set-pipeline

Vulkan render-pass compatibility is governed by attachment formats and sample counts, not load/store operations. Our textures are all single-sampled, so compare the two formats directly instead of the cached native render-pass handle (whose key also carries STORE-OP).

unless
and
equal
vulkan-render-pipeline-target-formatspipeline
mapcar#'gpu-texture-format
vulkan-render-pass-targetspass
eq
vulkan-render-pipeline-depth-formatpipeline
and
vulkan-render-pass-depth-targetpass
gpu-texture-format
vulkan-render-pass-depth-targetpass
reject-gpu-requestpipeline:incompatible-render-pass
list:pipeline-label
gpu-object-labelpipeline
:pipeline-target-formats
vulkan-render-pipeline-target-formatspipeline
:pipeline-depth-format
vulkan-render-pipeline-depth-formatpipeline
:target-formats
mapcar#'gpu-texture-format
vulkan-render-pass-targetspass
:depth-format
and
vulkan-render-pass-depth-targetpass
gpu-texture-format
vulkan-render-pass-depth-targetpass
:depth-store-op
vulkan-render-pass-depth-store-oppass
lvk:cmd-bind-graphics-pipeline
vulkan-command-encoder-command-bufferencoder
vulkan-handlepipeline
retain-vulkan-resourceencoderpipeline
setf
vulkan-render-pass-pipelinepass
pipeline
pass
defmethodencode
commandgpu-set-bind-group-command
with-vulkan-gpu-driver-environment
let
index
gpu-set-bind-group-command-indexcommand
bind-group
gpu-set-bind-group-command-bind-groupcommand
unless
zeropindex
reject-gpu-requestcommand:unsupported-bind-group-indexindex
unless
typepbind-group'vulkan-gpu-bind-group
reject-gpu-requestcommand:incompatible-bind-groupbind-group
let*
encoder
vulkan-render-pass-command-encoderpass
device
vulkan-command-encoder-deviceencoder
pipeline
or
vulkan-render-pass-pipelinepass
error'gpu-invalid-state-error:objectpass:operation:set-bind-group:state:no-pipeline:expected-state:pipeline-bound
ensure-vulkan-object-devicebind-group
vulkan-bind-group-devicebind-group
device:set-bind-group
unless
eq
vulkan-bind-group-layoutbind-group
vulkan-render-pipeline-bind-group-layoutpipeline
reject-gpu-requestbind-group:incompatible-pipeline-layoutpipeline
dolist
buffer
vulkan-bind-group-buffersbind-group
ensure-vulkan-object-devicebuffer
vulkan-buffer-devicebuffer
device:set-bind-group
dolist
texture-view
vulkan-bind-group-texture-viewsbind-group
let
texture
gpu-texture-view-texturetexture-view
ensure-vulkan-texture-for-commandencodertexturepass:texture-binding
transition-vulkan-textureencodertexture:shader-read-only-optimal
lvk:cmd-bind-graphics-descriptor-set
vulkan-command-encoder-command-bufferencoder
vulkan-render-pipeline-layoutpipeline
vulkan-handlebind-group
retain-vulkan-resourceencoderbind-group
dolist
texture-view
vulkan-bind-group-texture-viewsbind-group
retain-vulkan-resourceencodertexture-view
dolist
sampler
vulkan-bind-group-samplersbind-group
dolist
buffer
vulkan-bind-group-buffersbind-group
setf
vulkan-render-pass-bind-grouppass
bind-group
pass
defmethodencode
commandgpu-set-vertex-buffer-command
with-vulkan-gpu-driver-environment
ensure-vulkan-render-pass-statepass:set-vertex-buffer
let*
slot
gpu-set-vertex-buffer-command-slotcommand
buffer
gpu-set-vertex-buffer-command-buffercommand
offset
gpu-set-vertex-buffer-command-offsetcommand
encoder
vulkan-render-pass-command-encoderpass
device
vulkan-command-encoder-deviceencoder
unless
and
typepslot'
unsigned-byte32
typepbuffer'vulkan-gpu-buffer
typepoffset'
unsigned-byte64
zerop
modoffset4
<offset
gpu-buffer-sizebuffer
reject-gpu-requestcommand:invalid-vertex-buffer-binding
listslotbufferoffset
ensure-vulkan-object-devicebuffer
vulkan-buffer-devicebuffer
device:set-vertex-buffer
unless
member:vertex
gpu-buffer-usagebuffer
error'gpu-usage-error:objectbuffer:operation:set-vertex-buffer:required-usage:vertex:actual-usage
gpu-buffer-usagebuffer
lvk:cmd-bind-vertex-buffer
vulkan-command-encoder-command-bufferencoder
slot
vulkan-handlebuffer
offset
setf
gethashslot
vulkan-render-pass-vertex-bufferspass
buffer
pass
defmethodencode
commandgpu-set-scissor-command
with-vulkan-gpu-driver-environment
let
x
gpu-set-scissor-command-xcommand
y
gpu-set-scissor-command-ycommand
width
gpu-set-scissor-command-widthcommand
height
gpu-set-scissor-command-heightcommand
unless
and
typepx'
unsigned-byte31
typepy'
unsigned-byte31
typepwidth'
unsigned-byte32
typepheight'
unsigned-byte32
reject-gpu-requestcommand:invalid-scissor-rectangle
lvk:cmd-set-scissor
vulkan-command-encoder-command-buffer
vulkan-render-pass-command-encoderpass
xywidthheight
pass
defmethodencode
commandgpu-draw-command
with-vulkan-gpu-driver-environment
unless
and
vulkan-render-pass-pipelinepass
vulkan-render-pass-bind-grouppass
every
lambda
description
gethash
getfdescription:binding
vulkan-render-pass-vertex-bufferspass
vulkan-render-pipeline-vertex-buffers
vulkan-render-pass-pipelinepass
error'gpu-invalid-state-error:objectpass:operation:draw:state:incomplete-bindings:expected-state:pipeline-bind-group-and-vertex-buffers-bound
let
vertex-count
gpu-draw-command-vertex-countcommand
instance-count
gpu-draw-command-instance-countcommand
first-vertex
gpu-draw-command-first-vertexcommand
first-instance
gpu-draw-command-first-instancecommand
unless
every
lambda
value
typepvalue'
unsigned-byte32
listvertex-countinstance-countfirst-vertexfirst-instance
reject-gpu-requestcommand:invalid-draw-arguments
listvertex-countinstance-countfirst-vertexfirst-instance
lvk:cmd-draw
vulkan-command-encoder-command-buffer
vulkan-render-pass-command-encoderpass
vertex-countinstance-countfirst-vertexfirst-instance
pass
defunvulkan-index-format-size
format
ecaseformat
:uint162
:uint324
defmethodencode
commandgpu-draw-indexed-command
with-vulkan-gpu-driver-environment
unless
and
vulkan-render-pass-pipelinepass
vulkan-render-pass-bind-grouppass
every
lambda
description
gethash
getfdescription:binding
vulkan-render-pass-vertex-bufferspass
vulkan-render-pipeline-vertex-buffers
vulkan-render-pass-pipelinepass
error'gpu-invalid-state-error:objectpass:operation:draw-indexed:state:incomplete-bindings:expected-state:pipeline-bind-group-and-vertex-buffers-bound
let*
encoder
vulkan-render-pass-command-encoderpass
device
vulkan-command-encoder-deviceencoder
index-buffer
gpu-draw-indexed-command-index-buffercommand
index-format
gpu-draw-indexed-command-index-formatcommand
index-count
gpu-draw-indexed-command-index-countcommand
instance-count
gpu-draw-indexed-command-instance-countcommand
first-index
gpu-draw-indexed-command-first-indexcommand
base-vertex
gpu-draw-indexed-command-base-vertexcommand
first-instance
gpu-draw-indexed-command-first-instancecommand
unless
and
typepindex-buffer'vulkan-gpu-buffer
memberindex-format'
:uint16:uint32
typepindex-count'
integer1#.
-
ash132
1
typepinstance-count'
integer1#.
-
ash132
1
typepfirst-index'
unsigned-byte32
typepbase-vertex'
signed-byte32
typepfirst-instance'
unsigned-byte32
reject-gpu-requestcommand:invalid-indexed-draw-arguments
ensure-vulkan-object-deviceindex-buffer
vulkan-buffer-deviceindex-buffer
device:draw-indexed
unless
member:index
gpu-buffer-usageindex-buffer
error'gpu-usage-error:objectindex-buffer:operation:draw-indexed:required-usage:index:actual-usage
gpu-buffer-usageindex-buffer
let*
index-size
offset
*first-indexindex-size
unless
<=
+offset
*index-countindex-size
gpu-buffer-sizeindex-buffer
reject-gpu-requestcommand:index-buffer-range-exceeded
lvk:cmd-bind-index-buffer
vulkan-command-encoder-command-bufferencoder
vulkan-handleindex-buffer
offsetindex-format
retain-vulkan-resourceencoderindex-buffer
lvk:cmd-draw-indexed
vulkan-command-encoder-command-bufferencoder
index-countinstance-count0base-vertexfirst-instance
pass
defmethodencode
commandgpu-draw-mesh-command

Dispatch task or mesh workgroups instead of drawing vertices.

with-vulkan-gpu-driver-environment
ensure-vulkan-render-pass-statepass:draw-mesh-workgroups
unless
and
vulkan-render-pass-pipelinepass
vulkan-render-pass-bind-grouppass
error'gpu-invalid-state-error:objectpass:operation:draw-mesh-workgroups:state:incomplete-bindings:expected-state:pipeline-and-bind-group-bound
let
x
gpu-draw-mesh-command-xcommand
y
gpu-draw-mesh-command-ycommand
z
gpu-draw-mesh-command-zcommand
unless
every
lambda
value
typepvalue'
unsigned-byte32
listxyz
reject-gpu-requestcommand:invalid-draw-arguments
listxyz
let
encoder
vulkan-render-pass-command-encoderpass
lvk:cmd-draw-mesh-tasks
vulkan-handle
vulkan-command-encoder-deviceencoder
vulkan-command-encoder-command-bufferencoder
xyz
pass
defmethodend-pass
with-vulkan-gpu-driver-environment
let
encoder
vulkan-render-pass-command-encoderpass
lvk:cmd-end-render-pass
vulkan-command-encoder-command-bufferencoder
setf
vulkan-command-encoder-active-passencoder
nil
vulkan-render-pass-statepass
:ended
values
defmethodbegin-compute-pass
&optionaldescriptor
with-vulkan-gpu-driver-environment
ensure-vulkan-command-encoder-stateencoder:begin-compute-pass
ensure-no-active-vulkan-passencoder:begin-compute-pass
whendescriptor
reject-gpu-requestdescriptor:unsupported-compute-pass-descriptor
let
pass
make-instance'vulkan-gpu-compute-pass-encoder:encoderencoder
setf
vulkan-command-encoder-active-passencoder
pass
pass
defunensure-vulkan-compute-pass-state
passoperation
unless
eq:recording
vulkan-compute-pass-statepass
error'gpu-invalid-state-error:objectpass:operationoperation:state
vulkan-compute-pass-statepass
:expected-state:recording
let
encoder
vulkan-compute-pass-command-encoderpass
unless
eqpass
vulkan-command-encoder-active-passencoder
error'gpu-invalid-state-error:objectpass:operationoperation:state:detached:expected-state:active
pass
defmethodencode
commandgpu-set-pipeline-command
with-vulkan-gpu-driver-environment
let*
pipeline
gpu-set-pipeline-command-pipelinecommand
encoder
vulkan-compute-pass-command-encoderpass
device
vulkan-command-encoder-deviceencoder
unless
reject-gpu-requestcommand:incompatible-compute-pipelinepipeline
ensure-vulkan-object-devicepipeline
vulkan-compute-pipeline-devicepipeline
device:set-pipeline
lvk:cmd-bind-compute-pipeline
vulkan-command-encoder-command-bufferencoder
vulkan-handlepipeline
retain-vulkan-resourceencoderpipeline
setf
vulkan-compute-pass-pipelinepass
pipeline
pass
defmethodencode
commandgpu-set-bind-group-command
with-vulkan-gpu-driver-environment
let
index
gpu-set-bind-group-command-indexcommand
bind-group
gpu-set-bind-group-command-bind-groupcommand
unless
zeropindex
reject-gpu-requestcommand:unsupported-bind-group-indexindex
unless
typepbind-group'vulkan-gpu-bind-group
reject-gpu-requestcommand:incompatible-bind-groupbind-group
let*
encoder
vulkan-compute-pass-command-encoderpass
device
vulkan-command-encoder-deviceencoder
pipeline
or
vulkan-compute-pass-pipelinepass
error'gpu-invalid-state-error:objectpass:operation:set-bind-group:state:no-pipeline:expected-state:pipeline-bound
ensure-vulkan-object-devicebind-group
vulkan-bind-group-devicebind-group
device:set-bind-group
unless
eq
vulkan-bind-group-layoutbind-group
vulkan-compute-pipeline-bind-group-layoutpipeline
reject-gpu-requestbind-group:incompatible-pipeline-layoutpipeline
dolist
buffer
vulkan-bind-group-buffersbind-group
ensure-vulkan-object-devicebuffer
vulkan-buffer-devicebuffer
device:set-bind-group
dolist
texture-view
vulkan-bind-group-texture-viewsbind-group
let
texture
gpu-texture-view-texturetexture-view
ensure-vulkan-texture-for-commandencodertexturepass:storage-binding
transition-vulkan-textureencodertexture:general
lvk:cmd-bind-compute-descriptor-set
vulkan-command-encoder-command-bufferencoder
vulkan-compute-pipeline-layoutpipeline
vulkan-handlebind-group
retain-vulkan-resourceencoderbind-group
dolist
texture-view
vulkan-bind-group-texture-viewsbind-group
retain-vulkan-resourceencodertexture-view
dolist
sampler
vulkan-bind-group-samplersbind-group
dolist
buffer
vulkan-bind-group-buffersbind-group
setf
vulkan-compute-pass-bind-grouppass
bind-group
pass
defmethodencode
commandgpu-dispatch-workgroups-command
with-vulkan-gpu-driver-environment
ensure-vulkan-compute-pass-statepass:dispatch-workgroups
unless
and
vulkan-compute-pass-pipelinepass
vulkan-compute-pass-bind-grouppass
error'gpu-invalid-state-error:objectpass:operation:dispatch-workgroups:state:incomplete-bindings:expected-state:pipeline-and-bind-group-bound
let
x
gpu-dispatch-workgroups-command-xcommand
y
gpu-dispatch-workgroups-command-ycommand
z
gpu-dispatch-workgroups-command-zcommand
unless
every
lambda
value
typepvalue'
unsigned-byte32
listxyz
reject-gpu-requestcommand:invalid-workgroup-count
listxyz
lvk:cmd-dispatch
vulkan-command-encoder-command-buffer
vulkan-compute-pass-command-encoderpass
xyz
pass
defmethodend-pass
let
encoder
vulkan-compute-pass-command-encoderpass
setf
vulkan-command-encoder-active-passencoder
nil
vulkan-compute-pass-statepass
:ended
values
defunhash-table-alist
loopforkeybeingthehash-keysoftableusing
hash-valuevalue
collect
conskeyvalue
defunhash-table-keys
loopforkeybeingthehash-keysoftablecollectkey
defunmake-vulkan-finished-command-buffer
encoderdevicecommand-buffercommand-poolnative-resources

Publish encoder's ended native ownership as one command-buffer wrapper.

make-instance'vulkan-gpu-command-buffer:label
gpu-object-labelencoder
:handlecommand-buffer:devicedevice:command-poolcommand-pool:initial-texture-layouts
hash-table-alist
vulkan-command-encoder-initial-texture-layoutsencoder
:final-texture-layouts
hash-table-alist
vulkan-command-encoder-texture-layoutsencoder
:textures
hash-table-keys
vulkan-command-encoder-texturesencoder
:resources
hash-table-keys
vulkan-command-encoder-resourcesencoder
:native-resourcesnative-resources
defmethodfinish
with-vulkan-gpu-driver-environment
unless
member
vulkan-command-encoder-stateencoder
'
:recording:ended
error'gpu-invalid-state-error:objectencoder:operation:finish:state
vulkan-command-encoder-stateencoder
:expected-state:recording-or-ended
when
member
vulkan-command-encoder-stateencoder
'
:recording:ended
let
device
vulkan-command-encoder-deviceencoder
with-live-vulkan-device-queue
device:finish
unless
member
vulkan-command-encoder-stateencoder
'
:recording:ended
error'gpu-invalid-state-error:objectencoder:operation:finish:state
vulkan-command-encoder-stateencoder
:expected-state:recording-or-ended
when
eq:recording
vulkan-command-encoder-stateencoder
let
command-buffer
vulkan-command-encoder-command-bufferencoder
command-pool
vulkan-command-encoder-command-poolencoder
native-resources
when
eq:recording
vulkan-command-encoder-stateencoder

The encoder and its finalizer retain ownership in :ENDED until a fully initialized command-buffer wrapper takes over.

setf
vulkan-command-encoder-stateencoder
:ended
let
wrapper
make-vulkan-finished-command-bufferencoderdevicecommand-buffercommand-poolnative-resources
sb-ext:cancel-finalizationencoder
setf
vulkan-command-encoder-stateencoder
:finished
vulkan-command-encoder-command-poolencoder
nil
nil
wrapper
defuncheck-vulkan-command-buffer-for-submit
queuecommand-buffer
ensure-live-vulkan-objectcommand-buffer:submit
unless
eq
vulkan-queue-devicequeue
vulkan-command-buffer-devicecommand-buffer
error'gpu-device-mismatch-error:objectcommand-buffer:operation:submit:expected-device
vulkan-queue-devicequeue
:actual-device
vulkan-command-buffer-devicecommand-buffer
unless
eq:ready
vulkan-command-buffer-statecommand-buffer
error'gpu-invalid-state-error:objectcommand-buffer:operation:submit:state
vulkan-command-buffer-statecommand-buffer
:expected-state:ready
dolist
texture
vulkan-command-buffer-texturescommand-buffer
unless
eq
vulkan-queue-devicequeue
vulkan-texture-devicetexture
error'gpu-device-mismatch-error:objecttexture:operation:submit:expected-device
vulkan-queue-devicequeue
:actual-device
vulkan-texture-devicetexture
defunvulkan-submitted-texture-layouts
command-buffers

Validate encoded layout assumptions and return the post-batch layouts.

let
layouts
make-hash-table:test#'eq
labels
current-layout
texture
multiple-value-bind
layoutpresent-p
gethashtexturelayouts
ifpresent-playout
vulkan-texture-layouttexture
loopforcommand-bufferacrosscommand-buffersdo
dolist
entry
vulkan-command-buffer-initial-texture-layoutscommand-buffer
let*
texture
carentry
expected-layout
cdrentry
actual-layout
current-layouttexture
unless
eqactual-layoutexpected-layout
error'gpu-invalid-state-error:objecttexture:operation:submit:stateactual-layout:expected-stateexpected-layout
dolist
entry
vulkan-command-buffer-final-texture-layoutscommand-buffer
setf
gethash
carentry
layouts
cdrentry
layouts
defunsame-vulkan-native-handle-p
firstsecond

Compare native handles even when CFFI wrapped one address twice.

if
and
cffi:pointerpfirst
cffi:pointerpsecond
cffi:pointer-eqfirstsecond
eqlfirstsecond
defunvulkan-texture-external-semaphore-value
texture
let
state
vulkan-texture-external-semaphore-statetexture
ifstate
vulkan-external-semaphore-state-valuestate
vulkan-texture-private-external-semaphore-valuetexture
defun
valuetexture
let
state
vulkan-texture-external-semaphore-statetexture
ifstate
setf
vulkan-external-semaphore-state-valuestate
value
setf
vulkan-texture-private-external-semaphore-valuetexture
value
value
defunfind-vulkan-external-semaphore-state
queuesemaphore
findsemaphore
vulkan-queue-external-semaphore-statesqueue
:key#'vulkan-external-semaphore-state-semaphore:test#'same-vulkan-native-handle-p
defunretain-vulkan-external-semaphore-state
queuesemaphoreinitial-value

Share one high-water state for the retained native semaphore generation.

flet
retain
let
state
or
make-vulkan-external-semaphore-state:semaphoresemaphore:valueinitial-value
setf
vulkan-external-semaphore-state-valuestate
max
vulkan-external-semaphore-state-valuestate
initial-value
incf
vulkan-external-semaphore-state-referencesstate
when
andqueue
not
memberstate
vulkan-queue-external-semaphore-statesqueue
:test#'eq
pushstate
vulkan-queue-external-semaphore-statesqueue
state
ifqueue
sb-thread:with-recursive-lock
vulkan-queue-lockqueue
retain
retain
defunrelease-vulkan-external-semaphore-state
queuestate

Release one adopted texture reference and forget an exhausted generation.

flet
release
when
plusp
vulkan-external-semaphore-state-referencesstate
decf
vulkan-external-semaphore-state-referencesstate
when
andqueue
zerop
vulkan-external-semaphore-state-referencesstate
setf
vulkan-queue-external-semaphore-statesqueue
deletestate
vulkan-queue-external-semaphore-statesqueue
:test#'eq
ifqueue
sb-thread:with-recursive-lock
vulkan-queue-lockqueue
release
release
values
defunvulkan-external-submission-groups
texture-layouts

Group submitted external textures by semaphore and retain the newest wait.

let
groups'
maphash
lambda
texturelayout
declare
ignorelayout
let
semaphore
vulkan-texture-external-semaphoretexture
whensemaphore
let
group
findsemaphoregroups:key#'vulkan-external-submission-group-semaphore:test#'same-vulkan-native-handle-p
ifgroup
setf
vulkan-external-submission-group-current-valuegroup
max
vulkan-external-submission-group-current-valuegroup
vulkan-external-submission-group-texturesgroup
constexture
vulkan-external-submission-group-texturesgroup
push
make-vulkan-external-submission-group:semaphoresemaphore:current-value:textures
listtexture
groups
texture-layouts
dolist
groupgroups
setf
vulkan-external-submission-group-texturesgroup
nreverse
vulkan-external-submission-group-texturesgroup
nreversegroups
defunvulkan-external-submission-next-value
group
let
current
vulkan-external-submission-group-current-valuegroup
when
=current
1-
expt264
error'vulkan-gpu-error:operation:submit:reason:external-semaphore-value-exhausted:detailscurrent
1+current
defunmake-vulkan-external-callback-batch
callbacks

Return an attempt-all, retry-only-failures external callback obligation.

let
pending
copy-listcallbacks
lambda
let
retained'
failures'
dolist
entrypending
destructuring-bind
texturecallbacklayoutvalue
entry
handler-case
funcallcallbacklayoutvalue
serious-condition
cause
pushentryretained
push
constexturecause
failures
setfpending
nreverseretained
whenfailures
error'vulkan-gpu-error:operation:submit:reason:external-submission-callbacks-failed:details
nreversefailures
defunmake-vulkan-post-submit-publication
texture-layoutsgroups

Build the durable HAL-state and external-owner publication obligation.

let
callbacks'
dolist
groupgroups
let
dolist
texture
vulkan-external-submission-group-texturesgroup
let
callback
vulkan-texture-external-submittedtexture
whencallback
push
listtexturecallback
gethashtexturetexture-layouts
value
callbacks
apply#'make-gpu-retirement-sequence
append
list
lambda

Every HAL wrapper advances before any owner callback runs. A failing plane therefore cannot leave its siblings on stale values.

maphash
lambda
texturelayout
setf
vulkan-texture-layouttexture
layout
texture-layouts
dolist
groupgroups
let
dolist
texture
vulkan-external-submission-group-texturesgroup
whencallbacks
defuncomplete-vulkan-submission-publication
submission

Attempt submission's durable post-commit publication once.

let
publication
vulkan-gpu-submission-post-submit-publicationsubmission
whenpublication
funcallpublication
setf
vulkan-gpu-submission-post-submit-publicationsubmission
nil
t
defuncomplete-vulkan-queue-submission-publications
queue

Complete the live FIFO prefix of post-commit owner publications.

let
progress-pnil
dolist
submission
vulkan-queue-live-submissionsqueue
progress-p
when
setfprogress-pt
defmethodsubmit
submitqueue
vectorcommand-buffer
defunvulkan-queue-completed-frontier
queue

Return the index of the newest submission the GPU has fully completed.

lvk:semaphore-counter-value
vulkan-handle
vulkan-queue-devicequeue
vulkan-queue-timelinequeue
defunwait-for-vulkan-submission
queueindex

Block until queue's completion frontier reaches index.

lvk:wait-semaphore-value
vulkan-handle
vulkan-queue-devicequeue
vulkan-queue-timelinequeue
index
values
defunvulkan-retirement-custodian-quiescent-p
queue

Whether queue owns no submitted, publishing, or retirement work.

let
ledger
vulkan-queue-retirement-ledgerqueue
and
null
vulkan-queue-live-submissionsqueue
null
gpu-retirement-ledger-active-batchledger
null
gpu-retirement-ledger-entriesledger
defunmaybe-release-vulkan-retirement-custodian
queue

Unroot queue only after backend quiescence was proved under its lock.

defunmaintain-vulkan-queue
queue

Retire live submissions the completion frontier has passed.

Retiring a submission drops the queue's references to everything it retained. The independent retirement ledger then attempts every native teardown which that frontier makes safe; failures remain queue-owned.

with-vulkan-gpu-driver-environment
sb-thread:with-recursive-lock
vulkan-queue-lockqueue

External-owner publication is part of the live record. It must finish before a completed record can release those owners or retirement can advance beyond it.

let
loopwhile
and
vulkan-queue-live-submissionsqueue
<=
vulkan-gpu-submission-index
first
vulkan-queue-live-submissionsqueue
frontier
do
pop
vulkan-queue-live-submissionsqueue
maintain-gpu-retirement-ledger
vulkan-queue-retirement-ledgerqueue
frontier:operation:maintain-vulkan-queue
frontier
defmethodservice-gpu-retirement-custodian

Service queue from the process custodian registry without caller access.

with-vulkan-gpu-driver-environment
sb-thread:with-recursive-lock
vulkan-queue-lockqueue
let*
device
vulkan-queue-devicequeue
ledger
vulkan-queue-retirement-ledgerqueue
before
+
length
vulkan-queue-live-submissionsqueue
length
gpu-retirement-ledger-active-batchledger
length
gpu-retirement-ledger-entriesledger
cond
or
vulkan-object-destroyed-pqueue
vulkan-object-destroyed-pdevice
vulkan-device-retiring-pdevice

Never query a frontier through a retiring or retired VkDevice. Device teardown has already enforced the ledger/submission barrier; a stale empty root may now be removed without FFI.

t
let
after
+
length
vulkan-queue-live-submissionsqueue
length
gpu-retirement-ledger-active-batchledger
length
gpu-retirement-ledger-entriesledger
or
<afterbefore
zeropafter
defunlive-vulkan-retirement-queue-p
queuedevice
andqueue
eqqueue
vulkan-device-queuedevice
not
vulkan-object-destroyed-pdevice
not
vulkan-device-retiring-pdevice
not
vulkan-object-destroyed-pqueue
defunretire-vulkan-native-owner
resourcedeviceready-afterteardowninvalidateoperation

Transfer one native owner after revalidating its queue under the lock.

labels
retire-directly
&optionalqueue-snapshot
perform-gpu-retirement-directlyresource
lambda
unless
or
vulkan-object-destroyed-pdevice
when
andqueue-snapshot
or
not
eqqueue-snapshot
vulkan-device-queuedevice
vulkan-object-destroyed-pqueue-snapshot
error"The Vulkan retirement queue is no longer live."
funcallteardown
invalidate:operationoperation
let
queue
vulkan-device-queuedevice
ifqueue
sb-thread:with-recursive-lock
vulkan-queue-lockqueue

Device teardown uses this same lock. Revalidate and choose the queue or direct path without a race window between them.

if
progn
transfer-gpu-retirement
vulkan-queue-retirement-ledgerqueue
resourceready-afterteardowninvalidatequeue
retire-directlyqueue
retire-directly
values
defmethodretire-gpu-native-owner
ownerteardowninvalidate
retire-vulkan-native-ownerownerdevice0teardowninvalidate:retire-gpu-native-owner
defunvulkan-destroy-or-defer
resourcedeviceinvalidate

Transfer resource's native ownership, then logically invalidate it.

A live queue owns the retirement before invalidate marks the wrapper and cancels its finalizer. Queue maintenance immediately attempts anything already safe and retains failures. Without a live queue, native teardown must succeed before invalidate is called.

let
teardown
or
vulkan-object-retirement-teardownresource
setf
vulkan-object-retirement-teardownresource
retire-vulkan-native-ownerresourcedevice
vulkan-object-last-submissionresource
teardowninvalidate:destroy-vulkan-resource
defunsubmit-vulkan-command-buffers
queuecommand-buffers&key
wait-semaphores
signal-semaphores
wait-for-completion

Submit one WebGPU-style batch and track it on the queue's frontier.

wait-semaphores and signal-semaphores are LVK semaphore submit entries of the form (SEMAPHORE STAGES &optional VALUE). Every submission additionally signals the queue's timeline semaphore with a fresh submission index, which is returned. The submission record retains the command buffers and every resource they captured until the frontier passes the index, so callers may destroy any of them immediately after this returns.

with-vulkan-gpu-driver-environment
let
indexnil
sb-thread:with-recursive-lock
vulkan-queue-lockqueue

The first check can race while waiting for this lock. Device teardown closes admission under the same lock, so this is the authoritative check before any queue or device FFI.

A failed owner publication from an earlier native commit is a FIFO admission barrier. Retry it before scheduling dependent work.

loopforcommand-bufferacrosscommand-buffersdo
let*
texture-layouts
external-groups
when
plusp
lengthcommand-buffers
setfindex
1+
vulkan-queue-submission-counterqueue
let*
resources
loopforcommand-bufferacrosscommand-buffersappend
copy-list
vulkan-command-buffer-resourcescommand-buffer
submission
make-vulkan-gpu-submission:indexindex:command-bufferscommand-buffers:resourcesresources:post-submit-publication
make-vulkan-post-submit-publicationtexture-layoutsexternal-groups

Allocate the list cell before native commit. Once Vulkan accepts the batch, publication below performs no external callback before this durable queue record exists.

submission-cell
listsubmission
lvk:submit-command-buffers
vulkan-handlequeue
map'vector#'vulkan-handlecommand-buffers
:wait-semaphores
concatenate'vectorwait-semaphores
map'vector
lambda
group
list
vulkan-external-submission-group-semaphoregroup
'
:all-commands
vulkan-external-submission-group-current-valuegroup
external-groups
:signal-semaphores
concatenate'vectorsignal-semaphores
map'vector
lambda
group
list
vulkan-external-submission-group-semaphoregroup
'
:all-commands
external-groups
vector
list
vulkan-queue-timelinequeue
'
:all-commands
index

Native commit has happened. Publish every counter, wrapper state, dependency, and owner callback obligation before any fallible user callback can regain control.

setf
vulkan-queue-submission-counterqueue
index
loopforcommand-bufferacrosscommand-buffersdo
setf
vulkan-command-buffer-statecommand-buffer
:submitted
vulkan-object-last-submissioncommand-buffer
index
dolist
resourceresources
setf
vulkan-object-last-submissionresource
index
setf
vulkan-queue-live-submissionsqueue
nconc
vulkan-queue-live-submissionsqueue
submission-cell
retain-gpu-retirement-ledger-custodian
vulkan-queue-retirement-ledgerqueue
queue

Success clears the obligation slot. Failure propagates only after the rooted live record has retained its retry progress.

whenindex
whenwait-for-completion
index
defmethodsubmit
command-buffersvector

Schedule one WebGPU-style batch and return its submission index.

submit-vulkan-command-buffersqueuecommand-buffers
defmethodsubmitted-work-done
with-vulkan-gpu-driver-environment
ensure-live-vulkan-objectqueue:submitted-work-done
sb-thread:with-recursive-lock
vulkan-queue-lockqueue
ensure-live-vulkan-objectqueue:submitted-work-done
let
counter
vulkan-queue-submission-counterqueue
when
pluspcounter
values
defunmake-vulkan-device-retirement-step
devicefunction
lambda
with-vulkan-queue-teardown
devicenative-device
funcallfunctionnative-device
defunvulkan-command-native-resource-retirement-steps
deviceresource

Flatten one tagged command resource into one-native-call retry steps.

ecase
firstresource
:framebuffer
let
framebuffer
secondresource
list
make-vulkan-device-retirement-stepdevice
lambda
native-device
lvk:destroy-framebuffernative-deviceframebuffer
:upload-buffer
let
buffer
secondresource
memory
thirdresource
list
make-vulkan-device-retirement-stepdevice
lambda
native-device
lvk:destroy-buffernative-devicebuffer
make-vulkan-device-retirement-stepdevice
lambda
native-device
lvk:free-memorynative-devicememory
defunmake-vulkan-command-retirement-teardown
devicecommand-poolresources

Build one persistent, progress-tracked command ownership teardown.

apply#'make-gpu-retirement-sequence
append
whencommand-pool
list
make-vulkan-device-retirement-stepdevice
lambda
native-device
lvk:destroy-command-poolnative-devicecommand-pool
loopforresourceinresourcesappend
defmethoddestroy
with-vulkan-gpu-driver-environment
when
member
vulkan-command-encoder-stateencoder
'
:recording:ended
let
device
vulkan-command-encoder-deviceencoder
command-pool
vulkan-command-encoder-command-poolencoder
flet
invalidate
setfnil
vulkan-command-encoder-command-poolencoder
nil
vulkan-command-encoder-stateencoder
:destroyed
sb-ext:cancel-finalizationencoder
let
teardown
or
vulkan-command-encoder-retirement-teardownencoder
setf
vulkan-command-encoder-retirement-teardownencoder
make-vulkan-command-retirement-teardowndevicecommand-poolresources
retire-vulkan-native-ownerencoderdevice0teardown#'invalidate:destroy-vulkan-command-encoder
unless
eq:destroyed
vulkan-command-encoder-stateencoder
setf
vulkan-command-encoder-stateencoder
:destroyed
values
defmacrodefine-vulkan-resource-destroy
device-formbindings&bodynative-teardown

Define destroy and the queueable native teardown closure for class.

destroy first transfers native-teardown to the queue, then marks the wrapper destroyed and cancels its leak finalizer. Without a live queue, it marks and cancels only after native teardown succeeds.

bindings extract every native handle native-teardown needs, so the teardown closure captures raw handles and the device wrapper rather than variable itself and can therefore serve as the wrapper's leak finalizer. native-teardown runs only while the device is alive, under the queue teardown lock, with DEVICE anaphorically bound to the native handle.

let
device-object
gensym"DEVICE-OBJECT"
`
progn
defmethodvulkan-native-teardown-closure
,variable,class
let*
,device-object,device-form
,@bindings
make-gpu-retirement-sequence,@
loopforforminnative-teardowncollect`
lambda
with-vulkan-queue-teardown
,device-objectdevice
,form
defmethoddestroy
,variable,class
with-vulkan-gpu-driver-environment
unless
vulkan-object-destroyed-p,variable
vulkan-destroy-or-defer,variable,device-form
lambda
setf
vulkan-object-destroyed-p,variable
t
sb-ext:cancel-finalization,variable
values
',class
define-vulkan-resource-destroy
vulkan-command-buffer-devicecommand-buffer
command-pool
vulkan-command-buffer-command-poolcommand-buffer
resources
vulkan-command-buffer-native-resourcescommand-buffer
teardown
make-vulkan-command-retirement-teardown
vulkan-command-buffer-devicecommand-buffer
command-poolresources
funcallteardown
defmethoddestroy:after
setf
vulkan-command-buffer-statecommand-buffer
:destroyed
define-vulkan-resource-destroy
vulkan-bind-group-devicebind-group
descriptor-pool
vulkan-bind-group-descriptor-poolbind-group
lvk:destroy-descriptor-pooldevicedescriptor-pool
define-vulkan-resource-destroy
vulkan-compute-pipeline-devicepipeline
handle
vulkan-handlepipeline
pipeline-layout
vulkan-compute-pipeline-layoutpipeline
lvk:destroy-pipeline-layoutdevicepipeline-layout
define-vulkan-resource-destroy
vulkan-render-pipeline-devicepipeline
handle
vulkan-handlepipeline
pipeline-layout
vulkan-render-pipeline-layoutpipeline
lvk:destroy-pipeline-layoutdevicepipeline-layout
define-vulkan-resource-destroy
vulkan-sampler-devicesampler
handle
vulkan-handlesampler
define-vulkan-resource-destroy
vulkan-buffer-devicebuffer
handle
vulkan-handlebuffer
memory
vulkan-buffer-memorybuffer
lvk:unmap-memorydevicememory
lvk:destroy-bufferdevicehandle
lvk:free-memorydevicememory
define-vulkan-resource-destroy
vulkan-shader-module-devicemodule
handle
vulkan-handlemodule
define-vulkan-resource-destroy
vulkan-texture-view-deviceview
handle
vulkan-handleview
defmethodvulkan-native-teardown-closure
let
device
vulkan-texture-devicetexture
queue
vulkan-device-queue
vulkan-texture-devicetexture
handle
vulkan-handletexture
memory
vulkan-texture-memorytexture
owned-p
vulkan-texture-owned-ptexture
external-owner
vulkan-texture-external-ownertexture
semaphore-state
vulkan-texture-external-semaphore-statetexture
apply#'make-gpu-retirement-sequence
append
whenowned-p
list
make-vulkan-device-retirement-stepdevice
lambda
native-device
lvk:destroy-imagenative-devicehandle
make-vulkan-device-retirement-stepdevice
lambda
native-device
lvk:free-memorynative-devicememory
whenexternal-owner

This owner is not a VkDevice child. Keep it outside the guarded device steps so a finalizer after vkDestroyDevice still releases it.

list
lambda
funcallexternal-owner
whensemaphore-state
list
lambda
defmethoddestroy
with-vulkan-gpu-driver-environment
unless
vulkan-object-destroyed-ptexture
vulkan-destroy-or-defertexture
vulkan-texture-devicetexture
lambda
setf
vulkan-object-destroyed-ptexture
t
sb-ext:cancel-finalizationtexture
values
defunmake-vulkan-device-destroy-admission
devicequeue

Return a persistent idle-and-ledger barrier for device destruction.

let
waited-submissionnil
lambda
unless
vulkan-device-retiring-pdevice
let
submission
ifqueue
vulkan-queue-submission-counterqueue
0

A ledger failure leaves admission open. Preserve a completed wait across retry, but renew it if another submission arrived.

unless
eqlsubmissionwaited-submission
lvk:device-wait-idle
vulkan-handledevice
setfwaited-submissionsubmission
whenqueue
ensure-gpu-retirement-ledger-empty
vulkan-queue-retirement-ledgerqueue
:operation:destroy-vulkan-device

The caller holds queue's lock, so the waited generation cannot change between this barrier and closing admission.

setf
vulkan-device-retiring-pdevice
t
defunmake-vulkan-render-pass-retirement-teardown
native-devicerender-pass-table

Return a lazy, retryable drain of render-pass-table.

The snapshot happens only when teardown begins, after explicit destruction has closed device admission. Native destruction and hash bookkeeping have separate progress flags, so neither a late-created pass nor a Lisp-side error can make a successful native call repeat.

let
pendingnil
snapshotted-pnil
current-native-retired-pnil
lambda
unlesssnapshotted-p
setfpending
loopforformatbeingthehash-keysofrender-pass-tableusing
hash-valuerender-pass
collect
consformatrender-pass
snapshotted-pt
loopwhilependingforentry=
firstpending
do
unlesscurrent-native-retired-p
lvk:destroy-render-passnative-device
cdrentry
setfcurrent-native-retired-pt
remhash
carentry
render-pass-table
setfcurrent-native-retired-pnil
poppending
values
defunmake-vulkan-device-destroy-teardown
devicequeue

Return a native teardown which does not retain device or queue.

let
native-device
vulkan-handledevice
timeline
andqueue
vulkan-queue-timelinequeue
render-pass-table
vulkan-device-render-passesdevice
native-retired-box
vulkan-device-native-retired-boxdevice
debug-messenger
vulkan-device-debug-messengerdevice
instance
vulkan-device-instancedevice
apply#'make-gpu-retirement-sequence
append
whentimeline
list
lambda
lvk:destroy-semaphorenative-devicetimeline
list
make-vulkan-render-pass-retirement-teardownnative-devicerender-pass-table
list
lambda
lvk:destroy-devicenative-device

Finalizers must stop issuing device-level calls immediately, even if a later instance-level owner fails and overall destroy retries.

setf
carnative-retired-box
t
whendebug-messenger
list
lambda
list
lambda
defunensure-vulkan-device-retirement-teardowns
devicequeue

Return device's shared explicit and leak-finalizer teardown closures.

let
native-teardown
or
vulkan-device-destroy-teardowndevice
setf
vulkan-device-destroy-teardowndevice
valuesnative-teardown
or
vulkan-device-finalizer-teardowndevice
setf
vulkan-device-finalizer-teardowndevice
let
native-device
vulkan-handledevice
native-retired-box
vulkan-device-native-retired-boxdevice
wrap-vulkan-gpu-driver-teardown
make-gpu-retirement-sequence
lambda
unless
carnative-retired-box
native-teardown
defmethoddestroy
with-vulkan-gpu-driver-environment
unless
vulkan-object-destroyed-pdevice
let
queue
vulkan-device-queuedevice
flet
tear-down-device
funcall
or
vulkan-device-destroy-admissiondevice
setf
vulkan-device-destroy-admissiondevice

Admission is closed after the first successful barrier. Recheck on every partial-teardown retry so an invariant violation can never be silently skipped.

whenqueue
ensure-gpu-retirement-ledger-empty
vulkan-queue-retirement-ledgerqueue
:operation:destroy-vulkan-device
multiple-value-bind
native-teardownfinalizer-teardown
declare
ignorefinalizer-teardown
funcallnative-teardown

Logical publication is deliberately outside the shared native sequence so its leak finalizer never captures the device or QUEUE wrappers through their back reference.

sb-ext:cancel-finalizationdevice
setf
vulkan-object-destroyed-pdevice
t
whenqueue
setf
vulkan-object-destroyed-pqueue
t

This Lisp lock outlives every native queue/device phase and stays usable across failures in the persistent teardown sequence.

ifqueue
sb-thread:with-recursive-lock
vulkan-queue-lockqueue
tear-down-device
tear-down-device
values