blob: 7655e7249c6505d3aa4f818012dd70492377f614 [file] [log] [blame]
//
// 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.
//
#include "D3D12MemAlloc.h"
#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED
#include <dxgi.h>
#if D3D12MA_DXGI_1_4
#include <dxgi1_4.h>
#endif
#endif
#include <combaseapi.h>
#include <mutex>
#include <algorithm>
#include <utility>
#include <cstdlib>
#include <malloc.h> // for _aligned_malloc, _aligned_free
#ifndef _WIN32
#include <shared_mutex>
#endif
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Configuration Begin
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#ifndef D3D12MA_ASSERT
#include <cassert>
#define D3D12MA_ASSERT(cond) assert(cond)
#endif
// Assert that will be called very often, like inside data structures e.g. operator[].
// Making it non-empty can make program slow.
#ifndef D3D12MA_HEAVY_ASSERT
#ifdef _DEBUG
#define D3D12MA_HEAVY_ASSERT(expr) //D3D12MA_ASSERT(expr)
#else
#define D3D12MA_HEAVY_ASSERT(expr)
#endif
#endif
#ifndef D3D12MA_DEBUG_ALIGNMENT
/*
Minimum alignment of all allocations, in bytes.
Set to more than 1 for debugging purposes only. Must be power of two.
*/
#define D3D12MA_DEBUG_ALIGNMENT (1)
#endif
#ifndef D3D12MA_DEBUG_MARGIN
// Minimum margin before and after every allocation, in bytes.
// Set nonzero for debugging purposes only.
#define D3D12MA_DEBUG_MARGIN (0)
#endif
#ifndef D3D12MA_DEBUG_GLOBAL_MUTEX
/*
Set this to 1 for debugging purposes only, to enable single mutex protecting all
entry calls to the library. Can be useful for debugging multithreading issues.
*/
#define D3D12MA_DEBUG_GLOBAL_MUTEX (0)
#endif
/*
Define this macro for debugging purposes only to force specific D3D12_RESOURCE_HEAP_TIER,
especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs.
*/
//#define D3D12MA_FORCE_RESOURCE_HEAP_TIER D3D12_RESOURCE_HEAP_TIER_1
#ifndef D3D12MA_DEFAULT_BLOCK_SIZE
/// Default size of a block allocated as single ID3D12Heap.
#define D3D12MA_DEFAULT_BLOCK_SIZE (64ull * 1024 * 1024)
#endif
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Configuration End
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#define D3D12MA_IID_PPV_ARGS(ppType) __uuidof(**(ppType)), reinterpret_cast<void**>(ppType)
namespace D3D12MA
{
////////////////////////////////////////////////////////////////////////////////
// Private globals - CPU memory allocation
static void* DefaultAllocate(size_t Size, size_t Alignment, void* /*pUserData*/)
{
#ifdef _WIN32
return _aligned_malloc(Size, Alignment);
#else
return aligned_alloc(Alignment, Size);
#endif
}
static void DefaultFree(void* pMemory, void* /*pUserData*/)
{
#ifdef _WIN32
return _aligned_free(pMemory);
#else
return free(pMemory);
#endif
}
static void* Malloc(const ALLOCATION_CALLBACKS& allocs, size_t size, size_t alignment)
{
void* const result = (*allocs.pAllocate)(size, alignment, allocs.pUserData);
D3D12MA_ASSERT(result);
return result;
}
static void Free(const ALLOCATION_CALLBACKS& allocs, void* memory)
{
(*allocs.pFree)(memory, allocs.pUserData);
}
template<typename T>
static T* Allocate(const ALLOCATION_CALLBACKS& allocs)
{
return (T*)Malloc(allocs, sizeof(T), __alignof(T));
}
template<typename T>
static T* AllocateArray(const ALLOCATION_CALLBACKS& allocs, size_t count)
{
return (T*)Malloc(allocs, sizeof(T) * count, __alignof(T));
}
#define D3D12MA_NEW(allocs, type) new(D3D12MA::Allocate<type>(allocs))(type)
#define D3D12MA_NEW_ARRAY(allocs, type, count) new(D3D12MA::AllocateArray<type>((allocs), (count)))(type)
template<typename T>
void D3D12MA_DELETE(const ALLOCATION_CALLBACKS& allocs, T* memory)
{
if(memory)
{
memory->~T();
Free(allocs, memory);
}
}
template<typename T>
void D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS& allocs, T* memory, size_t count)
{
if(memory)
{
for(size_t i = count; i--; )
{
memory[i].~T();
}
Free(allocs, memory);
}
}
static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATION_CALLBACKS* allocationCallbacks)
{
if(allocationCallbacks)
{
outAllocs = *allocationCallbacks;
D3D12MA_ASSERT(outAllocs.pAllocate != NULL && outAllocs.pFree != NULL);
}
else
{
outAllocs.pAllocate = &DefaultAllocate;
outAllocs.pFree = &DefaultFree;
outAllocs.pUserData = NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
// Private globals - basic facilities
#define SAFE_RELEASE(ptr) do { if(ptr) { (ptr)->Release(); (ptr) = NULL; } } while(false)
#define D3D12MA_VALIDATE(cond) do { if(!(cond)) { \
D3D12MA_ASSERT(0 && "Validation failed: " #cond); \
return false; \
} } while(false)
const UINT NEW_BLOCK_SIZE_SHIFT_MAX = 3;
template<typename T>
static inline T D3D12MA_MIN(const T& a, const T& b)
{
return a <= b ? a : b;
}
template<typename T>
static inline T D3D12MA_MAX(const T& a, const T& b)
{
return a <= b ? b : a;
}
template<typename T>
static inline void D3D12MA_SWAP(T& a, T& b)
{
T tmp = a; a = b; b = tmp;
}
#ifndef D3D12MA_MUTEX
class Mutex
{
public:
void Lock() { m_Mutex.lock(); }
void Unlock() { m_Mutex.unlock(); }
private:
std::mutex m_Mutex;
};
#define D3D12MA_MUTEX Mutex
#endif
#ifdef _WIN32
#if !defined(WINVER) || WINVER < 0x0600
#error Required at least WinAPI version supporting: client = Windows Vista, server = Windows Server 2008.
#endif
#endif // #ifdef _WIN32
#ifndef D3D12MA_RW_MUTEX
#ifdef _WIN32
class RWMutex
{
public:
RWMutex() { InitializeSRWLock(&m_Lock); }
void LockRead() { AcquireSRWLockShared(&m_Lock); }
void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
private:
SRWLOCK m_Lock;
};
#else // #ifdef _WIN32
class RWMutex
{
public:
RWMutex() {}
void LockRead() { m_Mutex.lock_shared(); }
void UnlockRead() { m_Mutex.unlock_shared(); }
void LockWrite() { m_Mutex.lock(); }
void UnlockWrite() { m_Mutex.unlock(); }
private:
std::shared_timed_mutex m_Mutex;
};
#endif // #ifdef _WIN32
#define D3D12MA_RW_MUTEX RWMutex
#endif // #ifndef D3D12MA_RW_MUTEX
/*
Returns true if given number is a power of two.
T must be unsigned integer number or signed integer but always nonnegative.
For 0 returns true.
*/
template <typename T>
inline bool IsPow2(T x)
{
return (x & (x-1)) == 0;
}
// Aligns given value up to nearest multiply of align value. For example: AlignUp(11, 8) = 16.
// Use types like UINT, uint64_t as T.
template <typename T>
static inline T AlignUp(T val, T alignment)
{
D3D12MA_HEAVY_ASSERT(IsPow2(alignment));
return (val + alignment - 1) & ~(alignment - 1);
}
// Aligns given value down to nearest multiply of align value. For example: AlignUp(11, 8) = 8.
// Use types like UINT, uint64_t as T.
template <typename T>
static inline T AlignDown(T val, T alignment)
{
D3D12MA_HEAVY_ASSERT(IsPow2(alignment));
return val & ~(alignment - 1);
}
// Division with mathematical rounding to nearest number.
template <typename T>
static inline T RoundDiv(T x, T y)
{
return (x + (y / (T)2)) / y;
}
template <typename T>
static inline T DivideRoudingUp(T x, T y)
{
return (x + y - 1) / y;
}
// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
struct MutexLock
{
public:
MutexLock(D3D12MA_MUTEX& mutex, bool useMutex = true) :
m_pMutex(useMutex ? &mutex : NULL)
{
if(m_pMutex)
{
m_pMutex->Lock();
}
}
~MutexLock()
{
if(m_pMutex)
{
m_pMutex->Unlock();
}
}
private:
D3D12MA_MUTEX* m_pMutex;
D3D12MA_CLASS_NO_COPY(MutexLock)
};
// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
struct MutexLockRead
{
public:
MutexLockRead(D3D12MA_RW_MUTEX& mutex, bool useMutex) :
m_pMutex(useMutex ? &mutex : NULL)
{
if(m_pMutex)
{
m_pMutex->LockRead();
}
}
~MutexLockRead()
{
if(m_pMutex)
{
m_pMutex->UnlockRead();
}
}
private:
D3D12MA_RW_MUTEX* m_pMutex;
D3D12MA_CLASS_NO_COPY(MutexLockRead)
};
// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
struct MutexLockWrite
{
public:
MutexLockWrite(D3D12MA_RW_MUTEX& mutex, bool useMutex) :
m_pMutex(useMutex ? &mutex : NULL)
{
if(m_pMutex)
{
m_pMutex->LockWrite();
}
}
~MutexLockWrite()
{
if(m_pMutex)
{
m_pMutex->UnlockWrite();
}
}
private:
D3D12MA_RW_MUTEX* m_pMutex;
D3D12MA_CLASS_NO_COPY(MutexLockWrite)
};
#if D3D12MA_DEBUG_GLOBAL_MUTEX
static D3D12MA_MUTEX g_DebugGlobalMutex;
#define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK MutexLock debugGlobalMutexLock(g_DebugGlobalMutex, true);
#else
#define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK
#endif
// Minimum size of a free suballocation to register it in the free suballocation collection.
static const UINT64 MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
/*
Performs binary search and returns iterator to first element that is greater or
equal to `key`, according to comparison `cmp`.
Cmp should return true if first argument is less than second argument.
Returned value is the found element, if present in the collection or place where
new element with value (key) should be inserted.
*/
template <typename CmpLess, typename IterT, typename KeyT>
static IterT BinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, const CmpLess& cmp)
{
size_t down = 0, up = (end - beg);
while(down < up)
{
const size_t mid = (down + up) / 2;
if(cmp(*(beg+mid), key))
{
down = mid + 1;
}
else
{
up = mid;
}
}
return beg + down;
}
/*
Performs binary search and returns iterator to an element that is equal to `key`,
according to comparison `cmp`.
Cmp should return true if first argument is less than second argument.
Returned value is the found element, if present in the collection or end if not
found.
*/
template<typename CmpLess, typename IterT, typename KeyT>
IterT BinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)
{
IterT it = BinaryFindFirstNotLess<CmpLess, IterT, KeyT>(beg, end, value, cmp);
if(it == end ||
(!cmp(*it, value) && !cmp(value, *it)))
{
return it;
}
return end;
}
static UINT HeapTypeToIndex(D3D12_HEAP_TYPE type)
{
switch(type)
{
case D3D12_HEAP_TYPE_DEFAULT: return 0;
case D3D12_HEAP_TYPE_UPLOAD: return 1;
case D3D12_HEAP_TYPE_READBACK: return 2;
case D3D12_HEAP_TYPE_CUSTOM: return 3;
default: D3D12MA_ASSERT(0); return UINT_MAX;
}
}
static const WCHAR* const HeapTypeNames[] = {
L"DEFAULT",
L"UPLOAD",
L"READBACK",
L"CUSTOM",
};
// Stat helper functions
static void AddStatInfo(StatInfo& dst, const StatInfo& src)
{
dst.BlockCount += src.BlockCount;
dst.AllocationCount += src.AllocationCount;
dst.UnusedRangeCount += src.UnusedRangeCount;
dst.UsedBytes += src.UsedBytes;
dst.UnusedBytes += src.UnusedBytes;
dst.AllocationSizeMin = D3D12MA_MIN(dst.AllocationSizeMin, src.AllocationSizeMin);
dst.AllocationSizeMax = D3D12MA_MAX(dst.AllocationSizeMax, src.AllocationSizeMax);
dst.UnusedRangeSizeMin = D3D12MA_MIN(dst.UnusedRangeSizeMin, src.UnusedRangeSizeMin);
dst.UnusedRangeSizeMax = D3D12MA_MAX(dst.UnusedRangeSizeMax, src.UnusedRangeSizeMax);
}
static void PostProcessStatInfo(StatInfo& statInfo)
{
statInfo.AllocationSizeAvg = statInfo.AllocationCount ?
statInfo.UsedBytes / statInfo.AllocationCount : 0;
statInfo.UnusedRangeSizeAvg = statInfo.UnusedRangeCount ?
statInfo.UnusedBytes / statInfo.UnusedRangeCount : 0;
}
static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags)
{
/*
Documentation of D3D12_HEAP_DESC structure says:
- D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT defined as 64KB.
- D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT defined as 4MB. An
application must decide whether the heap will contain multi-sample
anti-aliasing (MSAA), in which case, the application must choose [this flag].
https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_heap_desc
*/
const D3D12_HEAP_FLAGS denyAllTexturesFlags =
D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
const bool canContainAnyTextures =
(flags & denyAllTexturesFlags) != denyAllTexturesFlags;
return canContainAnyTextures ?
D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
}
static bool IsFormatCompressed(DXGI_FORMAT format)
{
switch(format)
{
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
return true;
default:
return false;
}
}
// Only some formats are supported. For others it returns 0.
static UINT GetBitsPerPixel(DXGI_FORMAT format)
{
switch(format)
{
case DXGI_FORMAT_R32G32B32A32_TYPELESS:
case DXGI_FORMAT_R32G32B32A32_FLOAT:
case DXGI_FORMAT_R32G32B32A32_UINT:
case DXGI_FORMAT_R32G32B32A32_SINT:
return 128;
case DXGI_FORMAT_R32G32B32_TYPELESS:
case DXGI_FORMAT_R32G32B32_FLOAT:
case DXGI_FORMAT_R32G32B32_UINT:
case DXGI_FORMAT_R32G32B32_SINT:
return 96;
case DXGI_FORMAT_R16G16B16A16_TYPELESS:
case DXGI_FORMAT_R16G16B16A16_FLOAT:
case DXGI_FORMAT_R16G16B16A16_UNORM:
case DXGI_FORMAT_R16G16B16A16_UINT:
case DXGI_FORMAT_R16G16B16A16_SNORM:
case DXGI_FORMAT_R16G16B16A16_SINT:
return 64;
case DXGI_FORMAT_R32G32_TYPELESS:
case DXGI_FORMAT_R32G32_FLOAT:
case DXGI_FORMAT_R32G32_UINT:
case DXGI_FORMAT_R32G32_SINT:
return 64;
case DXGI_FORMAT_R32G8X24_TYPELESS:
case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
return 64;
case DXGI_FORMAT_R10G10B10A2_TYPELESS:
case DXGI_FORMAT_R10G10B10A2_UNORM:
case DXGI_FORMAT_R10G10B10A2_UINT:
case DXGI_FORMAT_R11G11B10_FLOAT:
return 32;
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_R8G8B8A8_UINT:
case DXGI_FORMAT_R8G8B8A8_SNORM:
case DXGI_FORMAT_R8G8B8A8_SINT:
return 32;
case DXGI_FORMAT_R16G16_TYPELESS:
case DXGI_FORMAT_R16G16_FLOAT:
case DXGI_FORMAT_R16G16_UNORM:
case DXGI_FORMAT_R16G16_UINT:
case DXGI_FORMAT_R16G16_SNORM:
case DXGI_FORMAT_R16G16_SINT:
return 32;
case DXGI_FORMAT_R32_TYPELESS:
case DXGI_FORMAT_D32_FLOAT:
case DXGI_FORMAT_R32_FLOAT:
case DXGI_FORMAT_R32_UINT:
case DXGI_FORMAT_R32_SINT:
return 32;
case DXGI_FORMAT_R24G8_TYPELESS:
case DXGI_FORMAT_D24_UNORM_S8_UINT:
case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
return 32;
case DXGI_FORMAT_R8G8_TYPELESS:
case DXGI_FORMAT_R8G8_UNORM:
case DXGI_FORMAT_R8G8_UINT:
case DXGI_FORMAT_R8G8_SNORM:
case DXGI_FORMAT_R8G8_SINT:
return 16;
case DXGI_FORMAT_R16_TYPELESS:
case DXGI_FORMAT_R16_FLOAT:
case DXGI_FORMAT_D16_UNORM:
case DXGI_FORMAT_R16_UNORM:
case DXGI_FORMAT_R16_UINT:
case DXGI_FORMAT_R16_SNORM:
case DXGI_FORMAT_R16_SINT:
return 16;
case DXGI_FORMAT_R8_TYPELESS:
case DXGI_FORMAT_R8_UNORM:
case DXGI_FORMAT_R8_UINT:
case DXGI_FORMAT_R8_SNORM:
case DXGI_FORMAT_R8_SINT:
case DXGI_FORMAT_A8_UNORM:
return 8;
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
return 4;
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
return 8;
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
return 8;
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM:
return 4;
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
return 8;
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
return 8;
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB:
return 8;
default:
return 0;
}
}
static const D3D12_HEAP_FLAGS RESOURCE_CLASS_HEAP_FLAGS =
D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;
enum class ResourceClass
{
Unknown, Buffer, Non_RT_DS_Texture, RT_DS_Texture
};
template<typename D3D12_RESOURCE_DESC_T>
static inline ResourceClass ResourceDescToResourceClass(const D3D12_RESOURCE_DESC_T& resDesc)
{
if(resDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
return ResourceClass::Buffer;
// Else: it's surely a texture.
const bool isRenderTargetOrDepthStencil =
(resDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;
return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture;
}
static inline ResourceClass HeapFlagsToResourceClass(D3D12_HEAP_FLAGS heapFlags)
{
const bool allowBuffers = (heapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS ) == 0;
const bool allowRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES ) == 0;
const bool allowNonRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;
const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);
if(allowedGroupCount != 1)
return ResourceClass::Unknown;
if(allowRtDsTextures)
return ResourceClass::RT_DS_Texture;
if(allowNonRtDsTextures)
return ResourceClass::Non_RT_DS_Texture;
return ResourceClass::Buffer;
}
// This algorithm is overly conservative.
template<typename D3D12_RESOURCE_DESC_T>
static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc)
{
if(resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)
return false;
if((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0)
return false;
if(resourceDesc.SampleDesc.Count > 1)
return false;
if(resourceDesc.DepthOrArraySize != 1)
return false;
UINT sizeX = (UINT)resourceDesc.Width;
UINT sizeY = resourceDesc.Height;
UINT bitsPerPixel = GetBitsPerPixel(resourceDesc.Format);
if(bitsPerPixel == 0)
return false;
if(IsFormatCompressed(resourceDesc.Format))
{
sizeX = DivideRoudingUp(sizeX / 4, 1u);
sizeY = DivideRoudingUp(sizeY / 4, 1u);
bitsPerPixel *= 16;
}
UINT tileSizeX = 0, tileSizeY = 0;
switch(bitsPerPixel)
{
case 8: tileSizeX = 64; tileSizeY = 64; break;
case 16: tileSizeX = 64; tileSizeY = 32; break;
case 32: tileSizeX = 32; tileSizeY = 32; break;
case 64: tileSizeX = 32; tileSizeY = 16; break;
case 128: tileSizeX = 16; tileSizeY = 16; break;
default: return false;
}
const UINT tileCount = DivideRoudingUp(sizeX, tileSizeX) * DivideRoudingUp(sizeY, tileSizeY);
return tileCount <= 16;
}
static inline bool IsHeapTypeStandard(D3D12_HEAP_TYPE type)
{
return type == D3D12_HEAP_TYPE_DEFAULT ||
type == D3D12_HEAP_TYPE_UPLOAD ||
type == D3D12_HEAP_TYPE_READBACK;
}
static inline D3D12_HEAP_PROPERTIES StandardHeapTypeToHeapProperties(D3D12_HEAP_TYPE type)
{
D3D12MA_ASSERT(IsHeapTypeStandard(type));
D3D12_HEAP_PROPERTIES result = {};
result.Type = type;
return result;
}
////////////////////////////////////////////////////////////////////////////////
// Private class Vector
/*
Dynamically resizing continuous array. Class with interface similar to std::vector.
T must be POD because constructors and destructors are not called and memcpy is
used for these objects.
*/
template<typename T>
class Vector
{
public:
using value_type = T;
// allocationCallbacks externally owned, must outlive this object.
Vector(const ALLOCATION_CALLBACKS& allocationCallbacks) :
m_AllocationCallbacks(allocationCallbacks),
m_pArray(NULL),
m_Count(0),
m_Capacity(0)
{
}
Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks) :
m_AllocationCallbacks(allocationCallbacks),
m_pArray(count ? AllocateArray<T>(allocationCallbacks, count) : NULL),
m_Count(count),
m_Capacity(count)
{
}
Vector(const Vector<T>& src) :
m_AllocationCallbacks(src.m_AllocationCallbacks),
m_pArray(src.m_Count ? AllocateArray<T>(src.m_AllocationCallbacks, src.m_Count) : NULL),
m_Count(src.m_Count),
m_Capacity(src.m_Count)
{
if(m_Count > 0)
{
memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
}
}
~Vector()
{
Free(m_AllocationCallbacks, m_pArray);
}
Vector& operator=(const Vector<T>& rhs)
{
if(&rhs != this)
{
resize(rhs.m_Count);
if(m_Count != 0)
{
memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
}
}
return *this;
}
bool empty() const { return m_Count == 0; }
size_t size() const { return m_Count; }
T* data() { return m_pArray; }
const T* data() const { return m_pArray; }
T& operator[](size_t index)
{
D3D12MA_HEAVY_ASSERT(index < m_Count);
return m_pArray[index];
}
const T& operator[](size_t index) const
{
D3D12MA_HEAVY_ASSERT(index < m_Count);
return m_pArray[index];
}
T& front()
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
return m_pArray[0];
}
const T& front() const
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
return m_pArray[0];
}
T& back()
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
return m_pArray[m_Count - 1];
}
const T& back() const
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
return m_pArray[m_Count - 1];
}
void reserve(size_t newCapacity, bool freeMemory = false)
{
newCapacity = D3D12MA_MAX(newCapacity, m_Count);
if((newCapacity < m_Capacity) && !freeMemory)
{
newCapacity = m_Capacity;
}
if(newCapacity != m_Capacity)
{
T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;
if(m_Count != 0)
{
memcpy(newArray, m_pArray, m_Count * sizeof(T));
}
Free(m_AllocationCallbacks, m_pArray);
m_Capacity = newCapacity;
m_pArray = newArray;
}
}
void resize(size_t newCount, bool freeMemory = false)
{
size_t newCapacity = m_Capacity;
if(newCount > m_Capacity)
{
newCapacity = D3D12MA_MAX(newCount, D3D12MA_MAX(m_Capacity * 3 / 2, (size_t)8));
}
else if(freeMemory)
{
newCapacity = newCount;
}
if(newCapacity != m_Capacity)
{
T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;
const size_t elementsToCopy = D3D12MA_MIN(m_Count, newCount);
if(elementsToCopy != 0)
{
memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
}
Free(m_AllocationCallbacks, m_pArray);
m_Capacity = newCapacity;
m_pArray = newArray;
}
m_Count = newCount;
}
void clear(bool freeMemory = false)
{
resize(0, freeMemory);
}
void insert(size_t index, const T& src)
{
D3D12MA_HEAVY_ASSERT(index <= m_Count);
const size_t oldCount = size();
resize(oldCount + 1);
if(index < oldCount)
{
memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
}
m_pArray[index] = src;
}
void remove(size_t index)
{
D3D12MA_HEAVY_ASSERT(index < m_Count);
const size_t oldCount = size();
if(index < oldCount - 1)
{
memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
}
resize(oldCount - 1);
}
void push_back(const T& src)
{
const size_t newIndex = size();
resize(newIndex + 1);
m_pArray[newIndex] = src;
}
void pop_back()
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
resize(size() - 1);
}
void push_front(const T& src)
{
insert(0, src);
}
void pop_front()
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
remove(0);
}
using iterator = T*;
iterator begin() { return m_pArray; }
iterator end() { return m_pArray + m_Count; }
template<typename CmpLess>
size_t InsertSorted(const T& value, const CmpLess& cmp)
{
const size_t indexToInsert = BinaryFindFirstNotLess<CmpLess, iterator, T>(
m_pArray,
m_pArray + m_Count,
value,
cmp) - m_pArray;
insert(indexToInsert, value);
return indexToInsert;
}
template<typename CmpLess>
bool RemoveSorted(const T& value, const CmpLess& cmp)
{
const iterator it = BinaryFindFirstNotLess(
m_pArray,
m_pArray + m_Count,
value,
cmp);
if((it != end()) && !cmp(*it, value) && !cmp(value, *it))
{
size_t indexToRemove = it - begin();
remove(indexToRemove);
return true;
}
return false;
}
private:
const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
T* m_pArray;
size_t m_Count;
size_t m_Capacity;
};
////////////////////////////////////////////////////////////////////////////////
// Private class StringBuilder
class StringBuilder
{
public:
StringBuilder(const ALLOCATION_CALLBACKS& allocationCallbacks) : m_Data(allocationCallbacks) { }
size_t GetLength() const { return m_Data.size(); }
LPCWSTR GetData() const { return m_Data.data(); }
void Add(WCHAR ch) { m_Data.push_back(ch); }
void Add(LPCWSTR str);
void AddNewLine() { Add(L'\n'); }
void AddNumber(UINT num);
void AddNumber(UINT64 num);
private:
Vector<WCHAR> m_Data;
};
void StringBuilder::Add(LPCWSTR str)
{
const size_t len = wcslen(str);
if (len > 0)
{
const size_t oldCount = m_Data.size();
m_Data.resize(oldCount + len);
memcpy(m_Data.data() + oldCount, str, len * sizeof(WCHAR));
}
}
void StringBuilder::AddNumber(UINT num)
{
WCHAR buf[11];
buf[10] = L'\0';
WCHAR *p = &buf[10];
do
{
*--p = L'0' + (num % 10);
num /= 10;
}
while (num);
Add(p);
}
void StringBuilder::AddNumber(UINT64 num)
{
WCHAR buf[21];
buf[20] = L'\0';
WCHAR *p = &buf[20];
do
{
*--p = L'0' + (num % 10);
num /= 10;
}
while (num);
Add(p);
}
////////////////////////////////////////////////////////////////////////////////
// Private class JsonWriter
/*
Allows to conveniently build a correct JSON document to be written to the
StringBuilder passed to the constructor.
*/
class JsonWriter
{
public:
// stringBuilder - string builder to write the document to. Must remain alive for the whole lifetime of this object.
JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder);
~JsonWriter();
// Begins object by writing "{".
// Inside an object, you must call pairs of WriteString and a value, e.g.:
// j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();
// Will write: { "A": 1, "B": 2 }
void BeginObject(bool singleLine = false);
// Ends object by writing "}".
void EndObject();
// Begins array by writing "[".
// Inside an array, you can write a sequence of any values.
void BeginArray(bool singleLine = false);
// Ends array by writing "[".
void EndArray();
// Writes a string value inside "".
// pStr can contain any UTF-16 characters, including '"', new line etc. - they will be properly escaped.
void WriteString(LPCWSTR pStr);
// Begins writing a string value.
// Call BeginString, ContinueString, ContinueString, ..., EndString instead of
// WriteString to conveniently build the string content incrementally, made of
// parts including numbers.
void BeginString(LPCWSTR pStr = NULL);
// Posts next part of an open string.
void ContinueString(LPCWSTR pStr);
// Posts next part of an open string. The number is converted to decimal characters.
void ContinueString(UINT num);
void ContinueString(UINT64 num);
// Posts next part of an open string. Pointer value is converted to characters
// using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00
// void ContinueString_Pointer(const void* ptr);
// Ends writing a string value by writing '"'.
void EndString(LPCWSTR pStr = NULL);
void AddAllocationToObject(const Allocation& alloc);
// Writes a number value.
void WriteNumber(UINT num);
void WriteNumber(UINT64 num);
// Writes a boolean value - false or true.
void WriteBool(bool b);
// Writes a null value.
void WriteNull();
private:
static const WCHAR* const INDENT;
enum CollectionType
{
COLLECTION_TYPE_OBJECT,
COLLECTION_TYPE_ARRAY,
};
struct StackItem
{
CollectionType type;
UINT valueCount;
bool singleLineMode;
};
StringBuilder& m_SB;
Vector<StackItem> m_Stack;
bool m_InsideString;
void BeginValue(bool isString);
void WriteIndent(bool oneLess = false);
};
const WCHAR* const JsonWriter::INDENT = L" ";
JsonWriter::JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder) :
m_SB(stringBuilder),
m_Stack(allocationCallbacks),
m_InsideString(false)
{
}
JsonWriter::~JsonWriter()
{
D3D12MA_ASSERT(!m_InsideString);
D3D12MA_ASSERT(m_Stack.empty());
}
void JsonWriter::BeginObject(bool singleLine)
{
D3D12MA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.Add(L'{');
StackItem stackItem;
stackItem.type = COLLECTION_TYPE_OBJECT;
stackItem.valueCount = 0;
stackItem.singleLineMode = singleLine;
m_Stack.push_back(stackItem);
}
void JsonWriter::EndObject()
{
D3D12MA_ASSERT(!m_InsideString);
D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
D3D12MA_ASSERT(m_Stack.back().valueCount % 2 == 0);
WriteIndent(true);
m_SB.Add(L'}');
m_Stack.pop_back();
}
void JsonWriter::BeginArray(bool singleLine)
{
D3D12MA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.Add(L'[');
StackItem stackItem;
stackItem.type = COLLECTION_TYPE_ARRAY;
stackItem.valueCount = 0;
stackItem.singleLineMode = singleLine;
m_Stack.push_back(stackItem);
}
void JsonWriter::EndArray()
{
D3D12MA_ASSERT(!m_InsideString);
D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
WriteIndent(true);
m_SB.Add(L']');
m_Stack.pop_back();
}
void JsonWriter::WriteString(LPCWSTR pStr)
{
BeginString(pStr);
EndString();
}
void JsonWriter::BeginString(LPCWSTR pStr)
{
D3D12MA_ASSERT(!m_InsideString);
BeginValue(true);
m_InsideString = true;
m_SB.Add(L'"');
if (pStr != NULL)
{
ContinueString(pStr);
}
}
void JsonWriter::ContinueString(LPCWSTR pStr)
{
D3D12MA_ASSERT(m_InsideString);
D3D12MA_ASSERT(pStr);
for (const WCHAR *p = pStr; *p; ++p)
{
// the strings we encode are assumed to be in UTF-16LE format, the native
// windows wide character Unicode format. In this encoding Unicode code
// points U+0000 to U+D7FF and U+E000 to U+FFFF are encoded in two bytes,
// and everything else takes more than two bytes. We will reject any
// multi wchar character encodings for simplicity.
UINT val = (UINT)*p;
D3D12MA_ASSERT(((val <= 0xD7FF) || (0xE000 <= val && val <= 0xFFFF)) &&
"Character not currently supported.");
switch (*p)
{
case L'"': m_SB.Add(L'\\'); m_SB.Add(L'"'); break;
case L'\\': m_SB.Add(L'\\'); m_SB.Add(L'\\'); break;
case L'/': m_SB.Add(L'\\'); m_SB.Add(L'/'); break;
case L'\b': m_SB.Add(L'\\'); m_SB.Add(L'b'); break;
case L'\f': m_SB.Add(L'\\'); m_SB.Add(L'f'); break;
case L'\n': m_SB.Add(L'\\'); m_SB.Add(L'n'); break;
case L'\r': m_SB.Add(L'\\'); m_SB.Add(L'r'); break;
case L'\t': m_SB.Add(L'\\'); m_SB.Add(L't'); break;
default:
// conservatively use encoding \uXXXX for any Unicode character
// requiring more than one byte.
if (32 <= val && val < 256)
m_SB.Add(*p);
else
{
m_SB.Add(L'\\');
m_SB.Add(L'u');
for (UINT i = 0; i < 4; ++i)
{
UINT hexDigit = (val & 0xF000) >> 12;
val <<= 4;
if (hexDigit < 10)
m_SB.Add(L'0' + (WCHAR)hexDigit);
else
m_SB.Add(L'A' + (WCHAR)hexDigit);
}
}
break;
}
}
}
void JsonWriter::ContinueString(UINT num)
{
D3D12MA_ASSERT(m_InsideString);
m_SB.AddNumber(num);
}
void JsonWriter::ContinueString(UINT64 num)
{
D3D12MA_ASSERT(m_InsideString);
m_SB.AddNumber(num);
}
void JsonWriter::EndString(LPCWSTR pStr)
{
D3D12MA_ASSERT(m_InsideString);
if (pStr)
ContinueString(pStr);
m_SB.Add(L'"');
m_InsideString = false;
}
void JsonWriter::WriteNumber(UINT num)
{
D3D12MA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.AddNumber(num);
}
void JsonWriter::WriteNumber(UINT64 num)
{
D3D12MA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.AddNumber(num);
}
void JsonWriter::WriteBool(bool b)
{
D3D12MA_ASSERT(!m_InsideString);
BeginValue(false);
if (b)
m_SB.Add(L"true");
else
m_SB.Add(L"false");
}
void JsonWriter::WriteNull()
{
D3D12MA_ASSERT(!m_InsideString);
BeginValue(false);
m_SB.Add(L"null");
}
void JsonWriter::BeginValue(bool isString)
{
if (!m_Stack.empty())
{
StackItem& currItem = m_Stack.back();
if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 0)
{
D3D12MA_ASSERT(isString);
}
if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 1)
{
m_SB.Add(L':'); m_SB.Add(L' ');
}
else if (currItem.valueCount > 0)
{
m_SB.Add(L','); m_SB.Add(L' ');
WriteIndent();
}
else
{
WriteIndent();
}
++currItem.valueCount;
}
}
void JsonWriter::WriteIndent(bool oneLess)
{
if (!m_Stack.empty() && !m_Stack.back().singleLineMode)
{
m_SB.AddNewLine();
size_t count = m_Stack.size();
if (count > 0 && oneLess)
{
--count;
}
for (size_t i = 0; i < count; ++i)
{
m_SB.Add(INDENT);
}
}
}
void JsonWriter::AddAllocationToObject(const Allocation& alloc)
{
WriteString(L"Type");
switch (alloc.m_PackedData.GetResourceDimension()) {
case D3D12_RESOURCE_DIMENSION_UNKNOWN:
WriteString(L"UNKNOWN");
break;
case D3D12_RESOURCE_DIMENSION_BUFFER:
WriteString(L"BUFFER");
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE1D:
WriteString(L"TEXTURE1D");
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE2D:
WriteString(L"TEXTURE2D");
break;
case D3D12_RESOURCE_DIMENSION_TEXTURE3D:
WriteString(L"TEXTURE3D");
break;
default: D3D12MA_ASSERT(0); break;
}
WriteString(L"Size");
WriteNumber(alloc.GetSize());
LPCWSTR name = alloc.GetName();
if(name != NULL)
{
WriteString(L"Name");
WriteString(name);
}
if(alloc.m_PackedData.GetResourceFlags())
{
WriteString(L"Flags");
WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags());
}
if(alloc.m_PackedData.GetTextureLayout())
{
WriteString(L"Layout");
WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout());
}
if(alloc.m_CreationFrameIndex)
{
WriteString(L"CreationFrameIndex");
WriteNumber(alloc.m_CreationFrameIndex);
}
}
////////////////////////////////////////////////////////////////////////////////
// Private class PoolAllocator
/*
Allocator for objects of type T using a list of arrays (pools) to speed up
allocation. Number of elements that can be allocated is not bounded because
allocator can create multiple blocks.
T should be POD because constructor and destructor is not called in Alloc or
Free.
*/
template<typename T>
class PoolAllocator
{
D3D12MA_CLASS_NO_COPY(PoolAllocator)
public:
// allocationCallbacks externally owned, must outlive this object.
PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity);
~PoolAllocator() { Clear(); }
void Clear();
template<typename... Types> T* Alloc(Types... args);
void Free(T* ptr);
private:
union Item
{
UINT NextFreeIndex; // UINT32_MAX means end of list.
alignas(T) char Value[sizeof(T)];
};
struct ItemBlock
{
Item* pItems;
UINT Capacity;
UINT FirstFreeIndex;
};
const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
const UINT m_FirstBlockCapacity;
Vector<ItemBlock> m_ItemBlocks;
ItemBlock& CreateNewBlock();
};
template<typename T>
PoolAllocator<T>::PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity) :
m_AllocationCallbacks(allocationCallbacks),
m_FirstBlockCapacity(firstBlockCapacity),
m_ItemBlocks(allocationCallbacks)
{
D3D12MA_ASSERT(m_FirstBlockCapacity > 1);
}
template<typename T>
void PoolAllocator<T>::Clear()
{
for(size_t i = m_ItemBlocks.size(); i--; )
{
D3D12MA_DELETE_ARRAY(m_AllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);
}
m_ItemBlocks.clear(true);
}
template<typename T>
template<typename... Types> T* PoolAllocator<T>::Alloc(Types... args)
{
for(size_t i = m_ItemBlocks.size(); i--; )
{
ItemBlock& block = m_ItemBlocks[i];
// This block has some free items: Use first one.
if(block.FirstFreeIndex != UINT32_MAX)
{
Item* const pItem = &block.pItems[block.FirstFreeIndex];
block.FirstFreeIndex = pItem->NextFreeIndex;
T* result = (T*)&pItem->Value;
new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
return result;
}
}
// No block has free item: Create new one and use it.
ItemBlock& newBlock = CreateNewBlock();
Item* const pItem = &newBlock.pItems[0];
newBlock.FirstFreeIndex = pItem->NextFreeIndex;
T* result = (T*)pItem->Value;
new(result)T(std::forward<Types>(args)...); // Explicit constructor call.
return result;
}
template<typename T>
void PoolAllocator<T>::Free(T* ptr)
{
// Search all memory blocks to find ptr.
for(size_t i = m_ItemBlocks.size(); i--; )
{
ItemBlock& block = m_ItemBlocks[i];
Item* pItemPtr;
memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
// Check if pItemPtr is in address range of this block.
if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))
{
ptr->~T(); // Explicit destructor call.
const UINT index = static_cast<UINT>(pItemPtr - block.pItems);
pItemPtr->NextFreeIndex = block.FirstFreeIndex;
block.FirstFreeIndex = index;
return;
}
}
D3D12MA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
}
template<typename T>
typename PoolAllocator<T>::ItemBlock& PoolAllocator<T>::CreateNewBlock()
{
const UINT newBlockCapacity = m_ItemBlocks.empty() ?
m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;
const ItemBlock newBlock = {
D3D12MA_NEW_ARRAY(m_AllocationCallbacks, Item, newBlockCapacity),
newBlockCapacity,
0 };
m_ItemBlocks.push_back(newBlock);
// Setup singly-linked list of all free items in this block.
for(UINT i = 0; i < newBlockCapacity - 1; ++i)
{
newBlock.pItems[i].NextFreeIndex = i + 1;
}
newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;
return m_ItemBlocks.back();
}
////////////////////////////////////////////////////////////////////////////////
// Private class List
/*
Doubly linked list, with elements allocated out of PoolAllocator.
Has custom interface, as well as STL-style interface, including iterator and
const_iterator.
*/
template<typename T>
class List
{
D3D12MA_CLASS_NO_COPY(List)
public:
struct Item
{
Item* pPrev;
Item* pNext;
T Value;
};
// allocationCallbacks externally owned, must outlive this object.
List(const ALLOCATION_CALLBACKS& allocationCallbacks);
// Intentionally not calling Clear, because that would be unnecessary
// computations to return all items to m_ItemAllocator as free.
// ~List() {}
void Clear();
size_t GetCount() const { return m_Count; }
bool IsEmpty() const { return m_Count == 0; }
Item* Front() { return m_pFront; }
const Item* Front() const { return m_pFront; }
Item* Back() { return m_pBack; }
const Item* Back() const { return m_pBack; }
Item* PushBack();
Item* PushFront();
Item* PushBack(const T& value);
Item* PushFront(const T& value);
void PopBack();
void PopFront();
// Item can be null - it means PushBack.
Item* InsertBefore(Item* pItem);
// Item can be null - it means PushFront.
Item* InsertAfter(Item* pItem);
Item* InsertBefore(Item* pItem, const T& value);
Item* InsertAfter(Item* pItem, const T& value);
void Remove(Item* pItem);
class reverse_iterator;
class const_reverse_iterator;
class iterator
{
public:
iterator() :
m_pList(NULL),
m_pItem(NULL)
{
}
iterator(const reverse_iterator& src) :
m_pList(src.m_pList),
m_pItem(src.m_pItem)
{
}
T& operator*() const
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
return m_pItem->Value;
}
T* operator->() const
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
return &m_pItem->Value;
}
iterator& operator++()
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
m_pItem = m_pItem->pNext;
return *this;
}
iterator& operator--()
{
if(m_pItem != NULL)
{
m_pItem = m_pItem->pPrev;
}
else
{
D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
m_pItem = m_pList->Back();
}
return *this;
}
iterator operator++(int)
{
iterator result = *this;
++*this;
return result;
}
iterator operator--(int)
{
iterator result = *this;
--*this;
return result;
}
bool operator==(const iterator& rhs) const
{
D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
return m_pItem == rhs.m_pItem;
}
bool operator!=(const iterator& rhs) const
{
D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
return m_pItem != rhs.m_pItem;
}
private:
List<T>* m_pList;
Item* m_pItem;
iterator(List<T>* pList, Item* pItem) :
m_pList(pList),
m_pItem(pItem)
{
}
friend class List<T>;
friend class const_iterator;
};
class reverse_iterator
{
public:
reverse_iterator() :
m_pList(NULL),
m_pItem(NULL)
{
}
reverse_iterator(const iterator& src) :
m_pList(src.m_pList),
m_pItem(src.m_pItem)
{
}
T& operator*() const
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
return m_pItem->Value;
}
T* operator->() const
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
return &m_pItem->Value;
}
reverse_iterator& operator++()
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
m_pItem = m_pItem->pPrev;
return *this;
}
reverse_iterator& operator--()
{
if(m_pItem != NULL)
{
m_pItem = m_pItem->pNext;
}
else
{
D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
m_pItem = m_pList->Front();
}
return *this;
}
reverse_iterator operator++(int)
{
reverse_iterator result = *this;
++*this;
return result;
}
reverse_iterator operator--(int)
{
reverse_iterator result = *this;
--*this;
return result;
}
bool operator==(const reverse_iterator& rhs) const
{
D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
return m_pItem == rhs.m_pItem;
}
bool operator!=(const reverse_iterator& rhs) const
{
D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
return m_pItem != rhs.m_pItem;
}
private:
List<T>* m_pList;
Item* m_pItem;
reverse_iterator(List<T>* pList, Item* pItem) :
m_pList(pList),
m_pItem(pItem)
{
}
friend class List<T>;
friend class const_reverse_iterator;
};
class const_iterator
{
public:
const_iterator() :
m_pList(NULL),
m_pItem(NULL)
{
}
const_iterator(const iterator& src) :
m_pList(src.m_pList),
m_pItem(src.m_pItem)
{
}
const_iterator(const reverse_iterator& src) :
m_pList(src.m_pList),
m_pItem(src.m_pItem)
{
}
const_iterator(const const_reverse_iterator& src) :
m_pList(src.m_pList),
m_pItem(src.m_pItem)
{
}
iterator dropConst() const
{
return iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));
}
const T& operator*() const
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
return m_pItem->Value;
}
const T* operator->() const
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
return &m_pItem->Value;
}
const_iterator& operator++()
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
m_pItem = m_pItem->pNext;
return *this;
}
const_iterator& operator--()
{
if(m_pItem != NULL)
{
m_pItem = m_pItem->pPrev;
}
else
{
D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
m_pItem = m_pList->Back();
}
return *this;
}
const_iterator operator++(int)
{
const_iterator result = *this;
++*this;
return result;
}
const_iterator operator--(int)
{
const_iterator result = *this;
--*this;
return result;
}
bool operator==(const const_iterator& rhs) const
{
D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
return m_pItem == rhs.m_pItem;
}
bool operator!=(const const_iterator& rhs) const
{
D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
return m_pItem != rhs.m_pItem;
}
private:
const_iterator(const List<T>* pList, const Item* pItem) :
m_pList(pList),
m_pItem(pItem)
{
}
const List<T>* m_pList;
const Item* m_pItem;
friend class List<T>;
};
class const_reverse_iterator
{
public:
const_reverse_iterator() :
m_pList(NULL),
m_pItem(NULL)
{
}
const_reverse_iterator(const iterator& src) :
m_pList(src.m_pList),
m_pItem(src.m_pItem)
{
}
const_reverse_iterator(const reverse_iterator& src) :
m_pList(src.m_pList),
m_pItem(src.m_pItem)
{
}
const_reverse_iterator(const const_iterator& src) :
m_pList(src.m_pList),
m_pItem(src.m_pItem)
{
}
reverse_iterator dropConst() const
{
return reverse_iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));
}
const T& operator*() const
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
return m_pItem->Value;
}
const T* operator->() const
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
return &m_pItem->Value;
}
const_reverse_iterator& operator++()
{
D3D12MA_HEAVY_ASSERT(m_pItem != NULL);
m_pItem = m_pItem->pPrev;
return *this;
}
const_reverse_iterator& operator--()
{
if(m_pItem != NULL)
{
m_pItem = m_pItem->pNext;
}
else
{
D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());
m_pItem = m_pList->Front();
}
return *this;
}
const_reverse_iterator operator++(int)
{
const_reverse_iterator result = *this;
++*this;
return result;
}
const_reverse_iterator operator--(int)
{
const_reverse_iterator result = *this;
--*this;
return result;
}
bool operator==(const const_reverse_iterator& rhs) const
{
D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
return m_pItem == rhs.m_pItem;
}
bool operator!=(const const_reverse_iterator& rhs) const
{
D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);
return m_pItem != rhs.m_pItem;
}
private:
const_reverse_iterator(const List<T>* pList, const Item* pItem) :
m_pList(pList),
m_pItem(pItem)
{
}
const List<T>* m_pList;
const Item* m_pItem;
friend class List<T>;
};
bool empty() const { return IsEmpty(); }
size_t size() const { return GetCount(); }
iterator begin() { return iterator(this, Front()); }
iterator end() { return iterator(this, NULL); }
const_iterator cbegin() const { return const_iterator(this, Front()); }
const_iterator cend() const { return const_iterator(this, NULL); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
reverse_iterator rbegin() { return reverse_iterator(this, Back()); }
reverse_iterator rend() { return reverse_iterator(this, NULL); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(this, Back()); }
const_reverse_iterator crend() const { return const_reverse_iterator(this, NULL); }
const_reverse_iterator rbegin() const { return crbegin(); }
const_reverse_iterator rend() const { return crend(); }
void clear() { Clear(); }
void push_back(const T& value) { PushBack(value); }
void erase(iterator it) { Remove(it.m_pItem); }
iterator insert(iterator it, const T& value) { return iterator(this, InsertBefore(it.m_pItem, value)); }
private:
const ALLOCATION_CALLBACKS& m_AllocationCallbacks;
PoolAllocator<Item> m_ItemAllocator;
Item* m_pFront;
Item* m_pBack;
size_t m_Count;
};
template<typename T>
List<T>::List(const ALLOCATION_CALLBACKS& allocationCallbacks) :
m_AllocationCallbacks(allocationCallbacks),
m_ItemAllocator(allocationCallbacks, 128),
m_pFront(NULL),
m_pBack(NULL),
m_Count(0)
{
}
template<typename T>
void List<T>::Clear()
{
if(!IsEmpty())
{
Item* pItem = m_pBack;
while(pItem != NULL)
{
Item* const pPrevItem = pItem->pPrev;
m_ItemAllocator.Free(pItem);
pItem = pPrevItem;
}
m_pFront = NULL;
m_pBack = NULL;
m_Count = 0;
}
}
template<typename T>
typename List<T>::Item* List<T>::PushBack()
{
Item* const pNewItem = m_ItemAllocator.Alloc();
pNewItem->pNext = NULL;
if(IsEmpty())
{
pNewItem->pPrev = NULL;
m_pFront = pNewItem;
m_pBack = pNewItem;
m_Count = 1;
}
else
{
pNewItem->pPrev = m_pBack;
m_pBack->pNext = pNewItem;
m_pBack = pNewItem;
++m_Count;
}
return pNewItem;
}
template<typename T>
typename List<T>::Item* List<T>::PushFront()
{
Item* const pNewItem = m_ItemAllocator.Alloc();
pNewItem->pPrev = NULL;
if(IsEmpty())
{
pNewItem->pNext = NULL;
m_pFront = pNewItem;
m_pBack = pNewItem;
m_Count = 1;
}
else
{
pNewItem->pNext = m_pFront;
m_pFront->pPrev = pNewItem;
m_pFront = pNewItem;
++m_Count;
}
return pNewItem;
}
template<typename T>
typename List<T>::Item* List<T>::PushBack(const T& value)
{
Item* const pNewItem = PushBack();
pNewItem->Value = value;
return pNewItem;
}
template<typename T>
typename List<T>::Item* List<T>::PushFront(const T& value)
{
Item* const pNewItem = PushFront();
pNewItem->Value = value;
return pNewItem;
}
template<typename T>
void List<T>::PopBack()
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
Item* const pBackItem = m_pBack;
Item* const pPrevItem = pBackItem->pPrev;
if(pPrevItem != NULL)
{
pPrevItem->pNext = NULL;
}
m_pBack = pPrevItem;
m_ItemAllocator.Free(pBackItem);
--m_Count;
}
template<typename T>
void List<T>::PopFront()
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
Item* const pFrontItem = m_pFront;
Item* const pNextItem = pFrontItem->pNext;
if(pNextItem != NULL)
{
pNextItem->pPrev = NULL;
}
m_pFront = pNextItem;
m_ItemAllocator.Free(pFrontItem);
--m_Count;
}
template<typename T>
void List<T>::Remove(Item* pItem)
{
D3D12MA_HEAVY_ASSERT(pItem != NULL);
D3D12MA_HEAVY_ASSERT(m_Count > 0);
if(pItem->pPrev != NULL)
{
pItem->pPrev->pNext = pItem->pNext;
}
else
{
D3D12MA_HEAVY_ASSERT(m_pFront == pItem);
m_pFront = pItem->pNext;
}
if(pItem->pNext != NULL)
{
pItem->pNext->pPrev = pItem->pPrev;
}
else
{
D3D12MA_HEAVY_ASSERT(m_pBack == pItem);
m_pBack = pItem->pPrev;
}
m_ItemAllocator.Free(pItem);
--m_Count;
}
template<typename T>
typename List<T>::Item* List<T>::InsertBefore(Item* pItem)
{
if(pItem != NULL)
{
Item* const prevItem = pItem->pPrev;
Item* const newItem = m_ItemAllocator.Alloc();
newItem->pPrev = prevItem;
newItem->pNext = pItem;
pItem->pPrev = newItem;
if(prevItem != NULL)
{
prevItem->pNext = newItem;
}
else
{
D3D12MA_HEAVY_ASSERT(m_pFront == pItem);
m_pFront = newItem;
}
++m_Count;
return newItem;
}
else
{
return PushBack();
}
}
template<typename T>
typename List<T>::Item* List<T>::InsertAfter(Item* pItem)
{
if(pItem != NULL)
{
Item* const nextItem = pItem->pNext;
Item* const newItem = m_ItemAllocator.Alloc();
newItem->pNext = nextItem;
newItem->pPrev = pItem;
pItem->pNext = newItem;
if(nextItem != NULL)
{
nextItem->pPrev = newItem;
}
else
{
D3D12MA_HEAVY_ASSERT(m_pBack == pItem);
m_pBack = newItem;
}
++m_Count;
return newItem;
}
else
return PushFront();
}
template<typename T>
typename List<T>::Item* List<T>::InsertBefore(Item* pItem, const T& value)
{
Item* const newItem = InsertBefore(pItem);
newItem->Value = value;
return newItem;
}
template<typename T>
typename List<T>::Item* List<T>::InsertAfter(Item* pItem, const T& value)
{
Item* const newItem = InsertAfter(pItem);
newItem->Value = value;
return newItem;
}
////////////////////////////////////////////////////////////////////////////////
// Private class IntrusiveLinkedList
/*
Expected interface of ItemTypeTraits:
struct MyItemTypeTraits
{
using ItemType = MyItem;
static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }
static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }
static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }
static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }
};
*/
template<typename ItemTypeTraits>
class IntrusiveLinkedList
{
public:
using ItemType = typename ItemTypeTraits::ItemType;
static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }
static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }
// Movable, not copyable.
IntrusiveLinkedList() { }
IntrusiveLinkedList(const IntrusiveLinkedList<ItemTypeTraits>& src) = delete;
IntrusiveLinkedList(IntrusiveLinkedList<ItemTypeTraits>&& src) :
m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)
{
src.m_Front = src.m_Back = NULL;
src.m_Count = 0;
}
~IntrusiveLinkedList()
{
D3D12MA_HEAVY_ASSERT(IsEmpty());
}
IntrusiveLinkedList<ItemTypeTraits>& operator=(const IntrusiveLinkedList<ItemTypeTraits>& src) = delete;
IntrusiveLinkedList<ItemTypeTraits>& operator=(IntrusiveLinkedList<ItemTypeTraits>&& src)
{
if(&src != this)
{
D3D12MA_HEAVY_ASSERT(IsEmpty());
m_Front = src.m_Front;
m_Back = src.m_Back;
m_Count = src.m_Count;
src.m_Front = src.m_Back = NULL;
src.m_Count = 0;
}
return *this;
}
void RemoveAll()
{
if(!IsEmpty())
{
ItemType* item = m_Back;
while(item != NULL)
{
ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);
ItemTypeTraits::AccessPrev(item) = NULL;
ItemTypeTraits::AccessNext(item) = NULL;
item = prevItem;
}
m_Front = NULL;
m_Back = NULL;
m_Count = 0;
}
}
size_t GetCount() const { return m_Count; }
bool IsEmpty() const { return m_Count == 0; }
ItemType* Front() { return m_Front; }
const ItemType* Front() const { return m_Front; }
ItemType* Back() { return m_Back; }
const ItemType* Back() const { return m_Back; }
void PushBack(ItemType* item)
{
D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);
if(IsEmpty())
{
m_Front = item;
m_Back = item;
m_Count = 1;
}
else
{
ItemTypeTraits::AccessPrev(item) = m_Back;
ItemTypeTraits::AccessNext(m_Back) = item;
m_Back = item;
++m_Count;
}
}
void PushFront(ItemType* item)
{
D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);
if(IsEmpty())
{
m_Front = item;
m_Back = item;
m_Count = 1;
}
else
{
ItemTypeTraits::AccessNext(item) = m_Front;
ItemTypeTraits::AccessPrev(m_Front) = item;
m_Front = item;
++m_Count;
}
}
ItemType* PopBack()
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
ItemType* const backItem = m_Back;
ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);
if(prevItem != NULL)
{
ItemTypeTraits::AccessNext(prevItem) = NULL;
}
m_Back = prevItem;
--m_Count;
ItemTypeTraits::AccessPrev(backItem) = NULL;
ItemTypeTraits::AccessNext(backItem) = NULL;
return backItem;
}
ItemType* PopFront()
{
D3D12MA_HEAVY_ASSERT(m_Count > 0);
ItemType* const frontItem = m_Front;
ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);
if(nextItem != NULL)
{
ItemTypeTraits::AccessPrev(nextItem) = NULL;
}
m_Front = nextItem;
--m_Count;
ItemTypeTraits::AccessPrev(frontItem) = NULL;
ItemTypeTraits::AccessNext(frontItem) = NULL;
return frontItem;
}
// MyItem can be null - it means PushBack.
void InsertBefore(ItemType* existingItem, ItemType* newItem)
{
D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);
if(existingItem != NULL)
{
ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);
ItemTypeTraits::AccessPrev(newItem) = prevItem;
ItemTypeTraits::AccessNext(newItem) = existingItem;
ItemTypeTraits::AccessPrev(existingItem) = newItem;
if(prevItem != NULL)
{
ItemTypeTraits::AccessNext(prevItem) = newItem;
}
else
{
D3D12MA_HEAVY_ASSERT(m_Front == existingItem);
m_Front = newItem;
}
++m_Count;
}
else
PushBack(newItem);
}
// MyItem can be null - it means PushFront.
void InsertAfter(ItemType* existingItem, ItemType* newItem)
{
D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);
if(existingItem != NULL)
{
ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);
ItemTypeTraits::AccessNext(newItem) = nextItem;
ItemTypeTraits::AccessPrev(newItem) = existingItem;
ItemTypeTraits::AccessNext(existingItem) = newItem;
if(nextItem != NULL)
{
ItemTypeTraits::AccessPrev(nextItem) = newItem;
}
else
{
D3D12MA_HEAVY_ASSERT(m_Back == existingItem);
m_Back = newItem;
}
++m_Count;
}
else
return PushFront(newItem);
}
void Remove(ItemType* item)
{
D3D12MA_HEAVY_ASSERT(item != NULL && m_Count > 0);
if(ItemTypeTraits::GetPrev(item) != NULL)
{
ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);
}
else
{
D3D12MA_HEAVY_ASSERT(m_Front == item);
m_Front = ItemTypeTraits::GetNext(item);
}
if(ItemTypeTraits::GetNext(item) != NULL)
{
ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);
}
else
{
D3D12MA_HEAVY_ASSERT(m_Back == item);
m_Back = ItemTypeTraits::GetPrev(item);
}
ItemTypeTraits::AccessPrev(item) = NULL;
ItemTypeTraits::AccessNext(item) = NULL;
--m_Count;
}
private:
ItemType* m_Front = NULL;
ItemType* m_Back = NULL;
size_t m_Count = 0;
};
////////////////////////////////////////////////////////////////////////////////
// Private class AllocationObjectAllocator definition
/*
Thread-safe wrapper over PoolAllocator free list, for allocation of Allocation objects.
*/
class AllocationObjectAllocator
{
D3D12MA_CLASS_NO_COPY(AllocationObjectAllocator);
public:
AllocationObjectAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks);
template<typename... Types> Allocation* Allocate(Types... args);
void Free(Allocation* alloc);
private:
D3D12MA_MUTEX m_Mutex;
PoolAllocator<Allocation> m_Allocator;
};
////////////////////////////////////////////////////////////////////////////////
// Private class BlockMetadata and derived classes - declarations
enum SuballocationType
{
SUBALLOCATION_TYPE_FREE = 0,
SUBALLOCATION_TYPE_ALLOCATION = 1,
};
/*
Represents a region of NormalBlock that is either assigned and returned as
allocated memory block or free.
*/
struct Suballocation
{
UINT64 offset;
UINT64 size;
void* userData;
SuballocationType type;
};
// Comparator for offsets.
struct SuballocationOffsetLess
{
bool operator()(const Suballocation& lhs, const Suballocation& rhs) const
{
return lhs.offset < rhs.offset;
}
};
struct SuballocationOffsetGreater
{
bool operator()(const Suballocation& lhs, const Suballocation& rhs) const
{
return lhs.offset > rhs.offset;
}
};
using SuballocationList = List<Suballocation>;
struct SuballocationItemSizeLess
{
bool operator()(const SuballocationList::iterator lhs, const SuballocationList::iterator rhs) const
{
return lhs->size < rhs->size;
}
bool operator()(const SuballocationList::iterator lhs, UINT64 rhsSize) const
{
return lhs->size < rhsSize;
}
};
/*
Parameters of planned allocation inside a NormalBlock.
*/
struct AllocationRequest
{
UINT64 offset;
UINT64 sumFreeSize; // Sum size of free items that overlap with proposed allocation.
UINT64 sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
SuballocationList::iterator item;
BOOL zeroInitialized;
};
/*
Keeps track of the range of bytes that are surely initialized with zeros.
Everything outside of it is considered uninitialized memory that may contain
garbage data.
The range is left-inclusive.
*/
class ZeroInitializedRange
{
public:
void Reset(UINT64 size)
{
D3D12MA_ASSERT(size > 0);
m_ZeroBeg = 0;
m_ZeroEnd = size;
}
BOOL IsRangeZeroInitialized(UINT64 beg, UINT64 end) const
{
D3D12MA_ASSERT(beg < end);
return m_ZeroBeg <= beg && end <= m_ZeroEnd;
}
void MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd)
{
D3D12MA_ASSERT(usedBeg < usedEnd);
// No new bytes marked.
if(usedEnd <= m_ZeroBeg || m_ZeroEnd <= usedBeg)
{
return;
}
// All bytes marked.
if(usedBeg <= m_ZeroBeg && m_ZeroEnd <= usedEnd)
{
m_ZeroBeg = m_ZeroEnd = 0;
}
// Some bytes marked.
else
{
const UINT64 remainingZeroBefore = usedBeg > m_ZeroBeg ? usedBeg - m_ZeroBeg : 0;
const UINT64 remainingZeroAfter = usedEnd < m_ZeroEnd ? m_ZeroEnd - usedEnd : 0;
D3D12MA_ASSERT(remainingZeroBefore > 0 || remainingZeroAfter > 0);
if(remainingZeroBefore > remainingZeroAfter)
{
m_ZeroEnd = usedBeg;
}
else
{
m_ZeroBeg = usedEnd;
}
}
}
private:
UINT64 m_ZeroBeg = 0, m_ZeroEnd = 0;
};
/*
Data structure used for bookkeeping of allocations and unused ranges of memory
in a single ID3D12Heap memory block.
*/
class BlockMetadata
{
public:
BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);
virtual ~BlockMetadata() { }
virtual void Init(UINT64 size) { m_Size = size; }
// Validates all data structures inside this object. If not valid, returns false.
virtual bool Validate() const = 0;
UINT64 GetSize() const { return m_Size; }
bool IsVirtual() const { return m_IsVirtual; }
virtual size_t GetAllocationCount() const = 0;
virtual UINT64 GetSumFreeSize() const = 0;
virtual UINT64 GetUnusedRangeSizeMax() const = 0;
// Returns true if this block is empty - contains only single free suballocation.
virtual bool IsEmpty() const = 0;
virtual void GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO& outInfo) const = 0;
// Tries to find a place for suballocation with given parameters inside this block.
// If succeeded, fills pAllocationRequest and returns true.
// If failed, returns false.
virtual bool CreateAllocationRequest(
UINT64 allocSize,
UINT64 allocAlignment,
AllocationRequest* pAllocationRequest) = 0;
// Makes actual allocation based on request. Request must already be checked and valid.
virtual void Alloc(
const AllocationRequest& request,
UINT64 allocSize,