VanGFX logoVanGFX logo

VanGFX

One interception and shader API across Direct3D 9, 11 and 12.

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.

BackendRequiredOptional
Direct3D 9d3d9_device 
Direct3D 11d3d11_device, d3d11_swap_chain 
Direct3D 12d3d12_device, d3d12_command_queued3d12_swap_chain, for present events

Events

CallbackFires on
on_drawEvery draw and dispatch, with vertex, index and instance counts
on_render_targetEvery render-target bind
on_presentEvery present or EndScene; set intercept to suppress it
on_resourceCreation, update and release of textures, targets, depth buffers and buffers
on_state_changeBlend, depth-stencil, rasterizer, sampler, topology, viewport and scissor changes
on_queryQuery begin, end and data reads
on_device_lost, on_device_resetDevice loss and reset

Changing shaders

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

Direct3D 12 extras

Requirements