A minimal, self-contained example demonstrating best practices for Vulkan development in a single file—no frameworks required.
A minimal, self-contained example demonstrating best practices for Vulkan development in a single file—no frameworks required.
This repository demonstrates contemporary Vulkan API usage patterns in two files: a reusable Vulkan framework header (src/vk_framework.h) and the sample itself (src/minimal_latest.cpp). The example showcases Vulkan 1.4 core functionality, implemented with current best practices. Therefore, Vulkan 1.4 is mandatory.
Note: This is not intended as a tutorial, but rather as a reference implementation demonstrating modern Vulkan development techniques.
This sample intentionally targets bleeding-edge Vulkan. Its whole reason to exist is to demonstrate what a modern Vulkan application looks like when it uses the newest features the ecosystem provides. That means you need a very recent driver and SDK to run it, and some readers will only be able to read the code rather than run it -- that is an accepted trade-off.
Vulkan 1.4 core is mandatory, both loader and device. The sample asserts this at startup.
Required device extensions (the build will fail to run on devices that lack any of these):
| Extension | Purpose |
|---|---|
VK_KHR_swapchain |
Window-system presentation. |
VK_KHR_unified_image_layouts |
Lets the sample use VK_IMAGE_LAYOUT_GENERAL for all attachments (color + depth + swapchain), removing every layout-transition barrier except present. |
VK_EXT_descriptor_heap |
Bindless sampler + resource heaps in place of descriptor sets / pools / layouts. |
VK_KHR_shader_untyped_pointers |
Required by VK_EXT_descriptor_heap. |
VK_EXT_shader_object |
No graphics VkPipeline; the graphics path is VkShaderEXT plus dynamic state. |
VK_EXT_extended_dynamic_state3 |
Blend / rasterization dynamic state for shader objects. |
VK_EXT_vertex_input_dynamic_state |
Dynamic vertex input for shader objects. |
Driver availability. At the time of writing VK_EXT_descriptor_heap is brand new: expect to need an NVIDIA R555-series driver or newer (possibly a Vulkan beta driver) to run this sample. AMD and Intel support for the full extension set is still emerging -- check vulkaninfo against the table above before filing build / run issues. If an extension is missing, the app will abort at startup with a clear "Required device extension not available" message.
What this sample deliberately does not teach. These topics are intentionally absent; if you need to learn them, look for a more traditional Vulkan tutorial.
VkPipeline / VkPipelineLayout (replaced by shader objects + per-frame vkCmdSet*EXT calls).VK_KHR_unified_image_layouts).For the swapchain sync model, the narrative walkthrough in doc/swapchain_restaurant.md is more approachable than a direct code read.
Maintenance cadence. Dependencies (GLM, VMA, Volk) track master so the sample stays current with the latest Vulkan headers -- see CMakeLists.txt. Expect periodic upstream sync to keep the build green. If you fork this for production, pin those GIT_TAGs to a release you have validated.
This sample application implements numerous Vulkan concepts and patterns:
VK_EXT_shader_object) for the graphics path -- no graphics VkPipeline at all; everything is a VkShaderEXT plus dynamic stateRenderTarget (color + depth) drawn into and shown via ImGui::Imagebuffer_reference) in shaders, backed by buffer device addressVK_EXT_descriptor_heap) for textures and samplers, replacing traditional descriptor sets/layouts/pools (ImGui still uses a small legacy descriptor pool)vkCmdPushDataEXT) for graphics (no pipeline layout) and push constants (vkCmdPushConstants2) for compute (traditional layout)vkCmdPipelineBarrier2)VK_EXT_shader_object).VkShaderEXT plus two fragment VkShaderEXT variants (specialization constant for useTexture true/false)VK_SHADER_CREATE_DESCRIPTOR_HEAP_BIT_EXT set on every shader; no descriptor set layouts, no push constant rangesvkCmdBindShadersEXT; unused stages (tess control / tess eval / geometry) explicitly bound to VK_NULL_HANDLE as required by specvkCmdSetVertexInputEXT, vkCmdSetCullMode, vkCmdSetPolygonModeEXT, vkCmdSetRasterizationSamplesEXT, vkCmdSetColorBlendEnableEXT / Equation / WriteMask, vkCmdSetDepthTestEnable/WriteEnable/CompareOp, etc.VkPipeline + VkPipelineLayout with a VkPushConstantRange). VK_EXT_shader_object supports compute too, but compute is intentionally kept on the traditional path here so the sample shows both styles side-by-side. Compute also doesn't benefit from shader objects' main wins (no dynamic state to make dynamic, single-shader pipeline so nothing to mix and match) -- see the comment block on createComputeShaderPipeline for the full rationale and a sketch of the shader-object equivalent.vkCmdBindShadersEXT, vkCmdBindVertexBuffers2, vkCmdBindSamplerHeapEXT, vkCmdBindResourceHeapEXTvkCmdUpdateBuffer against a BDA-addressed scene bufferWhen running the application, you'll see:
m_frameData, sized by frames-in-flight, not image count)RenderTarget (color + depth)VkShaderEXT per stage variant) and compute VkPipeline (traditional layout)vkCmdPushConstants2 + BDA)vkCmdUpdateBuffer)RenderTarget:vkCmdSetViewportWithCount, vkCmdSetVertexInputEXT, vkCmdSetCullMode, blend / depth / multisample setters, ...)vkCmdBind*HeapEXT)vkCmdBindShadersEXT); swap fragment shader between the two drawsvkCmdDraw calls with their per-draw vkCmdPushDataEXTRenderTarget quad and overlay UIvkQueueSubmit2: wait on acquireSemaphore, signal presentSemaphore + timeline value# Clone the repository
git clone https://github.com/nvpro-samples/vk_minimal_latest
cd vk_minimal_latest
# Configure and build
cmake -S . -B build
cmake --build build --config Release
Running (Windows):
build\Release\vk_minimal_latest.exe
Running (Linux / macOS):
./build/vk_minimal_latest
Besides the Vulkan SDK, all the following dependencies are fetched automatically when configuring CMake.
Note:
VULKAN_SDK environment variable set.USE_SLANG=1) lives at the top of src/minimal_latest.cpp; you can override it without editing source by configuring with -DUSE_SLANG=OFF.Apache-2.0
The swapchain synchronization model is also explained as a restaurant (one cook, three plates, two waiters) in
doc/swapchain_restaurant.md, with a translation table at the end mapping every prop back to its Vulkan object.
Two counts that look similar but mean different things:
| Count | Default | Sized by | Purpose |
|---|---|---|---|
| Swapchain images | 3 | vkGetSwapchainImagesKHR |
Presentation parallelism (display can scan one out while GPU writes the next). |
| Frames in flight | 2 | application choice | CPU parallelism (how far the CPU is allowed to race ahead of the GPU). |
Going higher than 2 frames in flight rarely helps — it just buys extra command buffers and one more frame of input lag. The conventional pairing is 3 swapchain images + 2 frames in flight, which this sample uses.
vkAcquireNextImageKHR may return swapchain images out of order (especially with MAILBOX present mode), so resources are tied to wh
No open issues yet, or sync has not completed.