Every window-system surface (xcb, wayland, …) routes your frames
through a compositor — an extra copy (at best) or an extra refresh of
latency (at worst). VK_KHR_display skips all of it: you enumerate the
GPU’s own display controllers and planes and present directly to
scanout hardware. This is how digital signage, VR compositors and
camera walls get zero-compositor frames.
path.txt
normal: app → swapchain → compositor → KMS → display
direct: app → swapchain ──────────────► KMS → display (VK_KHR_display)Enumerating displays and modes
display.cpp
// Instance extensions: VK_KHR_surface, VK_KHR_display
// (+ VK_EXT_direct_mode_display, VK_EXT_acquire_drm_display on Linux)
uint32_t n = 0;
phys.getDisplayPropertiesKHR(&n, nullptr);
std::vector<vk::DisplayPropertiesKHR> dps(n);
phys.getDisplayPropertiesKHR(&n, dps.data());
// dp.display, dp.displayName, dp.physicalResolution,
// dp.physicalDimensions, dp.supportedTransforms,
// dp.planeReorderPossible, dp.persistentContent
vk::DisplayKHR display = dps[0].display;
phys.getDisplayModePropertiesKHR(display, &n, nullptr);
std::vector<vk::DisplayModePropertiesKHR> modes(n);
phys.getDisplayModePropertiesKHR(display, &n, modes.data());
// mode.parameters.visibleRegion + .refreshRate (mHz, e.g. 59940)
// Planes: the compositor-in-hardware. Each display uses a subset.
phys.getDisplayPlanePropertiesKHR(&n, nullptr);
std::vector<vk::DisplayPlanePropertiesKHR> planes(n);
phys.getDisplayPlanePropertiesKHR(&n, planes.data());
uint32_t planeIdx = findPlaneForDisplay(planes, display);
// What can this plane do with this mode?
vk::DisplayPlaneCapabilitiesKHR pcaps{};
phys.getDisplayPlaneCapabilitiesKHR(modes[0].displayMode,
planeIdx, &pcaps);
// pcaps.supportedAlpha, .minSrcRect/maxSrcRect, .minDstRect/maxDstRect,
// pcaps.supportedDstPosition — can the plane place the image
// anywhere, or only at 0,0 covering the whole mode?
Creating the surface
surface.cpp
vk::DisplaySurfaceCreateInfoKHR sci{};
sci.displayMode = modes[0].displayMode;
sci.planeIndex = planeIdx;
sci.planeStackIndex = planes[planeIdx].currentStackIndex;
sci.transform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
sci.globalAlpha = 1.0f;
sci.alphaMode = vk::DisplayPlaneAlphaFlagBitsKHR::eOpaque;
sci.imageExtent = modes[0].parameters.visibleRegion;
vk::UniqueSurfaceKHR surf = inst.createDisplayPlaneSurfaceKHRUnique(sci);
// From here: normal swapchain + present — straight to scanout.
Acquiring the display on Linux
The display is owned by DRM — if a compositor or getty holds it you must take control first:
acquire.cpp
// VK_EXT_acquire_drm_display — hand Vulkan the drm fd you opened/leased:
int drmFd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
// (or drmModeGetLease on a leased fd from logind/X)
phys.acquireDrmDisplayEXT(drmFd, display);
// VK_EXT_direct_mode_display — release it back on exit:
dev.releaseDisplayEXT(display);Without VK_EXT_acquire_drm_display you can only use a display no one
else owns — on a framebuffer-console-only box that often “just works”,
on a desktop you’ll want the DRM lease path (drmModeCreateLease).
Why bother
wins.txt
compositor removed → -1 copy or -1 frame of latency
predictable pacing → no compositor scheduling jitter
fullscreen-only → perfect for walls, kiosks, signage, VR runtimes
plane control → alpha/positioning for overlays done in hardwarePractical rules
VK_KHR_displaygives exclusive control — your app IS the compositor: you must handle mode-setting, power states and cleanup yourself (crash = black screen until re-acquired).- Check
planeReorderPossible/supportedDstPosition: some planes are fullscreen-only — can’t do multi-window layouts on a single display plane. alphaMode+planeStackIndexlet you stack multiple surfaces (video under UI overlay) entirely in hardware — one plane each.- Not all drivers support it — NVIDIA + AMD/amdgpu DRM do; check
vkEnumerateInstanceExtensionPropertiesforVK_KHR_displaybefore designing for it. llvmpipe/lavapipe works for testing. - Combine with
VK_EXT_present_timingfor true end-to-end latency — with direct scanout the “presented” timestamp is real photons, not a compositor’s queue admission.
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow