vksdl is a C++20 wrapper library for Vulkan 1.3 and SDL3 that eliminates the repetitive setup code — instance creation, device selection, swapchain management, synchronization, pipeline construction — while leaving the actual rendering entirely in your hands.
One #include. Raw VkCommandBuffer inside. Full escape hatches everywhere.
Vulkan is explicit by design, and that is its strength. But a significant portion of any Vulkan application is pure ceremony: code that has exactly one correct answer. vksdl wraps that ceremony and leaves the real decisions to you.
- ~17,000 lines of C++20 across 56 public headers
- 43 tests, 20 working examples
- Vulkan 1.3 core: dynamic rendering, synchronization2, timeline semaphores
- Zero per-frame allocations in the hot path
- SDL3 windowing: Windows, Linux, and macOS from a single codebase
- No legacy render passes, no compatibility mode
- Vulkan SDK 1.3+
- CMake 3.21+
- C++20 compiler (GCC 14+, Clang 18+, MSVC 2022+)
git clone --recursive https://github.com/MrMartyK/vksdl.git
cd vksdl
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
cmake --build build
cd build && ctest --output-on-failureAll dependencies (SDL3, VMA, stb, cgltf, tinyobjloader) are fetched automatically via the bundled vcpkg submodule.
Setup is roughly 15 lines. The rest is your Vulkan code.
auto app = vksdl::App::create().value();
auto window = app.createWindow("Triangle", 1280, 720).value();
auto instance = vksdl::InstanceBuilder{}.appName("tri")
.requireVulkan(1, 3).enableWindowSupport().build().value();
auto surface = vksdl::Surface::create(instance, window).value();
auto device = vksdl::DeviceBuilder(instance, surface)
.needSwapchain().needDynamicRendering().needSync2()
.preferDiscreteGpu().build().value();
auto swapchain = vksdl::SwapchainBuilder(device, surface)
.size(window.pixelSize()).build().value();
auto frames = vksdl::FrameSync::create(device, swapchain.imageCount()).value();
auto pipeline = vksdl::PipelineBuilder(device)
.vertexShader("shaders/triangle.vert.spv")
.fragmentShader("shaders/triangle.frag.spv")
.colorFormat(swapchain).build().value();The render loop is standard Vulkan. You own the command buffer.
auto [frame, img] = vksdl::acquireFrame(swapchain, frames, device, window).value();
vksdl::beginOneTimeCommands(frame.cmd);
vksdl::transitionToColorAttachment(frame.cmd, img.image);
// vkCmdBeginRendering, vkCmdDraw, vkCmdEndRendering — your code, your decisions
vksdl::transitionToPresent(frame.cmd, img.image);
vksdl::endCommands(frame.cmd);
vksdl::presentFrame(device, swapchain, window, frame, img,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);The full triangle example is 113 lines, including the render loop and resize handling. The raw Vulkan equivalent is 800+.
| vksdl handles | You write |
|---|---|
| Instance, validation, debug messenger | Nothing — one builder call |
| GPU selection, queue families, feature chains | needSwapchain(), needRayTracingPipeline() |
| Swapchain format/present mode, image views, resize | recreate() on window resize |
| Fences, semaphores, round-robin acquire | Nothing — acquireFrame() / presentFrame() |
| SPIR-V loading, pipeline layout, blend/cull defaults | Record commands, bind, draw |
| VMA allocation, typed buffer/image builders | Choose usage, upload data |
| BLAS/TLAS construction, SBT layout, RT pipeline | Trace rays, write shaders |
| Render graph: barriers, resource lifetime, toposort | Declare passes, record in callbacks |
Every RAII object exposes its raw Vulkan handle — vkDevice(), vkPipeline(), vkBuffer(), vkImage() — so you can drop to the Vulkan API directly anywhere vksdl does not cover your use case.
| Example | What It Demonstrates | Lines |
|---|---|---|
| triangle | Window, device, swapchain, pipeline, render loop | 113 |
| quad | Vertex/index buffers, VMA staged uploads | ~145 |
| compute | Compute shader, storage image, blit to swapchain | 129 |
| cube | 3D depth, uniform buffers, descriptor sets | ~200 |
| textured_cube | Texture loading, mipmaps, samplers | ~250 |
| multi_object | Dynamic UBOs, multiple descriptor sets, debug names | ~300 |
| msaa | MSAA with inline resolve, dynamic recreation | ~180 |
| model | glTF/OBJ loading, PBR materials, directional lighting | ~220 |
| rt_triangle | Minimal ray tracing: BLAS, TLAS, SBT, traceRays | ~300 |
| rt_spheres | Path tracer: 475 spheres, GGX, physical sky, DOF | 593 |
| deferred | 40-pass render graph: shadow, G-buffer, lighting, tonemap | 709 |
| pipeline_compiler | GPL fast-linking, async compile, pipeline feedback | 1339 |
8 more examples
Pipeline cache, timeline sync, dynamic state, descriptor pools, async transfer, unified layouts, shader reflection, and device fault diagnostics.
The rt_spheres example: 475 spheres with GGX materials, physical sky, and depth of field in 593 lines of C++.
Core types (60+)
All types are RAII, move-only, and return Result<T> by default. orThrow() is available as an escape hatch — it throws when exceptions are enabled and fail-fasts when they are disabled.
Initialization — App, InstanceBuilder, Surface, DeviceBuilder
Presentation — SwapchainBuilder, FrameSync, acquireFrame, presentFrame
Pipelines — PipelineBuilder, ComputePipelineBuilder, RTPipelineBuilder, PipelineCache
Resources — Buffer, Image, Sampler, DescriptorSetLayout, DescriptorPool
Ray Tracing — Blas, Tlas, ShaderBindingTable
Render Graph — RenderGraph, RenderPass, automatic barrier insertion, topological sort
Utilities — ShaderModule, TimelineSemaphore, QueryPool, DebugName
Wrap ceremony. Leave intent raw.
Ceremony is code with one correct answer: creating an instance, selecting a GPU, destroying objects in the right order. vksdl wraps that.
Intent is code where you make real choices: recording commands, choosing wait stages, structuring submissions. vksdl leaves that alone.
The test: if two experienced Vulkan developers would write the same boilerplate identically, vksdl should eliminate it. If they would write it differently, vksdl stays out of the way.
| Platform | Compiler | Status |
|---|---|---|
| Windows 11 | GCC 15.2 (MSYS2 MinGW) | Tested, RTX 3060 |
| Linux | GCC 14 / Clang 18 | Tested via CI |
| macOS | Clang (via SDL3) | Expected to work, not yet tested |
v0.12.0 — The core API is stable across 60+ wrapped types. The render graph and pipeline model are functional and tested but still evolving. See the changelog for release history.
Released under the Zlib License.
