Prototype Direct3D 12 bindless renderers quickly with a modern, lightweight API and low overhead.
Ldx12 is a C++20 static library that removes repetitive Direct3D 12 setup while keeping rendering explicit. It follows modern design patterns through bindless resources, typed generational handles, reusable command buffers and GPU-safe deferred destruction.
Its philosophy is conceptually inspired by lightweightvk: a small, modern and bindless-oriented graphics API. Ldx12 applies that spirit to Direct3D 12 through an independent design and implementation.
It is intended for graphics experiments, tools and renderer prototypes—not as a full game engine.
Initialize Ldx12 from an existing Win32 window (hwnd):
#include "Ldx12/Ldx12.hpp"
using namespace ldx12;
ContextDesc contextDesc{};
SwapchainDesc swapchainDesc{};
swapchainDesc.window = MakeWin32WindowHandle( hwnd );
swapchainDesc.width = 1280;
swapchainDesc.height = 720;
swapchainDesc.vsync = true;
DeviceManager& manager = DeviceManager::Initialize( contextDesc, swapchainDesc );
RenderDevice& device = *manager.GetRenderDevice();With the triangle pipeline, render pass and framebuffer prepared, record and submit the draw:
CommandBuffer& commands = device.AcquireCommandBuffer();
commands.CmdBeginRendering( renderPass, framebuffer );
commands.CmdBindRenderPipeline( pipeline );
commands.CmdDraw( 3 );
commands.CmdEndRendering();
device.Submit( commands, backbuffer );- Compact command recording and explicit submission.
- Bindless CBV, SRV and UAV resource access.
- Automatic resource-state transitions and command-list reuse.
- Runtime HLSL compilation with Shader Model 6.6.
- Typed resource handles instead of exposed ownership.
- Optional access to native D3D12 objects when required.
With conventional descriptor tables, selecting a texture and a buffer can look like this (illustrative native D3D12):
commandList->SetGraphicsRootDescriptorTable( 0, textureDescriptor );
commandList->SetGraphicsRootDescriptorTable( 1, bufferDescriptor );The shader uses fixed registers matched by the root signature. Here the sampler is configured as a static sampler at s0:
Texture2D<float4> texture : register(t0);
StructuredBuffer<float4> colors : register(t1);
SamplerState linearClamp : register(s0);
float4 PSMain(float2 uv : TEXCOORD0) : SV_Target0
{
return texture.Sample(linearClamp, uv) * colors[0];
}With Ldx12, pass their indices instead. Here texture is an existing sampled texture and buffer is a structured buffer of float4 colors:
const uint32_t indices[] =
{
device.GetBindlessIndex( texture ),
device.GetBindlessIndex( buffer )
};
commands.CmdPushConstants( indices, sizeof( indices ) );The shader reads both directly from the shared heap:
cbuffer Resources : register(b0) { uint textureIndex; uint bufferIndex; }
float4 PSMain(float2 uv : TEXCOORD0) : SV_Target0
{
Texture2D<float4> texture = ResourceDescriptorHeap[textureIndex];
StructuredBuffer<float4> colors = ResourceDescriptorHeap[bufferIndex];
SamplerState linearClamp = SamplerDescriptorHeap[0]; // Built into Ldx12.
return texture.Sample(linearClamp, uv) * colors[0];
}Changing either resource means changing an index, not binding another resource table. Ldx12 configures the heaps and root signature for you. The snippets assume a prepared render pass, pipeline and resource states; bindless does not remove draws or synchronization.
Ldx12 began inside IFNITY and later became a standalone project.
Ldx12 is available under the MIT License. Third-party attribution is documented in THIRD_PARTY_NOTICES.md.
Requirements: Windows 10/11, Visual Studio 2022 with Desktop development with C++, CMake 3.24+, the Windows SDK with DXC, and an x64 target.
Ldx12 0.4.0 is available on NuGet.org:
Install-Package Ldx12 -Version 0.4.0The package includes the public Ldx12 and Ldx12Utils headers and automatically selects their Debug or Release static libraries.
Run:
GenerateSolution.batThen open build\Ldx12.sln. It contains the library, tests and numbered samples, with 01_Triangle as the startup project.
include(FetchContent)
FetchContent_Declare(
Ldx12
GIT_REPOSITORY https://github.com/alfonsmagd/LightDX12.git
GIT_TAG v0.4.0
)
FetchContent_MakeAvailable(Ldx12)
target_link_libraries(MyApplication PRIVATE Ldx12::Ldx12)The snippet targets the v0.4.0 release tag. The NuGet command above installs the matching 0.4.0 package.
When used as a subproject, Ldx12 builds only the core library by default. Installation with find_package is demonstrated in examples/InstalledLdx12.
CMake options
| Option | Top-level | As a subproject |
|---|---|---|
LDX12_BUILD_APP |
ON |
OFF |
LDX12_BUILD_EXAMPLES |
ON |
OFF |
LDX12_BUILD_TESTS |
ON |
OFF |
LDX12_BUILD_UTILS |
OFF |
OFF |
LDX12_INSTALL |
ON |
OFF |
- Win32
HWNDswapchains with VSync and tearing configuration. - Color and depth rendering, viewport, scissor, vertex/index buffers, instancing and indirect indexed draws.
- Typed handles for buffers, 2D/3D textures and swapchains.
- Buffer and texture uploads, 2D texture downloads and mip generation.
- Fixed or dynamic bindless descriptors and push constants.
- Submission batches, fences, waits and deferred GPU-safe destruction.
- Debug layer, GPU labels, optional PIX capture attachment and native D3D12 access.
- Optional
Ldx12::Utilsrenderer with indirectly batched cubes, spheres and colored arrows.
Normal rendering only requires Ldx12.hpp. Advanced integrations can include Ldx12Native.hpp and call GetNative() to obtain borrowed D3D12 objects.
Technical capacities
These are Ldx12 software capacities, not resources allocated immediately by the GPU.
Resource storage is a fixed contiguous array sized at compile time. Select one profile for both the library and its consumers:
| Profile define | Buffers + textures | Shared CBV/SRV/UAV | RTV | DSV | Tracked buffers/textures per command buffer |
|---|---|---|---|---|---|
LDX12_SMALL_RESOURCES |
256 | 256 | 16 | 4 | 16 per type |
LDX12_MEDIUM_RESOURCES |
1,024 | 1,024 | 32 | 8 | 64 per type |
LDX12_HIGH_RESOURCES (default) |
4,096 | 4,096 | 64 | 12 | 256 per type |
With CMake, configure -DLDX12_RESOURCE_PROFILE=SMALL, MEDIUM or HIGH. The Ldx12::Ldx12 target propagates the selected define, including installed packages. For manual builds, use the same profile define in every library and application translation unit. Selecting more than one profile is a compilation error.
CBV, SRV and UAV descriptors share one heap. Index 0 is invalid, indices 1-25 are predefined and the dynamic portion starts at index 26: 230 slots for SMALL, 998 for MEDIUM and 4,070 for HIGH. ContextDesc can reduce heap capacities, but cannot exceed the compiled profile.
Buffers and textures share the resource limit. Imported textures and swapchain backbuffers also count. A sampled render target uses one resource slot, one SRV and one RTV; descriptor limits count all allocated views, including views retained for deferred GPU cleanup. All CPU resource and descriptor bookkeeping arrays have static storage; there are no resource pages.
The following maxima describe the default HIGH profile:
| API capacity | Maximum |
|---|---|
| Live buffers and textures | 4,096 resources combined |
| Live swapchains | 16 swapchains |
| Backbuffers per swapchain | 2-3 backbuffers |
| Color attachments per render pass | 8 attachments |
| Vertex input elements per pipeline | 16 elements |
| Acquired command buffers awaiting submission | 64 command buffers |
| Command buffers per submission batch | 4 command buffers |
| Texture states tracked per command buffer | 256 textures |
| Push constants | 62 × 32-bit values (248 bytes) |
The predefined descriptor positions are conveniences, not resource limits. Applications can use five CBV, five SRV and three UAV predefined positions; other resources receive dynamic descriptor indices.
Windows 10/11, Direct3D 12 feature level 12.0, Resource Binding Tier 3 and Shader Model 6.6.
No ray tracing or mesh shaders yet.
See the tests documentation, technical reference and CMake guide for more details.
| Sample |
|---|
1. Triangle![]() |
2. ViewportScissor![]() |
3. CBSRVCubes![]() |
5. Z-buffer + MSAA x4![]() |
6. TexturedCube![]() |
7. TextureSamplers![]() |
8. Ldx12 + Dear ImGui![]() |
9. WorldGeometry![]() |
10. CubeMap![]() |
11. Texture2DArray![]() |
12. Transparency![]() |
| 13. CookbookChapter02 |
| 14. ImGuiDemoNative |
15. DepthPass (depth buffer)![]() |
19. MultiDrawIndirect glTF![]() |
20. TriplanarMapping![]() |
21. ConstantBufferRings![]() |















