VanGFX is a C++20 library for getting inside a Direct3D renderer. It tells you about every draw, state change, resource and present, lets you replace or extend the game's shaders, captures the G-buffer, and reads buffers back from the GPU.
It works by patching the device's vtables directly, so there's no proxy DLL and no dependency on the game's load order. Every patch belongs to an object that restores it, and destroying the context puts the renderer back the way it was.
Getting a context
#include <vangfx/vangfx.hpp>
auto ctx = vangfx::ContextBuilder{}
.backend(vangfx::BackendKind::D3D11)
.d3d11_device(device)
.d3d11_swap_chain(swapChain)
.build()
.value();
ctx->on_draw([](vangfx::FrameContext&, vangfx::DrawEvent& ev) {
if (ev.vertex_count > 10'000) { /* a big mesh */ }
});
// runs after every pixel shader, without touching the original
vangfx::inject_ps_post(*ctx, "color.rgb = min(color.rgb, 1.0);");
VanGFX never goes looking for a device. Direct3D 11 and 12 have no global to find one in, so you pass it in from wherever you already have it: your own hook, a proxy, or the host that loaded you. BackendKind::Auto picks the backend from the Direct3D runtime that's loaded.
| Backend | Required | Optional |
|---|---|---|
| Direct3D 9 | d3d9_device | |
| Direct3D 11 | d3d11_device, d3d11_swap_chain | |
| Direct3D 12 | d3d12_device, d3d12_command_queue | d3d12_swap_chain, for present events |
Events
| Callback | Fires on |
|---|---|
on_draw | Every draw and dispatch, with vertex, index and instance counts |
on_render_target | Every render-target bind |
on_present | Every present or EndScene; set intercept to suppress it |
on_resource | Creation, update and release of textures, targets, depth buffers and buffers |
on_state_change | Blend, depth-stencil, rasterizer, sampler, topology, viewport and scissor changes |
on_query | Query begin, end and data reads |
on_device_lost, on_device_reset | Device loss and reset |
Changing shaders
replace_ps,replace_vsandreplace_effectcompile a shader and install it in place of the game's.inject_ps_postruns a snippet after the pixel shader's output, withcolorin scope.inject_ps_preruns one before texture sampling, with the texture coordinate in scope.ScopedShaderReplaceandScopedInjectionundo themselves when they go out of scope.ReplaceFilterlimits a change to draws with a given vertex count, render-target size, or any test you write.Uniformsgives your HLSL eightfloat4slots you can change live, plus time, frame delta and resolution.ShaderBuildercompiles HLSL throughd3dcompiler_47ordxcompiler, loaded at runtime, or takes precompiled DXBC or DXIL.
Direct3D 12 has no per-draw shader binding, only pipeline state objects that can't change once they exist. So on 12 you change shaders when a pipeline is created, through on_pso_create, which hands you the description first and honours any bytecode you swap into it.
Reading data back
- G-buffer capture. A
CaptureSessionclassifies each render target as albedo, normal, depth, specular, emissive, motion, ambient occlusion or back buffer from its format, size and slot, and writes it out as PNG, DDS or raw bytes. - Buffer readback. Vertex, index, constant, structured, raw and UAV buffers, by byte range, on all three backends.
- From a draw to its geometry. On Direct3D 12,
buffer_at()takes the GPU address in a draw's vertex or index buffer view and tells you which buffer and offset it points into. That's how you find out what a draw actually is.
Direct3D 12 extras
- Command-list lifecycle events: reset, close, submit, signal and wait.
- Pipeline creation for graphics, compute and mesh-shader pipelines, with the chance to replace the bytecode.
- Resource barrier batches, which you can suppress.
- A descriptor-heap registry and root-signature tracking.
- Root bindings captured per command list at draw time.
- A 64 KB upload ring that feeds injected constant buffers.
Requirements
- MSVC 19.29 or later with
/std:c++20. - Windows SDK 10.0.19041 or later for Direct3D 9 and 11; 10.0.20348 or later for Direct3D 12.
- Link
vangfx.libwith the Direct3D libraries for your backend. The prebuilt libraries use/MT.

