3 Physical Devices and Devices
dxtr edited this page 2026-10-18 15:22:05 +02:00

Physical Devices and Devices

A physical device is a GPU, or a software renderer, that an instance can use. A device is your connection to one: it has the queues you submit work to, and the extensions and features you enabled.

Physical devices

(vk:physical-devices instance)
;; => (#<PHYSICAL-DEVICE "llvmpipe (LLVM 21.1.8, 256 bits)" cpu Vulkan 1.4.354>)

physical-devices reads each GPU's properties once and returns them as immutable physical-device objects:

Reader Value
physical-device-name The name, such as "AMD Radeon RX 7800 XT (RADV NAVI32)".
physical-device-type :discrete-gpu, :integrated-gpu, :virtual-gpu, :cpu or :other.
physical-device-api-version The Vulkan version the driver supports, (MAJOR MINOR PATCH).
physical-device-driver-version The driver's version, an integer in the vendor's own encoding.
physical-device-vendor-id, physical-device-device-id PCI-style identifiers.
physical-device-queue-families A list of queue-family.
physical-device-extensions The names of the device extensions it supports.
physical-device-instance The instance it belongs to.

A physical device is valid while its instance is, and is never destroyed.

Queue families

A GPU's queues come in families. All queues of a family can do the same things:

(mapcar (lambda (family)
          (list (vk:queue-family-index family)
                (vk:queue-family-flags family)
                (vk:queue-family-count family)))
        (vk:physical-device-queue-families gpu))
;; => ((0 (:GRAPHICS :COMPUTE :TRANSFER :SPARSE-BINDING) 1))

A queue-family has an index, flags (:graphics, :compute, :transfer, :sparse-binding, :protected, :video-decode and so on), a count of queues, and queue-family-timestamp-valid-bits.

find-queue-family returns the first family that satisfies a predicate:

(vk:find-queue-family gpu (lambda (family)
                            (member :compute (vk:queue-family-flags family))))

Whether a family can present to a surface depends on the surface; ask with surface-supported-p. See Surfaces and Swapchains.

Features

Features are optional capabilities, each a VkBool32 field of a feature struct. They are named by the struct, a symbol of cl-vk/structs, and the field, a keyword:

Struct Holds
vks:physical-device-features the Vulkan 1.0 features, such as :sampler-anisotropy
vks:physical-device-vulkan11-features Vulkan 1.1 features
vks:physical-device-vulkan12-features Vulkan 1.2 features, such as :timeline-semaphore
vks:physical-device-vulkan13-features Vulkan 1.3 features, such as :dynamic-rendering, :synchronization2
vks:physical-device-vulkan14-features Vulkan 1.4 features
any extension's features struct its features, such as vks:physical-device-mesh-shader-features-ext

physical-device-feature-p asks about one:

(vk:physical-device-feature-p gpu 'vks:physical-device-vulkan13-features :dynamic-rendering)
;; => T
(vk:physical-device-feature-p gpu 'vks:physical-device-features :sampler-anisotropy)
;; => T

A field that is not a feature, or a struct that is not a feature struct, signals an error that says so, and an unknown field lists the struct's fields.

Creating a device

(vk:with-device (device gpu
                 :extensions '("VK_KHR_swapchain")
                 :features '((vks:physical-device-vulkan13-features
                              :dynamic-rendering :synchronization2)
                             (vks:physical-device-vulkan12-features
                              :timeline-semaphore)))
  ...)

with-device calls create-device and, when its body is left, waits for the device to finish its work and destroys it. create-device takes the physical device and:

Argument Default Meaning
:queues one queue of the first family with :graphics The queues to make, a list of (FAMILY COUNT), one entry per family.
:extensions '() Device extensions to enable, by name.
:features '() Features to enable, a list of (STRUCT FIELD ...).

Before creating anything, it checks every request against the physical device and signals an unsupported-error for a queue family that does not exist or has too few queues, an extension it does not support, or a feature it lacks:

#<PHYSICAL-DEVICE "llvmpipe (LLVM 21.1.8, 256 bits)" cpu Vulkan 1.4.354> does not
support these features: physical-device-ray-tracing-motion-blur-features-nv
:RAY-TRACING-MOTION-BLUR.

A malformed request, such as a queue request that is not (FAMILY COUNT) or a feature request that is not a list, signals a type-error that shows the expected form; a queue family given twice signals a vk-error: "The queue family 0 is asked for twice; ask once, for all its queues."

To present, enable "VK_KHR_swapchain". To use a Vulkan 1.3 feature, enable it here even though the device supports Vulkan 1.3: features are off unless enabled.

Queues

device-queue returns a queue of the device:

(vk:device-queue device)                    ; the first queue the device was made with
(vk:device-queue device :family 2)          ; queue 0 of family 2
(vk:device-queue device :family 2 :index 1) ; queue 1 of family 2

device-queues lists them all. A queue knows its queue-device, its queue-family (a queue-family object) and its queue-index within the family. Asking for a queue the device was not created with signals a vk-error that lists the queues it has.

Several queues, for example a graphics queue and a separate transfer queue when the GPU has one:

(let* ((graphics (vk:find-queue-family gpu (lambda (f) (member :graphics (vk:queue-family-flags f)))))
       (transfer (vk:find-queue-family gpu (lambda (f)
                                             (and (member :transfer (vk:queue-family-flags f))
                                                  (not (member :graphics (vk:queue-family-flags f))))))))
  (vk:create-device gpu :queues (if transfer
                                    (list (list (vk:queue-family-index graphics) 1)
                                          (list (vk:queue-family-index transfer) 1))
                                    (list (list (vk:queue-family-index graphics) 1)))))

Asking about a device

Function Returns
device-physical-device The physical device it runs on.
device-queues Its queues.
device-extensions The extensions it was created with.
device-features The feature requests it was created with.
device-wait-idle Waits until all work submitted to its queues has finished.

Destroying a device

destroy-device needs the swapchains, fences and semaphores made from the device destroyed first; otherwise it signals live-children-error. Wait for the device's work to finish first, with device-wait-idle. Objects you made through the raw layer, such as command pools, are not tracked: destroy them yourself before the device.

Thread rules

A physical device is immutable and may be shared between threads. A device may be used from several threads at once, but creating and destroying the objects made from it must happen on one thread at a time, and it must not be destroyed while another thread uses it. Submitting to and presenting on a queue must happen on one thread at a time; different queues may be used from different threads.