Direct3D 12 Memory Allocator
Quick start

Project setup and initialization

This is a small, standalone C++ library. It consists of a pair of 2 files: "D3D12MemAlloc.h" header file with public interface and "D3D12MemAlloc.cpp" with internal implementation. The only external dependencies are WinAPI, Direct3D 12, and parts of C/C++ standard library (but STL containers, exceptions, or RTTI are not used).

The library is developed and tested using Microsoft Visual Studio 2019, but it should work with other compilers as well. It is designed for 64-bit code.

To use the library in your project:

(1.) Copy files D3D12MemAlloc.cpp, D3D12MemAlloc.h to your project.

(2.) Make D3D12MemAlloc.cpp compiling as part of the project, as C++ code.

(3.) Include library header in each CPP file that needs to use the library.

(4.) Right after you created ID3D12Device, fill D3D12MA::ALLOCATOR_DESC structure and call function D3D12MA::CreateAllocator to create the main D3D12MA::Allocator object.

Please note that all symbols of the library are declared inside D3D12MA namespace.

IDXGIAdapter* adapter = (...)
ID3D12Device* device = (...)
D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
allocatorDesc.pDevice = device;
allocatorDesc.pAdapter = adapter;
D3D12MA::Allocator* allocator;
HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator);
Represents main object of this library initialized for particular ID3D12Device.
Definition: D3D12MemAlloc.h:1067
D3D12MA_API HRESULT CreateAllocator(const ALLOCATOR_DESC *pDesc, Allocator **ppAllocator)
Creates new main D3D12MA::Allocator object and returns it through ppAllocator.
Parameters of created Allocator object. To be used with CreateAllocator().
Definition: D3D12MemAlloc.h:1028
IDXGIAdapter * pAdapter
Definition: D3D12MemAlloc.h:1054
ID3D12Device * pDevice
Definition: D3D12MemAlloc.h:1036

(5.) Right before destroying the D3D12 device, destroy the allocator object.

Objects of this library must be destroyed by calling Release method. They are somewhat compatible with COM: they implement IUnknown interface with its virtual methods: AddRef, Release, QueryInterface, and they are reference-counted internally. You can use smart pointers designed for COM with objects of this library - e.g. CComPtr or Microsoft::WRL::ComPtr. The reference counter is thread-safe. QueryInterface method supports only IUnknown, as classes of this library don't define their own GUIDs.

allocator->Release();

Creating resources

To use the library for creating resources (textures and buffers), call method D3D12MA::Allocator::CreateResource in the place where you would previously call ID3D12Device::CreateCommittedResource.

The function has similar syntax, but it expects structure D3D12MA::ALLOCATION_DESC to be passed along with D3D12_RESOURCE_DESC and other parameters for created resource. This structure describes parameters of the desired memory allocation, including choice of D3D12_HEAP_TYPE.

The function also returns a new object of type D3D12MA::Allocation, created along with usual ID3D12Resource. It represents allocated memory and can be queried for size, offset, ID3D12Resource, and ID3D12Heap if needed.

D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
resourceDesc.Alignment = 0;
resourceDesc.Width = 1024;
resourceDesc.Height = 1024;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.SampleDesc.Quality = 0;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
D3D12MA::ALLOCATION_DESC allocationDesc = {};
allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
D3D12Resource* resource;
D3D12MA::Allocation* allocation;
HRESULT hr = allocator->CreateResource(
&allocationDesc,
&resourceDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
NULL,
&allocation,
IID_PPV_ARGS(&resource));
Represents single memory allocation.
Definition: D3D12MemAlloc.h:461
HRESULT CreateResource(const ALLOCATION_DESC *pAllocDesc, const D3D12_RESOURCE_DESC *pResourceDesc, D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE *pOptimizedClearValue, Allocation **ppAllocation, REFIID riidResource, void **ppvResource)
Allocates memory and creates a D3D12 resource (buffer or texture). This is the main allocation functi...
Parameters of created D3D12MA::Allocation object. To be used with Allocator::CreateResource.
Definition: D3D12MemAlloc.h:277
D3D12_HEAP_TYPE HeapType
The type of memory heap where the new allocation should be placed.
Definition: D3D12MemAlloc.h:286

You need to remember both resource and allocation objects and destroy them separately when no longer needed.

allocation->Release();
resource->Release();

The advantage of using the allocator instead of creating committed resource, and the main purpose of this library, is that it can decide to allocate bigger memory heap internally using ID3D12Device::CreateHeap and place multiple resources in it, at different offsets, using ID3D12Device::CreatePlacedResource. The library manages its own collection of allocated memory blocks (heaps) and remembers which parts of them are occupied and which parts are free to be used for new resources.

It is important to remember that resources created as placed don't have their memory initialized to zeros, but may contain garbage data, so they need to be fully initialized before usage, e.g. using Clear (ClearRenderTargetView), Discard (DiscardResource), or copy (CopyResource).

The library also automatically handles resource heap tier. When D3D12_FEATURE_DATA_D3D12_OPTIONS::ResourceHeapTier equals D3D12_RESOURCE_HEAP_TIER_1, resources of 3 types: buffers, textures that are render targets or depth-stencil, and other textures must be kept in separate heaps. When D3D12_RESOURCE_HEAP_TIER_2, they can be kept together. By using this library, you don't need to handle this manually.

Mapping memory

The process of getting regular CPU-side pointer to the memory of a resource in Direct3D is called "mapping". There are rules and restrictions to this process, as described in D3D12 documentation of ID3D12Resource::Map method.

Mapping happens on the level of particular resources, not entire memory heaps, and so it is out of scope of this library. Just as the documentation of the Map function says:

  • Returned pointer refers to data of particular subresource, not entire memory heap.
  • You can map same resource multiple times. It is ref-counted internally.
  • Mapping is thread-safe.
  • Unmapping is not required before resource destruction.
  • Unmapping may not be required before using written data - some heap types on some platforms support resources persistently mapped.

When using this library, you can map and use your resources normally without considering whether they are created as committed resources or placed resources in one large heap.

Example for buffer created and filled in UPLOAD heap type:

const UINT64 bufSize = 65536;
const float* bufData = (...);
D3D12_RESOURCE_DESC resourceDesc = {};
resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourceDesc.Alignment = 0;
resourceDesc.Width = bufSize;
resourceDesc.Height = 1;
resourceDesc.DepthOrArraySize = 1;
resourceDesc.MipLevels = 1;
resourceDesc.Format = DXGI_FORMAT_UNKNOWN;
resourceDesc.SampleDesc.Count = 1;
resourceDesc.SampleDesc.Quality = 0;
resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
D3D12MA::ALLOCATION_DESC allocationDesc = {};
allocationDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
D3D12Resource* resource;
D3D12MA::Allocation* allocation;
HRESULT hr = allocator->CreateResource(
&allocationDesc,
&resourceDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
NULL,
&allocation,
IID_PPV_ARGS(&resource));
void* mappedPtr;
hr = resource->Map(0, NULL, &mappedPtr);
memcpy(mappedPtr, bufData, bufSize);
resource->Unmap(0, NULL);