7 Errors and Validation
dxtr edited this page 2026-10-18 16:56:34 +02:00

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.