Errors and Validation
Conditions
Every error cl-gfx signals is a gfx-error, except an argument of the
wrong type, which is a type-error, such as a usage that is no
buffer-usage or texture-usage. Messages say what went wrong and what to
do about it.
Every function declares the types of its arguments, such as a
bind-group-index or a 32-bit count, and SBCL checks them on entry, in
its own words:
The value
4
is not of type
(UNSIGNED-BYTE 2)
when binding CL-GFX::INDEX
A constant of the wrong type in a call is a warning already when the call is compiled.
error
└─ gfx-error gfx-error-message
├─ no-backend-error no-backend-error-backend
├─ unsupported-error
└─ destroyed-error destroyed-error-object
| Condition | Signalled when | What to do |
|---|---|---|
gfx-error |
An argument is wrong: a missing usage, a malformed bind group entry, a write past the end of a buffer, a texture drawn into and read in one pass, a frame or pass in the wrong state. | Fix the call; the message says how. |
no-backend-error |
The backend open-device was asked for, by default :vulkan, is not loaded. no-backend-error-backend is that backend. |
Load its system, cl-gfx/vulkan. |
unsupported-error |
The system lacks something: no GPU with Vulkan 1.4 and what cl-gfx needs, a device feature, a present mode or a surface format the GPU can't use. | Pick a fallback, check device-features first, or update the driver. |
destroyed-error |
A destroyed object, or a closed device, is used. destroyed-error-object is the object. |
Don't keep references to destroyed objects. |
Some messages, as they appear:
A buffer needs a :USAGE, a list of BUFFER-USAGE keywords, such as (:VERTEX) or
(:UNIFORM).
The GPU backend :OPENGL is not loaded. Load the system cl-gfx/opengl first.
Loaded backends: :VULKAN.
#<VULKAN-BUFFER destroyed {12040AEF33}> has been destroyed and cannot be used.
A pipeline with a geometry shader needs the device feature :GEOMETRY-SHADERS,
which <GPU name> lacks. DEVICE-FEATURES lists what it has.
Handling them:
(handler-case (gfx:open-device :present-to window-system)
(gfx:unsupported-error (e)
(format *error-output* "No usable GPU: ~A~%" e)
(uiop:quit 1)))
Errors during a frame
with-frame and with-render-pass end the frame or pass however their body
is left. If a draw signals an error and you pick a restart in the debugger,
what was recorded until then is submitted, and the next frame begins
normally. A live-coding session survives a mistake in the render loop.
Foreign code
The backend calls the driver only with floating-point traps masked, as C code
expects; SBCL unmasks some traps by default, and drivers such as
llvmpipe would otherwise kill the process with SIGFPE. Errors from the
driver become conditions, and no condition is signalled through foreign
frames.
Validation
cl-gfx checks the arguments of every call. The Vulkan validation layer checks what reaches the driver. Turn it on while developing:
(gfx:with-device (device :validation t)
...)
(gfx:with-device (device :validation-checks '(:synchronization))
...) ; also checks the barriers between commands
| Check | Looks for |
|---|---|
(always, with :validation) |
Invalid uses of the Vulkan API. |
:synchronization |
Missing or wrong barriers between commands that use the same memory. |
:best-practices |
Uses that are valid but slow or fragile. |
Giving :validation-checks turns validation on. The layer must be installed:
apt install vulkan-validationlayers on Debian and Ubuntu.
Reading the messages
(gfx:take-validation-messages device)
;; => ((:INFO . "...") (:VERBOSE . "...") (:ERROR . "...") ...)
(gfx:take-validation-messages device :minimum-severity :warning)
;; => ((:ERROR . "...") (:WARNING . "..."))
take-validation-messages returns the messages since the last call, oldest
first, and forgets them. Each is (severity . text), where severity is a
validation-severity: :error, :warning, :info or :verbose. They
are what validation says of the program's use of the API, Vulkan's
messages of the validation and performance types; the loader's notes on
the machine, of the general type, are left out, such as its warnings
about layers other software installed, GOG Galaxy's overlay or a driver's
layer registered twice, which it sends each time a device opens.
:minimum-severity keeps only those at least as severe, and forgets the
rest too.
A good place to call it is after each frame in a debug build, or at the end
of each test. cl-gfx's own tests run every device with
:validation-checks '(:synchronization) when the layer is installed and fail
on any :error.
A device opened without validation has no messages.
Common mistakes
| Symptom | Cause |
|---|---|
... cannot be read back: create it with :COPY-SRC in its :USAGE. |
read-buffer or read-texture on a resource without :copy-src. |
... cannot be written: create it with :COPY-DST in its :USAGE. |
write-texture on a texture without :copy-dst. |
A texture needs a :USAGE ... |
create-texture without :usage, which is required. |
... was made from ..., not ... |
A bind group's layout or resource from another device. |
The value (SIGNED-BYTE 8) is not of type (OR (MEMBER DOUBLE-FLOAT SINGLE-FLOAT) ...) when binding :ELEMENT-TYPE |
An :element-type that is no data-element-type, which read-buffer and read-texture take. |
READ-BUFFER waits for the GPU: call it between frames. |
Reading back inside with-frame. |
... is not open: record draw commands inside WITH-RENDER-PASS ... |
A draw command outside a pass, or with a pass object kept from an earlier pass. |
A pass is open already: end it before beginning another. |
Nesting passes. |
... was opened without :PRESENT-TO and cannot present. |
create-surface on an offscreen device. |
... is not a surface target ... |
cl-gfx/windowing is not loaded. |
| Image upside down | A shader written for Vulkan's y-down clip space. cl-gfx's clip space has y up. |