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:
- Allocate an Intermediate Resource: Create an upload
buffer using
D3D12_HEAP_TYPE_UPLOAD. - CPU Population: Map the upload buffer, write source data from system memory, and unmap (or leave persistently mapped).
- GPU Copy: Record a copy command on a direct or copy
command list using
CopyResourceorCopyBufferRegionto move the data from the upload resource to the target resource inD3D12_HEAP_TYPE_DEFAULT. - Transition Barrier: Execute a resource barrier
transitioning the default resource from
D3D12_RESOURCE_STATE_COPY_DESTto its intended usage state (e.g.,D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE). - Reclaim Staging Memory: Once the GPU completes the copy command list (tracked via fences), the upload buffer can be freed or reused.