How DXVK Translates DirectX 9, 10, and 11 to Vulkan

DXVK is a high-performance translation layer that converts Microsoft DirectX 9, 10, and 11 API calls into Vulkan commands in real time. Typically utilized inside compatibility layers like Wine or Valve's Proton, it replaces the native Windows dynamic link libraries (d3d9.dll, d3d11.dll, dxgi.dll) to allow non-Windows platforms like Linux to execute Windows games with near-native performance. DXVK achieves this efficiency through just-in-time shader compilation, asynchronous pipeline generation, thin abstraction layers, and low-overhead memory management.

API Interception and Translation Architecture

When a Windows-based game executes on Linux via Wine or Proton, it makes calls to DirectX runtime libraries. DXVK provides drop-in replacement dynamic libraries that intercept these API calls before they reach the operating system.

Instead of translating DirectX calls into intermediate software abstractions, DXVK directly transforms the high-level states and commands of Direct3D into low-level Vulkan commands. Because Vulkan is an explicit, modern graphics API designed to interact closely with physical GPU hardware, it provides the low-level controls necessary to emulate Direct3D behaviors with minimal CPU overhead.

DXBC to SPIR-V Shader Translation

DirectX 9, 10, and 11 games do not ship raw HLSL (High-Level Shader Language) code; they ship pre-compiled DirectX Bytecode (DXBC). Vulkan cannot execute DXBC directly and instead consumes SPIR-V, a standardized intermediate representation.

DXVK incorporates an internal bytecode compiler that parses DXBC tokens and translates them into equivalent SPIR-V instructions at runtime:

  • Instruction Mapping: Most arithmetic, texture sampling, and control flow instructions in DXBC have direct 1:1 or near-equivalent equivalents in SPIR-V.
  • Resource Binding: Direct3D binds resources (constant buffers, textures, samplers) using register slots (e.g., b0, t0, s0). DXVK maps these registers to Vulkan descriptor sets and bindings.
  • Register Flattening and Emulation: Where architectural mismatches occur—such as Direct3D 9's specific coordinate spaces or clip-plane handling—DXVK injects small math operations into the generated SPIR-V to match the expected Direct3D visual output.

Overcoming Pipeline State Mismatches

One of the largest architectural hurdles between Direct3D 11 and Vulkan is state management:

  • Direct3D 11 uses a mutable state model, allowing games to swap out individual rasterizer, blend, or depth-stencil states dynamically mid-frame.
  • Vulkan uses an immutable Pipeline State Object (PSO) model, requiring all shaders, blend modes, rasterizer states, and render pass formats to be compiled together into a single pipeline object before drawing.

To reconcile this without causing performance freezes (shader stutter), DXVK uses several techniques:

  1. State Tracking: DXVK tracks the active state flags of the Direct3D pipeline. When a draw command occurs, it computes a hash representing the current combined state.
  2. Pipeline Caching: If a pipeline with that hash exists in memory, DXVK binds it immediately. If not, it compiles the pipeline on demand.
  3. The DXVK State Cache: DXVK writes seen state combinations to a file on disk. On subsequent launches, DXVK pre-compiles these pipelines before the game needs them, eliminating in-game stutter.
  4. Vulkan Graphics Pipeline Library (VK_EXT_graphics_pipeline_library): On drivers that support it, DXVK decouples shader compilation from final state compilation. Shaders can be compiled into parts ahead of time and quickly linked with runtime state data, virtually removing pipeline compilation hitches during gameplay.

Dedicated Worker Threads and Command Submission

Direct3D 11 games often assume the driver manages background threading, or they rely on a single render thread issuing drawing commands. Vulkan, by contrast, relies on explicit command buffer recording and queue submissions.

To avoid blocking the game’s main simulation thread, DXVK offloads execution through an internal submission queue:

  • The calling thread writes high-level Direct3D commands into a lock-free ring buffer.
  • A dedicated DXVK worker thread consumes this buffer, performs the heavy lifting of state translation, and records native Vulkan command buffers (VkCommandBuffer).
  • Once recorded, the commands are submitted to the GPU via Vulkan queues (vkQueueSubmit).

This command-stream multithreading model decouples the game’s frame execution from Vulkan API submission overhead.

Memory and Resource Sub-Allocation

Vulkan requires manual GPU memory allocation and has strict limits on the total number of individual allocations a program can make (often between 4,096 and 65,536). Direct3D 9 and 11 abstract this entirely, allocating buffers and textures on demand.

DXVK manages this through custom sub-allocators:

  • DXVK allocates large chunks of device memory (typically 256 MiB blocks) using Vulkan’s memory management routines.
  • Game resources (vertex buffers, index buffers, dynamic textures) are sub-allocated from these large memory pools.
  • For dynamic resources that update every frame (using Direct3D D3D11_MAP_WRITE_DISCARD), DXVK dynamically reallocates memory ranges within its staging pools to prevent CPU-GPU synchronization stalls.