D3D12_HEAP_TYPE_DEFAULT vs UPLOAD in DirectX 12

In DirectX 12, memory allocation is explicitly managed by the developer, with D3D12_HEAP_TYPE_DEFAULT and D3D12_HEAP_TYPE_UPLOAD representing two foundational memory configurations for resource data flow. While the default heap is optimized exclusively for maximum GPU performance with no direct CPU visibility, the upload heap acts as a bridge, enabling the CPU to efficiently write data that the GPU can consume. Understanding the architectural differences, access patterns, and typical use cases of these two heaps is essential for avoiding performance bottlenecks and pipeline stalls in modern graphics programming.

Memory Location and Architecture

  • D3D12_HEAP_TYPE_DEFAULT: This heap resides in GPU-local physical memory (VRAM) on discrete graphics cards. It is physically closer to the GPU compute cores and memory controllers, providing the highest possible bandwidth and lowest latency for GPU read and write operations.
  • D3D12_HEAP_TYPE_UPLOAD: On systems with discrete GPUs, this heap typically resides in system RAM or in a specialized CPU-visible VRAM aperture (via Resizable BAR / Smart Access Memory). It is physically accessible to the CPU host controller and visible to the GPU over the PCIe bus.

CPU and GPU Access Permissions

The primary technical divide between these heaps is how the CPU and GPU are permitted to interact with the underlying memory:

Feature D3D12_HEAP_TYPE_DEFAULT D3D12_HEAP_TYPE_UPLOAD
CPU Read Not allowed (cannot be mapped) Allowed, but extremely slow (uncached)
CPU Write Not allowed (cannot be mapped) Optimized (Write-Combined memory)
GPU Read Optimized (Full VRAM bandwidth) Slower (Limited by PCIe bus bandwidth)
GPU Write Optimized (Render Targets, UAVs) Not recommended / heavily constrained

Because D3D12_HEAP_TYPE_DEFAULT cannot be mapped into CPU address space, any attempt to call ID3D12Resource::Map on a default resource will fail.

Conversely, D3D12_HEAP_TYPE_UPLOAD uses Write-Combined (WC) CPU caching semantics. Write-combining optimizes sequential CPU writes by buffering them into cache lines before sending them to the bus, but it bypasses CPU caches. Consequently, reading from an upload heap with the CPU causes massive performance penalties due to cache misses and bus roundtrips.

Typical Use Cases

When to Use D3D12_HEAP_TYPE_DEFAULT

Default heaps should be used for any resource that remains on the GPU for multiple frames or requires intensive GPU-side read/write bandwidth:

  • Static vertex and index buffers
  • Textures and mipmap chains
  • Render Targets and Depth-Stencil buffers
  • Unordered Access Views (UAVs) used in compute shaders

When to Use D3D12_HEAP_TYPE_UPLOAD

Upload heaps are designed for transient data or resources that must be updated frequently by the CPU:

  • Staging buffers used to transfer assets (textures, meshes) from system RAM to default GPU memory
  • Constant Buffers (CBVs) that are updated once per frame or once per draw call
  • Dynamic vertex buffers for procedural or immediate-mode rendering (e.g., UI overlays, debug lines)

Common Transfer Workflow

Because textures and static buffers achieve the best performance inside D3D12_HEAP_TYPE_DEFAULT, but the CPU cannot write to default memory directly, DirectX 12 uses a two-step staging pattern:

  1. Allocate an Intermediate Resource: Create an upload buffer using D3D12_HEAP_TYPE_UPLOAD.
  2. CPU Population: Map the upload buffer, write source data from system memory, and unmap (or leave persistently mapped).
  3. GPU Copy: Record a copy command on a direct or copy command list using CopyResource or CopyBufferRegion to move the data from the upload resource to the target resource in D3D12_HEAP_TYPE_DEFAULT.
  4. Transition Barrier: Execute a resource barrier transitioning the default resource from D3D12_RESOURCE_STATE_COPY_DEST to its intended usage state (e.g., D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE).
  5. Reclaim Staging Memory: Once the GPU completes the copy command list (tracked via fences), the upload buffer can be freed or reused.