NoGraphicsAPI: an experimental Vulkan 1.4 backend that removes descriptor sets and buffer objects
Minimal graphics API. Built on top of latest Vulkan extensions. As close as possibly to my "No Graphics API" blog post and the SIGGRAPH talk.
At a glance
- What is it?
- Sebastian Aaltonen's NoGraphicsAPI maps the ideas of his No Graphics API blog post onto Vulkan 1.4, replacing buffer objects, descriptor sets and pipeline layouts with GPU pointers, application-owned descriptor heaps and hazard barriers. The Vulkan backend is implemented and exercised by three examples; Metal is not implemented.
- Who is it for?
- Adopt NoGraphicsAPI if you are building a renderer around Vulkan 1.4, already comfortable with buffer device addresses and timeline semaphores, and want to see how far GPU pointers and application-owned descriptor heaps can go before writing your own abstraction.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 16, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
What NoGraphicsAPI removes, and who that is for
The project targets one specific complaint: a conventional graphics API spends a large amount of its surface area managing objects that exist only to describe memory. Buffer objects, descriptor sets, descriptor layouts, pipeline layouts and sampler objects all sit between the application and the GPU. NoGraphicsAPI asks what is left when those layers are deleted.
The README states the intent plainly. It is an experimental Vulkan 1.4 implementation of the ideas in Sebastian Aaltonen's No Graphics API blog post, exploring how much of a conventional graphics API disappears when shaders use 64-bit GPU pointers, texture and sampler descriptors live in application-owned GPU memory, and synchronization describes hazards instead of resource state.
The audience is narrow and technical. You need to be writing a renderer in C++20, you need to be willing to allocate and suballocate GPU memory yourself, and you need a device that exposes the extension set the README lists. This is not a library you drop into an existing engine to make it faster. It is a working reference for a design position, and the three example applications exist to exercise that position rather than to serve as a framework.
GPU pointers, descriptor heaps and hazard barriers in the Vulkan backend
The mechanism has three parts, and they reinforce each other.
First, memory. `create_gpu_heap()` returns a raw allocation with a GPU address and, for mapped memory, a CPU address. There are no public buffer objects and no internal suballocators. Commands consume `GpuRange {gpu, size}` directly, and shaders follow typed 64-bit pointers for vertex fetch and for arbitrary data structures. Texture and sampler descriptors are themselves just mapped GPU heaps: the application picks a slot, writes the descriptor through the CPU address, binds the GPU range, and passes a 32-bit index to the shader.
Second, binding. There is no `VkDescriptorSetLayout`, no `VkDescriptorPool`, no `VkDescriptorSet` and no `VkPipelineLayout` created anywhere in the backend. That is the point of the design, and the README says the backend creates them intentionally never. Instead, a shared C++/Slang root structure is copied per draw or dispatch with `vkCmdPushDataEXT`. Pointer fields in that root carry GPU addresses; ordinary values are copied as bytes. Because the copy happens immediately, the root does not need to outlive the call.
Third, synchronization. The public API exposes global stage and access barriers rather than per-resource transition lists, and normal textures stay in one unified layout. `VK_KHR_unified_image_layouts` is used when available to make ordinary texture access in `VK_IMAGE_LAYOUT_GENERAL` efficient. Submission is explicit and asynchronous: applications supply timeline points for reuse and deferred destruction, and submitted command buffers are one-shot.
The extensions that make this possible are listed in the README: `VK_EXT_descriptor_heap` for application-owned heaps and `vkCmdPushDataEXT`, `VK_KHR_device_address_commands` for address-based index, indirect and copy commands, `VK_KHR_shader_untyped_pointers` as the descriptor-heap SPIR-V prerequisite, and `VK_EXT_mesh_shader` for task and mesh pipelines. Vulkan 1.4 supplies buffer device addresses, timeline semaphores, dynamic rendering, synchronization2 and scalar block layout as core features.
The shared root ABI, and the 256-byte ceiling on push data
The most interesting constraint in the design is the root payload. Root structures are declared once and included by both C++ and Slang, with shared scalar, vector and matrix types coming from `<NoGraphicsAPIUtility/shader_types.h>`. On the CPU side, pointer fields receive GPU virtual addresses while ordinary values are copied directly. The Slang side declares the same structure as push-constant data and reads pointer and value fields identically.
The README is explicit about the limits. Root values must be trivially copyable, have a size divisible by four, and fit within 256 bytes and `DeviceCaps::max_push_data_size`. Anything larger has to be reached through a GPU pointer stored in the root. Shared structures use C layout, and roots containing matrices use row-major layout.
This is a real trade-off rather than a footnote. A 256-byte budget forces you to think about what actually varies per draw, and it means the root is a pointer table more often than it is a data block. The README also notes a deliberate divergence from the blog post: the blog describes GPU-resident, stage-specific roots, while this implementation gives graphics stages one CPU-supplied root. That is a simplification the project acknowledges rather than hides.
Building NoGraphicsAPI from source and drawing a triangle
The README lists hard prerequisites before any command runs. You need CMake 3.24 or newer, a C++20 compiler, and Vulkan SDK headers and development libraries version 1.4.357 or newer. The target must be little-endian x86-64. The optional utility math target additionally requires AVX2 and FMA. The device must expose the descriptor-heap, device-address-command, untyped-pointer and mesh extensions, plus coherent CPU-visible GPU memory through a PCIe BAR on a discrete GPU or UMA on an integrated GPU. Slang 2026.14.1 or newer and SPIRV-Tools 2026.3 or newer are needed when building the examples. MinGW, 32-bit x86 and ARM are not supported.
The utility implementation ships in-tree, so configuration has no Git or network dependency. The default configuration builds NoGraphicsAPI and its companion utility library, and examples and tests are opt-in so that embedding the library with `add_subdirectory()` does not pull in development targets.
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
cmake --install build --prefix path/to/installAfter installation, the same prefix provides two independent packages, so a consuming project can find either one on its own:
find_package(NoGraphicsAPI CONFIG REQUIRED)
find_package(NoGraphicsAPIUtility CONFIG REQUIRED)For a first real use, the repository ships three example applications under `examples/`: `triangle`, `cube` and `deferred_renderer`. Because examples are opt-in, you enable them at configure time rather than getting them by default. The `triangle` example is the smallest complete path through the API: it allocates a heap, builds a root structure, and issues a draw. The README's own sketch of that flow shows a bump allocator producing a `GpuCpuRange<Vertex>`, the GPU address being placed into the root's pointer field, and `gpu::draw(commands, root, vertex_count)` consuming it. On the shader side the matching declaration is a push constant block, and the vertex is fetched by indexing the pointer directly. If the triangle renders, the extension set, the memory type and the Slang toolchain are all wired correctly.
Where the design breaks down, and where it is the wrong tool
The clearest limitation is platform coverage. Metal support is not implemented. The `docs/metal-porting.md` file records a proposed mapping and open issues rather than working code, so anyone who needs a second backend is reading a design document, not shipping a port.
The second limitation is the extension requirement itself. This is not a Vulkan 1.4 project that happens to use a few extensions; it depends on `VK_EXT_descriptor_heap`, `VK_KHR_device_address_commands`, `VK_KHR_shader_untyped_pointers` and `VK_EXT_mesh_shader`, and on drivers that implement them together. On hardware or drivers missing any of those, the backend cannot run at all. There is no fallback path described in the README.
The third is the absence of the conveniences you may already rely on. No buffer objects means no engine-side buffer abstraction to hook into. No descriptor sets means no bindless fallback that looks like the conventional model. No internal suballocator means memory strategy is your problem, and the optional utility library only offers application-side data and texture allocation policies. If your team is not prepared to own GPU memory management, this design will cost more than it saves.
Finally, the project is a prototype by its own description. The README calls it a low-level, thin Vulkan wrapper and an experimental implementation, and the design comparison document separates faithful mappings from Vulkan-driven differences and from features that remain outside the prototype. That last category is the one to read carefully before committing to anything.
How this differs from writing plain Vulkan 1.4
The obvious alternative is not another engine. It is plain Vulkan 1.4 with descriptor indexing and the same modern extensions, which is what most teams reaching for these features would write.
The difference is what each approach keeps. Plain Vulkan 1.4 with descriptor indexing keeps descriptor sets, but makes them large and long-lived, with bindless indexing inside them. You still create a `VkDescriptorSetLayout` and a `VkPipelineLayout`, and you still transition image layouts per resource or rely on `VK_IMAGE_LAYOUT_GENERAL` with careful synchronization. NoGraphicsAPI deletes the descriptor set and pipeline layout objects outright and replaces per-resource barriers with global stage and access barriers.
That is a meaningful difference in failure mode. With descriptor sets, a mismatched layout is a validation error at bind time. With application-owned descriptor heaps, a wrong slot index is a wrong descriptor, and the README's model expects the application to choose slots and write them through the CPU address. The API gives you less to get wrong structurally and less to catch you when you get it wrong semantically. Whether that trade is good depends entirely on how much you trust your own indexing code.
The second alternative is to read the blog post and the SIGGRAPH talk and build the abstraction yourself. NoGraphicsAPI is useful precisely because it is a concrete mapping of those ideas onto shipping Vulkan extensions, including the places where the mapping is imperfect. If you only need the argument, the blog is shorter. If you need to see which extensions actually carry the design, this repository is the artifact.
Licence, maintenance and what an upgrade costs
The repository is MIT licensed, which permits use, modification and redistribution provided the copyright notice and permission notice are retained. The README does not discuss patent implications, and `THIRD_PARTY_NOTICES.md` exists at the top level, so anyone redistributing a binary should read that file rather than assume the MIT text covers everything shipped. This is a description of the repository contents, not legal advice.
The repository is not archived, and the last push was on 2026-09-16. There are no releases retrieved, so there is no tagged version to pin against and no changelog to read between updates. That matters for upgrade cost: consuming this as a dependency means tracking a branch rather than a version, and the README's install flow ships both `NoGraphicsAPI` and `NoGraphicsAPIUtility` packages from the same prefix, so a change in the utility library's allocation policies can reach you through a package you did not intend to update.
The toolchain floor is the other ongoing cost. Vulkan SDK 1.4.357 or newer, Slang 2026.14.1 or newer and SPIRV-Tools 2026.3 or newer are all moving targets, and the extension set this backend depends on is recent enough that driver coverage will shift under you. Budget for re-validating the extension set on each new driver you support rather than treating it as a one-time check.
Editorial conclusion
Adopt NoGraphicsAPI if you are building a renderer around Vulkan 1.4, already comfortable with buffer device addresses and timeline semaphores, and want to see how far GPU pointers and application-owned descriptor heaps can go before writing your own abstraction. Do not adopt it as a production renderer: the README calls it a low-level thin Vulkan wrapper and an experimental implementation, Metal support is not implemented, and MinGW, 32-bit x86 and ARM targets are unsupported. Verify first that your driver exposes VK_EXT_descriptor_heap, VK_KHR_device_address_commands, VK_KHR_shader_untyped_pointers and VK_EXT_mesh_shader, and that your build environment has Vulkan SDK 1.4.357 or newer plus Slang 2026.14.1 and SPIRV-Tools 2026.3 for the examples.
Frequently asked questions
What is a graphics API, and what does NoGraphicsAPI replace it with?
A graphics API is the conventional layer of buffer objects, descriptor sets, pipeline layouts and resource-state transitions that sits between an application and the GPU. NoGraphicsAPI removes most of that layer: GPU pointers replace buffer objects and bindings, texture and sampler descriptors live in application-owned heaps, and barriers describe execution and memory hazards instead of resource state.
Should I use OpenGL or Vulkan, and where does NoGraphicsAPI fit?
The README does not compare OpenGL with Vulkan or position the project against either. It states only that NoGraphicsAPI is an experimental Vulkan 1.4 implementation built on recent extensions, so it is not an option for an OpenGL codebase.
Does anyone still use OpenGL, and is NoGraphicsAPI relevant to that question?
The README does not address OpenGL adoption or its user base. NoGraphicsAPI requires a Vulkan 1.4 loader and device, so it has no bearing on OpenGL projects.
How do I find out which graphics API my machine supports?
The README does not document a detection command. What it does specify is the requirement set: a Vulkan 1.4 loader and device exposing VK_EXT_descriptor_heap, VK_KHR_device_address_commands, VK_KHR_shader_untyped_pointers and VK_EXT_mesh_shader, plus their required buffer-device-address, synchronization and 16-bit/scalar-layout features.
Community notes