Tutorial 85: Vulkan Backend: Setup and Quirks

CNA — C++ XNA 4.0 reimplementation

Why Vulkan?

The Vulkan backend offers the highest performance of any CNA backend, and is CNA's second-most mature backend overall (after EasyGL) — all five stock effects are implemented and pixel-tested, with explicit GPU control, lower driver overhead, and superior multi-threading potential.

Required: Vulkan SDK

# Ubuntu/Debian
sudo apt install libvulkan-dev vulkan-validationlayers spirv-tools glslc

# Arch Linux
sudo pacman -S vulkan-headers vulkan-validation-layers shaderc

# Windows: Download LunarG Vulkan SDK from https://vulkan.lunarg.com
# and set VULKAN_SDK environment variable

Enabling Vulkan backend in CMake

cmake -S . -B build-vulkan \
  -DCNA_GRAPHICS_BACKEND=VULKAN \
  -DCMAKE_BUILD_TYPE=Release
cmake --build build-vulkan -j$(nproc)

Validation layers for debugging

Always enable validation layers during development. They catch API misuse, resource leaks, and synchronization errors.

# Enable validation layers at runtime via environment variable:
export VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation
./build-vulkan/MyGame

# Or in CMake debug builds, CNA automatically enables validation:
cmake -S . -B build-vulkan-dbg \
  -DCNA_GRAPHICS_BACKEND=VULKAN \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCNA_VULKAN_VALIDATION=ON

Differences from EasyGL (SPIR-V shaders, explicit sync)

The Vulkan backend uses SPIR-V shaders compiled from GLSL, whereas EasyGL uses GLSL source directly. CNA compiles shaders at build time using glslc. Pipeline state (blend, rasterizer, depth) is baked into Vulkan PSO objects rather than set dynamically. This means state changes are slightly more expensive on Vulkan but draw calls are faster. CNA's Effect API hides this difference — you use the same effect->Apply() call regardless of backend.

Known limitations

  • Compute shaders: not exposed through the XNA API surface (extension point planned)
  • RasterizerState.DepthBias: does not take effect — the one open Vulkan pixel-test failure
  • RenderTarget2D with MSAA: supported on Vulkan but resolve pass is manual — use ResolveRenderTarget() explicitly
  • macOS/MoltenVK: not a supported target. CNA has no macOS toolchain or CI.

Vulkan on mobile

Android supports Vulkan from API level 24 (Android 7.0). Add uses-feature android:name="android.hardware.vulkan.level" android:version="0" to AndroidManifest.xml. CNA's Vulkan backend is not yet validated on Android — use EASYGL for production Android builds.

# CMakeLists.txt: enable Vulkan backend
cmake_minimum_required(VERSION 3.20)
project(MyGame CXX)
set(CMAKE_CXX_STANDARD 23)

find_package(Vulkan REQUIRED)
find_package(CNA REQUIRED)

add_executable(MyGame main.cpp)
target_link_libraries(MyGame PRIVATE CNA::CNA Vulkan::Vulkan)
target_compile_definitions(MyGame PRIVATE CNA_BACKEND_VULKAN)

SPIR-V compilation with glslc:

# Compile vertex shader
glslc shaders/basic.vert -o shaders/basic.vert.spv

# Compile fragment shader
glslc shaders/basic.frag -o shaders/basic.frag.spv

# CNA bundles pre-compiled SPIR-V for built-in effects (BasicEffect, etc.)
# Custom effects: place .spv files alongside your executable