mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator.git
synced 2024-11-22 04:00:05 +00:00
1655 lines
67 KiB
C++
1655 lines
67 KiB
C++
//
|
|
// Copyright (c) 2019-2021 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
#pragma once
|
|
|
|
/** \mainpage D3D12 Memory Allocator
|
|
|
|
<b>Version 2.0.0-development</b> (2021-07-26)
|
|
|
|
Copyright (c) 2019-2021 Advanced Micro Devices, Inc. All rights reserved. \n
|
|
License: MIT
|
|
|
|
Documentation of all members: D3D12MemAlloc.h
|
|
|
|
\section main_table_of_contents Table of contents
|
|
|
|
- <b>User guide</b>
|
|
- \subpage quick_start
|
|
- [Project setup](@ref quick_start_project_setup)
|
|
- [Creating resources](@ref quick_start_creating_resources)
|
|
- [Mapping memory](@ref quick_start_mapping_memory)
|
|
- \subpage custom_pools
|
|
- \subpage resource_aliasing
|
|
- \subpage virtual_allocator
|
|
- \subpage configuration
|
|
- [Custom CPU memory allocator](@ref custom_memory_allocator)
|
|
- \subpage general_considerations
|
|
- [Thread safety](@ref general_considerations_thread_safety)
|
|
- [Future plans](@ref general_considerations_future_plans)
|
|
- [Features not supported](@ref general_considerations_features_not_supported)
|
|
|
|
\section main_see_also See also
|
|
|
|
- [Product page on GPUOpen](https://gpuopen.com/gaming-product/d3d12-memory-allocator/)
|
|
- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator)
|
|
*/
|
|
|
|
// If using this library on a platform different than Windows PC or want to use different version of DXGI,
|
|
// you should include D3D12-compatible headers before this library on your own and define this macro.
|
|
#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
|
|
#include <d3d12.h>
|
|
#include <dxgi1_4.h>
|
|
#endif
|
|
|
|
// Define this macro to 0 to disable usage of DXGI 1.4 (needed for IDXGIAdapter3 and query for memory budget).
|
|
#ifndef D3D12MA_DXGI_1_4
|
|
#ifdef __IDXGIAdapter3_INTERFACE_DEFINED__
|
|
#define D3D12MA_DXGI_1_4 1
|
|
#else
|
|
#define D3D12MA_DXGI_1_4 0
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
When defined to value other than 0, the library will try to use
|
|
D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT or D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT
|
|
for created textures when possible, which can save memory because some small textures
|
|
may get their alignment 4K and their size a multiply of 4K instead of 64K.
|
|
|
|
#define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 0
|
|
Disables small texture alignment.
|
|
#define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 1
|
|
Enables conservative algorithm that will use small alignment only for some textures
|
|
that are surely known to support it.
|
|
#define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 2
|
|
Enables query for small alignment to D3D12 (based on Microsoft sample) which will
|
|
enable small alignment for more textures, but will also generate D3D Debug Layer
|
|
error #721 on call to ID3D12Device::GetResourceAllocationInfo, which you should just
|
|
ignore.
|
|
*/
|
|
#ifndef D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT
|
|
#define D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT 1
|
|
#endif
|
|
|
|
/// \cond INTERNAL
|
|
|
|
#define D3D12MA_CLASS_NO_COPY(className) \
|
|
private: \
|
|
className(const className&) = delete; \
|
|
className(className&&) = delete; \
|
|
className& operator=(const className&) = delete; \
|
|
className& operator=(className&&) = delete;
|
|
|
|
// To be used with MAKE_HRESULT to define custom error codes.
|
|
#define FACILITY_D3D12MA 3542
|
|
|
|
/*
|
|
If providing your own implementation, you need to implement a subset of std::atomic.
|
|
*/
|
|
#if !defined(D3D12MA_ATOMIC_UINT32) || !defined(D3D12MA_ATOMIC_UINT64)
|
|
#include <atomic>
|
|
#endif
|
|
|
|
#ifndef D3D12MA_ATOMIC_UINT32
|
|
#define D3D12MA_ATOMIC_UINT32 std::atomic<UINT>
|
|
#endif
|
|
|
|
#ifndef D3D12MA_ATOMIC_UINT64
|
|
#define D3D12MA_ATOMIC_UINT64 std::atomic<UINT64>
|
|
#endif
|
|
|
|
#ifdef D3D12MA_EXPORTS
|
|
#define D3D12MA_API __declspec(dllexport)
|
|
#elif defined(D3D12MA_IMPORTS)
|
|
#define D3D12MA_API __declspec(dllimport)
|
|
#else
|
|
#define D3D12MA_API
|
|
#endif
|
|
|
|
namespace D3D12MA
|
|
{
|
|
class D3D12MA_API IUnknownImpl : public IUnknown
|
|
{
|
|
public:
|
|
virtual ~IUnknownImpl() = default;
|
|
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);
|
|
virtual ULONG STDMETHODCALLTYPE AddRef();
|
|
virtual ULONG STDMETHODCALLTYPE Release();
|
|
protected:
|
|
virtual void ReleaseThis() { delete this; }
|
|
private:
|
|
D3D12MA_ATOMIC_UINT32 m_RefCount = 1;
|
|
};
|
|
} // namespace D3D12MA
|
|
|
|
/// \endcond
|
|
|
|
namespace D3D12MA
|
|
{
|
|
|
|
/// \cond INTERNAL
|
|
class AllocatorPimpl;
|
|
class PoolPimpl;
|
|
class NormalBlock;
|
|
class BlockVector;
|
|
class CommittedAllocationList;
|
|
class JsonWriter;
|
|
class VirtualBlockPimpl;
|
|
/// \endcond
|
|
|
|
class Pool;
|
|
class Allocator;
|
|
struct StatInfo;
|
|
|
|
/// Pointer to custom callback function that allocates CPU memory.
|
|
using ALLOCATE_FUNC_PTR = void* (*)(size_t Size, size_t Alignment, void* pUserData);
|
|
/**
|
|
\brief Pointer to custom callback function that deallocates CPU memory.
|
|
|
|
`pMemory = null` should be accepted and ignored.
|
|
*/
|
|
using FREE_FUNC_PTR = void (*)(void* pMemory, void* pUserData);
|
|
|
|
/// Custom callbacks to CPU memory allocation functions.
|
|
struct ALLOCATION_CALLBACKS
|
|
{
|
|
/// %Allocation function.
|
|
ALLOCATE_FUNC_PTR pAllocate;
|
|
/// Dellocation function.
|
|
FREE_FUNC_PTR pFree;
|
|
/// Custom data that will be passed to allocation and deallocation functions as `pUserData` parameter.
|
|
void* pUserData;
|
|
};
|
|
|
|
/// \brief Bit flags to be used with ALLOCATION_DESC::Flags.
|
|
enum ALLOCATION_FLAGS
|
|
{
|
|
/// Zero
|
|
ALLOCATION_FLAG_NONE = 0,
|
|
|
|
/**
|
|
Set this flag if the allocation should have its own dedicated memory allocation (committed resource with implicit heap).
|
|
|
|
Use it for special, big resources, like fullscreen textures used as render targets.
|
|
*/
|
|
ALLOCATION_FLAG_COMMITTED = 0x1,
|
|
|
|
/**
|
|
Set this flag to only try to allocate from existing memory heaps and never create new such heap.
|
|
|
|
If new allocation cannot be placed in any of the existing heaps, allocation
|
|
fails with `E_OUTOFMEMORY` error.
|
|
|
|
You should not use D3D12MA::ALLOCATION_FLAG_COMMITTED and
|
|
D3D12MA::ALLOCATION_FLAG_NEVER_ALLOCATE at the same time. It makes no sense.
|
|
*/
|
|
ALLOCATION_FLAG_NEVER_ALLOCATE = 0x2,
|
|
|
|
/** Create allocation only if additional memory required for it, if any, won't exceed
|
|
memory budget. Otherwise return `E_OUTOFMEMORY`.
|
|
*/
|
|
ALLOCATION_FLAG_WITHIN_BUDGET = 0x4,
|
|
};
|
|
|
|
/// \brief Parameters of created D3D12MA::Allocation object. To be used with Allocator::CreateResource.
|
|
struct ALLOCATION_DESC
|
|
{
|
|
/// Flags.
|
|
ALLOCATION_FLAGS Flags;
|
|
/** \brief The type of memory heap where the new allocation should be placed.
|
|
|
|
It must be one of: `D3D12_HEAP_TYPE_DEFAULT`, `D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`.
|
|
|
|
When D3D12MA::ALLOCATION_DESC::CustomPool != NULL this member is ignored.
|
|
*/
|
|
D3D12_HEAP_TYPE HeapType;
|
|
/** \brief Additional heap flags to be used when allocating memory.
|
|
|
|
In most cases it can be 0.
|
|
|
|
- If you use D3D12MA::Allocator::CreateResource(), you don't need to care.
|
|
Necessary flag `D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`, `D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`,
|
|
or `D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES` is added automatically.
|
|
- If you use D3D12MA::Allocator::AllocateMemory(), you should specify one of those `ALLOW_ONLY` flags.
|
|
Except when you validate that D3D12MA::Allocator::GetD3D12Options()`.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_1` -
|
|
then you can leave it 0.
|
|
- You can specify additional flags if needed. Then the memory will always be allocated as
|
|
separate block using `D3D12Device::CreateCommittedResource` or `CreateHeap`, not as part of an existing larget block.
|
|
|
|
When D3D12MA::ALLOCATION_DESC::CustomPool != NULL this member is ignored.
|
|
*/
|
|
D3D12_HEAP_FLAGS ExtraHeapFlags;
|
|
/** \brief Custom pool to place the new resource in. Optional.
|
|
|
|
When not NULL, the resource will be created inside specified custom pool.
|
|
It will then never be created as committed.
|
|
*/
|
|
Pool* CustomPool;
|
|
};
|
|
|
|
/** \brief Represents single memory allocation.
|
|
|
|
It may be either implicit memory heap dedicated to a single resource or a
|
|
specific region of a bigger heap plus unique offset.
|
|
|
|
To create such object, fill structure D3D12MA::ALLOCATION_DESC and call function
|
|
Allocator::CreateResource.
|
|
|
|
The object remembers size and some other information.
|
|
To retrieve this information, use methods of this class.
|
|
|
|
The object also remembers `ID3D12Resource` and "owns" a reference to it,
|
|
so it calls `%Release()` on the resource when destroyed.
|
|
*/
|
|
class D3D12MA_API Allocation : public IUnknownImpl
|
|
{
|
|
public:
|
|
/** \brief Returns offset in bytes from the start of memory heap.
|
|
|
|
You usually don't need to use this offset. If you create a buffer or a texture together with the allocation using function
|
|
D3D12MA::Allocator::CreateResource, functions that operate on that resource refer to the beginning of the resource,
|
|
not entire memory heap.
|
|
|
|
If the Allocation represents committed resource with implicit heap, returns 0.
|
|
*/
|
|
UINT64 GetOffset() const;
|
|
|
|
/** \brief Returns size in bytes of the allocation.
|
|
|
|
- If you created a buffer or a texture together with the allocation using function D3D12MA::Allocator::CreateResource,
|
|
this is the size of the resource returned by `ID3D12Device::GetResourceAllocationInfo`.
|
|
- For allocations made out of bigger memory blocks, this also is the size of the memory region assigned exclusively to this allocation.
|
|
- For resources created as committed, this value may not be accurate. DirectX implementation may optimize memory usage internally
|
|
so that you may even observe regions of `ID3D12Resource::GetGPUVirtualAddress()` + Allocation::GetSize() to overlap in memory and still work correctly.
|
|
*/
|
|
UINT64 GetSize() const { return m_Size; }
|
|
|
|
/** \brief Returns D3D12 resource associated with this object.
|
|
|
|
Calling this method doesn't increment resource's reference counter.
|
|
*/
|
|
ID3D12Resource* GetResource() const { return m_Resource; }
|
|
|
|
/** \brief Returns memory heap that the resource is created in.
|
|
|
|
If the Allocation represents committed resource with implicit heap, returns NULL.
|
|
*/
|
|
ID3D12Heap* GetHeap() const;
|
|
|
|
/** \brief Associates a name with the allocation object. This name is for use in debug diagnostics and tools.
|
|
|
|
Internal copy of the string is made, so the memory pointed by the argument can be
|
|
changed of freed immediately after this call.
|
|
|
|
`Name` can be null.
|
|
*/
|
|
void SetName(LPCWSTR Name);
|
|
|
|
/** \brief Returns the name associated with the allocation object.
|
|
|
|
Returned string points to an internal copy.
|
|
|
|
If no name was associated with the allocation, returns null.
|
|
*/
|
|
LPCWSTR GetName() const { return m_Name; }
|
|
|
|
/** \brief Returns `TRUE` if the memory of the allocation was filled with zeros when the allocation was created.
|
|
|
|
Returns `TRUE` only if the allocator is sure that the entire memory where the
|
|
allocation was created was filled with zeros at the moment the allocation was made.
|
|
|
|
Returns `FALSE` if the memory could potentially contain garbage data.
|
|
If it's a render-target or depth-stencil texture, it then needs proper
|
|
initialization with `ClearRenderTargetView`, `ClearDepthStencilView`, `DiscardResource`,
|
|
or a copy operation, as described on page:
|
|
[ID3D12Device::CreatePlacedResource method - Notes on the required resource initialization](https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource#notes-on-the-required-resource-initialization).
|
|
Please note that rendering a fullscreen triangle or quad to the texture as
|
|
a render target is not a proper way of initialization!
|
|
|
|
See also articles:
|
|
["Coming to DirectX 12: More control over memory allocation"](https://devblogs.microsoft.com/directx/coming-to-directx-12-more-control-over-memory-allocation/),
|
|
["Initializing DX12 Textures After Allocation and Aliasing"](https://asawicki.info/news_1724_initializing_dx12_textures_after_allocation_and_aliasing).
|
|
*/
|
|
BOOL WasZeroInitialized() const { return m_PackedData.WasZeroInitialized(); }
|
|
|
|
protected:
|
|
virtual void ReleaseThis();
|
|
|
|
private:
|
|
friend class AllocatorPimpl;
|
|
friend class BlockVector;
|
|
friend class CommittedAllocationList;
|
|
friend class JsonWriter;
|
|
friend struct CommittedAllocationListItemTraits;
|
|
template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*);
|
|
template<typename T> friend class PoolAllocator;
|
|
|
|
enum Type
|
|
{
|
|
TYPE_COMMITTED,
|
|
TYPE_PLACED,
|
|
TYPE_HEAP,
|
|
TYPE_COUNT
|
|
};
|
|
|
|
AllocatorPimpl* m_Allocator;
|
|
UINT64 m_Size;
|
|
ID3D12Resource* m_Resource;
|
|
UINT m_CreationFrameIndex;
|
|
wchar_t* m_Name;
|
|
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
CommittedAllocationList* list;
|
|
Allocation* prev;
|
|
Allocation* next;
|
|
} m_Committed;
|
|
|
|
struct
|
|
{
|
|
UINT64 offset;
|
|
NormalBlock* block;
|
|
} m_Placed;
|
|
|
|
struct
|
|
{
|
|
// Beginning must be compatible with m_Committed.
|
|
CommittedAllocationList* list;
|
|
Allocation* prev;
|
|
Allocation* next;
|
|
ID3D12Heap* heap;
|
|
} m_Heap;
|
|
};
|
|
|
|
struct PackedData
|
|
{
|
|
public:
|
|
PackedData() :
|
|
m_Type(0), m_ResourceDimension(0), m_ResourceFlags(0), m_TextureLayout(0), m_WasZeroInitialized(0) { }
|
|
|
|
Type GetType() const { return (Type)m_Type; }
|
|
D3D12_RESOURCE_DIMENSION GetResourceDimension() const { return (D3D12_RESOURCE_DIMENSION)m_ResourceDimension; }
|
|
D3D12_RESOURCE_FLAGS GetResourceFlags() const { return (D3D12_RESOURCE_FLAGS)m_ResourceFlags; }
|
|
D3D12_TEXTURE_LAYOUT GetTextureLayout() const { return (D3D12_TEXTURE_LAYOUT)m_TextureLayout; }
|
|
BOOL WasZeroInitialized() const { return (BOOL)m_WasZeroInitialized; }
|
|
|
|
void SetType(Type type);
|
|
void SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension);
|
|
void SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags);
|
|
void SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout);
|
|
void SetWasZeroInitialized(BOOL wasZeroInitialized) { m_WasZeroInitialized = wasZeroInitialized ? 1 : 0; }
|
|
|
|
private:
|
|
UINT m_Type : 2; // enum Type
|
|
UINT m_ResourceDimension : 3; // enum D3D12_RESOURCE_DIMENSION
|
|
UINT m_ResourceFlags : 24; // flags D3D12_RESOURCE_FLAGS
|
|
UINT m_TextureLayout : 9; // enum D3D12_TEXTURE_LAYOUT
|
|
UINT m_WasZeroInitialized : 1; // BOOL
|
|
} m_PackedData;
|
|
|
|
Allocation(AllocatorPimpl* allocator, UINT64 size, BOOL wasZeroInitialized);
|
|
// Nothing here, everything already done in Release.
|
|
// ~Allocation() {}
|
|
|
|
void InitCommitted(CommittedAllocationList* list);
|
|
void InitPlaced(UINT64 offset, UINT64 alignment, NormalBlock* block);
|
|
void InitHeap(CommittedAllocationList* list, ID3D12Heap* heap);
|
|
template<typename D3D12_RESOURCE_DESC_T>
|
|
void SetResource(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc);
|
|
void FreeName();
|
|
|
|
D3D12MA_CLASS_NO_COPY(Allocation)
|
|
};
|
|
|
|
/// \brief Parameters of created D3D12MA::Pool object. To be used with D3D12MA::Allocator::CreatePool.
|
|
struct POOL_DESC
|
|
{
|
|
/** \brief The parameters of memory heap where allocations of this pool should be placed.
|
|
|
|
In the simplest case, just fill it with zeros and set `Type` to one of: `D3D12_HEAP_TYPE_DEFAULT`,
|
|
`D3D12_HEAP_TYPE_UPLOAD`, `D3D12_HEAP_TYPE_READBACK`. Additional parameters can be used e.g. to utilize UMA.
|
|
*/
|
|
D3D12_HEAP_PROPERTIES HeapProperties;
|
|
/** \brief Heap flags to be used when allocating heaps of this pool.
|
|
|
|
It should contain one of these values, depending on type of resources you are going to create in this heap:
|
|
`D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`,
|
|
`D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`,
|
|
`D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`.
|
|
Except if ResourceHeapTier = 2, then it may be `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES` = 0.
|
|
|
|
You can specify additional flags if needed.
|
|
*/
|
|
D3D12_HEAP_FLAGS HeapFlags;
|
|
/** \brief Size of a single heap (memory block) to be allocated as part of this pool, in bytes. Optional.
|
|
|
|
Specify nonzero to set explicit, constant size of memory blocks used by this pool.
|
|
Leave 0 to use default and let the library manage block sizes automatically.
|
|
Then sizes of particular blocks may vary.
|
|
*/
|
|
UINT64 BlockSize;
|
|
/** \brief Minimum number of heaps (memory blocks) to be always allocated in this pool, even if they stay empty. Optional.
|
|
|
|
Set to 0 to have no preallocated blocks and allow the pool be completely empty.
|
|
*/
|
|
UINT MinBlockCount;
|
|
/** \brief Maximum number of heaps (memory blocks) that can be allocated in this pool. Optional.
|
|
|
|
Set to 0 to use default, which is `UINT64_MAX`, which means no limit.
|
|
|
|
Set to same value as D3D12MA::POOL_DESC::MinBlockCount to have fixed amount of memory allocated
|
|
throughout whole lifetime of this pool.
|
|
*/
|
|
UINT MaxBlockCount;
|
|
/** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0.
|
|
|
|
Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two.
|
|
*/
|
|
UINT64 MinAllocationAlignment;
|
|
};
|
|
|
|
/** \brief Custom memory pool
|
|
|
|
Represents a separate set of heaps (memory blocks) that can be used to create
|
|
D3D12MA::Allocation-s and resources in it. Usually there is no need to create custom
|
|
pools - creating resources in default pool is sufficient.
|
|
|
|
To create custom pool, fill D3D12MA::POOL_DESC and call D3D12MA::Allocator::CreatePool.
|
|
*/
|
|
class D3D12MA_API Pool : public IUnknownImpl
|
|
{
|
|
public:
|
|
/** \brief Returns copy of parameters of the pool.
|
|
|
|
These are the same parameters as passed to D3D12MA::Allocator::CreatePool.
|
|
*/
|
|
POOL_DESC GetDesc() const;
|
|
|
|
/** \brief Retrieves statistics from the current state of this pool.
|
|
*/
|
|
void CalculateStats(StatInfo* pStats);
|
|
|
|
/** \brief Associates a name with the pool. This name is for use in debug diagnostics and tools.
|
|
|
|
Internal copy of the string is made, so the memory pointed by the argument can be
|
|
changed of freed immediately after this call.
|
|
|
|
`Name` can be NULL.
|
|
*/
|
|
void SetName(LPCWSTR Name);
|
|
|
|
/** \brief Returns the name associated with the pool object.
|
|
|
|
Returned string points to an internal copy.
|
|
|
|
If no name was associated with the allocation, returns NULL.
|
|
*/
|
|
LPCWSTR GetName() const;
|
|
|
|
protected:
|
|
virtual void ReleaseThis();
|
|
|
|
private:
|
|
friend class Allocator;
|
|
friend class AllocatorPimpl;
|
|
template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*);
|
|
|
|
PoolPimpl* m_Pimpl;
|
|
|
|
Pool(Allocator* allocator, const POOL_DESC &desc);
|
|
~Pool();
|
|
|
|
D3D12MA_CLASS_NO_COPY(Pool)
|
|
};
|
|
|
|
/// \brief Bit flags to be used with ALLOCATOR_DESC::Flags.
|
|
enum ALLOCATOR_FLAGS
|
|
{
|
|
/// Zero
|
|
ALLOCATOR_FLAG_NONE = 0,
|
|
|
|
/**
|
|
Allocator and all objects created from it will not be synchronized internally,
|
|
so you must guarantee they are used from only one thread at a time or
|
|
synchronized by you.
|
|
|
|
Using this flag may increase performance because internal mutexes are not used.
|
|
*/
|
|
ALLOCATOR_FLAG_SINGLETHREADED = 0x1,
|
|
|
|
/**
|
|
Every allocation will have its own memory block.
|
|
To be used for debugging purposes.
|
|
*/
|
|
ALLOCATOR_FLAG_ALWAYS_COMMITTED = 0x2,
|
|
};
|
|
|
|
/// \brief Parameters of created Allocator object. To be used with CreateAllocator().
|
|
struct ALLOCATOR_DESC
|
|
{
|
|
/// Flags.
|
|
ALLOCATOR_FLAGS Flags;
|
|
|
|
/** Direct3D device object that the allocator should be attached to.
|
|
|
|
Allocator is doing `AddRef`/`Release` on this object.
|
|
*/
|
|
ID3D12Device* pDevice;
|
|
|
|
/** \brief Preferred size of a single `ID3D12Heap` block to be allocated.
|
|
|
|
Set to 0 to use default, which is currently 64 MiB.
|
|
*/
|
|
UINT64 PreferredBlockSize;
|
|
|
|
/** \brief Custom CPU memory allocation callbacks. Optional.
|
|
|
|
Optional, can be null. When specified, will be used for all CPU-side memory allocations.
|
|
*/
|
|
const ALLOCATION_CALLBACKS* pAllocationCallbacks;
|
|
|
|
/** DXGI Adapter object that you use for D3D12 and this allocator.
|
|
|
|
Allocator is doing `AddRef`/`Release` on this object.
|
|
*/
|
|
IDXGIAdapter* pAdapter;
|
|
};
|
|
|
|
/**
|
|
\brief Number of D3D12 memory heap types supported.
|
|
*/
|
|
const UINT HEAP_TYPE_COUNT = 4;
|
|
|
|
/**
|
|
\brief Calculated statistics of memory usage in entire allocator.
|
|
*/
|
|
struct StatInfo
|
|
{
|
|
/// Number of memory blocks (heaps) allocated.
|
|
UINT BlockCount;
|
|
/// Number of D3D12MA::Allocation objects allocated.
|
|
UINT AllocationCount;
|
|
/// Number of free ranges of memory between allocations.
|
|
UINT UnusedRangeCount;
|
|
/// Total number of bytes occupied by all allocations.
|
|
UINT64 UsedBytes;
|
|
/// Total number of bytes occupied by unused ranges.
|
|
UINT64 UnusedBytes;
|
|
UINT64 AllocationSizeMin;
|
|
UINT64 AllocationSizeAvg;
|
|
UINT64 AllocationSizeMax;
|
|
UINT64 UnusedRangeSizeMin;
|
|
UINT64 UnusedRangeSizeAvg;
|
|
UINT64 UnusedRangeSizeMax;
|
|
};
|
|
|
|
/**
|
|
\brief General statistics from the current state of the allocator.
|
|
*/
|
|
struct Stats
|
|
{
|
|
/// Total statistics from all heap types.
|
|
StatInfo Total;
|
|
/**
|
|
One StatInfo for each type of heap located at the following indices:
|
|
0 - DEFAULT, 1 - UPLOAD, 2 - READBACK, 3 - CUSTOM.
|
|
*/
|
|
StatInfo HeapType[HEAP_TYPE_COUNT];
|
|
};
|
|
|
|
/** \brief Statistics of current memory usage and available budget, in bytes, for GPU or CPU memory.
|
|
*/
|
|
struct Budget
|
|
{
|
|
/** \brief Sum size of all memory blocks allocated from particular heap type, in bytes.
|
|
*/
|
|
UINT64 BlockBytes;
|
|
|
|
/** \brief Sum size of all allocations created in particular heap type, in bytes.
|
|
|
|
Always less or equal than `BlockBytes`.
|
|
Difference `BlockBytes - AllocationBytes` is the amount of memory allocated but unused -
|
|
available for new allocations or wasted due to fragmentation.
|
|
*/
|
|
UINT64 AllocationBytes;
|
|
|
|
/** \brief Estimated current memory usage of the program, in bytes.
|
|
|
|
Fetched from system using `IDXGIAdapter3::QueryVideoMemoryInfo` if enabled.
|
|
|
|
It might be different than `BlockBytes` (usually higher) due to additional implicit objects
|
|
also occupying the memory, like swapchain, pipeline state objects, descriptor heaps, command lists, or
|
|
memory blocks allocated outside of this library, if any.
|
|
*/
|
|
UINT64 UsageBytes;
|
|
|
|
/** \brief Estimated amount of memory available to the program, in bytes.
|
|
|
|
Fetched from system using `IDXGIAdapter3::QueryVideoMemoryInfo` if enabled.
|
|
|
|
It might be different (most probably smaller) than memory sizes reported in `DXGI_ADAPTER_DESC` due to factors
|
|
external to the program, like other programs also consuming system resources.
|
|
Difference `BudgetBytes - UsageBytes` is the amount of additional memory that can probably
|
|
be allocated without problems. Exceeding the budget may result in various problems.
|
|
*/
|
|
UINT64 BudgetBytes;
|
|
};
|
|
|
|
/**
|
|
\brief Represents main object of this library initialized for particular `ID3D12Device`.
|
|
|
|
Fill structure D3D12MA::ALLOCATOR_DESC and call function CreateAllocator() to create it.
|
|
Call method `Release()` to destroy it.
|
|
|
|
It is recommended to create just one object of this type per `ID3D12Device` object,
|
|
right after Direct3D 12 is initialized and keep it alive until before Direct3D device is destroyed.
|
|
*/
|
|
class D3D12MA_API Allocator : public IUnknownImpl
|
|
{
|
|
public:
|
|
/// Returns cached options retrieved from D3D12 device.
|
|
const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const;
|
|
/** \brief Returns true if `D3D12_FEATURE_DATA_ARCHITECTURE1::UMA` was found to be true.
|
|
|
|
For more information about how to use it, see articles in Microsoft Docs:
|
|
- https://docs.microsoft.com/en-us/windows/win32/direct3d12/default-texture-mapping
|
|
- https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_feature_data_architecture
|
|
- https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getcustomheapproperties
|
|
*/
|
|
BOOL IsUMA() const;
|
|
/** \brief Returns true if `D3D12_FEATURE_DATA_ARCHITECTURE1::CacheCoherentUMA` was found to be true.
|
|
|
|
For more information about how to use it, see articles in Microsoft Docs:
|
|
- https://docs.microsoft.com/en-us/windows/win32/direct3d12/default-texture-mapping
|
|
- https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_feature_data_architecture
|
|
- https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getcustomheapproperties
|
|
*/
|
|
BOOL IsCacheCoherentUMA() const;
|
|
|
|
/** \brief Allocates memory and creates a D3D12 resource (buffer or texture). This is the main allocation function.
|
|
|
|
The function is similar to `ID3D12Device::CreateCommittedResource`, but it may
|
|
really call `ID3D12Device::CreatePlacedResource` to assign part of a larger,
|
|
existing memory heap to the new resource, which is the main purpose of this
|
|
whole library.
|
|
|
|
If `ppvResource` is null, you receive only `ppAllocation` object from this function.
|
|
It holds pointer to `ID3D12Resource` that can be queried using function D3D12MA::Allocation::GetResource().
|
|
Reference count of the resource object is 1.
|
|
It is automatically destroyed when you destroy the allocation object.
|
|
|
|
If `ppvResource` is not null, you receive pointer to the resource next to allocation object.
|
|
Reference count of the resource object is then increased by calling `QueryInterface`, so you need to manually `Release` it
|
|
along with the allocation.
|
|
|
|
\param pAllocDesc Parameters of the allocation.
|
|
\param pResourceDesc Description of created resource.
|
|
\param InitialResourceState Initial resource state.
|
|
\param pOptimizedClearValue Optional. Either null or optimized clear value.
|
|
\param[out] ppAllocation Filled with pointer to new allocation object created.
|
|
\param riidResource IID of a resource to be returned via `ppvResource`.
|
|
\param[out] ppvResource Optional. If not null, filled with pointer to new resouce created.
|
|
|
|
\note This function creates a new resource. Sub-allocation of parts of one large buffer,
|
|
although recommended as a good practice, is out of scope of this library and could be implemented
|
|
by the user as a higher-level logic on top of it, e.g. using the \ref virtual_allocator feature.
|
|
*/
|
|
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);
|
|
|
|
#ifdef __ID3D12Device4_INTERFACE_DEFINED__
|
|
/** \brief Similar to Allocator::CreateResource, but supports additional parameter `pProtectedSession`.
|
|
|
|
If `pProtectedSession` is not null, current implementation always creates the resource as committed
|
|
using `ID3D12Device4::CreateCommittedResource1`.
|
|
|
|
To work correctly, `ID3D12Device4` interface must be available in the current system. Otherwise, `E_NOINTERFACE` is returned.
|
|
*/
|
|
HRESULT CreateResource1(
|
|
const ALLOCATION_DESC* pAllocDesc,
|
|
const D3D12_RESOURCE_DESC* pResourceDesc,
|
|
D3D12_RESOURCE_STATES InitialResourceState,
|
|
const D3D12_CLEAR_VALUE *pOptimizedClearValue,
|
|
ID3D12ProtectedResourceSession *pProtectedSession,
|
|
Allocation** ppAllocation,
|
|
REFIID riidResource,
|
|
void** ppvResource);
|
|
#endif // #ifdef __ID3D12Device4_INTERFACE_DEFINED__
|
|
|
|
#ifdef __ID3D12Device8_INTERFACE_DEFINED__
|
|
/** \brief Similar to Allocator::CreateResource1, but supports new structure `D3D12_RESOURCE_DESC1`.
|
|
|
|
It internally uses `ID3D12Device8::CreateCommittedResource2` or `ID3D12Device8::CreatePlacedResource1`.
|
|
|
|
To work correctly, `ID3D12Device8` interface must be available in the current system. Otherwise, `E_NOINTERFACE` is returned.
|
|
*/
|
|
HRESULT CreateResource2(
|
|
const ALLOCATION_DESC* pAllocDesc,
|
|
const D3D12_RESOURCE_DESC1* pResourceDesc,
|
|
D3D12_RESOURCE_STATES InitialResourceState,
|
|
const D3D12_CLEAR_VALUE *pOptimizedClearValue,
|
|
ID3D12ProtectedResourceSession *pProtectedSession,
|
|
Allocation** ppAllocation,
|
|
REFIID riidResource,
|
|
void** ppvResource);
|
|
#endif // #ifdef __ID3D12Device4_INTERFACE_DEFINED__
|
|
|
|
/** \brief Allocates memory without creating any resource placed in it.
|
|
|
|
This function is similar to `ID3D12Device::CreateHeap`, but it may really assign
|
|
part of a larger, existing heap to the allocation.
|
|
|
|
`pAllocDesc->heapFlags` should contain one of these values, depending on type of resources you are going to create in this memory:
|
|
`D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS`,
|
|
`D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES`,
|
|
`D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES`.
|
|
Except if you validate that ResourceHeapTier = 2 - then `heapFlags`
|
|
may be `D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES` = 0.
|
|
Additional flags in `heapFlags` are allowed as well.
|
|
|
|
`pAllocInfo->SizeInBytes` must be multiply of 64KB.
|
|
`pAllocInfo->Alignment` must be one of the legal values as described in documentation of `D3D12_HEAP_DESC`.
|
|
|
|
If you use D3D12MA::ALLOCATION_FLAG_COMMITTED you will get a separate memory block -
|
|
a heap that always has offset 0.
|
|
*/
|
|
HRESULT AllocateMemory(
|
|
const ALLOCATION_DESC* pAllocDesc,
|
|
const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
|
|
Allocation** ppAllocation);
|
|
|
|
#ifdef __ID3D12Device4_INTERFACE_DEFINED__
|
|
/** \brief Similar to Allocator::AllocateMemory, but supports additional parameter `pProtectedSession`.
|
|
|
|
If `pProtectedSession` is not null, current implementation always creates separate heap
|
|
using `ID3D12Device4::CreateHeap1`.
|
|
|
|
To work correctly, `ID3D12Device4` interface must be available in the current system. Otherwise, `E_NOINTERFACE` is returned.
|
|
*/
|
|
HRESULT AllocateMemory1(
|
|
const ALLOCATION_DESC* pAllocDesc,
|
|
const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,
|
|
ID3D12ProtectedResourceSession *pProtectedSession,
|
|
Allocation** ppAllocation);
|
|
#endif // #ifdef __ID3D12Device4_INTERFACE_DEFINED__
|
|
|
|
/** \brief Creates a new resource in place of an existing allocation. This is useful for memory aliasing.
|
|
|
|
\param pAllocation Existing allocation indicating the memory where the new resource should be created.
|
|
It can be created using D3D12MA::Allocator::CreateResource and already have a resource bound to it,
|
|
or can be a raw memory allocated with D3D12MA::Allocator::AllocateMemory.
|
|
It must not be created as committed so that `ID3D12Heap` is available and not implicit.
|
|
\param AllocationLocalOffset Additional offset in bytes to be applied when allocating the resource.
|
|
Local from the start of `pAllocation`, not the beginning of the whole `ID3D12Heap`!
|
|
If the new resource should start from the beginning of the `pAllocation` it should be 0.
|
|
\param pResourceDesc Description of the new resource to be created.
|
|
\param InitialResourceState
|
|
\param pOptimizedClearValue
|
|
\param riidResource
|
|
\param[out] ppvResource Returns pointer to the new resource.
|
|
The resource is not bound with `pAllocation`.
|
|
This pointer must not be null - you must get the resource pointer and `Release` it when no longer needed.
|
|
|
|
Memory requirements of the new resource are checked for validation.
|
|
If its size exceeds the end of `pAllocation` or required alignment is not fulfilled
|
|
considering `pAllocation->GetOffset() + AllocationLocalOffset`, the function
|
|
returns `E_INVALIDARG`.
|
|
*/
|
|
HRESULT CreateAliasingResource(
|
|
Allocation* pAllocation,
|
|
UINT64 AllocationLocalOffset,
|
|
const D3D12_RESOURCE_DESC* pResourceDesc,
|
|
D3D12_RESOURCE_STATES InitialResourceState,
|
|
const D3D12_CLEAR_VALUE *pOptimizedClearValue,
|
|
REFIID riidResource,
|
|
void** ppvResource);
|
|
|
|
/** \brief Creates custom pool.
|
|
*/
|
|
HRESULT CreatePool(
|
|
const POOL_DESC* pPoolDesc,
|
|
Pool** ppPool);
|
|
|
|
/** \brief Sets the index of the current frame.
|
|
|
|
This function is used to set the frame index in the allocator when a new game frame begins.
|
|
*/
|
|
void SetCurrentFrameIndex(UINT frameIndex);
|
|
|
|
/** \brief Retrieves statistics from the current state of the allocator.
|
|
*/
|
|
void CalculateStats(Stats* pStats);
|
|
|
|
/** \brief Retrieves information about current memory budget.
|
|
|
|
\param[out] pGpuBudget Optional, can be null.
|
|
\param[out] pCpuBudget Optional, can be null.
|
|
|
|
This function is called "get" not "calculate" because it is very fast, suitable to be called
|
|
every frame or every allocation. For more detailed statistics use CalculateStats().
|
|
|
|
Note that when using allocator from multiple threads, returned information may immediately
|
|
become outdated.
|
|
*/
|
|
void GetBudget(Budget* pGpuBudget, Budget* pCpuBudget);
|
|
|
|
/// Builds and returns statistics as a string in JSON format.
|
|
/** @param[out] ppStatsString Must be freed using Allocator::FreeStatsString.
|
|
@param DetailedMap `TRUE` to include full list of allocations (can make the string quite long), `FALSE` to only return statistics.
|
|
*/
|
|
void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const;
|
|
|
|
/// Frees memory of a string returned from Allocator::BuildStatsString.
|
|
void FreeStatsString(WCHAR* pStatsString) const;
|
|
|
|
protected:
|
|
virtual void ReleaseThis();
|
|
|
|
private:
|
|
friend D3D12MA_API HRESULT CreateAllocator(const ALLOCATOR_DESC*, Allocator**);
|
|
template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*);
|
|
friend class Pool;
|
|
|
|
Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc);
|
|
~Allocator();
|
|
|
|
AllocatorPimpl* m_Pimpl;
|
|
|
|
D3D12MA_CLASS_NO_COPY(Allocator)
|
|
};
|
|
|
|
/// Parameters of created D3D12MA::VirtualBlock object to be passed to CreateVirtualBlock().
|
|
struct VIRTUAL_BLOCK_DESC
|
|
{
|
|
/** \brief Total size of the block.
|
|
|
|
Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.
|
|
For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.
|
|
*/
|
|
UINT64 Size;
|
|
/** \brief Custom CPU memory allocation callbacks. Optional.
|
|
|
|
Optional, can be null. When specified, will be used for all CPU-side memory allocations.
|
|
*/
|
|
const ALLOCATION_CALLBACKS* pAllocationCallbacks;
|
|
};
|
|
|
|
/// Parameters of created virtual allocation to be passed to VirtualBlock::Allocate().
|
|
struct VIRTUAL_ALLOCATION_DESC
|
|
{
|
|
/** \brief Size of the allocation.
|
|
|
|
Cannot be zero.
|
|
*/
|
|
UINT64 Size;
|
|
/** \brief Required alignment of the allocation.
|
|
|
|
Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset.
|
|
*/
|
|
UINT64 Alignment;
|
|
/** \brief Custom pointer to be associated with the allocation.
|
|
|
|
It can be fetched or changed later.
|
|
*/
|
|
void* pUserData;
|
|
};
|
|
|
|
/// Parameters of an existing virtual allocation, returned by VirtualBlock::GetAllocationInfo().
|
|
struct VIRTUAL_ALLOCATION_INFO
|
|
{
|
|
/** \brief Size of the allocation.
|
|
|
|
Same value as passed in VIRTUAL_ALLOCATION_DESC::Size.
|
|
*/
|
|
UINT64 size;
|
|
/** \brief Custom pointer associated with the allocation.
|
|
|
|
Same value as passed in VIRTUAL_ALLOCATION_DESC::pUserData or VirtualBlock::SetAllocationUserData().
|
|
*/
|
|
void* pUserData;
|
|
};
|
|
|
|
/** \brief Represents pure allocation algorithm and a data structure with allocations in some memory block, without actually allocating any GPU memory.
|
|
|
|
This class allows to use the core algorithm of the library custom allocations e.g. CPU memory or
|
|
sub-allocation regions inside a single GPU buffer.
|
|
|
|
To create this object, fill in D3D12MA::VIRTUAL_BLOCK_DESC and call CreateVirtualBlock().
|
|
To destroy it, call its method `VirtualBlock::Release()`.
|
|
You need to free all the allocations within this block or call Clear() before destroying it.
|
|
*/
|
|
class D3D12MA_API VirtualBlock : public IUnknownImpl
|
|
{
|
|
public:
|
|
/** \brief Returns true if the block is empty - contains 0 allocations.
|
|
*/
|
|
BOOL IsEmpty() const;
|
|
/** \brief Returns information about an allocation at given offset - its size and custom pointer.
|
|
*/
|
|
void GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO* pInfo) const;
|
|
|
|
/** \brief Creates new allocation.
|
|
\param pDesc
|
|
\param[out] pOffset Offset of the new allocation, which can also be treated as an unique identifier of the allocation within this block. `UINT64_MAX` if allocation failed.
|
|
\return `S_OK` if allocation succeeded, `E_OUTOFMEMORY` if it failed.
|
|
*/
|
|
HRESULT Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, UINT64* pOffset);
|
|
/** \brief Frees the allocation at given offset.
|
|
*/
|
|
void FreeAllocation(UINT64 offset);
|
|
/** \brief Frees all the allocations.
|
|
*/
|
|
void Clear();
|
|
/** \brief Changes custom pointer for an allocation at given offset to a new value.
|
|
*/
|
|
void SetAllocationUserData(UINT64 offset, void* pUserData);
|
|
|
|
/** \brief Retrieves statistics from the current state of the block.
|
|
*/
|
|
void CalculateStats(StatInfo* pInfo) const;
|
|
|
|
/** \brief Builds and returns statistics as a string in JSON format, including the list of allocations with their parameters.
|
|
@param[out] ppStatsString Must be freed using VirtualBlock::FreeStatsString.
|
|
*/
|
|
void BuildStatsString(WCHAR** ppStatsString) const;
|
|
|
|
/** \brief Frees memory of a string returned from VirtualBlock::BuildStatsString.
|
|
*/
|
|
void FreeStatsString(WCHAR* pStatsString) const;
|
|
|
|
protected:
|
|
virtual void ReleaseThis();
|
|
|
|
private:
|
|
friend D3D12MA_API HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC*, VirtualBlock**);
|
|
template<typename T> friend void D3D12MA_DELETE(const ALLOCATION_CALLBACKS&, T*);
|
|
|
|
VirtualBlockPimpl* m_Pimpl;
|
|
|
|
VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc);
|
|
~VirtualBlock();
|
|
|
|
D3D12MA_CLASS_NO_COPY(VirtualBlock)
|
|
};
|
|
|
|
/** \brief Creates new main D3D12MA::Allocator object and returns it through `ppAllocator`.
|
|
|
|
You normally only need to call it once and keep a single Allocator object for your `ID3D12Device`.
|
|
*/
|
|
D3D12MA_API HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator);
|
|
|
|
/** \brief Creates new D3D12MA::VirtualBlock object and returns it through `ppVirtualBlock`.
|
|
|
|
Note you don't need to create D3D12MA::Allocator to use virtual blocks.
|
|
*/
|
|
D3D12MA_API HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock);
|
|
|
|
} // namespace D3D12MA
|
|
|
|
/// \cond INTERNAL
|
|
DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::ALLOCATION_FLAGS);
|
|
DEFINE_ENUM_FLAG_OPERATORS(D3D12MA::ALLOCATOR_FLAGS);
|
|
/// \endcond
|
|
|
|
/**
|
|
\page quick_start Quick start
|
|
|
|
\section quick_start_project_setup 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.
|
|
|
|
\code
|
|
#include "D3D12MemAlloc.h"
|
|
\endcode
|
|
|
|
(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.
|
|
|
|
\code
|
|
IDXGIAdapter* adapter = (...)
|
|
ID3D12Device* device = (...)
|
|
|
|
D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
|
|
allocatorDesc.pDevice = device;
|
|
allocatorDesc.pAdapter = adapter;
|
|
|
|
D3D12MA::Allocator* allocator;
|
|
HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator);
|
|
\endcode
|
|
|
|
(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.
|
|
|
|
\code
|
|
allocator->Release();
|
|
\endcode
|
|
|
|
|
|
\section quick_start_creating_resources 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.
|
|
|
|
\code
|
|
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 = {};
|
|
allocDesc.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));
|
|
\endcode
|
|
|
|
You need to remember both resource and allocation objects and destroy them
|
|
separately when no longer needed.
|
|
|
|
\code
|
|
allocation->Release();
|
|
resource->Release();
|
|
\endcode
|
|
|
|
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.
|
|
|
|
|
|
\section quick_start_mapping_memory 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](https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nf-d3d12-id3d12resource-map).
|
|
|
|
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 linked documentation 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:
|
|
|
|
\code
|
|
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);
|
|
\endcode
|
|
|
|
|
|
\page custom_pools Custom memory pools
|
|
|
|
A "pool" is a collection of memory blocks that share certain properties.
|
|
Allocator creates 3 default pools: for `D3D12_HEAP_TYPE_DEFAULT`, `UPLOAD`, `READBACK`.
|
|
A default pool automatically grows in size. Size of allocated blocks is also variable and managed automatically.
|
|
Typical allocations are created in these pools. You can also create custom pools.
|
|
|
|
\section custom_pools_usage Usage
|
|
|
|
To create a custom pool, fill in structure D3D12MA::POOL_DESC and call function D3D12MA::Allocator::CreatePool
|
|
to obtain object D3D12MA::Pool. Example:
|
|
|
|
\code
|
|
POOL_DESC poolDesc = {};
|
|
poolDesc.HeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
|
|
|
Pool* pool;
|
|
HRESULT hr = allocator->CreatePool(&poolDesc, &pool);
|
|
\endcode
|
|
|
|
To allocate resources out of a custom pool, only set member D3D12MA::ALLOCATION_DESC::CustomPool.
|
|
Example:
|
|
|
|
\code
|
|
ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.CustomPool = pool;
|
|
|
|
D3D12_RESOURCE_DESC resDesc = ...
|
|
Allocation* alloc;
|
|
hr = allocator->CreateResource(&allocDesc, &resDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &alloc, IID_NULL, NULL);
|
|
\endcode
|
|
|
|
All allocations must be released before releasing the pool.
|
|
The pool must be released before relasing the allocator.
|
|
|
|
\code
|
|
alloc->Release();
|
|
pool->Release();
|
|
\endcode
|
|
|
|
\section custom_pools_features_and_benefits Features and benefits
|
|
|
|
While it is recommended to use default pools whenever possible for simplicity and to give the allocator
|
|
more opportunities for internal optimizations, custom pools may be useful in following cases:
|
|
|
|
- To keep some resources separate from others in memory.
|
|
- To keep track of memory usage of just a specific group of resources. Statistics can be queried using
|
|
D3D12MA::Pool::CalculateStats.
|
|
- To use specific size of a memory block (`ID3D12Heap`). To set it, use member D3D12MA::POOL_DESC::BlockSize.
|
|
When set to 0, the library uses automatically determined, variable block sizes.
|
|
- To reserve some minimum amount of memory allocated. To use it, set member D3D12MA::POOL_DESC::MinBlockCount.
|
|
- To limit maximum amount of memory allocated. To use it, set member D3D12MA::POOL_DESC::MaxBlockCount.
|
|
- To use extended parameters of the D3D12 memory allocation. While resources created from default pools
|
|
can only specify `D3D12_HEAP_TYPE_DEFAULT`, `UPLOAD`, `READBACK`, a custom pool may use non-standard
|
|
`D3D12_HEAP_PROPERTIES` (member D3D12MA::POOL_DESC::HeapProperties) and `D3D12_HEAP_FLAGS`
|
|
(D3D12MA::POOL_DESC::HeapFlags), which is useful e.g. for cross-adapter sharing or UMA
|
|
(see also D3D12MA::Allocator::IsUMA).
|
|
|
|
New versions of this library support creating **committed allocations in custom pools**.
|
|
It is supported only when D3D12MA::POOL_DESC::BlockSize = 0.
|
|
To use this feature, set D3D12MA::ALLOCATION_DESC::CustomPool to the pointer to your custom pool and
|
|
D3D12MA::ALLOCATION_DESC::Flags to D3D12MA::ALLOCATION_FLAG_COMMITTED. Example:
|
|
|
|
\code
|
|
ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.CustomPool = pool;
|
|
allocDesc.Flags = ALLOCATION_FLAG_COMMITTED;
|
|
|
|
D3D12_RESOURCE_DESC resDesc = ...
|
|
Allocation* alloc;
|
|
ID3D12Resource* res;
|
|
hr = allocator->CreateResource(&allocDesc, &resDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &alloc, IID_PPV_ARGS(&res));
|
|
\endcode
|
|
|
|
This feature may seem unnecessary, but creating committed allocations from custom pools may be useful
|
|
in some cases, e.g. to have separate memory usage statistics for some group of resources or to use
|
|
extended allocation parameters, like custom `D3D12_HEAP_PROPERTIES`, which are available only in custom pools.
|
|
|
|
|
|
\page resource_aliasing Resource aliasing (overlap)
|
|
|
|
New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory
|
|
management, give an opportunity to alias (overlap) multiple resources in the
|
|
same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).
|
|
It can be useful to save video memory, but it must be used with caution.
|
|
|
|
For example, if you know the flow of your whole render frame in advance, you
|
|
are going to use some intermediate textures or buffers only during a small range of render passes,
|
|
and you know these ranges don't overlap in time, you can create these resources in
|
|
the same place in memory, even if they have completely different parameters (width, height, format etc.).
|
|
|
|
![Resource aliasing (overlap)](../gfx/Aliasing.png)
|
|
|
|
Such scenario is possible using D3D12MA, but you need to create your resources
|
|
using special function D3D12MA::Allocator::CreateAliasingResource.
|
|
Before that, you need to allocate memory with parameters calculated using formula:
|
|
|
|
- allocation size = max(size of each resource)
|
|
- allocation alignment = max(alignment of each resource)
|
|
|
|
Following example shows two different textures created in the same place in memory,
|
|
allocated to fit largest of them.
|
|
|
|
\code
|
|
D3D12_RESOURCE_DESC resDesc1 = {};
|
|
resDesc1.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
|
resDesc1.Alignment = 0;
|
|
resDesc1.Width = 1920;
|
|
resDesc1.Height = 1080;
|
|
resDesc1.DepthOrArraySize = 1;
|
|
resDesc1.MipLevels = 1;
|
|
resDesc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
resDesc1.SampleDesc.Count = 1;
|
|
resDesc1.SampleDesc.Quality = 0;
|
|
resDesc1.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
|
resDesc1.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
|
|
|
D3D12_RESOURCE_DESC resDesc2 = {};
|
|
resDesc2.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
|
resDesc2.Alignment = 0;
|
|
resDesc2.Width = 1024;
|
|
resDesc2.Height = 1024;
|
|
resDesc2.DepthOrArraySize = 1;
|
|
resDesc2.MipLevels = 0;
|
|
resDesc2.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
resDesc2.SampleDesc.Count = 1;
|
|
resDesc2.SampleDesc.Quality = 0;
|
|
resDesc2.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
|
resDesc2.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
|
|
|
|
const D3D12_RESOURCE_ALLOCATION_INFO allocInfo1 =
|
|
device->GetResourceAllocationInfo(0, 1, &resDesc1);
|
|
const D3D12_RESOURCE_ALLOCATION_INFO allocInfo2 =
|
|
device->GetResourceAllocationInfo(0, 1, &resDesc2);
|
|
|
|
D3D12_RESOURCE_ALLOCATION_INFO finalAllocInfo = {};
|
|
finalAllocInfo.Alignment = std::max(allocInfo1.Alignment, allocInfo2.Alignment);
|
|
finalAllocInfo.SizeInBytes = std::max(allocInfo1.SizeInBytes, allocInfo2.SizeInBytes);
|
|
|
|
D3D12MA::ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
|
|
allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;
|
|
|
|
D3D12MA::Allocation* alloc;
|
|
hr = allocator->AllocateMemory(&allocDesc, &finalAllocInfo, &alloc);
|
|
assert(alloc != NULL && alloc->GetHeap() != NULL);
|
|
|
|
ID3D12Resource* res1;
|
|
hr = allocator->CreateAliasingResource(
|
|
alloc,
|
|
0, // AllocationLocalOffset
|
|
&resDesc1,
|
|
D3D12_RESOURCE_STATE_COMMON,
|
|
NULL, // pOptimizedClearValue
|
|
IID_PPV_ARGS(&res1));
|
|
|
|
ID3D12Resource* res2;
|
|
hr = allocator->CreateAliasingResource(
|
|
alloc,
|
|
0, // AllocationLocalOffset
|
|
&resDesc2,
|
|
D3D12_RESOURCE_STATE_COMMON,
|
|
NULL, // pOptimizedClearValue
|
|
IID_PPV_ARGS(&res2));
|
|
|
|
// You can use res1 and res2, but not at the same time!
|
|
|
|
res2->Release();
|
|
res1->Release();
|
|
alloc->Release();
|
|
\endcode
|
|
|
|
Remember that using resouces that alias in memory requires proper synchronization.
|
|
You need to issue a special barrier of type `D3D12_RESOURCE_BARRIER_TYPE_ALIASING`.
|
|
You also need to treat a resource after aliasing as uninitialized - containing garbage data.
|
|
For example, if you use `res1` and then want to use `res2`, you need to first initialize `res2`
|
|
using either Clear, Discard, or Copy to the entire resource.
|
|
|
|
Additional considerations:
|
|
|
|
- D3D12 also allows to interpret contents of memory between aliasing resources consistently in some cases,
|
|
which is called "data inheritance". For details, see
|
|
Microsoft documentation, chapter [Memory Aliasing and Data Inheritance](https://docs.microsoft.com/en-us/windows/win32/direct3d12/memory-aliasing-and-data-inheritance).
|
|
- You can create more complex layout where different textures and buffers are bound
|
|
at different offsets inside one large allocation. For example, one can imagine
|
|
a big texture used in some render passes, aliasing with a set of many small buffers
|
|
used in some further passes. To bind a resource at non-zero offset of an allocation,
|
|
call D3D12MA::Allocator::CreateAliasingResource with appropriate value of `AllocationLocalOffset` parameter.
|
|
- Resources of the three categories: buffers, textures with `RENDER_TARGET` or `DEPTH_STENCIL` flags, and all other textures,
|
|
can be placed in the same memory only when `allocator->GetD3D12Options().ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2`.
|
|
Otherwise they must be placed in different memory heap types, and thus aliasing them is not possible.
|
|
|
|
|
|
\page virtual_allocator Virtual allocator
|
|
|
|
As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator".
|
|
It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block".
|
|
You can use it to allocate your own memory or other objects, even completely unrelated to D3D12.
|
|
A common use case is sub-allocation of pieces of one large GPU buffer.
|
|
|
|
\section virtual_allocator_creating_virtual_block Creating virtual block
|
|
|
|
To use this functionality, there is no main "allocator" object.
|
|
You don't need to have D3D12MA::Allocator object created.
|
|
All you need to do is to create a separate D3D12MA::VirtualBlock object for each block of memory you want to be managed by the allocator:
|
|
|
|
-# Fill in D3D12MA::ALLOCATOR_DESC structure.
|
|
-# Call D3D12MA::CreateVirtualBlock. Get new D3D12MA::VirtualBlock object.
|
|
|
|
Example:
|
|
|
|
\code
|
|
D3D12MA::VIRTUAL_BLOCK_DESC blockDesc = {};
|
|
blockDesc.Size = 1048576; // 1 MB
|
|
|
|
D3D12MA::VirtualBlock *block;
|
|
HRESULT hr = CreateVirtualBlock(&blockDesc, &block);
|
|
\endcode
|
|
|
|
\section virtual_allocator_making_virtual_allocations Making virtual allocations
|
|
|
|
D3D12MA::VirtualBlock object contains internal data structure that keeps track of free and occupied regions
|
|
using the same code as the main D3D12 memory allocator.
|
|
However, there is no "virtual allocation" object.
|
|
When you request a new allocation, a `UINT64` number is returned.
|
|
It is an offset inside the block where the allocation has been placed, but it also uniquely identifies the allocation within this block.
|
|
|
|
In order to make an allocation:
|
|
|
|
-# Fill in D3D12MA::VIRTUAL_ALLOCATION_DESC structure.
|
|
-# Call D3D12MA::VirtualBlock::Allocate. Get new `UINT64 offset` that identifies the allocation.
|
|
|
|
Example:
|
|
|
|
\code
|
|
D3D12MA::VIRTUAL_ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.Size = 4096; // 4 KB
|
|
|
|
UINT64 allocOffset;
|
|
hr = block->Allocate(&allocDesc, &allocOffset);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
// Use the 4 KB of your memory starting at allocOffset.
|
|
}
|
|
else
|
|
{
|
|
// Allocation failed - no space for it could be found. Handle this error!
|
|
}
|
|
\endcode
|
|
|
|
\section virtual_allocator_deallocation Deallocation
|
|
|
|
When no longer needed, an allocation can be freed by calling D3D12MA::VirtualBlock::FreeAllocation.
|
|
You can only pass to this function the exact offset that was previously returned by D3D12MA::VirtualBlock::Allocate
|
|
and not any other location within the memory.
|
|
|
|
When whole block is no longer needed, the block object can be released by calling D3D12MA::VirtualBlock::Release.
|
|
All allocations must be freed before the block is destroyed, which is checked internally by an assert.
|
|
However, if you don't want to call `block->FreeAllocation` for each allocation, you can use D3D12MA::VirtualBlock::Clear to free them all at once -
|
|
a feature not available in normal D3D12 memory allocator. Example:
|
|
|
|
\code
|
|
block->FreeAllocation(allocOffset);
|
|
block->Release();
|
|
\endcode
|
|
|
|
\section virtual_allocator_allocation_parameters Allocation parameters
|
|
|
|
You can attach a custom pointer to each allocation by using D3D12MA::VirtualBlock::SetAllocationUserData.
|
|
Its default value is `NULL`.
|
|
It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some
|
|
larger data structure containing more information. Example:
|
|
|
|
\code
|
|
struct CustomAllocData
|
|
{
|
|
std::string m_AllocName;
|
|
};
|
|
CustomAllocData* allocData = new CustomAllocData();
|
|
allocData->m_AllocName = "My allocation 1";
|
|
block->SetAllocationUserData(allocOffset, allocData);
|
|
\endcode
|
|
|
|
The pointer can later be fetched, along with allocation size, by passing the allocation offset to function
|
|
D3D12MA::VirtualBlock::GetAllocationInfo and inspecting returned structure D3D12MA::VIRTUAL_ALLOCATION_INFO.
|
|
If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation!
|
|
Example:
|
|
|
|
\code
|
|
VIRTUAL_ALLOCATION_INFO allocInfo;
|
|
block->GetAllocationInfo(allocOffset, &allocInfo);
|
|
delete (CustomAllocData*)allocInfo.pUserData;
|
|
|
|
block->FreeAllocation(allocOffset);
|
|
\endcode
|
|
|
|
\section virtual_allocator_alignment_and_units Alignment and units
|
|
|
|
It feels natural to express sizes and offsets in bytes.
|
|
If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member
|
|
D3D12MA::VIRTUAL_ALLOCATION_DESC::Alignment to request it. Example:
|
|
|
|
\code
|
|
D3D12MA::VIRTUAL_ALLOCATION_DESC allocDesc = {};
|
|
allocDesc.Size = 4096; // 4 KB
|
|
allocDesc.Alignment = 4; // Returned offset must be a multiply of 4 B
|
|
|
|
UINT64 allocOffset;
|
|
hr = block->Allocate(&allocDesc, &allocOffset);
|
|
\endcode
|
|
|
|
Alignments of different allocations made from one block may vary.
|
|
However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`,
|
|
you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes.
|
|
It might be more convenient, but you need to make sure to use this new unit consistently in all the places:
|
|
|
|
- D3D12MA::VIRTUAL_BLOCK_DESC::Size
|
|
- D3D12MA::VIRTUAL_ALLOCATION_DESC::Size and D3D12MA::VIRTUAL_ALLOCATION_DESC::Alignment
|
|
- Using offset returned by D3D12MA::VirtualBlock::Allocate
|
|
|
|
\section virtual_allocator_statistics Statistics
|
|
|
|
You can obtain statistics of a virtual block using D3D12MA::VirtualBlock::CalculateStats.
|
|
The function fills structure D3D12MA::StatInfo - same as used by the normal D3D12 memory allocator.
|
|
Example:
|
|
|
|
\code
|
|
D3D12MA::StatInfo statInfo;
|
|
block->CalculateStats(&statInfo);
|
|
printf("My virtual block has %llu bytes used by %u virtual allocations\n",
|
|
statInfo.UsedBytes, statInfo.AllocationCount);
|
|
\endcode
|
|
|
|
You can also request a full list of allocations and free regions as a string in JSON format by calling
|
|
D3D12MA::VirtualBlock::BuildStatsString.
|
|
Returned string must be later freed using D3D12MA::VirtualBlock::FreeStatsString.
|
|
The format of this string may differ from the one returned by the main D3D12 allocator, but it is similar.
|
|
|
|
\section virtual_allocator_additional_considerations Additional considerations
|
|
|
|
Note that the "virtual allocator" functionality is implemented on a level of individual memory blocks.
|
|
Keeping track of a whole collection of blocks, allocating new ones when out of free space,
|
|
deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.
|
|
|
|
|
|
\page configuration Configuration
|
|
|
|
Please check file `D3D12MemAlloc.cpp` lines between "Configuration Begin" and
|
|
"Configuration End" to find macros that you can define to change the behavior of
|
|
the library, primarily for debugging purposes.
|
|
|
|
\section custom_memory_allocator Custom CPU memory allocator
|
|
|
|
If you use custom allocator for CPU memory rather than default C++ operator `new`
|
|
and `delete` or `malloc` and `free` functions, you can make this library using
|
|
your allocator as well by filling structure D3D12MA::ALLOCATION_CALLBACKS and
|
|
passing it as optional member D3D12MA::ALLOCATOR_DESC::pAllocationCallbacks.
|
|
Functions pointed there will be used by the library to make any CPU-side
|
|
allocations. Example:
|
|
|
|
\code
|
|
#include <malloc.h>
|
|
|
|
void* CustomAllocate(size_t Size, size_t Alignment, void* pUserData)
|
|
{
|
|
void* memory = _aligned_malloc(Size, Alignment);
|
|
// Your extra bookkeeping here...
|
|
return memory;
|
|
}
|
|
|
|
void CustomFree(void* pMemory, void* pUserData)
|
|
{
|
|
// Your extra bookkeeping here...
|
|
_aligned_free(pMemory);
|
|
}
|
|
|
|
(...)
|
|
|
|
D3D12MA::ALLOCATION_CALLBACKS allocationCallbacks = {};
|
|
allocationCallbacks.pAllocate = &CustomAllocate;
|
|
allocationCallbacks.pFree = &CustomFree;
|
|
|
|
D3D12MA::ALLOCATOR_DESC allocatorDesc = {};
|
|
allocatorDesc.pDevice = device;
|
|
allocatorDesc.pAdapter = adapter;
|
|
allocatorDesc.pAllocationCallbacks = &allocationCallbacks;
|
|
|
|
D3D12MA::Allocator* allocator;
|
|
HRESULT hr = D3D12MA::CreateAllocator(&allocatorDesc, &allocator);
|
|
\endcode
|
|
|
|
|
|
\page general_considerations General considerations
|
|
|
|
\section general_considerations_thread_safety Thread safety
|
|
|
|
- The library has no global state, so separate D3D12MA::Allocator objects can be used independently.
|
|
In typical applications there should be no need to create multiple such objects though - one per `ID3D12Device` is enough.
|
|
- All calls to methods of D3D12MA::Allocator class are safe to be made from multiple
|
|
threads simultaneously because they are synchronized internally when needed.
|
|
- When the allocator is created with D3D12MA::ALLOCATOR_FLAG_SINGLETHREADED,
|
|
calls to methods of D3D12MA::Allocator class must be made from a single thread or synchronized by the user.
|
|
Using this flag may improve performance.
|
|
|
|
\section general_considerations_future_plans Future plans
|
|
|
|
Features planned for future releases:
|
|
|
|
Near future: feature parity with [Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/), including:
|
|
|
|
- Alternative allocation algorithms: linear allocator, buddy allocator
|
|
- Support for priorities using `ID3D12Device1::SetResidencyPriority`
|
|
|
|
Later:
|
|
|
|
- Memory defragmentation
|
|
- Support for multi-GPU (multi-adapter)
|
|
|
|
\section general_considerations_features_not_supported Features not supported
|
|
|
|
Features deliberately excluded from the scope of this library:
|
|
|
|
- **Descriptor allocation.** Although also called "heaps", objects that represent
|
|
descriptors are separate part of the D3D12 API from buffers and textures.
|
|
- **Support for reserved (tiled) resources.** We don't recommend using them.
|
|
- Support for `ID3D12Device::Evict` and `MakeResident`. We don't recommend using them.
|
|
You can call them on the D3D12 objects manually.
|
|
Plese keep in mind, however, that eviction happens on the level of entire `ID3D12Heap` memory blocks
|
|
and not individual buffers or textures which may be placed inside them.
|
|
- **Handling CPU memory allocation failures.** When dynamically creating small C++
|
|
objects in CPU memory (not the GPU memory), allocation failures are not
|
|
handled gracefully, because that would complicate code significantly and
|
|
is usually not needed in desktop PC applications anyway.
|
|
Success of an allocation is just checked with an assert.
|
|
- **Code free of any compiler warnings.**
|
|
There are many preprocessor macros that make some variables unused, function parameters unreferenced,
|
|
or conditional expressions constant in some configurations.
|
|
The code of this library should not be bigger or more complicated just to silence these warnings.
|
|
It is recommended to disable such warnings instead.
|
|
- This is a C++ library. **Bindings or ports to any other programming languages** are welcome as external projects but
|
|
are not going to be included into this repository.
|
|
*/
|