3 Vulkan Surfaces
dxtr edited this page 2026-10-14 04:26:09 +02:00

Vulkan Surfaces

The optional system cl-windowing/vulkan creates Vulkan surfaces for windows, through cl-vk. The rest of cl-windowing never depends on Vulkan.

If you draw with cl-gfx, you do not need this page: cl-gfx/windowing uses cl-windowing/vulkan for you.

Creating a surface

(asdf:load-system :cl-windowing/wayland)   ; on Windows, :cl-windowing/win32
(asdf:load-system :cl-windowing/vulkan)

(win:with-window-system (window-system)
  (win:with-window (window window-system :title "Vulkan")
    (vk:with-instance (instance :extensions (cl-windowing/vulkan:vulkan-instance-extensions
                                             window-system))
      (let ((surface (cl-windowing/vulkan:create-vulkan-surface window instance)))
        ;; Create a device and a swapchain for SURFACE, and draw.
        (vk:destroy-surface surface)))))

Two functions:

(cl-windowing/vulkan:vulkan-instance-extensions window-system) => list of strings
(cl-windowing/vulkan:create-vulkan-surface window instance)    => cl-vk:surface
  • vulkan-instance-extensions names the instance extensions that surfaces of the window system need. On Wayland: ("VK_KHR_surface" "VK_KHR_wayland_surface"); on Windows: ("VK_KHR_surface" "VK_KHR_win32_surface"). Pass them, with any others you need, as the instance's :extensions.
  • create-vulkan-surface creates a surface for a window, and returns it as a cl-vk:surface, which you then own. It may be called before the window has its first size.

If the instance lacks an extension, create-vulkan-surface signals cl-vk:unsupported-error, and says what to do:

A Vulkan surface for #<WAYLAND-WINDOW "Vulkan" 800x600> needs the instance
extensions VK_KHR_surface, VK_KHR_wayland_surface; create the instance with
(CL-WINDOWING/VULKAN:VULKAN-INSTANCE-EXTENSIONS #<WAYLAND-WINDOW-SYSTEM
wayland>) in :EXTENSIONS.

For a destroyed window, it signals closed-error.

Destroying

Destroy the surface with cl-vk:destroy-surface before the window, after destroying its swapchain. A surface that outlives its window refers to a native window that no longer exists.

Sizing the swapchain

On Wayland a surface has no size of its own: the swapchain decides it. On Windows the surface has a size of its own, the window's client area, which can differ from window-pixel-size by a pixel at a fractional scale; cl-vk's create-swapchain takes that size where a surface has one, so read the swapchain's size back with vk:swapchain-extent. Size it by the window's size in pixels, and follow the resize handshake:

  1. Wait until window-configured-p is true. Until then nothing may be presented, and window-pixel-size is 0 by 0. See Window Systems and Windows.
  2. Create the swapchain at window-pixel-size.
  3. Each frame, check window-pending-pixel-size. When it returns a size that differs from the swapchain's, recreate the swapchain at that size.
  4. Draw the frame.
  5. If a size was pending, call adopt-window-size just before presenting.
  6. Present.

An outline with cl-vk, once the window is configured:

(defun swapchain-extent (window)
  (multiple-value-list (win:window-pixel-size window)))

(let ((swapchain (vk:create-swapchain device surface :extent (swapchain-extent window))))
  (loop
    (let ((adopt nil))
      (multiple-value-bind (width height) (win:window-pending-pixel-size window)
        (when width
          (setf adopt t)
          (unless (equal (vk:swapchain-extent swapchain) (list width height))
            ;; After the device has finished with the old images.
            (setf swapchain (vk:recreate-swapchain swapchain :extent (list width height))))))
      ;; Acquire an image with vk:acquire-next-image, record and submit.
      (when adopt
        (win:adopt-window-size window))
      ;; Present with vk:present.  On :out-of-date, recreate the swapchain
      ;; at (swapchain-extent window).
      )))

Threads

create-vulkan-surface may be called from any thread. The surface and the swapchain follow cl-vk's thread rules, and usually belong to the render thread. See Threads.

How it works

A backend says what a surface for a window is made from, through the backend functions backend-vulkan-extensions and backend-vulkan-target. cl-windowing/vulkan makes the VkSurfaceKHR from that and hands it to cl-vk with cl-vk:adopt-surface. Native pointers, such as the wl_display and the wl_surface, or the module and window handles on Windows, cross between the libraries only there. See Backend Protocol.