Path: blob/master/dep/d3d12ma/src/D3D12MemAlloc.cpp
4253 views
//1// Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved.2//3// Permission is hereby granted, free of charge, to any person obtaining a copy4// of this software and associated documentation files (the "Software"), to deal5// in the Software without restriction, including without limitation the rights6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell7// copies of the Software, and to permit persons to whom the Software is8// furnished to do so, subject to the following conditions:9//10// The above copyright notice and this permission notice shall be included in11// all copies or substantial portions of the Software.12//13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN19// THE SOFTWARE.20//2122#include "D3D12MemAlloc.h"2324#include <combaseapi.h>25#include <mutex>26#include <algorithm>27#include <utility>28#include <cstdlib>29#include <cstdint>30#include <malloc.h> // for _aligned_malloc, _aligned_free31#ifndef _WIN3232#include <shared_mutex>33#endif3435////////////////////////////////////////////////////////////////////////////////36////////////////////////////////////////////////////////////////////////////////37//38// Configuration Begin39//40////////////////////////////////////////////////////////////////////////////////41////////////////////////////////////////////////////////////////////////////////42#ifndef _D3D12MA_CONFIGURATION4344#ifdef _WIN3245#if !defined(WINVER) || WINVER < 0x060046#error Required at least WinAPI version supporting: client = Windows Vista, server = Windows Server 2008.47#endif48#endif4950#ifndef D3D12MA_SORT51#define D3D12MA_SORT(beg, end, cmp) std::sort(beg, end, cmp)52#endif5354#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED55#include <dxgi.h>56#if D3D12MA_DXGI_1_457#include <dxgi1_4.h>58#endif59#endif6061#ifndef D3D12MA_ASSERT62#include <cassert>63#define D3D12MA_ASSERT(cond) assert(cond)64#endif6566// Assert that will be called very often, like inside data structures e.g. operator[].67// Making it non-empty can make program slow.68#ifndef D3D12MA_HEAVY_ASSERT69#ifdef _DEBUG70#define D3D12MA_HEAVY_ASSERT(expr) //D3D12MA_ASSERT(expr)71#else72#define D3D12MA_HEAVY_ASSERT(expr)73#endif74#endif7576#ifndef D3D12MA_DEBUG_ALIGNMENT77/*78Minimum alignment of all allocations, in bytes.79Set to more than 1 for debugging purposes only. Must be power of two.80*/81#define D3D12MA_DEBUG_ALIGNMENT (1)82#endif8384#ifndef D3D12MA_DEBUG_MARGIN85// Minimum margin before and after every allocation, in bytes.86// Set nonzero for debugging purposes only.87#define D3D12MA_DEBUG_MARGIN (0)88#endif8990#ifndef D3D12MA_DEBUG_GLOBAL_MUTEX91/*92Set this to 1 for debugging purposes only, to enable single mutex protecting all93entry calls to the library. Can be useful for debugging multithreading issues.94*/95#define D3D12MA_DEBUG_GLOBAL_MUTEX (0)96#endif9798/*99Define this macro for debugging purposes only to force specific D3D12_RESOURCE_HEAP_TIER,100especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs.101*/102//#define D3D12MA_FORCE_RESOURCE_HEAP_TIER D3D12_RESOURCE_HEAP_TIER_1103104#ifndef D3D12MA_DEFAULT_BLOCK_SIZE105/// Default size of a block allocated as single ID3D12Heap.106#define D3D12MA_DEFAULT_BLOCK_SIZE (64ull * 1024 * 1024)107#endif108109#ifndef D3D12MA_DEBUG_LOG110#define D3D12MA_DEBUG_LOG(format, ...)111/*112#define D3D12MA_DEBUG_LOG(format, ...) do { \113wprintf(format, __VA_ARGS__); \114wprintf(L"\n"); \115} while(false)116*/117#endif118119#endif // _D3D12MA_CONFIGURATION120////////////////////////////////////////////////////////////////////////////////121////////////////////////////////////////////////////////////////////////////////122//123// Configuration End124//125////////////////////////////////////////////////////////////////////////////////126////////////////////////////////////////////////////////////////////////////////127128#define D3D12MA_IID_PPV_ARGS(ppType) __uuidof(**(ppType)), reinterpret_cast<void**>(ppType)129130#ifdef __ID3D12Device8_INTERFACE_DEFINED__131#define D3D12MA_CREATE_NOT_ZEROED_AVAILABLE 1132#endif133134namespace D3D12MA135{136static constexpr UINT HEAP_TYPE_COUNT = 4;137static constexpr UINT STANDARD_HEAP_TYPE_COUNT = 3; // Only DEFAULT, UPLOAD, READBACK.138static constexpr UINT DEFAULT_POOL_MAX_COUNT = 9;139static const UINT NEW_BLOCK_SIZE_SHIFT_MAX = 3;140// Minimum size of a free suballocation to register it in the free suballocation collection.141static const UINT64 MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;142143static const WCHAR* const HeapTypeNames[] =144{145L"DEFAULT",146L"UPLOAD",147L"READBACK",148L"CUSTOM",149};150static const WCHAR* const StandardHeapTypeNames[] =151{152L"DEFAULT",153L"UPLOAD",154L"READBACK",155};156157static const D3D12_HEAP_FLAGS RESOURCE_CLASS_HEAP_FLAGS =158D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;159160static const D3D12_RESIDENCY_PRIORITY D3D12_RESIDENCY_PRIORITY_NONE = D3D12_RESIDENCY_PRIORITY(0);161162#ifndef _D3D12MA_ENUM_DECLARATIONS163164// Local copy of this enum, as it is provided only by <dxgi1_4.h>, so it may not be available.165enum DXGI_MEMORY_SEGMENT_GROUP_COPY166{167DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY = 0,168DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY = 1,169DXGI_MEMORY_SEGMENT_GROUP_COUNT170};171172enum class ResourceClass173{174Unknown, Buffer, Non_RT_DS_Texture, RT_DS_Texture175};176177enum SuballocationType178{179SUBALLOCATION_TYPE_FREE = 0,180SUBALLOCATION_TYPE_ALLOCATION = 1,181};182183#endif // _D3D12MA_ENUM_DECLARATIONS184185186#ifndef _D3D12MA_FUNCTIONS187188static void* DefaultAllocate(size_t Size, size_t Alignment, void* /*pPrivateData*/)189{190#ifdef _WIN32191return _aligned_malloc(Size, Alignment);192#else193return aligned_alloc(Alignment, Size);194#endif195}196static void DefaultFree(void* pMemory, void* /*pPrivateData*/)197{198#ifdef _WIN32199return _aligned_free(pMemory);200#else201return free(pMemory);202#endif203}204205static void* Malloc(const ALLOCATION_CALLBACKS& allocs, size_t size, size_t alignment)206{207void* const result = (*allocs.pAllocate)(size, alignment, allocs.pPrivateData);208D3D12MA_ASSERT(result);209return result;210}211static void Free(const ALLOCATION_CALLBACKS& allocs, void* memory)212{213(*allocs.pFree)(memory, allocs.pPrivateData);214}215216template<typename T>217static T* Allocate(const ALLOCATION_CALLBACKS& allocs)218{219return (T*)Malloc(allocs, sizeof(T), __alignof(T));220}221template<typename T>222static T* AllocateArray(const ALLOCATION_CALLBACKS& allocs, size_t count)223{224return (T*)Malloc(allocs, sizeof(T) * count, __alignof(T));225}226227#define D3D12MA_NEW(allocs, type) new(D3D12MA::Allocate<type>(allocs))(type)228#define D3D12MA_NEW_ARRAY(allocs, type, count) new(D3D12MA::AllocateArray<type>((allocs), (count)))(type)229230template<typename T>231void D3D12MA_DELETE(const ALLOCATION_CALLBACKS& allocs, T* memory)232{233if (memory)234{235memory->~T();236Free(allocs, memory);237}238}239template<typename T>240void D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS& allocs, T* memory, size_t count)241{242if (memory)243{244for (size_t i = count; i--; )245{246memory[i].~T();247}248Free(allocs, memory);249}250}251252static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATION_CALLBACKS* allocationCallbacks)253{254if (allocationCallbacks)255{256outAllocs = *allocationCallbacks;257D3D12MA_ASSERT(outAllocs.pAllocate != NULL && outAllocs.pFree != NULL);258}259else260{261outAllocs.pAllocate = &DefaultAllocate;262outAllocs.pFree = &DefaultFree;263outAllocs.pPrivateData = NULL;264}265}266267#define SAFE_RELEASE(ptr) do { if(ptr) { (ptr)->Release(); (ptr) = NULL; } } while(false)268269#define D3D12MA_VALIDATE(cond) do { if(!(cond)) { \270D3D12MA_ASSERT(0 && "Validation failed: " #cond); \271return false; \272} } while(false)273274template<typename T>275static T D3D12MA_MIN(const T& a, const T& b) { return a <= b ? a : b; }276template<typename T>277static T D3D12MA_MAX(const T& a, const T& b) { return a <= b ? b : a; }278279template<typename T>280static void D3D12MA_SWAP(T& a, T& b) { T tmp = a; a = b; b = tmp; }281282// Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX283static UINT8 BitScanLSB(UINT64 mask)284{285#if defined(_MSC_VER) && defined(_WIN64)286unsigned long pos;287if (_BitScanForward64(&pos, mask))288return static_cast<UINT8>(pos);289return UINT8_MAX;290#elif defined __GNUC__ || defined __clang__291return static_cast<UINT8>(__builtin_ffsll(mask)) - 1U;292#else293UINT8 pos = 0;294UINT64 bit = 1;295do296{297if (mask & bit)298return pos;299bit <<= 1;300} while (pos++ < 63);301return UINT8_MAX;302#endif303}304// Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX305static UINT8 BitScanLSB(UINT32 mask)306{307#ifdef _MSC_VER308unsigned long pos;309if (_BitScanForward(&pos, mask))310return static_cast<UINT8>(pos);311return UINT8_MAX;312#elif defined __GNUC__ || defined __clang__313return static_cast<UINT8>(__builtin_ffs(mask)) - 1U;314#else315UINT8 pos = 0;316UINT32 bit = 1;317do318{319if (mask & bit)320return pos;321bit <<= 1;322} while (pos++ < 31);323return UINT8_MAX;324#endif325}326327// Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX328static UINT8 BitScanMSB(UINT64 mask)329{330#if defined(_MSC_VER) && defined(_WIN64)331unsigned long pos;332if (_BitScanReverse64(&pos, mask))333return static_cast<UINT8>(pos);334#elif defined __GNUC__ || defined __clang__335if (mask)336return 63 - static_cast<UINT8>(__builtin_clzll(mask));337#else338UINT8 pos = 63;339UINT64 bit = 1ULL << 63;340do341{342if (mask & bit)343return pos;344bit >>= 1;345} while (pos-- > 0);346#endif347return UINT8_MAX;348}349// Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX350static UINT8 BitScanMSB(UINT32 mask)351{352#ifdef _MSC_VER353unsigned long pos;354if (_BitScanReverse(&pos, mask))355return static_cast<UINT8>(pos);356#elif defined __GNUC__ || defined __clang__357if (mask)358return 31 - static_cast<UINT8>(__builtin_clz(mask));359#else360UINT8 pos = 31;361UINT32 bit = 1UL << 31;362do363{364if (mask & bit)365return pos;366bit >>= 1;367} while (pos-- > 0);368#endif369return UINT8_MAX;370}371372/*373Returns true if given number is a power of two.374T must be unsigned integer number or signed integer but always nonnegative.375For 0 returns true.376*/377template <typename T>378static bool IsPow2(T x) { return (x & (x - 1)) == 0; }379380// Aligns given value up to nearest multiply of align value. For example: AlignUp(11, 8) = 16.381// Use types like UINT, uint64_t as T.382template <typename T>383static T AlignUp(T val, T alignment)384{385D3D12MA_HEAVY_ASSERT(IsPow2(alignment));386return (val + alignment - 1) & ~(alignment - 1);387}388// Aligns given value down to nearest multiply of align value. For example: AlignUp(11, 8) = 8.389// Use types like UINT, uint64_t as T.390template <typename T>391static T AlignDown(T val, T alignment)392{393D3D12MA_HEAVY_ASSERT(IsPow2(alignment));394return val & ~(alignment - 1);395}396397// Division with mathematical rounding to nearest number.398template <typename T>399static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; }400template <typename T>401static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; }402403static WCHAR HexDigitToChar(UINT8 digit)404{405if(digit < 10)406return L'0' + digit;407else408return L'A' + (digit - 10);409}410411/*412Performs binary search and returns iterator to first element that is greater or413equal to `key`, according to comparison `cmp`.414415Cmp should return true if first argument is less than second argument.416417Returned value is the found element, if present in the collection or place where418new element with value (key) should be inserted.419*/420template <typename CmpLess, typename IterT, typename KeyT>421static IterT BinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)422{423size_t down = 0, up = (end - beg);424while (down < up)425{426const size_t mid = (down + up) / 2;427if (cmp(*(beg + mid), key))428{429down = mid + 1;430}431else432{433up = mid;434}435}436return beg + down;437}438439/*440Performs binary search and returns iterator to an element that is equal to `key`,441according to comparison `cmp`.442443Cmp should return true if first argument is less than second argument.444445Returned value is the found element, if present in the collection or end if not446found.447*/448template<typename CmpLess, typename IterT, typename KeyT>449static IterT BinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)450{451IterT it = BinaryFindFirstNotLess<CmpLess, IterT, KeyT>(beg, end, value, cmp);452if (it == end ||453(!cmp(*it, value) && !cmp(value, *it)))454{455return it;456}457return end;458}459460static UINT StandardHeapTypeToIndex(D3D12_HEAP_TYPE type)461{462switch (type)463{464case D3D12_HEAP_TYPE_DEFAULT: return 0;465case D3D12_HEAP_TYPE_UPLOAD: return 1;466case D3D12_HEAP_TYPE_READBACK: return 2;467default: D3D12MA_ASSERT(0); return UINT_MAX;468}469}470471static D3D12_HEAP_TYPE IndexToStandardHeapType(UINT heapTypeIndex)472{473switch(heapTypeIndex)474{475case 0: return D3D12_HEAP_TYPE_DEFAULT;476case 1: return D3D12_HEAP_TYPE_UPLOAD;477case 2: return D3D12_HEAP_TYPE_READBACK;478default: D3D12MA_ASSERT(0); return D3D12_HEAP_TYPE_CUSTOM;479}480}481482static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags, bool denyMsaaTextures)483{484/*485Documentation of D3D12_HEAP_DESC structure says:486487- D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT defined as 64KB.488- D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT defined as 4MB. An489application must decide whether the heap will contain multi-sample490anti-aliasing (MSAA), in which case, the application must choose [this flag].491492https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_heap_desc493*/494495if (denyMsaaTextures)496return D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;497498const D3D12_HEAP_FLAGS denyAllTexturesFlags =499D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;500const bool canContainAnyTextures =501(flags & denyAllTexturesFlags) != denyAllTexturesFlags;502return canContainAnyTextures ?503D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;504}505506static ResourceClass HeapFlagsToResourceClass(D3D12_HEAP_FLAGS heapFlags)507{508const bool allowBuffers = (heapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;509const bool allowRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;510const bool allowNonRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;511512const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);513if (allowedGroupCount != 1)514return ResourceClass::Unknown;515516if (allowRtDsTextures)517return ResourceClass::RT_DS_Texture;518if (allowNonRtDsTextures)519return ResourceClass::Non_RT_DS_Texture;520return ResourceClass::Buffer;521}522523static bool IsHeapTypeStandard(D3D12_HEAP_TYPE type)524{525return type == D3D12_HEAP_TYPE_DEFAULT ||526type == D3D12_HEAP_TYPE_UPLOAD ||527type == D3D12_HEAP_TYPE_READBACK;528}529530static D3D12_HEAP_PROPERTIES StandardHeapTypeToHeapProperties(D3D12_HEAP_TYPE type)531{532D3D12MA_ASSERT(IsHeapTypeStandard(type));533D3D12_HEAP_PROPERTIES result = {};534result.Type = type;535return result;536}537538static bool IsFormatCompressed(DXGI_FORMAT format)539{540switch (format)541{542case DXGI_FORMAT_BC1_TYPELESS:543case DXGI_FORMAT_BC1_UNORM:544case DXGI_FORMAT_BC1_UNORM_SRGB:545case DXGI_FORMAT_BC2_TYPELESS:546case DXGI_FORMAT_BC2_UNORM:547case DXGI_FORMAT_BC2_UNORM_SRGB:548case DXGI_FORMAT_BC3_TYPELESS:549case DXGI_FORMAT_BC3_UNORM:550case DXGI_FORMAT_BC3_UNORM_SRGB:551case DXGI_FORMAT_BC4_TYPELESS:552case DXGI_FORMAT_BC4_UNORM:553case DXGI_FORMAT_BC4_SNORM:554case DXGI_FORMAT_BC5_TYPELESS:555case DXGI_FORMAT_BC5_UNORM:556case DXGI_FORMAT_BC5_SNORM:557case DXGI_FORMAT_BC6H_TYPELESS:558case DXGI_FORMAT_BC6H_UF16:559case DXGI_FORMAT_BC6H_SF16:560case DXGI_FORMAT_BC7_TYPELESS:561case DXGI_FORMAT_BC7_UNORM:562case DXGI_FORMAT_BC7_UNORM_SRGB:563return true;564default:565return false;566}567}568569// Only some formats are supported. For others it returns 0.570static UINT GetBitsPerPixel(DXGI_FORMAT format)571{572switch (format)573{574case DXGI_FORMAT_R32G32B32A32_TYPELESS:575case DXGI_FORMAT_R32G32B32A32_FLOAT:576case DXGI_FORMAT_R32G32B32A32_UINT:577case DXGI_FORMAT_R32G32B32A32_SINT:578return 128;579case DXGI_FORMAT_R32G32B32_TYPELESS:580case DXGI_FORMAT_R32G32B32_FLOAT:581case DXGI_FORMAT_R32G32B32_UINT:582case DXGI_FORMAT_R32G32B32_SINT:583return 96;584case DXGI_FORMAT_R16G16B16A16_TYPELESS:585case DXGI_FORMAT_R16G16B16A16_FLOAT:586case DXGI_FORMAT_R16G16B16A16_UNORM:587case DXGI_FORMAT_R16G16B16A16_UINT:588case DXGI_FORMAT_R16G16B16A16_SNORM:589case DXGI_FORMAT_R16G16B16A16_SINT:590return 64;591case DXGI_FORMAT_R32G32_TYPELESS:592case DXGI_FORMAT_R32G32_FLOAT:593case DXGI_FORMAT_R32G32_UINT:594case DXGI_FORMAT_R32G32_SINT:595return 64;596case DXGI_FORMAT_R32G8X24_TYPELESS:597case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:598case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:599case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:600return 64;601case DXGI_FORMAT_R10G10B10A2_TYPELESS:602case DXGI_FORMAT_R10G10B10A2_UNORM:603case DXGI_FORMAT_R10G10B10A2_UINT:604case DXGI_FORMAT_R11G11B10_FLOAT:605return 32;606case DXGI_FORMAT_R8G8B8A8_TYPELESS:607case DXGI_FORMAT_R8G8B8A8_UNORM:608case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:609case DXGI_FORMAT_R8G8B8A8_UINT:610case DXGI_FORMAT_R8G8B8A8_SNORM:611case DXGI_FORMAT_R8G8B8A8_SINT:612return 32;613case DXGI_FORMAT_R16G16_TYPELESS:614case DXGI_FORMAT_R16G16_FLOAT:615case DXGI_FORMAT_R16G16_UNORM:616case DXGI_FORMAT_R16G16_UINT:617case DXGI_FORMAT_R16G16_SNORM:618case DXGI_FORMAT_R16G16_SINT:619return 32;620case DXGI_FORMAT_R32_TYPELESS:621case DXGI_FORMAT_D32_FLOAT:622case DXGI_FORMAT_R32_FLOAT:623case DXGI_FORMAT_R32_UINT:624case DXGI_FORMAT_R32_SINT:625return 32;626case DXGI_FORMAT_R24G8_TYPELESS:627case DXGI_FORMAT_D24_UNORM_S8_UINT:628case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:629case DXGI_FORMAT_X24_TYPELESS_G8_UINT:630return 32;631case DXGI_FORMAT_R8G8_TYPELESS:632case DXGI_FORMAT_R8G8_UNORM:633case DXGI_FORMAT_R8G8_UINT:634case DXGI_FORMAT_R8G8_SNORM:635case DXGI_FORMAT_R8G8_SINT:636return 16;637case DXGI_FORMAT_R16_TYPELESS:638case DXGI_FORMAT_R16_FLOAT:639case DXGI_FORMAT_D16_UNORM:640case DXGI_FORMAT_R16_UNORM:641case DXGI_FORMAT_R16_UINT:642case DXGI_FORMAT_R16_SNORM:643case DXGI_FORMAT_R16_SINT:644return 16;645case DXGI_FORMAT_R8_TYPELESS:646case DXGI_FORMAT_R8_UNORM:647case DXGI_FORMAT_R8_UINT:648case DXGI_FORMAT_R8_SNORM:649case DXGI_FORMAT_R8_SINT:650case DXGI_FORMAT_A8_UNORM:651return 8;652case DXGI_FORMAT_BC1_TYPELESS:653case DXGI_FORMAT_BC1_UNORM:654case DXGI_FORMAT_BC1_UNORM_SRGB:655return 4;656case DXGI_FORMAT_BC2_TYPELESS:657case DXGI_FORMAT_BC2_UNORM:658case DXGI_FORMAT_BC2_UNORM_SRGB:659return 8;660case DXGI_FORMAT_BC3_TYPELESS:661case DXGI_FORMAT_BC3_UNORM:662case DXGI_FORMAT_BC3_UNORM_SRGB:663return 8;664case DXGI_FORMAT_BC4_TYPELESS:665case DXGI_FORMAT_BC4_UNORM:666case DXGI_FORMAT_BC4_SNORM:667return 4;668case DXGI_FORMAT_BC5_TYPELESS:669case DXGI_FORMAT_BC5_UNORM:670case DXGI_FORMAT_BC5_SNORM:671return 8;672case DXGI_FORMAT_BC6H_TYPELESS:673case DXGI_FORMAT_BC6H_UF16:674case DXGI_FORMAT_BC6H_SF16:675return 8;676case DXGI_FORMAT_BC7_TYPELESS:677case DXGI_FORMAT_BC7_UNORM:678case DXGI_FORMAT_BC7_UNORM_SRGB:679return 8;680default:681return 0;682}683}684685template<typename D3D12_RESOURCE_DESC_T>686static ResourceClass ResourceDescToResourceClass(const D3D12_RESOURCE_DESC_T& resDesc)687{688if (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)689return ResourceClass::Buffer;690// Else: it's surely a texture.691const bool isRenderTargetOrDepthStencil =692(resDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;693return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture;694}695696// This algorithm is overly conservative.697template<typename D3D12_RESOURCE_DESC_T>698static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc)699{700if (resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)701return false;702if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0)703return false;704if (resourceDesc.SampleDesc.Count > 1)705return false;706if (resourceDesc.DepthOrArraySize != 1)707return false;708709UINT sizeX = (UINT)resourceDesc.Width;710UINT sizeY = resourceDesc.Height;711UINT bitsPerPixel = GetBitsPerPixel(resourceDesc.Format);712if (bitsPerPixel == 0)713return false;714715if (IsFormatCompressed(resourceDesc.Format))716{717sizeX = DivideRoundingUp(sizeX, 4u);718sizeY = DivideRoundingUp(sizeY, 4u);719bitsPerPixel *= 16;720}721722UINT tileSizeX = 0, tileSizeY = 0;723switch (bitsPerPixel)724{725case 8: tileSizeX = 64; tileSizeY = 64; break;726case 16: tileSizeX = 64; tileSizeY = 32; break;727case 32: tileSizeX = 32; tileSizeY = 32; break;728case 64: tileSizeX = 32; tileSizeY = 16; break;729case 128: tileSizeX = 16; tileSizeY = 16; break;730default: return false;731}732733const UINT tileCount = DivideRoundingUp(sizeX, tileSizeX) * DivideRoundingUp(sizeY, tileSizeY);734return tileCount <= 16;735}736737static bool ValidateAllocateMemoryParameters(738const ALLOCATION_DESC* pAllocDesc,739const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,740Allocation** ppAllocation)741{742return pAllocDesc &&743pAllocInfo &&744ppAllocation &&745(pAllocInfo->Alignment == 0 ||746pAllocInfo->Alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT ||747pAllocInfo->Alignment == D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT) &&748pAllocInfo->SizeInBytes != 0 &&749pAllocInfo->SizeInBytes % (64ull * 1024) == 0;750}751752#endif // _D3D12MA_FUNCTIONS753754#ifndef _D3D12MA_STATISTICS_FUNCTIONS755756static void ClearStatistics(Statistics& outStats)757{758outStats.BlockCount = 0;759outStats.AllocationCount = 0;760outStats.BlockBytes = 0;761outStats.AllocationBytes = 0;762}763764static void ClearDetailedStatistics(DetailedStatistics& outStats)765{766ClearStatistics(outStats.Stats);767outStats.UnusedRangeCount = 0;768outStats.AllocationSizeMin = UINT64_MAX;769outStats.AllocationSizeMax = 0;770outStats.UnusedRangeSizeMin = UINT64_MAX;771outStats.UnusedRangeSizeMax = 0;772}773774static void AddStatistics(Statistics& inoutStats, const Statistics& src)775{776inoutStats.BlockCount += src.BlockCount;777inoutStats.AllocationCount += src.AllocationCount;778inoutStats.BlockBytes += src.BlockBytes;779inoutStats.AllocationBytes += src.AllocationBytes;780}781782static void AddDetailedStatistics(DetailedStatistics& inoutStats, const DetailedStatistics& src)783{784AddStatistics(inoutStats.Stats, src.Stats);785inoutStats.UnusedRangeCount += src.UnusedRangeCount;786inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, src.AllocationSizeMin);787inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, src.AllocationSizeMax);788inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, src.UnusedRangeSizeMin);789inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, src.UnusedRangeSizeMax);790}791792static void AddDetailedStatisticsAllocation(DetailedStatistics& inoutStats, UINT64 size)793{794inoutStats.Stats.AllocationCount++;795inoutStats.Stats.AllocationBytes += size;796inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, size);797inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, size);798}799800static void AddDetailedStatisticsUnusedRange(DetailedStatistics& inoutStats, UINT64 size)801{802inoutStats.UnusedRangeCount++;803inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, size);804inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, size);805}806807#endif // _D3D12MA_STATISTICS_FUNCTIONS808809810#ifndef _D3D12MA_MUTEX811812#ifndef D3D12MA_MUTEX813class Mutex814{815public:816void Lock() { m_Mutex.lock(); }817void Unlock() { m_Mutex.unlock(); }818819private:820std::mutex m_Mutex;821};822#define D3D12MA_MUTEX Mutex823#endif824825#ifndef D3D12MA_RW_MUTEX826#ifdef _WIN32827class RWMutex828{829public:830RWMutex() { InitializeSRWLock(&m_Lock); }831void LockRead() { AcquireSRWLockShared(&m_Lock); }832void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }833void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }834void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }835836private:837SRWLOCK m_Lock;838};839#else // #ifdef _WIN32840class RWMutex841{842public:843RWMutex() {}844void LockRead() { m_Mutex.lock_shared(); }845void UnlockRead() { m_Mutex.unlock_shared(); }846void LockWrite() { m_Mutex.lock(); }847void UnlockWrite() { m_Mutex.unlock(); }848849private:850std::shared_timed_mutex m_Mutex;851};852#endif // #ifdef _WIN32853#define D3D12MA_RW_MUTEX RWMutex854#endif // #ifndef D3D12MA_RW_MUTEX855856// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).857struct MutexLock858{859D3D12MA_CLASS_NO_COPY(MutexLock);860public:861MutexLock(D3D12MA_MUTEX& mutex, bool useMutex = true) :862m_pMutex(useMutex ? &mutex : NULL)863{864if (m_pMutex) m_pMutex->Lock();865}866~MutexLock() { if (m_pMutex) m_pMutex->Unlock(); }867868private:869D3D12MA_MUTEX* m_pMutex;870};871872// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.873struct MutexLockRead874{875D3D12MA_CLASS_NO_COPY(MutexLockRead);876public:877MutexLockRead(D3D12MA_RW_MUTEX& mutex, bool useMutex)878: m_pMutex(useMutex ? &mutex : NULL)879{880if(m_pMutex)881{882m_pMutex->LockRead();883}884}885~MutexLockRead() { if (m_pMutex) m_pMutex->UnlockRead(); }886887private:888D3D12MA_RW_MUTEX* m_pMutex;889};890891// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.892struct MutexLockWrite893{894D3D12MA_CLASS_NO_COPY(MutexLockWrite);895public:896MutexLockWrite(D3D12MA_RW_MUTEX& mutex, bool useMutex)897: m_pMutex(useMutex ? &mutex : NULL)898{899if (m_pMutex) m_pMutex->LockWrite();900}901~MutexLockWrite() { if (m_pMutex) m_pMutex->UnlockWrite(); }902903private:904D3D12MA_RW_MUTEX* m_pMutex;905};906907#if D3D12MA_DEBUG_GLOBAL_MUTEX908static D3D12MA_MUTEX g_DebugGlobalMutex;909#define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK MutexLock debugGlobalMutexLock(g_DebugGlobalMutex, true);910#else911#define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK912#endif913#endif // _D3D12MA_MUTEX914915#ifndef _D3D12MA_VECTOR916/*917Dynamically resizing continuous array. Class with interface similar to std::vector.918T must be POD because constructors and destructors are not called and memcpy is919used for these objects.920*/921template<typename T>922class Vector923{924public:925using value_type = T;926using iterator = T*;927using const_iterator = const T*;928929// allocationCallbacks externally owned, must outlive this object.930Vector(const ALLOCATION_CALLBACKS& allocationCallbacks);931Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks);932Vector(const Vector<T>& src);933~Vector();934935const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }936bool empty() const { return m_Count == 0; }937size_t size() const { return m_Count; }938T* data() { return m_pArray; }939const T* data() const { return m_pArray; }940void clear(bool freeMemory = false) { resize(0, freeMemory); }941942iterator begin() { return m_pArray; }943iterator end() { return m_pArray + m_Count; }944const_iterator cbegin() const { return m_pArray; }945const_iterator cend() const { return m_pArray + m_Count; }946const_iterator begin() const { return cbegin(); }947const_iterator end() const { return cend(); }948949void push_front(const T& src) { insert(0, src); }950void push_back(const T& src);951void pop_front();952void pop_back();953954T& front();955T& back();956const T& front() const;957const T& back() const;958959void reserve(size_t newCapacity, bool freeMemory = false);960void resize(size_t newCount, bool freeMemory = false);961void insert(size_t index, const T& src);962void remove(size_t index);963964template<typename CmpLess>965size_t InsertSorted(const T& value, const CmpLess& cmp);966template<typename CmpLess>967bool RemoveSorted(const T& value, const CmpLess& cmp);968969Vector& operator=(const Vector<T>& rhs);970T& operator[](size_t index);971const T& operator[](size_t index) const;972973private:974const ALLOCATION_CALLBACKS& m_AllocationCallbacks;975T* m_pArray;976size_t m_Count;977size_t m_Capacity;978};979980#ifndef _D3D12MA_VECTOR_FUNCTIONS981template<typename T>982Vector<T>::Vector(const ALLOCATION_CALLBACKS& allocationCallbacks)983: m_AllocationCallbacks(allocationCallbacks),984m_pArray(NULL),985m_Count(0),986m_Capacity(0) {}987988template<typename T>989Vector<T>::Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks)990: m_AllocationCallbacks(allocationCallbacks),991m_pArray(count ? AllocateArray<T>(allocationCallbacks, count) : NULL),992m_Count(count),993m_Capacity(count) {}994995template<typename T>996Vector<T>::Vector(const Vector<T>& src)997: m_AllocationCallbacks(src.m_AllocationCallbacks),998m_pArray(src.m_Count ? AllocateArray<T>(src.m_AllocationCallbacks, src.m_Count) : NULL),999m_Count(src.m_Count),1000m_Capacity(src.m_Count)1001{1002if (m_Count > 0)1003{1004memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));1005}1006}10071008template<typename T>1009Vector<T>::~Vector()1010{1011Free(m_AllocationCallbacks, m_pArray);1012}10131014template<typename T>1015void Vector<T>::push_back(const T& src)1016{1017const size_t newIndex = size();1018resize(newIndex + 1);1019m_pArray[newIndex] = src;1020}10211022template<typename T>1023void Vector<T>::pop_front()1024{1025D3D12MA_HEAVY_ASSERT(m_Count > 0);1026remove(0);1027}10281029template<typename T>1030void Vector<T>::pop_back()1031{1032D3D12MA_HEAVY_ASSERT(m_Count > 0);1033resize(size() - 1);1034}10351036template<typename T>1037T& Vector<T>::front()1038{1039D3D12MA_HEAVY_ASSERT(m_Count > 0);1040return m_pArray[0];1041}10421043template<typename T>1044T& Vector<T>::back()1045{1046D3D12MA_HEAVY_ASSERT(m_Count > 0);1047return m_pArray[m_Count - 1];1048}10491050template<typename T>1051const T& Vector<T>::front() const1052{1053D3D12MA_HEAVY_ASSERT(m_Count > 0);1054return m_pArray[0];1055}10561057template<typename T>1058const T& Vector<T>::back() const1059{1060D3D12MA_HEAVY_ASSERT(m_Count > 0);1061return m_pArray[m_Count - 1];1062}10631064template<typename T>1065void Vector<T>::reserve(size_t newCapacity, bool freeMemory)1066{1067newCapacity = D3D12MA_MAX(newCapacity, m_Count);10681069if ((newCapacity < m_Capacity) && !freeMemory)1070{1071newCapacity = m_Capacity;1072}10731074if (newCapacity != m_Capacity)1075{1076T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;1077if (m_Count != 0)1078{1079memcpy(newArray, m_pArray, m_Count * sizeof(T));1080}1081Free(m_AllocationCallbacks, m_pArray);1082m_Capacity = newCapacity;1083m_pArray = newArray;1084}1085}10861087template<typename T>1088void Vector<T>::resize(size_t newCount, bool freeMemory)1089{1090size_t newCapacity = m_Capacity;1091if (newCount > m_Capacity)1092{1093newCapacity = D3D12MA_MAX(newCount, D3D12MA_MAX(m_Capacity * 3 / 2, (size_t)8));1094}1095else if (freeMemory)1096{1097newCapacity = newCount;1098}10991100if (newCapacity != m_Capacity)1101{1102T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;1103const size_t elementsToCopy = D3D12MA_MIN(m_Count, newCount);1104if (elementsToCopy != 0)1105{1106memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));1107}1108Free(m_AllocationCallbacks, m_pArray);1109m_Capacity = newCapacity;1110m_pArray = newArray;1111}11121113m_Count = newCount;1114}11151116template<typename T>1117void Vector<T>::insert(size_t index, const T& src)1118{1119D3D12MA_HEAVY_ASSERT(index <= m_Count);1120const size_t oldCount = size();1121resize(oldCount + 1);1122if (index < oldCount)1123{1124memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));1125}1126m_pArray[index] = src;1127}11281129template<typename T>1130void Vector<T>::remove(size_t index)1131{1132D3D12MA_HEAVY_ASSERT(index < m_Count);1133const size_t oldCount = size();1134if (index < oldCount - 1)1135{1136memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));1137}1138resize(oldCount - 1);1139}11401141template<typename T> template<typename CmpLess>1142size_t Vector<T>::InsertSorted(const T& value, const CmpLess& cmp)1143{1144const size_t indexToInsert = BinaryFindFirstNotLess<CmpLess, iterator, T>(1145m_pArray,1146m_pArray + m_Count,1147value,1148cmp) - m_pArray;1149insert(indexToInsert, value);1150return indexToInsert;1151}11521153template<typename T> template<typename CmpLess>1154bool Vector<T>::RemoveSorted(const T& value, const CmpLess& cmp)1155{1156const iterator it = BinaryFindFirstNotLess(1157m_pArray,1158m_pArray + m_Count,1159value,1160cmp);1161if ((it != end()) && !cmp(*it, value) && !cmp(value, *it))1162{1163size_t indexToRemove = it - begin();1164remove(indexToRemove);1165return true;1166}1167return false;1168}11691170template<typename T>1171Vector<T>& Vector<T>::operator=(const Vector<T>& rhs)1172{1173if (&rhs != this)1174{1175resize(rhs.m_Count);1176if (m_Count != 0)1177{1178memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));1179}1180}1181return *this;1182}11831184template<typename T>1185T& Vector<T>::operator[](size_t index)1186{1187D3D12MA_HEAVY_ASSERT(index < m_Count);1188return m_pArray[index];1189}11901191template<typename T>1192const T& Vector<T>::operator[](size_t index) const1193{1194D3D12MA_HEAVY_ASSERT(index < m_Count);1195return m_pArray[index];1196}1197#endif // _D3D12MA_VECTOR_FUNCTIONS1198#endif // _D3D12MA_VECTOR11991200#ifndef _D3D12MA_STRING_BUILDER1201class StringBuilder1202{1203public:1204StringBuilder(const ALLOCATION_CALLBACKS& allocationCallbacks) : m_Data(allocationCallbacks) {}12051206size_t GetLength() const { return m_Data.size(); }1207LPCWSTR GetData() const { return m_Data.data(); }12081209void Add(WCHAR ch) { m_Data.push_back(ch); }1210void Add(LPCWSTR str);1211void AddNewLine() { Add(L'\n'); }1212void AddNumber(UINT num);1213void AddNumber(UINT64 num);1214void AddPointer(const void* ptr);12151216private:1217Vector<WCHAR> m_Data;1218};12191220#ifndef _D3D12MA_STRING_BUILDER_FUNCTIONS1221void StringBuilder::Add(LPCWSTR str)1222{1223const size_t len = wcslen(str);1224if (len > 0)1225{1226const size_t oldCount = m_Data.size();1227m_Data.resize(oldCount + len);1228memcpy(m_Data.data() + oldCount, str, len * sizeof(WCHAR));1229}1230}12311232void StringBuilder::AddNumber(UINT num)1233{1234WCHAR buf[11];1235buf[10] = L'\0';1236WCHAR *p = &buf[10];1237do1238{1239*--p = L'0' + (num % 10);1240num /= 10;1241}1242while (num);1243Add(p);1244}12451246void StringBuilder::AddNumber(UINT64 num)1247{1248WCHAR buf[21];1249buf[20] = L'\0';1250WCHAR *p = &buf[20];1251do1252{1253*--p = L'0' + (num % 10);1254num /= 10;1255}1256while (num);1257Add(p);1258}12591260void StringBuilder::AddPointer(const void* ptr)1261{1262WCHAR buf[21];1263uintptr_t num = (uintptr_t)ptr;1264buf[20] = L'\0';1265WCHAR *p = &buf[20];1266do1267{1268*--p = HexDigitToChar((UINT8)(num & 0xF));1269num >>= 4;1270}1271while (num);1272Add(p);1273}12741275#endif // _D3D12MA_STRING_BUILDER_FUNCTIONS1276#endif // _D3D12MA_STRING_BUILDER12771278#ifndef _D3D12MA_JSON_WRITER1279/*1280Allows to conveniently build a correct JSON document to be written to the1281StringBuilder passed to the constructor.1282*/1283class JsonWriter1284{1285public:1286// stringBuilder - string builder to write the document to. Must remain alive for the whole lifetime of this object.1287JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder);1288~JsonWriter();12891290// Begins object by writing "{".1291// Inside an object, you must call pairs of WriteString and a value, e.g.:1292// j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();1293// Will write: { "A": 1, "B": 2 }1294void BeginObject(bool singleLine = false);1295// Ends object by writing "}".1296void EndObject();12971298// Begins array by writing "[".1299// Inside an array, you can write a sequence of any values.1300void BeginArray(bool singleLine = false);1301// Ends array by writing "[".1302void EndArray();13031304// Writes a string value inside "".1305// pStr can contain any UTF-16 characters, including '"', new line etc. - they will be properly escaped.1306void WriteString(LPCWSTR pStr);13071308// Begins writing a string value.1309// Call BeginString, ContinueString, ContinueString, ..., EndString instead of1310// WriteString to conveniently build the string content incrementally, made of1311// parts including numbers.1312void BeginString(LPCWSTR pStr = NULL);1313// Posts next part of an open string.1314void ContinueString(LPCWSTR pStr);1315// Posts next part of an open string. The number is converted to decimal characters.1316void ContinueString(UINT num);1317void ContinueString(UINT64 num);1318void ContinueString_Pointer(const void* ptr);1319// Posts next part of an open string. Pointer value is converted to characters1320// using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad001321// void ContinueString_Pointer(const void* ptr);1322// Ends writing a string value by writing '"'.1323void EndString(LPCWSTR pStr = NULL);13241325// Writes a number value.1326void WriteNumber(UINT num);1327void WriteNumber(UINT64 num);1328// Writes a boolean value - false or true.1329void WriteBool(bool b);1330// Writes a null value.1331void WriteNull();13321333void AddAllocationToObject(const Allocation& alloc);1334void AddDetailedStatisticsInfoObject(const DetailedStatistics& stats);13351336private:1337static const WCHAR* const INDENT;13381339enum CollectionType1340{1341COLLECTION_TYPE_OBJECT,1342COLLECTION_TYPE_ARRAY,1343};1344struct StackItem1345{1346CollectionType type;1347UINT valueCount;1348bool singleLineMode;1349};13501351StringBuilder& m_SB;1352Vector<StackItem> m_Stack;1353bool m_InsideString;13541355void BeginValue(bool isString);1356void WriteIndent(bool oneLess = false);1357};13581359#ifndef _D3D12MA_JSON_WRITER_FUNCTIONS1360const WCHAR* const JsonWriter::INDENT = L" ";13611362JsonWriter::JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder)1363: m_SB(stringBuilder),1364m_Stack(allocationCallbacks),1365m_InsideString(false) {}13661367JsonWriter::~JsonWriter()1368{1369D3D12MA_ASSERT(!m_InsideString);1370D3D12MA_ASSERT(m_Stack.empty());1371}13721373void JsonWriter::BeginObject(bool singleLine)1374{1375D3D12MA_ASSERT(!m_InsideString);13761377BeginValue(false);1378m_SB.Add(L'{');13791380StackItem stackItem;1381stackItem.type = COLLECTION_TYPE_OBJECT;1382stackItem.valueCount = 0;1383stackItem.singleLineMode = singleLine;1384m_Stack.push_back(stackItem);1385}13861387void JsonWriter::EndObject()1388{1389D3D12MA_ASSERT(!m_InsideString);1390D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);1391D3D12MA_ASSERT(m_Stack.back().valueCount % 2 == 0);13921393WriteIndent(true);1394m_SB.Add(L'}');13951396m_Stack.pop_back();1397}13981399void JsonWriter::BeginArray(bool singleLine)1400{1401D3D12MA_ASSERT(!m_InsideString);14021403BeginValue(false);1404m_SB.Add(L'[');14051406StackItem stackItem;1407stackItem.type = COLLECTION_TYPE_ARRAY;1408stackItem.valueCount = 0;1409stackItem.singleLineMode = singleLine;1410m_Stack.push_back(stackItem);1411}14121413void JsonWriter::EndArray()1414{1415D3D12MA_ASSERT(!m_InsideString);1416D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);14171418WriteIndent(true);1419m_SB.Add(L']');14201421m_Stack.pop_back();1422}14231424void JsonWriter::WriteString(LPCWSTR pStr)1425{1426BeginString(pStr);1427EndString();1428}14291430void JsonWriter::BeginString(LPCWSTR pStr)1431{1432D3D12MA_ASSERT(!m_InsideString);14331434BeginValue(true);1435m_InsideString = true;1436m_SB.Add(L'"');1437if (pStr != NULL)1438{1439ContinueString(pStr);1440}1441}14421443void JsonWriter::ContinueString(LPCWSTR pStr)1444{1445D3D12MA_ASSERT(m_InsideString);1446D3D12MA_ASSERT(pStr);14471448for (const WCHAR *p = pStr; *p; ++p)1449{1450// the strings we encode are assumed to be in UTF-16LE format, the native1451// windows wide character Unicode format. In this encoding Unicode code1452// points U+0000 to U+D7FF and U+E000 to U+FFFF are encoded in two bytes,1453// and everything else takes more than two bytes. We will reject any1454// multi wchar character encodings for simplicity.1455UINT val = (UINT)*p;1456D3D12MA_ASSERT(((val <= 0xD7FF) || (0xE000 <= val && val <= 0xFFFF)) &&1457"Character not currently supported.");1458switch (*p)1459{1460case L'"': m_SB.Add(L'\\'); m_SB.Add(L'"'); break;1461case L'\\': m_SB.Add(L'\\'); m_SB.Add(L'\\'); break;1462case L'/': m_SB.Add(L'\\'); m_SB.Add(L'/'); break;1463case L'\b': m_SB.Add(L'\\'); m_SB.Add(L'b'); break;1464case L'\f': m_SB.Add(L'\\'); m_SB.Add(L'f'); break;1465case L'\n': m_SB.Add(L'\\'); m_SB.Add(L'n'); break;1466case L'\r': m_SB.Add(L'\\'); m_SB.Add(L'r'); break;1467case L'\t': m_SB.Add(L'\\'); m_SB.Add(L't'); break;1468default:1469// conservatively use encoding \uXXXX for any Unicode character1470// requiring more than one byte.1471if (32 <= val && val < 256)1472m_SB.Add(*p);1473else1474{1475m_SB.Add(L'\\');1476m_SB.Add(L'u');1477for (UINT i = 0; i < 4; ++i)1478{1479UINT hexDigit = (val & 0xF000) >> 12;1480val <<= 4;1481if (hexDigit < 10)1482m_SB.Add(L'0' + (WCHAR)hexDigit);1483else1484m_SB.Add(L'A' + (WCHAR)hexDigit);1485}1486}1487break;1488}1489}1490}14911492void JsonWriter::ContinueString(UINT num)1493{1494D3D12MA_ASSERT(m_InsideString);1495m_SB.AddNumber(num);1496}14971498void JsonWriter::ContinueString(UINT64 num)1499{1500D3D12MA_ASSERT(m_InsideString);1501m_SB.AddNumber(num);1502}15031504void JsonWriter::ContinueString_Pointer(const void* ptr)1505{1506D3D12MA_ASSERT(m_InsideString);1507m_SB.AddPointer(ptr);1508}15091510void JsonWriter::EndString(LPCWSTR pStr)1511{1512D3D12MA_ASSERT(m_InsideString);15131514if (pStr)1515ContinueString(pStr);1516m_SB.Add(L'"');1517m_InsideString = false;1518}15191520void JsonWriter::WriteNumber(UINT num)1521{1522D3D12MA_ASSERT(!m_InsideString);1523BeginValue(false);1524m_SB.AddNumber(num);1525}15261527void JsonWriter::WriteNumber(UINT64 num)1528{1529D3D12MA_ASSERT(!m_InsideString);1530BeginValue(false);1531m_SB.AddNumber(num);1532}15331534void JsonWriter::WriteBool(bool b)1535{1536D3D12MA_ASSERT(!m_InsideString);1537BeginValue(false);1538if (b)1539m_SB.Add(L"true");1540else1541m_SB.Add(L"false");1542}15431544void JsonWriter::WriteNull()1545{1546D3D12MA_ASSERT(!m_InsideString);1547BeginValue(false);1548m_SB.Add(L"null");1549}15501551void JsonWriter::AddAllocationToObject(const Allocation& alloc)1552{1553WriteString(L"Type");1554switch (alloc.m_PackedData.GetResourceDimension()) {1555case D3D12_RESOURCE_DIMENSION_UNKNOWN:1556WriteString(L"UNKNOWN");1557break;1558case D3D12_RESOURCE_DIMENSION_BUFFER:1559WriteString(L"BUFFER");1560break;1561case D3D12_RESOURCE_DIMENSION_TEXTURE1D:1562WriteString(L"TEXTURE1D");1563break;1564case D3D12_RESOURCE_DIMENSION_TEXTURE2D:1565WriteString(L"TEXTURE2D");1566break;1567case D3D12_RESOURCE_DIMENSION_TEXTURE3D:1568WriteString(L"TEXTURE3D");1569break;1570default: D3D12MA_ASSERT(0); break;1571}15721573WriteString(L"Size");1574WriteNumber(alloc.GetSize());1575WriteString(L"Usage");1576WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags());15771578void* privateData = alloc.GetPrivateData();1579if (privateData)1580{1581WriteString(L"CustomData");1582BeginString();1583ContinueString_Pointer(privateData);1584EndString();1585}15861587LPCWSTR name = alloc.GetName();1588if (name != NULL)1589{1590WriteString(L"Name");1591WriteString(name);1592}1593if (alloc.m_PackedData.GetTextureLayout())1594{1595WriteString(L"Layout");1596WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout());1597}1598}15991600void JsonWriter::AddDetailedStatisticsInfoObject(const DetailedStatistics& stats)1601{1602BeginObject();16031604WriteString(L"BlockCount");1605WriteNumber(stats.Stats.BlockCount);1606WriteString(L"BlockBytes");1607WriteNumber(stats.Stats.BlockBytes);1608WriteString(L"AllocationCount");1609WriteNumber(stats.Stats.AllocationCount);1610WriteString(L"AllocationBytes");1611WriteNumber(stats.Stats.AllocationBytes);1612WriteString(L"UnusedRangeCount");1613WriteNumber(stats.UnusedRangeCount);16141615if (stats.Stats.AllocationCount > 1)1616{1617WriteString(L"AllocationSizeMin");1618WriteNumber(stats.AllocationSizeMin);1619WriteString(L"AllocationSizeMax");1620WriteNumber(stats.AllocationSizeMax);1621}1622if (stats.UnusedRangeCount > 1)1623{1624WriteString(L"UnusedRangeSizeMin");1625WriteNumber(stats.UnusedRangeSizeMin);1626WriteString(L"UnusedRangeSizeMax");1627WriteNumber(stats.UnusedRangeSizeMax);1628}1629EndObject();1630}16311632void JsonWriter::BeginValue(bool isString)1633{1634if (!m_Stack.empty())1635{1636StackItem& currItem = m_Stack.back();1637if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 0)1638{1639D3D12MA_ASSERT(isString);1640}16411642if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 1)1643{1644m_SB.Add(L':'); m_SB.Add(L' ');1645}1646else if (currItem.valueCount > 0)1647{1648m_SB.Add(L','); m_SB.Add(L' ');1649WriteIndent();1650}1651else1652{1653WriteIndent();1654}1655++currItem.valueCount;1656}1657}16581659void JsonWriter::WriteIndent(bool oneLess)1660{1661if (!m_Stack.empty() && !m_Stack.back().singleLineMode)1662{1663m_SB.AddNewLine();16641665size_t count = m_Stack.size();1666if (count > 0 && oneLess)1667{1668--count;1669}1670for (size_t i = 0; i < count; ++i)1671{1672m_SB.Add(INDENT);1673}1674}1675}1676#endif // _D3D12MA_JSON_WRITER_FUNCTIONS1677#endif // _D3D12MA_JSON_WRITER16781679#ifndef _D3D12MA_POOL_ALLOCATOR1680/*1681Allocator for objects of type T using a list of arrays (pools) to speed up1682allocation. Number of elements that can be allocated is not bounded because1683allocator can create multiple blocks.1684T should be POD because constructor and destructor is not called in Alloc or1685Free.1686*/1687template<typename T>1688class PoolAllocator1689{1690D3D12MA_CLASS_NO_COPY(PoolAllocator)1691public:1692// allocationCallbacks externally owned, must outlive this object.1693PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity);1694~PoolAllocator() { Clear(); }16951696void Clear();1697template<typename... Types>1698T* Alloc(Types... args);1699void Free(T* ptr);17001701private:1702union Item1703{1704UINT NextFreeIndex; // UINT32_MAX means end of list.1705alignas(T) char Value[sizeof(T)];1706};17071708struct ItemBlock1709{1710Item* pItems;1711UINT Capacity;1712UINT FirstFreeIndex;1713};17141715const ALLOCATION_CALLBACKS& m_AllocationCallbacks;1716const UINT m_FirstBlockCapacity;1717Vector<ItemBlock> m_ItemBlocks;17181719ItemBlock& CreateNewBlock();1720};17211722#ifndef _D3D12MA_POOL_ALLOCATOR_FUNCTIONS1723template<typename T>1724PoolAllocator<T>::PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity)1725: m_AllocationCallbacks(allocationCallbacks),1726m_FirstBlockCapacity(firstBlockCapacity),1727m_ItemBlocks(allocationCallbacks)1728{1729D3D12MA_ASSERT(m_FirstBlockCapacity > 1);1730}17311732template<typename T>1733void PoolAllocator<T>::Clear()1734{1735for(size_t i = m_ItemBlocks.size(); i--; )1736{1737D3D12MA_DELETE_ARRAY(m_AllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);1738}1739m_ItemBlocks.clear(true);1740}17411742template<typename T> template<typename... Types>1743T* PoolAllocator<T>::Alloc(Types... args)1744{1745for(size_t i = m_ItemBlocks.size(); i--; )1746{1747ItemBlock& block = m_ItemBlocks[i];1748// This block has some free items: Use first one.1749if(block.FirstFreeIndex != UINT32_MAX)1750{1751Item* const pItem = &block.pItems[block.FirstFreeIndex];1752block.FirstFreeIndex = pItem->NextFreeIndex;1753T* result = (T*)&pItem->Value;1754new(result)T(std::forward<Types>(args)...); // Explicit constructor call.1755return result;1756}1757}17581759// No block has free item: Create new one and use it.1760ItemBlock& newBlock = CreateNewBlock();1761Item* const pItem = &newBlock.pItems[0];1762newBlock.FirstFreeIndex = pItem->NextFreeIndex;1763T* result = (T*)pItem->Value;1764new(result)T(std::forward<Types>(args)...); // Explicit constructor call.1765return result;1766}17671768template<typename T>1769void PoolAllocator<T>::Free(T* ptr)1770{1771// Search all memory blocks to find ptr.1772for(size_t i = m_ItemBlocks.size(); i--; )1773{1774ItemBlock& block = m_ItemBlocks[i];17751776Item* pItemPtr;1777memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));17781779// Check if pItemPtr is in address range of this block.1780if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))1781{1782ptr->~T(); // Explicit destructor call.1783const UINT index = static_cast<UINT>(pItemPtr - block.pItems);1784pItemPtr->NextFreeIndex = block.FirstFreeIndex;1785block.FirstFreeIndex = index;1786return;1787}1788}1789D3D12MA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");1790}17911792template<typename T>1793typename PoolAllocator<T>::ItemBlock& PoolAllocator<T>::CreateNewBlock()1794{1795const UINT newBlockCapacity = m_ItemBlocks.empty() ?1796m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;17971798const ItemBlock newBlock = {1799D3D12MA_NEW_ARRAY(m_AllocationCallbacks, Item, newBlockCapacity),1800newBlockCapacity,18010 };18021803m_ItemBlocks.push_back(newBlock);18041805// Setup singly-linked list of all free items in this block.1806for(UINT i = 0; i < newBlockCapacity - 1; ++i)1807{1808newBlock.pItems[i].NextFreeIndex = i + 1;1809}1810newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;1811return m_ItemBlocks.back();1812}1813#endif // _D3D12MA_POOL_ALLOCATOR_FUNCTIONS1814#endif // _D3D12MA_POOL_ALLOCATOR18151816#ifndef _D3D12MA_LIST1817/*1818Doubly linked list, with elements allocated out of PoolAllocator.1819Has custom interface, as well as STL-style interface, including iterator and1820const_iterator.1821*/1822template<typename T>1823class List1824{1825D3D12MA_CLASS_NO_COPY(List)1826public:1827struct Item1828{1829Item* pPrev;1830Item* pNext;1831T Value;1832};18331834class reverse_iterator;1835class const_reverse_iterator;1836class iterator1837{1838friend class List<T>;1839friend class const_iterator;18401841public:1842iterator() = default;1843iterator(const reverse_iterator& src)1844: m_pList(src.m_pList), m_pItem(src.m_pItem) {}18451846T& operator*() const;1847T* operator->() const;18481849iterator& operator++();1850iterator& operator--();1851iterator operator++(int);1852iterator operator--(int);18531854bool operator==(const iterator& rhs) const;1855bool operator!=(const iterator& rhs) const;18561857private:1858List<T>* m_pList = NULL;1859Item* m_pItem = NULL;18601861iterator(List<T>* pList, Item* pItem) : m_pList(pList), m_pItem(pItem) {}1862};18631864class reverse_iterator1865{1866friend class List<T>;1867friend class const_reverse_iterator;18681869public:1870reverse_iterator() = default;1871reverse_iterator(const iterator& src)1872: m_pList(src.m_pList), m_pItem(src.m_pItem) {}18731874T& operator*() const;1875T* operator->() const;18761877reverse_iterator& operator++();1878reverse_iterator& operator--();1879reverse_iterator operator++(int);1880reverse_iterator operator--(int);18811882bool operator==(const reverse_iterator& rhs) const;1883bool operator!=(const reverse_iterator& rhs) const;18841885private:1886List<T>* m_pList = NULL;1887Item* m_pItem = NULL;18881889reverse_iterator(List<T>* pList, Item* pItem)1890: m_pList(pList), m_pItem(pItem) {}1891};18921893class const_iterator1894{1895friend class List<T>;18961897public:1898const_iterator() = default;1899const_iterator(const iterator& src)1900: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1901const_iterator(const reverse_iterator& src)1902: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1903const_iterator(const const_reverse_iterator& src)1904: m_pList(src.m_pList), m_pItem(src.m_pItem) {}19051906iterator dropConst() const;1907const T& operator*() const;1908const T* operator->() const;19091910const_iterator& operator++();1911const_iterator& operator--();1912const_iterator operator++(int);1913const_iterator operator--(int);19141915bool operator==(const const_iterator& rhs) const;1916bool operator!=(const const_iterator& rhs) const;19171918private:1919const List<T>* m_pList = NULL;1920const Item* m_pItem = NULL;19211922const_iterator(const List<T>* pList, const Item* pItem)1923: m_pList(pList), m_pItem(pItem) {}1924};19251926class const_reverse_iterator1927{1928friend class List<T>;19291930public:1931const_reverse_iterator() = default;1932const_reverse_iterator(const iterator& src)1933: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1934const_reverse_iterator(const reverse_iterator& src)1935: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1936const_reverse_iterator(const const_iterator& src)1937: m_pList(src.m_pList), m_pItem(src.m_pItem) {}19381939reverse_iterator dropConst() const;1940const T& operator*() const;1941const T* operator->() const;19421943const_reverse_iterator& operator++();1944const_reverse_iterator& operator--();1945const_reverse_iterator operator++(int);1946const_reverse_iterator operator--(int);19471948bool operator==(const const_reverse_iterator& rhs) const;1949bool operator!=(const const_reverse_iterator& rhs) const;19501951private:1952const List<T>* m_pList = NULL;1953const Item* m_pItem = NULL;19541955const_reverse_iterator(const List<T>* pList, const Item* pItem)1956: m_pList(pList), m_pItem(pItem) {}1957};19581959// allocationCallbacks externally owned, must outlive this object.1960List(const ALLOCATION_CALLBACKS& allocationCallbacks);1961// Intentionally not calling Clear, because that would be unnecessary1962// computations to return all items to m_ItemAllocator as free.1963~List() = default;19641965size_t GetCount() const { return m_Count; }1966bool IsEmpty() const { return m_Count == 0; }19671968Item* Front() { return m_pFront; }1969const Item* Front() const { return m_pFront; }1970Item* Back() { return m_pBack; }1971const Item* Back() const { return m_pBack; }19721973bool empty() const { return IsEmpty(); }1974size_t size() const { return GetCount(); }1975void push_back(const T& value) { PushBack(value); }1976iterator insert(iterator it, const T& value) { return iterator(this, InsertBefore(it.m_pItem, value)); }1977void clear() { Clear(); }1978void erase(iterator it) { Remove(it.m_pItem); }19791980iterator begin() { return iterator(this, Front()); }1981iterator end() { return iterator(this, NULL); }1982reverse_iterator rbegin() { return reverse_iterator(this, Back()); }1983reverse_iterator rend() { return reverse_iterator(this, NULL); }19841985const_iterator cbegin() const { return const_iterator(this, Front()); }1986const_iterator cend() const { return const_iterator(this, NULL); }1987const_iterator begin() const { return cbegin(); }1988const_iterator end() const { return cend(); }19891990const_reverse_iterator crbegin() const { return const_reverse_iterator(this, Back()); }1991const_reverse_iterator crend() const { return const_reverse_iterator(this, NULL); }1992const_reverse_iterator rbegin() const { return crbegin(); }1993const_reverse_iterator rend() const { return crend(); }19941995Item* PushBack();1996Item* PushFront();1997Item* PushBack(const T& value);1998Item* PushFront(const T& value);1999void PopBack();2000void PopFront();20012002// Item can be null - it means PushBack.2003Item* InsertBefore(Item* pItem);2004// Item can be null - it means PushFront.2005Item* InsertAfter(Item* pItem);2006Item* InsertBefore(Item* pItem, const T& value);2007Item* InsertAfter(Item* pItem, const T& value);20082009void Clear();2010void Remove(Item* pItem);20112012private:2013const ALLOCATION_CALLBACKS& m_AllocationCallbacks;2014PoolAllocator<Item> m_ItemAllocator;2015Item* m_pFront;2016Item* m_pBack;2017size_t m_Count;2018};20192020#ifndef _D3D12MA_LIST_ITERATOR_FUNCTIONS2021template<typename T>2022T& List<T>::iterator::operator*() const2023{2024D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2025return m_pItem->Value;2026}20272028template<typename T>2029T* List<T>::iterator::operator->() const2030{2031D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2032return &m_pItem->Value;2033}20342035template<typename T>2036typename List<T>::iterator& List<T>::iterator::operator++()2037{2038D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2039m_pItem = m_pItem->pNext;2040return *this;2041}20422043template<typename T>2044typename List<T>::iterator& List<T>::iterator::operator--()2045{2046if (m_pItem != NULL)2047{2048m_pItem = m_pItem->pPrev;2049}2050else2051{2052D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2053m_pItem = m_pList->Back();2054}2055return *this;2056}20572058template<typename T>2059typename List<T>::iterator List<T>::iterator::operator++(int)2060{2061iterator result = *this;2062++* this;2063return result;2064}20652066template<typename T>2067typename List<T>::iterator List<T>::iterator::operator--(int)2068{2069iterator result = *this;2070--* this;2071return result;2072}20732074template<typename T>2075bool List<T>::iterator::operator==(const iterator& rhs) const2076{2077D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2078return m_pItem == rhs.m_pItem;2079}20802081template<typename T>2082bool List<T>::iterator::operator!=(const iterator& rhs) const2083{2084D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2085return m_pItem != rhs.m_pItem;2086}2087#endif // _D3D12MA_LIST_ITERATOR_FUNCTIONS20882089#ifndef _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS2090template<typename T>2091T& List<T>::reverse_iterator::operator*() const2092{2093D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2094return m_pItem->Value;2095}20962097template<typename T>2098T* List<T>::reverse_iterator::operator->() const2099{2100D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2101return &m_pItem->Value;2102}21032104template<typename T>2105typename List<T>::reverse_iterator& List<T>::reverse_iterator::operator++()2106{2107D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2108m_pItem = m_pItem->pPrev;2109return *this;2110}21112112template<typename T>2113typename List<T>::reverse_iterator& List<T>::reverse_iterator::operator--()2114{2115if (m_pItem != NULL)2116{2117m_pItem = m_pItem->pNext;2118}2119else2120{2121D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2122m_pItem = m_pList->Front();2123}2124return *this;2125}21262127template<typename T>2128typename List<T>::reverse_iterator List<T>::reverse_iterator::operator++(int)2129{2130reverse_iterator result = *this;2131++* this;2132return result;2133}21342135template<typename T>2136typename List<T>::reverse_iterator List<T>::reverse_iterator::operator--(int)2137{2138reverse_iterator result = *this;2139--* this;2140return result;2141}21422143template<typename T>2144bool List<T>::reverse_iterator::operator==(const reverse_iterator& rhs) const2145{2146D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2147return m_pItem == rhs.m_pItem;2148}21492150template<typename T>2151bool List<T>::reverse_iterator::operator!=(const reverse_iterator& rhs) const2152{2153D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2154return m_pItem != rhs.m_pItem;2155}2156#endif // _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS21572158#ifndef _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS2159template<typename T>2160typename List<T>::iterator List<T>::const_iterator::dropConst() const2161{2162return iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));2163}21642165template<typename T>2166const T& List<T>::const_iterator::operator*() const2167{2168D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2169return m_pItem->Value;2170}21712172template<typename T>2173const T* List<T>::const_iterator::operator->() const2174{2175D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2176return &m_pItem->Value;2177}21782179template<typename T>2180typename List<T>::const_iterator& List<T>::const_iterator::operator++()2181{2182D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2183m_pItem = m_pItem->pNext;2184return *this;2185}21862187template<typename T>2188typename List<T>::const_iterator& List<T>::const_iterator::operator--()2189{2190if (m_pItem != NULL)2191{2192m_pItem = m_pItem->pPrev;2193}2194else2195{2196D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2197m_pItem = m_pList->Back();2198}2199return *this;2200}22012202template<typename T>2203typename List<T>::const_iterator List<T>::const_iterator::operator++(int)2204{2205const_iterator result = *this;2206++* this;2207return result;2208}22092210template<typename T>2211typename List<T>::const_iterator List<T>::const_iterator::operator--(int)2212{2213const_iterator result = *this;2214--* this;2215return result;2216}22172218template<typename T>2219bool List<T>::const_iterator::operator==(const const_iterator& rhs) const2220{2221D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2222return m_pItem == rhs.m_pItem;2223}22242225template<typename T>2226bool List<T>::const_iterator::operator!=(const const_iterator& rhs) const2227{2228D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2229return m_pItem != rhs.m_pItem;2230}2231#endif // _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS22322233#ifndef _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS2234template<typename T>2235typename List<T>::reverse_iterator List<T>::const_reverse_iterator::dropConst() const2236{2237return reverse_iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));2238}22392240template<typename T>2241const T& List<T>::const_reverse_iterator::operator*() const2242{2243D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2244return m_pItem->Value;2245}22462247template<typename T>2248const T* List<T>::const_reverse_iterator::operator->() const2249{2250D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2251return &m_pItem->Value;2252}22532254template<typename T>2255typename List<T>::const_reverse_iterator& List<T>::const_reverse_iterator::operator++()2256{2257D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2258m_pItem = m_pItem->pPrev;2259return *this;2260}22612262template<typename T>2263typename List<T>::const_reverse_iterator& List<T>::const_reverse_iterator::operator--()2264{2265if (m_pItem != NULL)2266{2267m_pItem = m_pItem->pNext;2268}2269else2270{2271D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2272m_pItem = m_pList->Front();2273}2274return *this;2275}22762277template<typename T>2278typename List<T>::const_reverse_iterator List<T>::const_reverse_iterator::operator++(int)2279{2280const_reverse_iterator result = *this;2281++* this;2282return result;2283}22842285template<typename T>2286typename List<T>::const_reverse_iterator List<T>::const_reverse_iterator::operator--(int)2287{2288const_reverse_iterator result = *this;2289--* this;2290return result;2291}22922293template<typename T>2294bool List<T>::const_reverse_iterator::operator==(const const_reverse_iterator& rhs) const2295{2296D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2297return m_pItem == rhs.m_pItem;2298}22992300template<typename T>2301bool List<T>::const_reverse_iterator::operator!=(const const_reverse_iterator& rhs) const2302{2303D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2304return m_pItem != rhs.m_pItem;2305}2306#endif // _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS23072308#ifndef _D3D12MA_LIST_FUNCTIONS2309template<typename T>2310List<T>::List(const ALLOCATION_CALLBACKS& allocationCallbacks)2311: m_AllocationCallbacks(allocationCallbacks),2312m_ItemAllocator(allocationCallbacks, 128),2313m_pFront(NULL),2314m_pBack(NULL),2315m_Count(0) {}23162317template<typename T>2318void List<T>::Clear()2319{2320if(!IsEmpty())2321{2322Item* pItem = m_pBack;2323while(pItem != NULL)2324{2325Item* const pPrevItem = pItem->pPrev;2326m_ItemAllocator.Free(pItem);2327pItem = pPrevItem;2328}2329m_pFront = NULL;2330m_pBack = NULL;2331m_Count = 0;2332}2333}23342335template<typename T>2336typename List<T>::Item* List<T>::PushBack()2337{2338Item* const pNewItem = m_ItemAllocator.Alloc();2339pNewItem->pNext = NULL;2340if(IsEmpty())2341{2342pNewItem->pPrev = NULL;2343m_pFront = pNewItem;2344m_pBack = pNewItem;2345m_Count = 1;2346}2347else2348{2349pNewItem->pPrev = m_pBack;2350m_pBack->pNext = pNewItem;2351m_pBack = pNewItem;2352++m_Count;2353}2354return pNewItem;2355}23562357template<typename T>2358typename List<T>::Item* List<T>::PushFront()2359{2360Item* const pNewItem = m_ItemAllocator.Alloc();2361pNewItem->pPrev = NULL;2362if(IsEmpty())2363{2364pNewItem->pNext = NULL;2365m_pFront = pNewItem;2366m_pBack = pNewItem;2367m_Count = 1;2368}2369else2370{2371pNewItem->pNext = m_pFront;2372m_pFront->pPrev = pNewItem;2373m_pFront = pNewItem;2374++m_Count;2375}2376return pNewItem;2377}23782379template<typename T>2380typename List<T>::Item* List<T>::PushBack(const T& value)2381{2382Item* const pNewItem = PushBack();2383pNewItem->Value = value;2384return pNewItem;2385}23862387template<typename T>2388typename List<T>::Item* List<T>::PushFront(const T& value)2389{2390Item* const pNewItem = PushFront();2391pNewItem->Value = value;2392return pNewItem;2393}23942395template<typename T>2396void List<T>::PopBack()2397{2398D3D12MA_HEAVY_ASSERT(m_Count > 0);2399Item* const pBackItem = m_pBack;2400Item* const pPrevItem = pBackItem->pPrev;2401if(pPrevItem != NULL)2402{2403pPrevItem->pNext = NULL;2404}2405m_pBack = pPrevItem;2406m_ItemAllocator.Free(pBackItem);2407--m_Count;2408}24092410template<typename T>2411void List<T>::PopFront()2412{2413D3D12MA_HEAVY_ASSERT(m_Count > 0);2414Item* const pFrontItem = m_pFront;2415Item* const pNextItem = pFrontItem->pNext;2416if(pNextItem != NULL)2417{2418pNextItem->pPrev = NULL;2419}2420m_pFront = pNextItem;2421m_ItemAllocator.Free(pFrontItem);2422--m_Count;2423}24242425template<typename T>2426void List<T>::Remove(Item* pItem)2427{2428D3D12MA_HEAVY_ASSERT(pItem != NULL);2429D3D12MA_HEAVY_ASSERT(m_Count > 0);24302431if(pItem->pPrev != NULL)2432{2433pItem->pPrev->pNext = pItem->pNext;2434}2435else2436{2437D3D12MA_HEAVY_ASSERT(m_pFront == pItem);2438m_pFront = pItem->pNext;2439}24402441if(pItem->pNext != NULL)2442{2443pItem->pNext->pPrev = pItem->pPrev;2444}2445else2446{2447D3D12MA_HEAVY_ASSERT(m_pBack == pItem);2448m_pBack = pItem->pPrev;2449}24502451m_ItemAllocator.Free(pItem);2452--m_Count;2453}24542455template<typename T>2456typename List<T>::Item* List<T>::InsertBefore(Item* pItem)2457{2458if(pItem != NULL)2459{2460Item* const prevItem = pItem->pPrev;2461Item* const newItem = m_ItemAllocator.Alloc();2462newItem->pPrev = prevItem;2463newItem->pNext = pItem;2464pItem->pPrev = newItem;2465if(prevItem != NULL)2466{2467prevItem->pNext = newItem;2468}2469else2470{2471D3D12MA_HEAVY_ASSERT(m_pFront == pItem);2472m_pFront = newItem;2473}2474++m_Count;2475return newItem;2476}2477else2478{2479return PushBack();2480}2481}24822483template<typename T>2484typename List<T>::Item* List<T>::InsertAfter(Item* pItem)2485{2486if(pItem != NULL)2487{2488Item* const nextItem = pItem->pNext;2489Item* const newItem = m_ItemAllocator.Alloc();2490newItem->pNext = nextItem;2491newItem->pPrev = pItem;2492pItem->pNext = newItem;2493if(nextItem != NULL)2494{2495nextItem->pPrev = newItem;2496}2497else2498{2499D3D12MA_HEAVY_ASSERT(m_pBack == pItem);2500m_pBack = newItem;2501}2502++m_Count;2503return newItem;2504}2505else2506return PushFront();2507}25082509template<typename T>2510typename List<T>::Item* List<T>::InsertBefore(Item* pItem, const T& value)2511{2512Item* const newItem = InsertBefore(pItem);2513newItem->Value = value;2514return newItem;2515}25162517template<typename T>2518typename List<T>::Item* List<T>::InsertAfter(Item* pItem, const T& value)2519{2520Item* const newItem = InsertAfter(pItem);2521newItem->Value = value;2522return newItem;2523}2524#endif // _D3D12MA_LIST_FUNCTIONS2525#endif // _D3D12MA_LIST25262527#ifndef _D3D12MA_INTRUSIVE_LINKED_LIST2528/*2529Expected interface of ItemTypeTraits:2530struct MyItemTypeTraits2531{2532using ItemType = MyItem;2533static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }2534static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }2535static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }2536static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }2537};2538*/2539template<typename ItemTypeTraits>2540class IntrusiveLinkedList2541{2542public:2543using ItemType = typename ItemTypeTraits::ItemType;2544static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }2545static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }25462547// Movable, not copyable.2548IntrusiveLinkedList() = default;2549IntrusiveLinkedList(const IntrusiveLinkedList&) = delete;2550IntrusiveLinkedList(IntrusiveLinkedList&& src);2551IntrusiveLinkedList& operator=(const IntrusiveLinkedList&) = delete;2552IntrusiveLinkedList& operator=(IntrusiveLinkedList&& src);2553~IntrusiveLinkedList() { D3D12MA_HEAVY_ASSERT(IsEmpty()); }25542555size_t GetCount() const { return m_Count; }2556bool IsEmpty() const { return m_Count == 0; }25572558ItemType* Front() { return m_Front; }2559ItemType* Back() { return m_Back; }2560const ItemType* Front() const { return m_Front; }2561const ItemType* Back() const { return m_Back; }25622563void PushBack(ItemType* item);2564void PushFront(ItemType* item);2565ItemType* PopBack();2566ItemType* PopFront();25672568// MyItem can be null - it means PushBack.2569void InsertBefore(ItemType* existingItem, ItemType* newItem);2570// MyItem can be null - it means PushFront.2571void InsertAfter(ItemType* existingItem, ItemType* newItem);25722573void Remove(ItemType* item);2574void RemoveAll();25752576private:2577ItemType* m_Front = NULL;2578ItemType* m_Back = NULL;2579size_t m_Count = 0;2580};25812582#ifndef _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS2583template<typename ItemTypeTraits>2584IntrusiveLinkedList<ItemTypeTraits>::IntrusiveLinkedList(IntrusiveLinkedList&& src)2585: m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)2586{2587src.m_Front = src.m_Back = NULL;2588src.m_Count = 0;2589}25902591template<typename ItemTypeTraits>2592IntrusiveLinkedList<ItemTypeTraits>& IntrusiveLinkedList<ItemTypeTraits>::operator=(IntrusiveLinkedList&& src)2593{2594if (&src != this)2595{2596D3D12MA_HEAVY_ASSERT(IsEmpty());2597m_Front = src.m_Front;2598m_Back = src.m_Back;2599m_Count = src.m_Count;2600src.m_Front = src.m_Back = NULL;2601src.m_Count = 0;2602}2603return *this;2604}26052606template<typename ItemTypeTraits>2607void IntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)2608{2609D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);2610if (IsEmpty())2611{2612m_Front = item;2613m_Back = item;2614m_Count = 1;2615}2616else2617{2618ItemTypeTraits::AccessPrev(item) = m_Back;2619ItemTypeTraits::AccessNext(m_Back) = item;2620m_Back = item;2621++m_Count;2622}2623}26242625template<typename ItemTypeTraits>2626void IntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)2627{2628D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);2629if (IsEmpty())2630{2631m_Front = item;2632m_Back = item;2633m_Count = 1;2634}2635else2636{2637ItemTypeTraits::AccessNext(item) = m_Front;2638ItemTypeTraits::AccessPrev(m_Front) = item;2639m_Front = item;2640++m_Count;2641}2642}26432644template<typename ItemTypeTraits>2645typename IntrusiveLinkedList<ItemTypeTraits>::ItemType* IntrusiveLinkedList<ItemTypeTraits>::PopBack()2646{2647D3D12MA_HEAVY_ASSERT(m_Count > 0);2648ItemType* const backItem = m_Back;2649ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);2650if (prevItem != NULL)2651{2652ItemTypeTraits::AccessNext(prevItem) = NULL;2653}2654m_Back = prevItem;2655--m_Count;2656ItemTypeTraits::AccessPrev(backItem) = NULL;2657ItemTypeTraits::AccessNext(backItem) = NULL;2658return backItem;2659}26602661template<typename ItemTypeTraits>2662typename IntrusiveLinkedList<ItemTypeTraits>::ItemType* IntrusiveLinkedList<ItemTypeTraits>::PopFront()2663{2664D3D12MA_HEAVY_ASSERT(m_Count > 0);2665ItemType* const frontItem = m_Front;2666ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);2667if (nextItem != NULL)2668{2669ItemTypeTraits::AccessPrev(nextItem) = NULL;2670}2671m_Front = nextItem;2672--m_Count;2673ItemTypeTraits::AccessPrev(frontItem) = NULL;2674ItemTypeTraits::AccessNext(frontItem) = NULL;2675return frontItem;2676}26772678template<typename ItemTypeTraits>2679void IntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)2680{2681D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);2682if (existingItem != NULL)2683{2684ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);2685ItemTypeTraits::AccessPrev(newItem) = prevItem;2686ItemTypeTraits::AccessNext(newItem) = existingItem;2687ItemTypeTraits::AccessPrev(existingItem) = newItem;2688if (prevItem != NULL)2689{2690ItemTypeTraits::AccessNext(prevItem) = newItem;2691}2692else2693{2694D3D12MA_HEAVY_ASSERT(m_Front == existingItem);2695m_Front = newItem;2696}2697++m_Count;2698}2699else2700PushBack(newItem);2701}27022703template<typename ItemTypeTraits>2704void IntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)2705{2706D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);2707if (existingItem != NULL)2708{2709ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);2710ItemTypeTraits::AccessNext(newItem) = nextItem;2711ItemTypeTraits::AccessPrev(newItem) = existingItem;2712ItemTypeTraits::AccessNext(existingItem) = newItem;2713if (nextItem != NULL)2714{2715ItemTypeTraits::AccessPrev(nextItem) = newItem;2716}2717else2718{2719D3D12MA_HEAVY_ASSERT(m_Back == existingItem);2720m_Back = newItem;2721}2722++m_Count;2723}2724else2725return PushFront(newItem);2726}27272728template<typename ItemTypeTraits>2729void IntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)2730{2731D3D12MA_HEAVY_ASSERT(item != NULL && m_Count > 0);2732if (ItemTypeTraits::GetPrev(item) != NULL)2733{2734ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);2735}2736else2737{2738D3D12MA_HEAVY_ASSERT(m_Front == item);2739m_Front = ItemTypeTraits::GetNext(item);2740}27412742if (ItemTypeTraits::GetNext(item) != NULL)2743{2744ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);2745}2746else2747{2748D3D12MA_HEAVY_ASSERT(m_Back == item);2749m_Back = ItemTypeTraits::GetPrev(item);2750}2751ItemTypeTraits::AccessPrev(item) = NULL;2752ItemTypeTraits::AccessNext(item) = NULL;2753--m_Count;2754}27552756template<typename ItemTypeTraits>2757void IntrusiveLinkedList<ItemTypeTraits>::RemoveAll()2758{2759if (!IsEmpty())2760{2761ItemType* item = m_Back;2762while (item != NULL)2763{2764ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);2765ItemTypeTraits::AccessPrev(item) = NULL;2766ItemTypeTraits::AccessNext(item) = NULL;2767item = prevItem;2768}2769m_Front = NULL;2770m_Back = NULL;2771m_Count = 0;2772}2773}2774#endif // _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS2775#endif // _D3D12MA_INTRUSIVE_LINKED_LIST27762777#ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR2778/*2779Thread-safe wrapper over PoolAllocator free list, for allocation of Allocation objects.2780*/2781class AllocationObjectAllocator2782{2783D3D12MA_CLASS_NO_COPY(AllocationObjectAllocator);2784public:2785AllocationObjectAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks)2786: m_Allocator(allocationCallbacks, 1024) {}27872788template<typename... Types>2789Allocation* Allocate(Types... args);2790void Free(Allocation* alloc);27912792private:2793D3D12MA_MUTEX m_Mutex;2794PoolAllocator<Allocation> m_Allocator;2795};27962797#ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS2798template<typename... Types>2799Allocation* AllocationObjectAllocator::Allocate(Types... args)2800{2801MutexLock mutexLock(m_Mutex);2802return m_Allocator.Alloc(std::forward<Types>(args)...);2803}28042805void AllocationObjectAllocator::Free(Allocation* alloc)2806{2807MutexLock mutexLock(m_Mutex);2808m_Allocator.Free(alloc);2809}2810#endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS2811#endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR28122813#ifndef _D3D12MA_SUBALLOCATION2814/*2815Represents a region of NormalBlock that is either assigned and returned as2816allocated memory block or free.2817*/2818struct Suballocation2819{2820UINT64 offset;2821UINT64 size;2822void* privateData;2823SuballocationType type;2824};2825using SuballocationList = List<Suballocation>;28262827// Comparator for offsets.2828struct SuballocationOffsetLess2829{2830bool operator()(const Suballocation& lhs, const Suballocation& rhs) const2831{2832return lhs.offset < rhs.offset;2833}2834};28352836struct SuballocationOffsetGreater2837{2838bool operator()(const Suballocation& lhs, const Suballocation& rhs) const2839{2840return lhs.offset > rhs.offset;2841}2842};28432844struct SuballocationItemSizeLess2845{2846bool operator()(const SuballocationList::iterator lhs, const SuballocationList::iterator rhs) const2847{2848return lhs->size < rhs->size;2849}2850bool operator()(const SuballocationList::iterator lhs, UINT64 rhsSize) const2851{2852return lhs->size < rhsSize;2853}2854};2855#endif // _D3D12MA_SUBALLOCATION28562857#ifndef _D3D12MA_ALLOCATION_REQUEST2858/*2859Parameters of planned allocation inside a NormalBlock.2860*/2861struct AllocationRequest2862{2863AllocHandle allocHandle;2864UINT64 size;2865UINT64 algorithmData;2866UINT64 sumFreeSize; // Sum size of free items that overlap with proposed allocation.2867UINT64 sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.2868SuballocationList::iterator item;2869BOOL zeroInitialized = FALSE; // TODO Implement proper handling in TLSF and Linear, using ZeroInitializedRange class.2870};2871#endif // _D3D12MA_ALLOCATION_REQUEST28722873#ifndef _D3D12MA_ZERO_INITIALIZED_RANGE2874/*2875Keeps track of the range of bytes that are surely initialized with zeros.2876Everything outside of it is considered uninitialized memory that may contain2877garbage data.28782879The range is left-inclusive.2880*/2881class ZeroInitializedRange2882{2883public:2884void Reset(UINT64 size);2885BOOL IsRangeZeroInitialized(UINT64 beg, UINT64 end) const;2886void MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd);28872888private:2889UINT64 m_ZeroBeg = 0, m_ZeroEnd = 0;2890};28912892#ifndef _D3D12MA_ZERO_INITIALIZED_RANGE_FUNCTIONS2893void ZeroInitializedRange::Reset(UINT64 size)2894{2895D3D12MA_ASSERT(size > 0);2896m_ZeroBeg = 0;2897m_ZeroEnd = size;2898}28992900BOOL ZeroInitializedRange::IsRangeZeroInitialized(UINT64 beg, UINT64 end) const2901{2902D3D12MA_ASSERT(beg < end);2903return m_ZeroBeg <= beg && end <= m_ZeroEnd;2904}29052906void ZeroInitializedRange::MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd)2907{2908D3D12MA_ASSERT(usedBeg < usedEnd);2909// No new bytes marked.2910if (usedEnd <= m_ZeroBeg || m_ZeroEnd <= usedBeg)2911{2912return;2913}2914// All bytes marked.2915if (usedBeg <= m_ZeroBeg && m_ZeroEnd <= usedEnd)2916{2917m_ZeroBeg = m_ZeroEnd = 0;2918}2919// Some bytes marked.2920else2921{2922const UINT64 remainingZeroBefore = usedBeg > m_ZeroBeg ? usedBeg - m_ZeroBeg : 0;2923const UINT64 remainingZeroAfter = usedEnd < m_ZeroEnd ? m_ZeroEnd - usedEnd : 0;2924D3D12MA_ASSERT(remainingZeroBefore > 0 || remainingZeroAfter > 0);2925if (remainingZeroBefore > remainingZeroAfter)2926{2927m_ZeroEnd = usedBeg;2928}2929else2930{2931m_ZeroBeg = usedEnd;2932}2933}2934}2935#endif // _D3D12MA_ZERO_INITIALIZED_RANGE_FUNCTIONS2936#endif // _D3D12MA_ZERO_INITIALIZED_RANGE29372938#ifndef _D3D12MA_BLOCK_METADATA2939/*2940Data structure used for bookkeeping of allocations and unused ranges of memory2941in a single ID3D12Heap memory block.2942*/2943class BlockMetadata2944{2945public:2946BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);2947virtual ~BlockMetadata() = default;29482949virtual void Init(UINT64 size) { m_Size = size; }2950// Validates all data structures inside this object. If not valid, returns false.2951virtual bool Validate() const = 0;2952UINT64 GetSize() const { return m_Size; }2953bool IsVirtual() const { return m_IsVirtual; }2954virtual size_t GetAllocationCount() const = 0;2955virtual size_t GetFreeRegionsCount() const = 0;2956virtual UINT64 GetSumFreeSize() const = 0;2957virtual UINT64 GetAllocationOffset(AllocHandle allocHandle) const = 0;2958// Returns true if this block is empty - contains only single free suballocation.2959virtual bool IsEmpty() const = 0;29602961virtual void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const = 0;29622963// Tries to find a place for suballocation with given parameters inside this block.2964// If succeeded, fills pAllocationRequest and returns true.2965// If failed, returns false.2966virtual bool CreateAllocationRequest(2967UINT64 allocSize,2968UINT64 allocAlignment,2969bool upperAddress,2970UINT32 strategy,2971AllocationRequest* pAllocationRequest) = 0;29722973// Makes actual allocation based on request. Request must already be checked and valid.2974virtual void Alloc(2975const AllocationRequest& request,2976UINT64 allocSize,2977void* PrivateData) = 0;29782979virtual void Free(AllocHandle allocHandle) = 0;2980// Frees all allocations.2981// Careful! Don't call it if there are Allocation objects owned by pPrivateData of of cleared allocations!2982virtual void Clear() = 0;29832984virtual AllocHandle GetAllocationListBegin() const = 0;2985virtual AllocHandle GetNextAllocation(AllocHandle prevAlloc) const = 0;2986virtual UINT64 GetNextFreeRegionSize(AllocHandle alloc) const = 0;2987virtual void* GetAllocationPrivateData(AllocHandle allocHandle) const = 0;2988virtual void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) = 0;29892990virtual void AddStatistics(Statistics& inoutStats) const = 0;2991virtual void AddDetailedStatistics(DetailedStatistics& inoutStats) const = 0;2992virtual void WriteAllocationInfoToJson(JsonWriter& json) const = 0;2993virtual void DebugLogAllAllocations() const = 0;29942995protected:2996const ALLOCATION_CALLBACKS* GetAllocs() const { return m_pAllocationCallbacks; }2997UINT64 GetDebugMargin() const { return IsVirtual() ? 0 : D3D12MA_DEBUG_MARGIN; }29982999void DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const;3000void PrintDetailedMap_Begin(JsonWriter& json,3001UINT64 unusedBytes,3002size_t allocationCount,3003size_t unusedRangeCount) const;3004void PrintDetailedMap_Allocation(JsonWriter& json,3005UINT64 offset, UINT64 size, void* privateData) const;3006void PrintDetailedMap_UnusedRange(JsonWriter& json,3007UINT64 offset, UINT64 size) const;3008void PrintDetailedMap_End(JsonWriter& json) const;30093010private:3011UINT64 m_Size;3012bool m_IsVirtual;3013const ALLOCATION_CALLBACKS* m_pAllocationCallbacks;30143015D3D12MA_CLASS_NO_COPY(BlockMetadata);3016};30173018#ifndef _D3D12MA_BLOCK_METADATA_FUNCTIONS3019BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)3020: m_Size(0),3021m_IsVirtual(isVirtual),3022m_pAllocationCallbacks(allocationCallbacks)3023{3024D3D12MA_ASSERT(allocationCallbacks);3025}30263027void BlockMetadata::DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const3028{3029if (IsVirtual())3030{3031D3D12MA_DEBUG_LOG(L"UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p", offset, size, privateData);3032}3033else3034{3035D3D12MA_ASSERT(privateData != NULL);3036Allocation* allocation = reinterpret_cast<Allocation*>(privateData);30373038privateData = allocation->GetPrivateData();3039LPCWSTR name = allocation->GetName();30403041D3D12MA_DEBUG_LOG(L"UNFREED ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p; Name: %s",3042offset, size, privateData, name ? name : L"D3D12MA_Empty");3043}3044}30453046void BlockMetadata::PrintDetailedMap_Begin(JsonWriter& json,3047UINT64 unusedBytes, size_t allocationCount, size_t unusedRangeCount) const3048{3049json.WriteString(L"TotalBytes");3050json.WriteNumber(GetSize());30513052json.WriteString(L"UnusedBytes");3053json.WriteNumber(unusedBytes);30543055json.WriteString(L"Allocations");3056json.WriteNumber((UINT64)allocationCount);30573058json.WriteString(L"UnusedRanges");3059json.WriteNumber((UINT64)unusedRangeCount);30603061json.WriteString(L"Suballocations");3062json.BeginArray();3063}30643065void BlockMetadata::PrintDetailedMap_Allocation(JsonWriter& json,3066UINT64 offset, UINT64 size, void* privateData) const3067{3068json.BeginObject(true);30693070json.WriteString(L"Offset");3071json.WriteNumber(offset);30723073if (IsVirtual())3074{3075json.WriteString(L"Size");3076json.WriteNumber(size);3077if (privateData)3078{3079json.WriteString(L"CustomData");3080json.WriteNumber((uintptr_t)privateData);3081}3082}3083else3084{3085const Allocation* const alloc = (const Allocation*)privateData;3086D3D12MA_ASSERT(alloc);3087json.AddAllocationToObject(*alloc);3088}3089json.EndObject();3090}30913092void BlockMetadata::PrintDetailedMap_UnusedRange(JsonWriter& json,3093UINT64 offset, UINT64 size) const3094{3095json.BeginObject(true);30963097json.WriteString(L"Offset");3098json.WriteNumber(offset);30993100json.WriteString(L"Type");3101json.WriteString(L"FREE");31023103json.WriteString(L"Size");3104json.WriteNumber(size);31053106json.EndObject();3107}31083109void BlockMetadata::PrintDetailedMap_End(JsonWriter& json) const3110{3111json.EndArray();3112}3113#endif // _D3D12MA_BLOCK_METADATA_FUNCTIONS3114#endif // _D3D12MA_BLOCK_METADATA31153116#if 03117#ifndef _D3D12MA_BLOCK_METADATA_GENERIC3118class BlockMetadata_Generic : public BlockMetadata3119{3120public:3121BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);3122virtual ~BlockMetadata_Generic() = default;31233124size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; }3125UINT64 GetSumFreeSize() const override { return m_SumFreeSize; }3126UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; }31273128void Init(UINT64 size) override;3129bool Validate() const override;3130bool IsEmpty() const override;3131void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;31323133bool CreateAllocationRequest(3134UINT64 allocSize,3135UINT64 allocAlignment,3136bool upperAddress,3137AllocationRequest* pAllocationRequest) override;31383139void Alloc(3140const AllocationRequest& request,3141UINT64 allocSize,3142void* privateData) override;31433144void Free(AllocHandle allocHandle) override;3145void Clear() override;31463147void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;31483149void AddStatistics(Statistics& inoutStats) const override;3150void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;3151void WriteAllocationInfoToJson(JsonWriter& json) const override;31523153private:3154UINT m_FreeCount;3155UINT64 m_SumFreeSize;3156SuballocationList m_Suballocations;3157// Suballocations that are free and have size greater than certain threshold.3158// Sorted by size, ascending.3159Vector<SuballocationList::iterator> m_FreeSuballocationsBySize;3160ZeroInitializedRange m_ZeroInitializedRange;31613162SuballocationList::const_iterator FindAtOffset(UINT64 offset) const;3163bool ValidateFreeSuballocationList() const;31643165// Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.3166// If yes, fills pOffset and returns true. If no, returns false.3167bool CheckAllocation(3168UINT64 allocSize,3169UINT64 allocAlignment,3170SuballocationList::const_iterator suballocItem,3171AllocHandle* pAllocHandle,3172UINT64* pSumFreeSize,3173UINT64* pSumItemSize,3174BOOL *pZeroInitialized) const;3175// Given free suballocation, it merges it with following one, which must also be free.3176void MergeFreeWithNext(SuballocationList::iterator item);3177// Releases given suballocation, making it free.3178// Merges it with adjacent free suballocations if applicable.3179// Returns iterator to new free suballocation at this place.3180SuballocationList::iterator FreeSuballocation(SuballocationList::iterator suballocItem);3181// Given free suballocation, it inserts it into sorted list of3182// m_FreeSuballocationsBySize if it's suitable.3183void RegisterFreeSuballocation(SuballocationList::iterator item);3184// Given free suballocation, it removes it from sorted list of3185// m_FreeSuballocationsBySize if it's suitable.3186void UnregisterFreeSuballocation(SuballocationList::iterator item);31873188D3D12MA_CLASS_NO_COPY(BlockMetadata_Generic)3189};31903191#ifndef _D3D12MA_BLOCK_METADATA_GENERIC_FUNCTIONS3192BlockMetadata_Generic::BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)3193: BlockMetadata(allocationCallbacks, isVirtual),3194m_FreeCount(0),3195m_SumFreeSize(0),3196m_Suballocations(*allocationCallbacks),3197m_FreeSuballocationsBySize(*allocationCallbacks)3198{3199D3D12MA_ASSERT(allocationCallbacks);3200}32013202void BlockMetadata_Generic::Init(UINT64 size)3203{3204BlockMetadata::Init(size);3205m_ZeroInitializedRange.Reset(size);32063207m_FreeCount = 1;3208m_SumFreeSize = size;32093210Suballocation suballoc = {};3211suballoc.offset = 0;3212suballoc.size = size;3213suballoc.type = SUBALLOCATION_TYPE_FREE;3214suballoc.privateData = NULL;32153216D3D12MA_ASSERT(size > MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);3217m_Suballocations.push_back(suballoc);3218SuballocationList::iterator suballocItem = m_Suballocations.end();3219--suballocItem;3220m_FreeSuballocationsBySize.push_back(suballocItem);3221}32223223bool BlockMetadata_Generic::Validate() const3224{3225D3D12MA_VALIDATE(!m_Suballocations.empty());32263227// Expected offset of new suballocation as calculated from previous ones.3228UINT64 calculatedOffset = 0;3229// Expected number of free suballocations as calculated from traversing their list.3230UINT calculatedFreeCount = 0;3231// Expected sum size of free suballocations as calculated from traversing their list.3232UINT64 calculatedSumFreeSize = 0;3233// Expected number of free suballocations that should be registered in3234// m_FreeSuballocationsBySize calculated from traversing their list.3235size_t freeSuballocationsToRegister = 0;3236// True if previous visited suballocation was free.3237bool prevFree = false;32383239for (const auto& subAlloc : m_Suballocations)3240{3241// Actual offset of this suballocation doesn't match expected one.3242D3D12MA_VALIDATE(subAlloc.offset == calculatedOffset);32433244const bool currFree = (subAlloc.type == SUBALLOCATION_TYPE_FREE);3245// Two adjacent free suballocations are invalid. They should be merged.3246D3D12MA_VALIDATE(!prevFree || !currFree);32473248const Allocation* const alloc = (Allocation*)subAlloc.privateData;3249if (!IsVirtual())3250{3251D3D12MA_VALIDATE(currFree == (alloc == NULL));3252}32533254if (currFree)3255{3256calculatedSumFreeSize += subAlloc.size;3257++calculatedFreeCount;3258if (subAlloc.size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)3259{3260++freeSuballocationsToRegister;3261}32623263// Margin required between allocations - every free space must be at least that large.3264D3D12MA_VALIDATE(subAlloc.size >= GetDebugMargin());3265}3266else3267{3268if (!IsVirtual())3269{3270D3D12MA_VALIDATE(alloc->GetOffset() == subAlloc.offset);3271D3D12MA_VALIDATE(alloc->GetSize() == subAlloc.size);3272}32733274// Margin required between allocations - previous allocation must be free.3275D3D12MA_VALIDATE(GetDebugMargin() == 0 || prevFree);3276}32773278calculatedOffset += subAlloc.size;3279prevFree = currFree;3280}32813282// Number of free suballocations registered in m_FreeSuballocationsBySize doesn't3283// match expected one.3284D3D12MA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);32853286UINT64 lastSize = 0;3287for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)3288{3289SuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];32903291// Only free suballocations can be registered in m_FreeSuballocationsBySize.3292D3D12MA_VALIDATE(suballocItem->type == SUBALLOCATION_TYPE_FREE);3293// They must be sorted by size ascending.3294D3D12MA_VALIDATE(suballocItem->size >= lastSize);32953296lastSize = suballocItem->size;3297}32983299// Check if totals match calculacted values.3300D3D12MA_VALIDATE(ValidateFreeSuballocationList());3301D3D12MA_VALIDATE(calculatedOffset == GetSize());3302D3D12MA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);3303D3D12MA_VALIDATE(calculatedFreeCount == m_FreeCount);33043305return true;3306}33073308bool BlockMetadata_Generic::IsEmpty() const3309{3310return (m_Suballocations.size() == 1) && (m_FreeCount == 1);3311}33123313void BlockMetadata_Generic::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const3314{3315Suballocation& suballoc = *FindAtOffset((UINT64)allocHandle - 1).dropConst();3316outInfo.Offset = suballoc.offset;3317outInfo.Size = suballoc.size;3318outInfo.pPrivateData = suballoc.privateData;3319}33203321bool BlockMetadata_Generic::CreateAllocationRequest(3322UINT64 allocSize,3323UINT64 allocAlignment,3324bool upperAddress,3325AllocationRequest* pAllocationRequest)3326{3327D3D12MA_ASSERT(allocSize > 0);3328D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm.");3329D3D12MA_ASSERT(pAllocationRequest != NULL);3330D3D12MA_HEAVY_ASSERT(Validate());33313332// There is not enough total free space in this block to fullfill the request: Early return.3333if (m_SumFreeSize < allocSize + GetDebugMargin())3334{3335return false;3336}33373338// New algorithm, efficiently searching freeSuballocationsBySize.3339const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();3340if (freeSuballocCount > 0)3341{3342// Find first free suballocation with size not less than allocSize + GetDebugMargin().3343SuballocationList::iterator* const it = BinaryFindFirstNotLess(3344m_FreeSuballocationsBySize.data(),3345m_FreeSuballocationsBySize.data() + freeSuballocCount,3346allocSize + GetDebugMargin(),3347SuballocationItemSizeLess());3348size_t index = it - m_FreeSuballocationsBySize.data();3349for (; index < freeSuballocCount; ++index)3350{3351if (CheckAllocation(3352allocSize,3353allocAlignment,3354m_FreeSuballocationsBySize[index],3355&pAllocationRequest->allocHandle,3356&pAllocationRequest->sumFreeSize,3357&pAllocationRequest->sumItemSize,3358&pAllocationRequest->zeroInitialized))3359{3360pAllocationRequest->item = m_FreeSuballocationsBySize[index];3361return true;3362}3363}3364}33653366return false;3367}33683369void BlockMetadata_Generic::Alloc(3370const AllocationRequest& request,3371UINT64 allocSize,3372void* privateData)3373{3374D3D12MA_ASSERT(request.item != m_Suballocations.end());3375Suballocation& suballoc = *request.item;3376// Given suballocation is a free block.3377D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE);3378// Given offset is inside this suballocation.3379UINT64 offset = (UINT64)request.allocHandle - 1;3380D3D12MA_ASSERT(offset >= suballoc.offset);3381const UINT64 paddingBegin = offset - suballoc.offset;3382D3D12MA_ASSERT(suballoc.size >= paddingBegin + allocSize);3383const UINT64 paddingEnd = suballoc.size - paddingBegin - allocSize;33843385// Unregister this free suballocation from m_FreeSuballocationsBySize and update3386// it to become used.3387UnregisterFreeSuballocation(request.item);33883389suballoc.offset = offset;3390suballoc.size = allocSize;3391suballoc.type = SUBALLOCATION_TYPE_ALLOCATION;3392suballoc.privateData = privateData;33933394// If there are any free bytes remaining at the end, insert new free suballocation after current one.3395if (paddingEnd)3396{3397Suballocation paddingSuballoc = {};3398paddingSuballoc.offset = offset + allocSize;3399paddingSuballoc.size = paddingEnd;3400paddingSuballoc.type = SUBALLOCATION_TYPE_FREE;3401SuballocationList::iterator next = request.item;3402++next;3403const SuballocationList::iterator paddingEndItem =3404m_Suballocations.insert(next, paddingSuballoc);3405RegisterFreeSuballocation(paddingEndItem);3406}34073408// If there are any free bytes remaining at the beginning, insert new free suballocation before current one.3409if (paddingBegin)3410{3411Suballocation paddingSuballoc = {};3412paddingSuballoc.offset = offset - paddingBegin;3413paddingSuballoc.size = paddingBegin;3414paddingSuballoc.type = SUBALLOCATION_TYPE_FREE;3415const SuballocationList::iterator paddingBeginItem =3416m_Suballocations.insert(request.item, paddingSuballoc);3417RegisterFreeSuballocation(paddingBeginItem);3418}34193420// Update totals.3421m_FreeCount = m_FreeCount - 1;3422if (paddingBegin > 0)3423{3424++m_FreeCount;3425}3426if (paddingEnd > 0)3427{3428++m_FreeCount;3429}3430m_SumFreeSize -= allocSize;34313432m_ZeroInitializedRange.MarkRangeAsUsed(offset, offset + allocSize);3433}34343435void BlockMetadata_Generic::Free(AllocHandle allocHandle)3436{3437FreeSuballocation(FindAtOffset((UINT64)allocHandle - 1).dropConst());3438}34393440void BlockMetadata_Generic::Clear()3441{3442m_FreeCount = 1;3443m_SumFreeSize = GetSize();34443445m_Suballocations.clear();3446Suballocation suballoc = {};3447suballoc.offset = 0;3448suballoc.size = GetSize();3449suballoc.type = SUBALLOCATION_TYPE_FREE;3450m_Suballocations.push_back(suballoc);34513452m_FreeSuballocationsBySize.clear();3453m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());3454}34553456SuballocationList::const_iterator BlockMetadata_Generic::FindAtOffset(UINT64 offset) const3457{3458const UINT64 last = m_Suballocations.crbegin()->offset;3459if (last == offset)3460return m_Suballocations.crbegin();3461const UINT64 first = m_Suballocations.cbegin()->offset;3462if (first == offset)3463return m_Suballocations.cbegin();34643465const size_t suballocCount = m_Suballocations.size();3466const UINT64 step = (last - first + m_Suballocations.cbegin()->size) / suballocCount;3467auto findSuballocation = [&](auto begin, auto end) -> SuballocationList::const_iterator3468{3469for (auto suballocItem = begin;3470suballocItem != end;3471++suballocItem)3472{3473const Suballocation& suballoc = *suballocItem;3474if (suballoc.offset == offset)3475return suballocItem;3476}3477D3D12MA_ASSERT(false && "Not found!");3478return m_Suballocations.end();3479};3480// If requested offset is closer to the end of range, search from the end3481if ((offset - first) > suballocCount * step / 2)3482{3483return findSuballocation(m_Suballocations.crbegin(), m_Suballocations.crend());3484}3485return findSuballocation(m_Suballocations.cbegin(), m_Suballocations.cend());3486}34873488bool BlockMetadata_Generic::ValidateFreeSuballocationList() const3489{3490UINT64 lastSize = 0;3491for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)3492{3493const SuballocationList::iterator it = m_FreeSuballocationsBySize[i];34943495D3D12MA_VALIDATE(it->type == SUBALLOCATION_TYPE_FREE);3496D3D12MA_VALIDATE(it->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);3497D3D12MA_VALIDATE(it->size >= lastSize);3498lastSize = it->size;3499}3500return true;3501}35023503bool BlockMetadata_Generic::CheckAllocation(3504UINT64 allocSize,3505UINT64 allocAlignment,3506SuballocationList::const_iterator suballocItem,3507AllocHandle* pAllocHandle,3508UINT64* pSumFreeSize,3509UINT64* pSumItemSize,3510BOOL* pZeroInitialized) const3511{3512D3D12MA_ASSERT(allocSize > 0);3513D3D12MA_ASSERT(suballocItem != m_Suballocations.cend());3514D3D12MA_ASSERT(pAllocHandle != NULL && pZeroInitialized != NULL);35153516*pSumFreeSize = 0;3517*pSumItemSize = 0;3518*pZeroInitialized = FALSE;35193520const Suballocation& suballoc = *suballocItem;3521D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE);35223523*pSumFreeSize = suballoc.size;35243525// Size of this suballocation is too small for this request: Early return.3526if (suballoc.size < allocSize)3527{3528return false;3529}35303531// Start from offset equal to beginning of this suballocation and debug margin of previous allocation if present.3532UINT64 offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin());35333534// Apply alignment.3535offset = AlignUp(offset, allocAlignment);35363537// Calculate padding at the beginning based on current offset.3538const UINT64 paddingBegin = offset - suballoc.offset;35393540// Fail if requested size plus margin after is bigger than size of this suballocation.3541if (paddingBegin + allocSize + GetDebugMargin() > suballoc.size)3542{3543return false;3544}35453546// All tests passed: Success. Offset is already filled.3547*pZeroInitialized = m_ZeroInitializedRange.IsRangeZeroInitialized(offset, offset + allocSize);3548*pAllocHandle = (AllocHandle)(offset + 1);3549return true;3550}35513552void BlockMetadata_Generic::MergeFreeWithNext(SuballocationList::iterator item)3553{3554D3D12MA_ASSERT(item != m_Suballocations.end());3555D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);35563557SuballocationList::iterator nextItem = item;3558++nextItem;3559D3D12MA_ASSERT(nextItem != m_Suballocations.end());3560D3D12MA_ASSERT(nextItem->type == SUBALLOCATION_TYPE_FREE);35613562item->size += nextItem->size;3563--m_FreeCount;3564m_Suballocations.erase(nextItem);3565}35663567SuballocationList::iterator BlockMetadata_Generic::FreeSuballocation(SuballocationList::iterator suballocItem)3568{3569// Change this suballocation to be marked as free.3570Suballocation& suballoc = *suballocItem;3571suballoc.type = SUBALLOCATION_TYPE_FREE;3572suballoc.privateData = NULL;35733574// Update totals.3575++m_FreeCount;3576m_SumFreeSize += suballoc.size;35773578// Merge with previous and/or next suballocation if it's also free.3579bool mergeWithNext = false;3580bool mergeWithPrev = false;35813582SuballocationList::iterator nextItem = suballocItem;3583++nextItem;3584if ((nextItem != m_Suballocations.end()) && (nextItem->type == SUBALLOCATION_TYPE_FREE))3585{3586mergeWithNext = true;3587}35883589SuballocationList::iterator prevItem = suballocItem;3590if (suballocItem != m_Suballocations.begin())3591{3592--prevItem;3593if (prevItem->type == SUBALLOCATION_TYPE_FREE)3594{3595mergeWithPrev = true;3596}3597}35983599if (mergeWithNext)3600{3601UnregisterFreeSuballocation(nextItem);3602MergeFreeWithNext(suballocItem);3603}36043605if (mergeWithPrev)3606{3607UnregisterFreeSuballocation(prevItem);3608MergeFreeWithNext(prevItem);3609RegisterFreeSuballocation(prevItem);3610return prevItem;3611}3612else3613{3614RegisterFreeSuballocation(suballocItem);3615return suballocItem;3616}3617}36183619void BlockMetadata_Generic::RegisterFreeSuballocation(SuballocationList::iterator item)3620{3621D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);3622D3D12MA_ASSERT(item->size > 0);36233624// You may want to enable this validation at the beginning or at the end of3625// this function, depending on what do you want to check.3626D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());36273628if (item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)3629{3630if (m_FreeSuballocationsBySize.empty())3631{3632m_FreeSuballocationsBySize.push_back(item);3633}3634else3635{3636m_FreeSuballocationsBySize.InsertSorted(item, SuballocationItemSizeLess());3637}3638}36393640//D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());3641}36423643void BlockMetadata_Generic::UnregisterFreeSuballocation(SuballocationList::iterator item)3644{3645D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);3646D3D12MA_ASSERT(item->size > 0);36473648// You may want to enable this validation at the beginning or at the end of3649// this function, depending on what do you want to check.3650D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());36513652if (item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)3653{3654SuballocationList::iterator* const it = BinaryFindFirstNotLess(3655m_FreeSuballocationsBySize.data(),3656m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),3657item,3658SuballocationItemSizeLess());3659for (size_t index = it - m_FreeSuballocationsBySize.data();3660index < m_FreeSuballocationsBySize.size();3661++index)3662{3663if (m_FreeSuballocationsBySize[index] == item)3664{3665m_FreeSuballocationsBySize.remove(index);3666return;3667}3668D3D12MA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");3669}3670D3D12MA_ASSERT(0 && "Not found.");3671}36723673//D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());3674}36753676void BlockMetadata_Generic::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)3677{3678Suballocation& suballoc = *FindAtOffset((UINT64)allocHandle - 1).dropConst();3679suballoc.privateData = privateData;3680}36813682void BlockMetadata_Generic::AddStatistics(Statistics& inoutStats) const3683{3684inoutStats.BlockCount++;3685inoutStats.AllocationCount += (UINT)m_Suballocations.size() - m_FreeCount;3686inoutStats.BlockBytes += GetSize();3687inoutStats.AllocationBytes += GetSize() - m_SumFreeSize;3688}36893690void BlockMetadata_Generic::AddDetailedStatistics(DetailedStatistics& inoutStats) const3691{3692inoutStats.Stats.BlockCount++;3693inoutStats.Stats.BlockBytes += GetSize();36943695for (const auto& suballoc : m_Suballocations)3696{3697if (suballoc.type == SUBALLOCATION_TYPE_FREE)3698AddDetailedStatisticsUnusedRange(inoutStats, suballoc.size);3699else3700AddDetailedStatisticsAllocation(inoutStats, suballoc.size);3701}3702}37033704void BlockMetadata_Generic::WriteAllocationInfoToJson(JsonWriter& json) const3705{3706PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_FreeCount);3707for (const auto& suballoc : m_Suballocations)3708{3709if (suballoc.type == SUBALLOCATION_TYPE_FREE)3710PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);3711else3712PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);3713}3714PrintDetailedMap_End(json);3715}3716#endif // _D3D12MA_BLOCK_METADATA_GENERIC_FUNCTIONS3717#endif // _D3D12MA_BLOCK_METADATA_GENERIC3718#endif // #if 037193720#ifndef _D3D12MA_BLOCK_METADATA_LINEAR3721class BlockMetadata_Linear : public BlockMetadata3722{3723public:3724BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);3725virtual ~BlockMetadata_Linear() = default;37263727UINT64 GetSumFreeSize() const override { return m_SumFreeSize; }3728bool IsEmpty() const override { return GetAllocationCount() == 0; }3729UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; };37303731void Init(UINT64 size) override;3732bool Validate() const override;3733size_t GetAllocationCount() const override;3734size_t GetFreeRegionsCount() const override;3735void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;37363737bool CreateAllocationRequest(3738UINT64 allocSize,3739UINT64 allocAlignment,3740bool upperAddress,3741UINT32 strategy,3742AllocationRequest* pAllocationRequest) override;37433744void Alloc(3745const AllocationRequest& request,3746UINT64 allocSize,3747void* privateData) override;37483749void Free(AllocHandle allocHandle) override;3750void Clear() override;37513752AllocHandle GetAllocationListBegin() const override;3753AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override;3754UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override;3755void* GetAllocationPrivateData(AllocHandle allocHandle) const override;3756void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;37573758void AddStatistics(Statistics& inoutStats) const override;3759void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;3760void WriteAllocationInfoToJson(JsonWriter& json) const override;3761void DebugLogAllAllocations() const override;37623763private:3764/*3765There are two suballocation vectors, used in ping-pong way.3766The one with index m_1stVectorIndex is called 1st.3767The one with index (m_1stVectorIndex ^ 1) is called 2nd.37682nd can be non-empty only when 1st is not empty.3769When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.3770*/3771typedef Vector<Suballocation> SuballocationVectorType;37723773enum ALLOC_REQUEST_TYPE3774{3775ALLOC_REQUEST_UPPER_ADDRESS,3776ALLOC_REQUEST_END_OF_1ST,3777ALLOC_REQUEST_END_OF_2ND,3778};37793780enum SECOND_VECTOR_MODE3781{3782SECOND_VECTOR_EMPTY,3783/*3784Suballocations in 2nd vector are created later than the ones in 1st, but they3785all have smaller offset.3786*/3787SECOND_VECTOR_RING_BUFFER,3788/*3789Suballocations in 2nd vector are upper side of double stack.3790They all have offsets higher than those in 1st vector.3791Top of this stack means smaller offsets, but higher indices in this vector.3792*/3793SECOND_VECTOR_DOUBLE_STACK,3794};37953796UINT64 m_SumFreeSize;3797SuballocationVectorType m_Suballocations0, m_Suballocations1;3798UINT32 m_1stVectorIndex;3799SECOND_VECTOR_MODE m_2ndVectorMode;3800// Number of items in 1st vector with hAllocation = null at the beginning.3801size_t m_1stNullItemsBeginCount;3802// Number of other items in 1st vector with hAllocation = null somewhere in the middle.3803size_t m_1stNullItemsMiddleCount;3804// Number of items in 2nd vector with hAllocation = null.3805size_t m_2ndNullItemsCount;38063807SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }3808SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }3809const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }3810const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }38113812Suballocation& FindSuballocation(UINT64 offset) const;3813bool ShouldCompact1st() const;3814void CleanupAfterFree();38153816bool CreateAllocationRequest_LowerAddress(3817UINT64 allocSize,3818UINT64 allocAlignment,3819AllocationRequest* pAllocationRequest);3820bool CreateAllocationRequest_UpperAddress(3821UINT64 allocSize,3822UINT64 allocAlignment,3823AllocationRequest* pAllocationRequest);38243825D3D12MA_CLASS_NO_COPY(BlockMetadata_Linear)3826};38273828#ifndef _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS3829BlockMetadata_Linear::BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)3830: BlockMetadata(allocationCallbacks, isVirtual),3831m_SumFreeSize(0),3832m_Suballocations0(*allocationCallbacks),3833m_Suballocations1(*allocationCallbacks),3834m_1stVectorIndex(0),3835m_2ndVectorMode(SECOND_VECTOR_EMPTY),3836m_1stNullItemsBeginCount(0),3837m_1stNullItemsMiddleCount(0),3838m_2ndNullItemsCount(0)3839{3840D3D12MA_ASSERT(allocationCallbacks);3841}38423843void BlockMetadata_Linear::Init(UINT64 size)3844{3845BlockMetadata::Init(size);3846m_SumFreeSize = size;3847}38483849bool BlockMetadata_Linear::Validate() const3850{3851D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize());3852const SuballocationVectorType& suballocations1st = AccessSuballocations1st();3853const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();38543855D3D12MA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));3856D3D12MA_VALIDATE(!suballocations1st.empty() ||3857suballocations2nd.empty() ||3858m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);38593860if (!suballocations1st.empty())3861{3862// Null item at the beginning should be accounted into m_1stNullItemsBeginCount.3863D3D12MA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != SUBALLOCATION_TYPE_FREE);3864// Null item at the end should be just pop_back().3865D3D12MA_VALIDATE(suballocations1st.back().type != SUBALLOCATION_TYPE_FREE);3866}3867if (!suballocations2nd.empty())3868{3869// Null item at the end should be just pop_back().3870D3D12MA_VALIDATE(suballocations2nd.back().type != SUBALLOCATION_TYPE_FREE);3871}38723873D3D12MA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());3874D3D12MA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());38753876UINT64 sumUsedSize = 0;3877const size_t suballoc1stCount = suballocations1st.size();3878UINT64 offset = 0;38793880if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)3881{3882const size_t suballoc2ndCount = suballocations2nd.size();3883size_t nullItem2ndCount = 0;3884for (size_t i = 0; i < suballoc2ndCount; ++i)3885{3886const Suballocation& suballoc = suballocations2nd[i];3887const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);38883889const Allocation* alloc = (Allocation*)suballoc.privateData;3890if (!IsVirtual())3891{3892D3D12MA_VALIDATE(currFree == (alloc == NULL));3893}3894D3D12MA_VALIDATE(suballoc.offset >= offset);38953896if (!currFree)3897{3898if (!IsVirtual())3899{3900D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3901D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3902}3903sumUsedSize += suballoc.size;3904}3905else3906{3907++nullItem2ndCount;3908}39093910offset = suballoc.offset + suballoc.size + GetDebugMargin();3911}39123913D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);3914}39153916for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)3917{3918const Suballocation& suballoc = suballocations1st[i];3919D3D12MA_VALIDATE(suballoc.type == SUBALLOCATION_TYPE_FREE &&3920suballoc.privateData == NULL);3921}39223923size_t nullItem1stCount = m_1stNullItemsBeginCount;39243925for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)3926{3927const Suballocation& suballoc = suballocations1st[i];3928const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);39293930const Allocation* alloc = (Allocation*)suballoc.privateData;3931if (!IsVirtual())3932{3933D3D12MA_VALIDATE(currFree == (alloc == NULL));3934}3935D3D12MA_VALIDATE(suballoc.offset >= offset);3936D3D12MA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);39373938if (!currFree)3939{3940if (!IsVirtual())3941{3942D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3943D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3944}3945sumUsedSize += suballoc.size;3946}3947else3948{3949++nullItem1stCount;3950}39513952offset = suballoc.offset + suballoc.size + GetDebugMargin();3953}3954D3D12MA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);39553956if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)3957{3958const size_t suballoc2ndCount = suballocations2nd.size();3959size_t nullItem2ndCount = 0;3960for (size_t i = suballoc2ndCount; i--; )3961{3962const Suballocation& suballoc = suballocations2nd[i];3963const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);39643965const Allocation* alloc = (Allocation*)suballoc.privateData;3966if (!IsVirtual())3967{3968D3D12MA_VALIDATE(currFree == (alloc == NULL));3969}3970D3D12MA_VALIDATE(suballoc.offset >= offset);39713972if (!currFree)3973{3974if (!IsVirtual())3975{3976D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3977D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3978}3979sumUsedSize += suballoc.size;3980}3981else3982{3983++nullItem2ndCount;3984}39853986offset = suballoc.offset + suballoc.size + GetDebugMargin();3987}39883989D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);3990}39913992D3D12MA_VALIDATE(offset <= GetSize());3993D3D12MA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);39943995return true;3996}39973998size_t BlockMetadata_Linear::GetAllocationCount() const3999{4000return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +4001AccessSuballocations2nd().size() - m_2ndNullItemsCount;4002}40034004size_t BlockMetadata_Linear::GetFreeRegionsCount() const4005{4006// Function only used for defragmentation, which is disabled for this algorithm4007D3D12MA_ASSERT(0);4008return SIZE_MAX;4009}40104011void BlockMetadata_Linear::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const4012{4013const Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1);4014outInfo.Offset = suballoc.offset;4015outInfo.Size = suballoc.size;4016outInfo.pPrivateData = suballoc.privateData;4017}40184019bool BlockMetadata_Linear::CreateAllocationRequest(4020UINT64 allocSize,4021UINT64 allocAlignment,4022bool upperAddress,4023UINT32 strategy,4024AllocationRequest* pAllocationRequest)4025{4026D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");4027D3D12MA_ASSERT(pAllocationRequest != NULL);4028D3D12MA_HEAVY_ASSERT(Validate());4029pAllocationRequest->size = allocSize;4030return upperAddress ?4031CreateAllocationRequest_UpperAddress(4032allocSize, allocAlignment, pAllocationRequest) :4033CreateAllocationRequest_LowerAddress(4034allocSize, allocAlignment, pAllocationRequest);4035}40364037void BlockMetadata_Linear::Alloc(4038const AllocationRequest& request,4039UINT64 allocSize,4040void* privateData)4041{4042UINT64 offset = (UINT64)request.allocHandle - 1;4043const Suballocation newSuballoc = { offset, request.size, privateData, SUBALLOCATION_TYPE_ALLOCATION };40444045switch (request.algorithmData)4046{4047case ALLOC_REQUEST_UPPER_ADDRESS:4048{4049D3D12MA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&4050"CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");4051SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4052suballocations2nd.push_back(newSuballoc);4053m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;4054break;4055}4056case ALLOC_REQUEST_END_OF_1ST:4057{4058SuballocationVectorType& suballocations1st = AccessSuballocations1st();40594060D3D12MA_ASSERT(suballocations1st.empty() ||4061offset >= suballocations1st.back().offset + suballocations1st.back().size);4062// Check if it fits before the end of the block.4063D3D12MA_ASSERT(offset + request.size <= GetSize());40644065suballocations1st.push_back(newSuballoc);4066break;4067}4068case ALLOC_REQUEST_END_OF_2ND:4069{4070SuballocationVectorType& suballocations1st = AccessSuballocations1st();4071// New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.4072D3D12MA_ASSERT(!suballocations1st.empty() &&4073offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);4074SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();40754076switch (m_2ndVectorMode)4077{4078case SECOND_VECTOR_EMPTY:4079// First allocation from second part ring buffer.4080D3D12MA_ASSERT(suballocations2nd.empty());4081m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;4082break;4083case SECOND_VECTOR_RING_BUFFER:4084// 2-part ring buffer is already started.4085D3D12MA_ASSERT(!suballocations2nd.empty());4086break;4087case SECOND_VECTOR_DOUBLE_STACK:4088D3D12MA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");4089break;4090default:4091D3D12MA_ASSERT(0);4092}40934094suballocations2nd.push_back(newSuballoc);4095break;4096}4097default:4098D3D12MA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");4099}4100m_SumFreeSize -= newSuballoc.size;4101}41024103void BlockMetadata_Linear::Free(AllocHandle allocHandle)4104{4105SuballocationVectorType& suballocations1st = AccessSuballocations1st();4106SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4107UINT64 offset = (UINT64)allocHandle - 1;41084109if (!suballocations1st.empty())4110{4111// First allocation: Mark it as next empty at the beginning.4112Suballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];4113if (firstSuballoc.offset == offset)4114{4115firstSuballoc.type = SUBALLOCATION_TYPE_FREE;4116firstSuballoc.privateData = NULL;4117m_SumFreeSize += firstSuballoc.size;4118++m_1stNullItemsBeginCount;4119CleanupAfterFree();4120return;4121}4122}41234124// Last allocation in 2-part ring buffer or top of upper stack (same logic).4125if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||4126m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4127{4128Suballocation& lastSuballoc = suballocations2nd.back();4129if (lastSuballoc.offset == offset)4130{4131m_SumFreeSize += lastSuballoc.size;4132suballocations2nd.pop_back();4133CleanupAfterFree();4134return;4135}4136}4137// Last allocation in 1st vector.4138else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)4139{4140Suballocation& lastSuballoc = suballocations1st.back();4141if (lastSuballoc.offset == offset)4142{4143m_SumFreeSize += lastSuballoc.size;4144suballocations1st.pop_back();4145CleanupAfterFree();4146return;4147}4148}41494150Suballocation refSuballoc;4151refSuballoc.offset = offset;4152// Rest of members stays uninitialized intentionally for better performance.41534154// Item from the middle of 1st vector.4155{4156const SuballocationVectorType::iterator it = BinaryFindSorted(4157suballocations1st.begin() + m_1stNullItemsBeginCount,4158suballocations1st.end(),4159refSuballoc,4160SuballocationOffsetLess());4161if (it != suballocations1st.end())4162{4163it->type = SUBALLOCATION_TYPE_FREE;4164it->privateData = NULL;4165++m_1stNullItemsMiddleCount;4166m_SumFreeSize += it->size;4167CleanupAfterFree();4168return;4169}4170}41714172if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)4173{4174// Item from the middle of 2nd vector.4175const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?4176BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) :4177BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater());4178if (it != suballocations2nd.end())4179{4180it->type = SUBALLOCATION_TYPE_FREE;4181it->privateData = NULL;4182++m_2ndNullItemsCount;4183m_SumFreeSize += it->size;4184CleanupAfterFree();4185return;4186}4187}41884189D3D12MA_ASSERT(0 && "Allocation to free not found in linear allocator!");4190}41914192void BlockMetadata_Linear::Clear()4193{4194m_SumFreeSize = GetSize();4195m_Suballocations0.clear();4196m_Suballocations1.clear();4197// Leaving m_1stVectorIndex unchanged - it doesn't matter.4198m_2ndVectorMode = SECOND_VECTOR_EMPTY;4199m_1stNullItemsBeginCount = 0;4200m_1stNullItemsMiddleCount = 0;4201m_2ndNullItemsCount = 0;4202}42034204AllocHandle BlockMetadata_Linear::GetAllocationListBegin() const4205{4206// Function only used for defragmentation, which is disabled for this algorithm4207D3D12MA_ASSERT(0);4208return (AllocHandle)0;4209}42104211AllocHandle BlockMetadata_Linear::GetNextAllocation(AllocHandle prevAlloc) const4212{4213// Function only used for defragmentation, which is disabled for this algorithm4214D3D12MA_ASSERT(0);4215return (AllocHandle)0;4216}42174218UINT64 BlockMetadata_Linear::GetNextFreeRegionSize(AllocHandle alloc) const4219{4220// Function only used for defragmentation, which is disabled for this algorithm4221D3D12MA_ASSERT(0);4222return 0;4223}42244225void* BlockMetadata_Linear::GetAllocationPrivateData(AllocHandle allocHandle) const4226{4227return FindSuballocation((UINT64)allocHandle - 1).privateData;4228}42294230void BlockMetadata_Linear::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)4231{4232Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1);4233suballoc.privateData = privateData;4234}42354236void BlockMetadata_Linear::AddStatistics(Statistics& inoutStats) const4237{4238inoutStats.BlockCount++;4239inoutStats.AllocationCount += (UINT)GetAllocationCount();4240inoutStats.BlockBytes += GetSize();4241inoutStats.AllocationBytes += GetSize() - m_SumFreeSize;4242}42434244void BlockMetadata_Linear::AddDetailedStatistics(DetailedStatistics& inoutStats) const4245{4246inoutStats.Stats.BlockCount++;4247inoutStats.Stats.BlockBytes += GetSize();42484249const UINT64 size = GetSize();4250const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4251const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4252const size_t suballoc1stCount = suballocations1st.size();4253const size_t suballoc2ndCount = suballocations2nd.size();42544255UINT64 lastOffset = 0;4256if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4257{4258const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;4259size_t nextAlloc2ndIndex = 0;4260while (lastOffset < freeSpace2ndTo1stEnd)4261{4262// Find next non-null allocation or move nextAllocIndex to the end.4263while (nextAlloc2ndIndex < suballoc2ndCount &&4264suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4265{4266++nextAlloc2ndIndex;4267}42684269// Found non-null allocation.4270if (nextAlloc2ndIndex < suballoc2ndCount)4271{4272const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];42734274// 1. Process free space before this allocation.4275if (lastOffset < suballoc.offset)4276{4277// There is free space from lastOffset to suballoc.offset.4278const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4279AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4280}42814282// 2. Process this allocation.4283// There is allocation with suballoc.offset, suballoc.size.4284AddDetailedStatisticsAllocation(inoutStats, suballoc.size);42854286// 3. Prepare for next iteration.4287lastOffset = suballoc.offset + suballoc.size;4288++nextAlloc2ndIndex;4289}4290// We are at the end.4291else4292{4293// There is free space from lastOffset to freeSpace2ndTo1stEnd.4294if (lastOffset < freeSpace2ndTo1stEnd)4295{4296const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;4297AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4298}42994300// End of loop.4301lastOffset = freeSpace2ndTo1stEnd;4302}4303}4304}43054306size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;4307const UINT64 freeSpace1stTo2ndEnd =4308m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;4309while (lastOffset < freeSpace1stTo2ndEnd)4310{4311// Find next non-null allocation or move nextAllocIndex to the end.4312while (nextAlloc1stIndex < suballoc1stCount &&4313suballocations1st[nextAlloc1stIndex].privateData == NULL)4314{4315++nextAlloc1stIndex;4316}43174318// Found non-null allocation.4319if (nextAlloc1stIndex < suballoc1stCount)4320{4321const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];43224323// 1. Process free space before this allocation.4324if (lastOffset < suballoc.offset)4325{4326// There is free space from lastOffset to suballoc.offset.4327const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4328AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4329}43304331// 2. Process this allocation.4332// There is allocation with suballoc.offset, suballoc.size.4333AddDetailedStatisticsAllocation(inoutStats, suballoc.size);43344335// 3. Prepare for next iteration.4336lastOffset = suballoc.offset + suballoc.size;4337++nextAlloc1stIndex;4338}4339// We are at the end.4340else4341{4342// There is free space from lastOffset to freeSpace1stTo2ndEnd.4343if (lastOffset < freeSpace1stTo2ndEnd)4344{4345const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;4346AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4347}43484349// End of loop.4350lastOffset = freeSpace1stTo2ndEnd;4351}4352}43534354if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4355{4356size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;4357while (lastOffset < size)4358{4359// Find next non-null allocation or move nextAllocIndex to the end.4360while (nextAlloc2ndIndex != SIZE_MAX &&4361suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4362{4363--nextAlloc2ndIndex;4364}43654366// Found non-null allocation.4367if (nextAlloc2ndIndex != SIZE_MAX)4368{4369const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];43704371// 1. Process free space before this allocation.4372if (lastOffset < suballoc.offset)4373{4374// There is free space from lastOffset to suballoc.offset.4375const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4376AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4377}43784379// 2. Process this allocation.4380// There is allocation with suballoc.offset, suballoc.size.4381AddDetailedStatisticsAllocation(inoutStats, suballoc.size);43824383// 3. Prepare for next iteration.4384lastOffset = suballoc.offset + suballoc.size;4385--nextAlloc2ndIndex;4386}4387// We are at the end.4388else4389{4390// There is free space from lastOffset to size.4391if (lastOffset < size)4392{4393const UINT64 unusedRangeSize = size - lastOffset;4394AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4395}43964397// End of loop.4398lastOffset = size;4399}4400}4401}4402}44034404void BlockMetadata_Linear::WriteAllocationInfoToJson(JsonWriter& json) const4405{4406const UINT64 size = GetSize();4407const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4408const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4409const size_t suballoc1stCount = suballocations1st.size();4410const size_t suballoc2ndCount = suballocations2nd.size();44114412// FIRST PASS44134414size_t unusedRangeCount = 0;4415UINT64 usedBytes = 0;44164417UINT64 lastOffset = 0;44184419size_t alloc2ndCount = 0;4420if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4421{4422const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;4423size_t nextAlloc2ndIndex = 0;4424while (lastOffset < freeSpace2ndTo1stEnd)4425{4426// Find next non-null allocation or move nextAlloc2ndIndex to the end.4427while (nextAlloc2ndIndex < suballoc2ndCount &&4428suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4429{4430++nextAlloc2ndIndex;4431}44324433// Found non-null allocation.4434if (nextAlloc2ndIndex < suballoc2ndCount)4435{4436const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];44374438// 1. Process free space before this allocation.4439if (lastOffset < suballoc.offset)4440{4441// There is free space from lastOffset to suballoc.offset.4442++unusedRangeCount;4443}44444445// 2. Process this allocation.4446// There is allocation with suballoc.offset, suballoc.size.4447++alloc2ndCount;4448usedBytes += suballoc.size;44494450// 3. Prepare for next iteration.4451lastOffset = suballoc.offset + suballoc.size;4452++nextAlloc2ndIndex;4453}4454// We are at the end.4455else4456{4457if (lastOffset < freeSpace2ndTo1stEnd)4458{4459// There is free space from lastOffset to freeSpace2ndTo1stEnd.4460++unusedRangeCount;4461}44624463// End of loop.4464lastOffset = freeSpace2ndTo1stEnd;4465}4466}4467}44684469size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;4470size_t alloc1stCount = 0;4471const UINT64 freeSpace1stTo2ndEnd =4472m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;4473while (lastOffset < freeSpace1stTo2ndEnd)4474{4475// Find next non-null allocation or move nextAllocIndex to the end.4476while (nextAlloc1stIndex < suballoc1stCount &&4477suballocations1st[nextAlloc1stIndex].privateData == NULL)4478{4479++nextAlloc1stIndex;4480}44814482// Found non-null allocation.4483if (nextAlloc1stIndex < suballoc1stCount)4484{4485const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];44864487// 1. Process free space before this allocation.4488if (lastOffset < suballoc.offset)4489{4490// There is free space from lastOffset to suballoc.offset.4491++unusedRangeCount;4492}44934494// 2. Process this allocation.4495// There is allocation with suballoc.offset, suballoc.size.4496++alloc1stCount;4497usedBytes += suballoc.size;44984499// 3. Prepare for next iteration.4500lastOffset = suballoc.offset + suballoc.size;4501++nextAlloc1stIndex;4502}4503// We are at the end.4504else4505{4506if (lastOffset < size)4507{4508// There is free space from lastOffset to freeSpace1stTo2ndEnd.4509++unusedRangeCount;4510}45114512// End of loop.4513lastOffset = freeSpace1stTo2ndEnd;4514}4515}45164517if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4518{4519size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;4520while (lastOffset < size)4521{4522// Find next non-null allocation or move nextAlloc2ndIndex to the end.4523while (nextAlloc2ndIndex != SIZE_MAX &&4524suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4525{4526--nextAlloc2ndIndex;4527}45284529// Found non-null allocation.4530if (nextAlloc2ndIndex != SIZE_MAX)4531{4532const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];45334534// 1. Process free space before this allocation.4535if (lastOffset < suballoc.offset)4536{4537// There is free space from lastOffset to suballoc.offset.4538++unusedRangeCount;4539}45404541// 2. Process this allocation.4542// There is allocation with suballoc.offset, suballoc.size.4543++alloc2ndCount;4544usedBytes += suballoc.size;45454546// 3. Prepare for next iteration.4547lastOffset = suballoc.offset + suballoc.size;4548--nextAlloc2ndIndex;4549}4550// We are at the end.4551else4552{4553if (lastOffset < size)4554{4555// There is free space from lastOffset to size.4556++unusedRangeCount;4557}45584559// End of loop.4560lastOffset = size;4561}4562}4563}45644565const UINT64 unusedBytes = size - usedBytes;4566PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);45674568// SECOND PASS4569lastOffset = 0;4570if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4571{4572const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;4573size_t nextAlloc2ndIndex = 0;4574while (lastOffset < freeSpace2ndTo1stEnd)4575{4576// Find next non-null allocation or move nextAlloc2ndIndex to the end.4577while (nextAlloc2ndIndex < suballoc2ndCount &&4578suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4579{4580++nextAlloc2ndIndex;4581}45824583// Found non-null allocation.4584if (nextAlloc2ndIndex < suballoc2ndCount)4585{4586const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];45874588// 1. Process free space before this allocation.4589if (lastOffset < suballoc.offset)4590{4591// There is free space from lastOffset to suballoc.offset.4592const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4593PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4594}45954596// 2. Process this allocation.4597// There is allocation with suballoc.offset, suballoc.size.4598PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);45994600// 3. Prepare for next iteration.4601lastOffset = suballoc.offset + suballoc.size;4602++nextAlloc2ndIndex;4603}4604// We are at the end.4605else4606{4607if (lastOffset < freeSpace2ndTo1stEnd)4608{4609// There is free space from lastOffset to freeSpace2ndTo1stEnd.4610const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;4611PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4612}46134614// End of loop.4615lastOffset = freeSpace2ndTo1stEnd;4616}4617}4618}46194620nextAlloc1stIndex = m_1stNullItemsBeginCount;4621while (lastOffset < freeSpace1stTo2ndEnd)4622{4623// Find next non-null allocation or move nextAllocIndex to the end.4624while (nextAlloc1stIndex < suballoc1stCount &&4625suballocations1st[nextAlloc1stIndex].privateData == NULL)4626{4627++nextAlloc1stIndex;4628}46294630// Found non-null allocation.4631if (nextAlloc1stIndex < suballoc1stCount)4632{4633const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];46344635// 1. Process free space before this allocation.4636if (lastOffset < suballoc.offset)4637{4638// There is free space from lastOffset to suballoc.offset.4639const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4640PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4641}46424643// 2. Process this allocation.4644// There is allocation with suballoc.offset, suballoc.size.4645PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);46464647// 3. Prepare for next iteration.4648lastOffset = suballoc.offset + suballoc.size;4649++nextAlloc1stIndex;4650}4651// We are at the end.4652else4653{4654if (lastOffset < freeSpace1stTo2ndEnd)4655{4656// There is free space from lastOffset to freeSpace1stTo2ndEnd.4657const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;4658PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4659}46604661// End of loop.4662lastOffset = freeSpace1stTo2ndEnd;4663}4664}46654666if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4667{4668size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;4669while (lastOffset < size)4670{4671// Find next non-null allocation or move nextAlloc2ndIndex to the end.4672while (nextAlloc2ndIndex != SIZE_MAX &&4673suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4674{4675--nextAlloc2ndIndex;4676}46774678// Found non-null allocation.4679if (nextAlloc2ndIndex != SIZE_MAX)4680{4681const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];46824683// 1. Process free space before this allocation.4684if (lastOffset < suballoc.offset)4685{4686// There is free space from lastOffset to suballoc.offset.4687const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4688PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4689}46904691// 2. Process this allocation.4692// There is allocation with suballoc.offset, suballoc.size.4693PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);46944695// 3. Prepare for next iteration.4696lastOffset = suballoc.offset + suballoc.size;4697--nextAlloc2ndIndex;4698}4699// We are at the end.4700else4701{4702if (lastOffset < size)4703{4704// There is free space from lastOffset to size.4705const UINT64 unusedRangeSize = size - lastOffset;4706PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4707}47084709// End of loop.4710lastOffset = size;4711}4712}4713}47144715PrintDetailedMap_End(json);4716}47174718void BlockMetadata_Linear::DebugLogAllAllocations() const4719{4720const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4721for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)4722if (it->type != SUBALLOCATION_TYPE_FREE)4723DebugLogAllocation(it->offset, it->size, it->privateData);47244725const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4726for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)4727if (it->type != SUBALLOCATION_TYPE_FREE)4728DebugLogAllocation(it->offset, it->size, it->privateData);4729}47304731Suballocation& BlockMetadata_Linear::FindSuballocation(UINT64 offset) const4732{4733const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4734const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();47354736Suballocation refSuballoc;4737refSuballoc.offset = offset;4738// Rest of members stays uninitialized intentionally for better performance.47394740// Item from the 1st vector.4741{4742const SuballocationVectorType::const_iterator it = BinaryFindSorted(4743suballocations1st.begin() + m_1stNullItemsBeginCount,4744suballocations1st.end(),4745refSuballoc,4746SuballocationOffsetLess());4747if (it != suballocations1st.end())4748{4749return const_cast<Suballocation&>(*it);4750}4751}47524753if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)4754{4755// Rest of members stays uninitialized intentionally for better performance.4756const SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?4757BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) :4758BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater());4759if (it != suballocations2nd.end())4760{4761return const_cast<Suballocation&>(*it);4762}4763}47644765D3D12MA_ASSERT(0 && "Allocation not found in linear allocator!");4766return const_cast<Suballocation&>(suballocations1st.back()); // Should never occur.4767}47684769bool BlockMetadata_Linear::ShouldCompact1st() const4770{4771const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;4772const size_t suballocCount = AccessSuballocations1st().size();4773return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;4774}47754776void BlockMetadata_Linear::CleanupAfterFree()4777{4778SuballocationVectorType& suballocations1st = AccessSuballocations1st();4779SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();47804781if (IsEmpty())4782{4783suballocations1st.clear();4784suballocations2nd.clear();4785m_1stNullItemsBeginCount = 0;4786m_1stNullItemsMiddleCount = 0;4787m_2ndNullItemsCount = 0;4788m_2ndVectorMode = SECOND_VECTOR_EMPTY;4789}4790else4791{4792const size_t suballoc1stCount = suballocations1st.size();4793const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;4794D3D12MA_ASSERT(nullItem1stCount <= suballoc1stCount);47954796// Find more null items at the beginning of 1st vector.4797while (m_1stNullItemsBeginCount < suballoc1stCount &&4798suballocations1st[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE)4799{4800++m_1stNullItemsBeginCount;4801--m_1stNullItemsMiddleCount;4802}48034804// Find more null items at the end of 1st vector.4805while (m_1stNullItemsMiddleCount > 0 &&4806suballocations1st.back().type == SUBALLOCATION_TYPE_FREE)4807{4808--m_1stNullItemsMiddleCount;4809suballocations1st.pop_back();4810}48114812// Find more null items at the end of 2nd vector.4813while (m_2ndNullItemsCount > 0 &&4814suballocations2nd.back().type == SUBALLOCATION_TYPE_FREE)4815{4816--m_2ndNullItemsCount;4817suballocations2nd.pop_back();4818}48194820// Find more null items at the beginning of 2nd vector.4821while (m_2ndNullItemsCount > 0 &&4822suballocations2nd[0].type == SUBALLOCATION_TYPE_FREE)4823{4824--m_2ndNullItemsCount;4825suballocations2nd.remove(0);4826}48274828if (ShouldCompact1st())4829{4830const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;4831size_t srcIndex = m_1stNullItemsBeginCount;4832for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)4833{4834while (suballocations1st[srcIndex].type == SUBALLOCATION_TYPE_FREE)4835{4836++srcIndex;4837}4838if (dstIndex != srcIndex)4839{4840suballocations1st[dstIndex] = suballocations1st[srcIndex];4841}4842++srcIndex;4843}4844suballocations1st.resize(nonNullItemCount);4845m_1stNullItemsBeginCount = 0;4846m_1stNullItemsMiddleCount = 0;4847}48484849// 2nd vector became empty.4850if (suballocations2nd.empty())4851{4852m_2ndVectorMode = SECOND_VECTOR_EMPTY;4853}48544855// 1st vector became empty.4856if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)4857{4858suballocations1st.clear();4859m_1stNullItemsBeginCount = 0;48604861if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4862{4863// Swap 1st with 2nd. Now 2nd is empty.4864m_2ndVectorMode = SECOND_VECTOR_EMPTY;4865m_1stNullItemsMiddleCount = m_2ndNullItemsCount;4866while (m_1stNullItemsBeginCount < suballocations2nd.size() &&4867suballocations2nd[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE)4868{4869++m_1stNullItemsBeginCount;4870--m_1stNullItemsMiddleCount;4871}4872m_2ndNullItemsCount = 0;4873m_1stVectorIndex ^= 1;4874}4875}4876}48774878D3D12MA_HEAVY_ASSERT(Validate());4879}48804881bool BlockMetadata_Linear::CreateAllocationRequest_LowerAddress(4882UINT64 allocSize,4883UINT64 allocAlignment,4884AllocationRequest* pAllocationRequest)4885{4886const UINT64 blockSize = GetSize();4887SuballocationVectorType& suballocations1st = AccessSuballocations1st();4888SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();48894890if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4891{4892// Try to allocate at the end of 1st vector.48934894UINT64 resultBaseOffset = 0;4895if (!suballocations1st.empty())4896{4897const Suballocation& lastSuballoc = suballocations1st.back();4898resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin();4899}49004901// Start from offset equal to beginning of free space.4902UINT64 resultOffset = resultBaseOffset;4903// Apply alignment.4904resultOffset = AlignUp(resultOffset, allocAlignment);49054906const UINT64 freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?4907suballocations2nd.back().offset : blockSize;49084909// There is enough free space at the end after alignment.4910if (resultOffset + allocSize + GetDebugMargin() <= freeSpaceEnd)4911{4912// All tests passed: Success.4913pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);4914// pAllocationRequest->item, customData unused.4915pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_1ST;4916return true;4917}4918}49194920// Wrap-around to end of 2nd vector. Try to allocate there, watching for the4921// beginning of 1st vector as the end of free space.4922if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4923{4924D3D12MA_ASSERT(!suballocations1st.empty());49254926UINT64 resultBaseOffset = 0;4927if (!suballocations2nd.empty())4928{4929const Suballocation& lastSuballoc = suballocations2nd.back();4930resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin();4931}49324933// Start from offset equal to beginning of free space.4934UINT64 resultOffset = resultBaseOffset;49354936// Apply alignment.4937resultOffset = AlignUp(resultOffset, allocAlignment);49384939size_t index1st = m_1stNullItemsBeginCount;4940// There is enough free space at the end after alignment.4941if ((index1st == suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= blockSize) ||4942(index1st < suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= suballocations1st[index1st].offset))4943{4944// All tests passed: Success.4945pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);4946pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_2ND;4947// pAllocationRequest->item, customData unused.4948return true;4949}4950}4951return false;4952}49534954bool BlockMetadata_Linear::CreateAllocationRequest_UpperAddress(4955UINT64 allocSize,4956UINT64 allocAlignment,4957AllocationRequest* pAllocationRequest)4958{4959const UINT64 blockSize = GetSize();4960SuballocationVectorType& suballocations1st = AccessSuballocations1st();4961SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();49624963if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4964{4965D3D12MA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");4966return false;4967}49684969// Try to allocate before 2nd.back(), or end of block if 2nd.empty().4970if (allocSize > blockSize)4971{4972return false;4973}4974UINT64 resultBaseOffset = blockSize - allocSize;4975if (!suballocations2nd.empty())4976{4977const Suballocation& lastSuballoc = suballocations2nd.back();4978resultBaseOffset = lastSuballoc.offset - allocSize;4979if (allocSize > lastSuballoc.offset)4980{4981return false;4982}4983}49844985// Start from offset equal to end of free space.4986UINT64 resultOffset = resultBaseOffset;4987// Apply debugMargin at the end.4988if (GetDebugMargin() > 0)4989{4990if (resultOffset < GetDebugMargin())4991{4992return false;4993}4994resultOffset -= GetDebugMargin();4995}49964997// Apply alignment.4998resultOffset = AlignDown(resultOffset, allocAlignment);4999// There is enough free space.5000const UINT64 endOf1st = !suballocations1st.empty() ?5001suballocations1st.back().offset + suballocations1st.back().size : 0;50025003if (endOf1st + GetDebugMargin() <= resultOffset)5004{5005// All tests passed: Success.5006pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);5007// pAllocationRequest->item unused.5008pAllocationRequest->algorithmData = ALLOC_REQUEST_UPPER_ADDRESS;5009return true;5010}5011return false;5012}5013#endif // _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS5014#endif // _D3D12MA_BLOCK_METADATA_LINEAR50155016#ifndef _D3D12MA_BLOCK_METADATA_TLSF5017class BlockMetadata_TLSF : public BlockMetadata5018{5019public:5020BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);5021virtual ~BlockMetadata_TLSF();50225023size_t GetAllocationCount() const override { return m_AllocCount; }5024size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }5025UINT64 GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }5026bool IsEmpty() const override { return m_NullBlock->offset == 0; }5027UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; };50285029void Init(UINT64 size) override;5030bool Validate() const override;5031void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;50325033bool CreateAllocationRequest(5034UINT64 allocSize,5035UINT64 allocAlignment,5036bool upperAddress,5037UINT32 strategy,5038AllocationRequest* pAllocationRequest) override;50395040void Alloc(5041const AllocationRequest& request,5042UINT64 allocSize,5043void* privateData) override;50445045void Free(AllocHandle allocHandle) override;5046void Clear() override;50475048AllocHandle GetAllocationListBegin() const override;5049AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override;5050UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override;5051void* GetAllocationPrivateData(AllocHandle allocHandle) const override;5052void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;50535054void AddStatistics(Statistics& inoutStats) const override;5055void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;5056void WriteAllocationInfoToJson(JsonWriter& json) const override;5057void DebugLogAllAllocations() const override;50585059private:5060// According to original paper it should be preferable 4 or 5:5061// M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"5062// http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf5063static const UINT8 SECOND_LEVEL_INDEX = 5;5064static const UINT16 SMALL_BUFFER_SIZE = 256;5065static const UINT INITIAL_BLOCK_ALLOC_COUNT = 16;5066static const UINT8 MEMORY_CLASS_SHIFT = 7;5067static const UINT8 MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;50685069class Block5070{5071public:5072UINT64 offset;5073UINT64 size;5074Block* prevPhysical;5075Block* nextPhysical;50765077void MarkFree() { prevFree = NULL; }5078void MarkTaken() { prevFree = this; }5079bool IsFree() const { return prevFree != this; }5080void*& PrivateData() { D3D12MA_HEAVY_ASSERT(!IsFree()); return privateData; }5081Block*& PrevFree() { return prevFree; }5082Block*& NextFree() { D3D12MA_HEAVY_ASSERT(IsFree()); return nextFree; }50835084private:5085Block* prevFree; // Address of the same block here indicates that block is taken5086union5087{5088Block* nextFree;5089void* privateData;5090};5091};50925093size_t m_AllocCount = 0;5094// Total number of free blocks besides null block5095size_t m_BlocksFreeCount = 0;5096// Total size of free blocks excluding null block5097UINT64 m_BlocksFreeSize = 0;5098UINT32 m_IsFreeBitmap = 0;5099UINT8 m_MemoryClasses = 0;5100UINT32 m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];5101UINT32 m_ListsCount = 0;5102/*5103* 0: 0-3 lists for small buffers5104* 1+: 0-(2^SLI-1) lists for normal buffers5105*/5106Block** m_FreeList = NULL;5107PoolAllocator<Block> m_BlockAllocator;5108Block* m_NullBlock = NULL;51095110UINT8 SizeToMemoryClass(UINT64 size) const;5111UINT16 SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const;5112UINT32 GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const;5113UINT32 GetListIndex(UINT64 size) const;51145115void RemoveFreeBlock(Block* block);5116void InsertFreeBlock(Block* block);5117void MergeBlock(Block* block, Block* prev);51185119Block* FindFreeBlock(UINT64 size, UINT32& listIndex) const;5120bool CheckBlock(5121Block& block,5122UINT32 listIndex,5123UINT64 allocSize,5124UINT64 allocAlignment,5125AllocationRequest* pAllocationRequest);51265127D3D12MA_CLASS_NO_COPY(BlockMetadata_TLSF)5128};51295130#ifndef _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS5131BlockMetadata_TLSF::BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)5132: BlockMetadata(allocationCallbacks, isVirtual),5133m_BlockAllocator(*allocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT)5134{5135D3D12MA_ASSERT(allocationCallbacks);5136}51375138BlockMetadata_TLSF::~BlockMetadata_TLSF()5139{5140D3D12MA_DELETE_ARRAY(*GetAllocs(), m_FreeList, m_ListsCount);5141}51425143void BlockMetadata_TLSF::Init(UINT64 size)5144{5145BlockMetadata::Init(size);51465147m_NullBlock = m_BlockAllocator.Alloc();5148m_NullBlock->size = size;5149m_NullBlock->offset = 0;5150m_NullBlock->prevPhysical = NULL;5151m_NullBlock->nextPhysical = NULL;5152m_NullBlock->MarkFree();5153m_NullBlock->NextFree() = NULL;5154m_NullBlock->PrevFree() = NULL;5155UINT8 memoryClass = SizeToMemoryClass(size);5156UINT16 sli = SizeToSecondIndex(size, memoryClass);5157m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;5158if (IsVirtual())5159m_ListsCount += 1UL << SECOND_LEVEL_INDEX;5160else5161m_ListsCount += 4;51625163m_MemoryClasses = memoryClass + 2;5164memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(UINT32));51655166m_FreeList = D3D12MA_NEW_ARRAY(*GetAllocs(), Block*, m_ListsCount);5167memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));5168}51695170bool BlockMetadata_TLSF::Validate() const5171{5172D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize());51735174UINT64 calculatedSize = m_NullBlock->size;5175UINT64 calculatedFreeSize = m_NullBlock->size;5176size_t allocCount = 0;5177size_t freeCount = 0;51785179// Check integrity of free lists5180for (UINT32 list = 0; list < m_ListsCount; ++list)5181{5182Block* block = m_FreeList[list];5183if (block != NULL)5184{5185D3D12MA_VALIDATE(block->IsFree());5186D3D12MA_VALIDATE(block->PrevFree() == NULL);5187while (block->NextFree())5188{5189D3D12MA_VALIDATE(block->NextFree()->IsFree());5190D3D12MA_VALIDATE(block->NextFree()->PrevFree() == block);5191block = block->NextFree();5192}5193}5194}51955196D3D12MA_VALIDATE(m_NullBlock->nextPhysical == NULL);5197if (m_NullBlock->prevPhysical)5198{5199D3D12MA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);5200}52015202// Check all blocks5203UINT64 nextOffset = m_NullBlock->offset;5204for (Block* prev = m_NullBlock->prevPhysical; prev != NULL; prev = prev->prevPhysical)5205{5206D3D12MA_VALIDATE(prev->offset + prev->size == nextOffset);5207nextOffset = prev->offset;5208calculatedSize += prev->size;52095210UINT32 listIndex = GetListIndex(prev->size);5211if (prev->IsFree())5212{5213++freeCount;5214// Check if free block belongs to free list5215Block* freeBlock = m_FreeList[listIndex];5216D3D12MA_VALIDATE(freeBlock != NULL);52175218bool found = false;5219do5220{5221if (freeBlock == prev)5222found = true;52235224freeBlock = freeBlock->NextFree();5225} while (!found && freeBlock != NULL);52265227D3D12MA_VALIDATE(found);5228calculatedFreeSize += prev->size;5229}5230else5231{5232++allocCount;5233// Check if taken block is not on a free list5234Block* freeBlock = m_FreeList[listIndex];5235while (freeBlock)5236{5237D3D12MA_VALIDATE(freeBlock != prev);5238freeBlock = freeBlock->NextFree();5239}5240}52415242if (prev->prevPhysical)5243{5244D3D12MA_VALIDATE(prev->prevPhysical->nextPhysical == prev);5245}5246}52475248D3D12MA_VALIDATE(nextOffset == 0);5249D3D12MA_VALIDATE(calculatedSize == GetSize());5250D3D12MA_VALIDATE(calculatedFreeSize == GetSumFreeSize());5251D3D12MA_VALIDATE(allocCount == m_AllocCount);5252D3D12MA_VALIDATE(freeCount == m_BlocksFreeCount);52535254return true;5255}52565257void BlockMetadata_TLSF::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const5258{5259Block* block = (Block*)allocHandle;5260D3D12MA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");5261outInfo.Offset = block->offset;5262outInfo.Size = block->size;5263outInfo.pPrivateData = block->PrivateData();5264}52655266bool BlockMetadata_TLSF::CreateAllocationRequest(5267UINT64 allocSize,5268UINT64 allocAlignment,5269bool upperAddress,5270UINT32 strategy,5271AllocationRequest* pAllocationRequest)5272{5273D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");5274D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm.");5275D3D12MA_ASSERT(pAllocationRequest != NULL);5276D3D12MA_HEAVY_ASSERT(Validate());52775278allocSize += GetDebugMargin();5279// Quick check for too small pool5280if (allocSize > GetSumFreeSize())5281return false;52825283// If no free blocks in pool then check only null block5284if (m_BlocksFreeCount == 0)5285return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest);52865287// Round up to the next block5288UINT64 sizeForNextList = allocSize;5289UINT16 smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4);5290if (allocSize > SMALL_BUFFER_SIZE)5291{5292sizeForNextList += (1ULL << (BitScanMSB(allocSize) - SECOND_LEVEL_INDEX));5293}5294else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)5295sizeForNextList = SMALL_BUFFER_SIZE + 1;5296else5297sizeForNextList += smallSizeStep;52985299UINT32 nextListIndex = 0;5300UINT32 prevListIndex = 0;5301Block* nextListBlock = NULL;5302Block* prevListBlock = NULL;53035304// Check blocks according to strategies5305if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_TIME)5306{5307// Quick check for larger block first5308nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);5309if (nextListBlock != NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5310return true;53115312// If not fitted then null block5313if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))5314return true;53155316// Null block failed, search larger bucket5317while (nextListBlock)5318{5319if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5320return true;5321nextListBlock = nextListBlock->NextFree();5322}53235324// Failed again, check best fit bucket5325prevListBlock = FindFreeBlock(allocSize, prevListIndex);5326while (prevListBlock)5327{5328if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))5329return true;5330prevListBlock = prevListBlock->NextFree();5331}5332}5333else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_MEMORY)5334{5335// Check best fit bucket5336prevListBlock = FindFreeBlock(allocSize, prevListIndex);5337while (prevListBlock)5338{5339if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))5340return true;5341prevListBlock = prevListBlock->NextFree();5342}53435344// If failed check null block5345if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))5346return true;53475348// Check larger bucket5349nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);5350while (nextListBlock)5351{5352if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5353return true;5354nextListBlock = nextListBlock->NextFree();5355}5356}5357else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_OFFSET)5358{5359// Perform search from the start5360Vector<Block*> blockList(m_BlocksFreeCount, *GetAllocs());53615362size_t i = m_BlocksFreeCount;5363for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5364{5365if (block->IsFree() && block->size >= allocSize)5366blockList[--i] = block;5367}53685369for (; i < m_BlocksFreeCount; ++i)5370{5371Block& block = *blockList[i];5372if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, pAllocationRequest))5373return true;5374}53755376// If failed check null block5377if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))5378return true;53795380// Whole range searched, no more memory5381return false;5382}5383else5384{5385// Check larger bucket5386nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);5387while (nextListBlock)5388{5389if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5390return true;5391nextListBlock = nextListBlock->NextFree();5392}53935394// If failed check null block5395if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))5396return true;53975398// Check best fit bucket5399prevListBlock = FindFreeBlock(allocSize, prevListIndex);5400while (prevListBlock)5401{5402if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))5403return true;5404prevListBlock = prevListBlock->NextFree();5405}5406}54075408// Worst case, full search has to be done5409while (++nextListIndex < m_ListsCount)5410{5411nextListBlock = m_FreeList[nextListIndex];5412while (nextListBlock)5413{5414if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5415return true;5416nextListBlock = nextListBlock->NextFree();5417}5418}54195420// No more memory sadly5421return false;5422}54235424void BlockMetadata_TLSF::Alloc(5425const AllocationRequest& request,5426UINT64 allocSize,5427void* privateData)5428{5429// Get block and pop it from the free list5430Block* currentBlock = (Block*)request.allocHandle;5431UINT64 offset = request.algorithmData;5432D3D12MA_ASSERT(currentBlock != NULL);5433D3D12MA_ASSERT(currentBlock->offset <= offset);54345435if (currentBlock != m_NullBlock)5436RemoveFreeBlock(currentBlock);54375438// Append missing alignment to prev block or create new one5439UINT64 misssingAlignment = offset - currentBlock->offset;5440if (misssingAlignment)5441{5442Block* prevBlock = currentBlock->prevPhysical;5443D3D12MA_ASSERT(prevBlock != NULL && "There should be no missing alignment at offset 0!");54445445if (prevBlock->IsFree() && prevBlock->size != GetDebugMargin())5446{5447UINT32 oldList = GetListIndex(prevBlock->size);5448prevBlock->size += misssingAlignment;5449// Check if new size crosses list bucket5450if (oldList != GetListIndex(prevBlock->size))5451{5452prevBlock->size -= misssingAlignment;5453RemoveFreeBlock(prevBlock);5454prevBlock->size += misssingAlignment;5455InsertFreeBlock(prevBlock);5456}5457else5458m_BlocksFreeSize += misssingAlignment;5459}5460else5461{5462Block* newBlock = m_BlockAllocator.Alloc();5463currentBlock->prevPhysical = newBlock;5464prevBlock->nextPhysical = newBlock;5465newBlock->prevPhysical = prevBlock;5466newBlock->nextPhysical = currentBlock;5467newBlock->size = misssingAlignment;5468newBlock->offset = currentBlock->offset;5469newBlock->MarkTaken();54705471InsertFreeBlock(newBlock);5472}54735474currentBlock->size -= misssingAlignment;5475currentBlock->offset += misssingAlignment;5476}54775478UINT64 size = request.size + GetDebugMargin();5479if (currentBlock->size == size)5480{5481if (currentBlock == m_NullBlock)5482{5483// Setup new null block5484m_NullBlock = m_BlockAllocator.Alloc();5485m_NullBlock->size = 0;5486m_NullBlock->offset = currentBlock->offset + size;5487m_NullBlock->prevPhysical = currentBlock;5488m_NullBlock->nextPhysical = NULL;5489m_NullBlock->MarkFree();5490m_NullBlock->PrevFree() = NULL;5491m_NullBlock->NextFree() = NULL;5492currentBlock->nextPhysical = m_NullBlock;5493currentBlock->MarkTaken();5494}5495}5496else5497{5498D3D12MA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");54995500// Create new free block5501Block* newBlock = m_BlockAllocator.Alloc();5502newBlock->size = currentBlock->size - size;5503newBlock->offset = currentBlock->offset + size;5504newBlock->prevPhysical = currentBlock;5505newBlock->nextPhysical = currentBlock->nextPhysical;5506currentBlock->nextPhysical = newBlock;5507currentBlock->size = size;55085509if (currentBlock == m_NullBlock)5510{5511m_NullBlock = newBlock;5512m_NullBlock->MarkFree();5513m_NullBlock->NextFree() = NULL;5514m_NullBlock->PrevFree() = NULL;5515currentBlock->MarkTaken();5516}5517else5518{5519newBlock->nextPhysical->prevPhysical = newBlock;5520newBlock->MarkTaken();5521InsertFreeBlock(newBlock);5522}5523}5524currentBlock->PrivateData() = privateData;55255526if (GetDebugMargin() > 0)5527{5528currentBlock->size -= GetDebugMargin();5529Block* newBlock = m_BlockAllocator.Alloc();5530newBlock->size = GetDebugMargin();5531newBlock->offset = currentBlock->offset + currentBlock->size;5532newBlock->prevPhysical = currentBlock;5533newBlock->nextPhysical = currentBlock->nextPhysical;5534newBlock->MarkTaken();5535currentBlock->nextPhysical->prevPhysical = newBlock;5536currentBlock->nextPhysical = newBlock;5537InsertFreeBlock(newBlock);5538}5539++m_AllocCount;5540}55415542void BlockMetadata_TLSF::Free(AllocHandle allocHandle)5543{5544Block* block = (Block*)allocHandle;5545Block* next = block->nextPhysical;5546D3D12MA_ASSERT(!block->IsFree() && "Block is already free!");55475548--m_AllocCount;5549if (GetDebugMargin() > 0)5550{5551RemoveFreeBlock(next);5552MergeBlock(next, block);5553block = next;5554next = next->nextPhysical;5555}55565557// Try merging5558Block* prev = block->prevPhysical;5559if (prev != NULL && prev->IsFree() && prev->size != GetDebugMargin())5560{5561RemoveFreeBlock(prev);5562MergeBlock(block, prev);5563}55645565if (!next->IsFree())5566InsertFreeBlock(block);5567else if (next == m_NullBlock)5568MergeBlock(m_NullBlock, block);5569else5570{5571RemoveFreeBlock(next);5572MergeBlock(next, block);5573InsertFreeBlock(next);5574}5575}55765577void BlockMetadata_TLSF::Clear()5578{5579m_AllocCount = 0;5580m_BlocksFreeCount = 0;5581m_BlocksFreeSize = 0;5582m_IsFreeBitmap = 0;5583m_NullBlock->offset = 0;5584m_NullBlock->size = GetSize();5585Block* block = m_NullBlock->prevPhysical;5586m_NullBlock->prevPhysical = NULL;5587while (block)5588{5589Block* prev = block->prevPhysical;5590m_BlockAllocator.Free(block);5591block = prev;5592}5593memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));5594memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(UINT32));5595}55965597AllocHandle BlockMetadata_TLSF::GetAllocationListBegin() const5598{5599if (m_AllocCount == 0)5600return (AllocHandle)0;56015602for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)5603{5604if (!block->IsFree())5605return (AllocHandle)block;5606}5607D3D12MA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");5608return (AllocHandle)0;5609}56105611AllocHandle BlockMetadata_TLSF::GetNextAllocation(AllocHandle prevAlloc) const5612{5613Block* startBlock = (Block*)prevAlloc;5614D3D12MA_ASSERT(!startBlock->IsFree() && "Incorrect block!");56155616for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)5617{5618if (!block->IsFree())5619return (AllocHandle)block;5620}5621return (AllocHandle)0;5622}56235624UINT64 BlockMetadata_TLSF::GetNextFreeRegionSize(AllocHandle alloc) const5625{5626Block* block = (Block*)alloc;5627D3D12MA_ASSERT(!block->IsFree() && "Incorrect block!");56285629if (block->prevPhysical)5630return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;5631return 0;5632}56335634void* BlockMetadata_TLSF::GetAllocationPrivateData(AllocHandle allocHandle) const5635{5636Block* block = (Block*)allocHandle;5637D3D12MA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");5638return block->PrivateData();5639}56405641void BlockMetadata_TLSF::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)5642{5643Block* block = (Block*)allocHandle;5644D3D12MA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");5645block->PrivateData() = privateData;5646}56475648void BlockMetadata_TLSF::AddStatistics(Statistics& inoutStats) const5649{5650inoutStats.BlockCount++;5651inoutStats.AllocationCount += static_cast<UINT>(m_AllocCount);5652inoutStats.BlockBytes += GetSize();5653inoutStats.AllocationBytes += GetSize() - GetSumFreeSize();5654}56555656void BlockMetadata_TLSF::AddDetailedStatistics(DetailedStatistics& inoutStats) const5657{5658inoutStats.Stats.BlockCount++;5659inoutStats.Stats.BlockBytes += GetSize();56605661for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5662{5663if (block->IsFree())5664AddDetailedStatisticsUnusedRange(inoutStats, block->size);5665else5666AddDetailedStatisticsAllocation(inoutStats, block->size);5667}56685669if (m_NullBlock->size > 0)5670AddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);5671}56725673void BlockMetadata_TLSF::WriteAllocationInfoToJson(JsonWriter& json) const5674{5675size_t blockCount = m_AllocCount + m_BlocksFreeCount;5676Vector<Block*> blockList(blockCount, *GetAllocs());56775678size_t i = blockCount;5679if (m_NullBlock->size > 0)5680{5681++blockCount;5682blockList.push_back(m_NullBlock);5683}5684for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5685{5686blockList[--i] = block;5687}5688D3D12MA_ASSERT(i == 0);56895690PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_BlocksFreeCount + static_cast<bool>(m_NullBlock->size));5691for (; i < blockCount; ++i)5692{5693Block* block = blockList[i];5694if (block->IsFree())5695PrintDetailedMap_UnusedRange(json, block->offset, block->size);5696else5697PrintDetailedMap_Allocation(json, block->offset, block->size, block->PrivateData());5698}5699PrintDetailedMap_End(json);5700}57015702void BlockMetadata_TLSF::DebugLogAllAllocations() const5703{5704for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5705{5706if (!block->IsFree())5707{5708DebugLogAllocation(block->offset, block->size, block->PrivateData());5709}5710}5711}57125713UINT8 BlockMetadata_TLSF::SizeToMemoryClass(UINT64 size) const5714{5715if (size > SMALL_BUFFER_SIZE)5716return BitScanMSB(size) - MEMORY_CLASS_SHIFT;5717return 0;5718}57195720UINT16 BlockMetadata_TLSF::SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const5721{5722if (memoryClass == 0)5723{5724if (IsVirtual())5725return static_cast<UINT16>((size - 1) / 8);5726else5727return static_cast<UINT16>((size - 1) / 64);5728}5729return static_cast<UINT16>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));5730}57315732UINT32 BlockMetadata_TLSF::GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const5733{5734if (memoryClass == 0)5735return secondIndex;57365737const UINT32 index = static_cast<UINT32>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;5738if (IsVirtual())5739return index + (1 << SECOND_LEVEL_INDEX);5740else5741return index + 4;5742}57435744UINT32 BlockMetadata_TLSF::GetListIndex(UINT64 size) const5745{5746UINT8 memoryClass = SizeToMemoryClass(size);5747return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));5748}57495750void BlockMetadata_TLSF::RemoveFreeBlock(Block* block)5751{5752D3D12MA_ASSERT(block != m_NullBlock);5753D3D12MA_ASSERT(block->IsFree());57545755if (block->NextFree() != NULL)5756block->NextFree()->PrevFree() = block->PrevFree();5757if (block->PrevFree() != NULL)5758block->PrevFree()->NextFree() = block->NextFree();5759else5760{5761UINT8 memClass = SizeToMemoryClass(block->size);5762UINT16 secondIndex = SizeToSecondIndex(block->size, memClass);5763UINT32 index = GetListIndex(memClass, secondIndex);5764m_FreeList[index] = block->NextFree();5765if (block->NextFree() == NULL)5766{5767m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);5768if (m_InnerIsFreeBitmap[memClass] == 0)5769m_IsFreeBitmap &= ~(1UL << memClass);5770}5771}5772block->MarkTaken();5773block->PrivateData() = NULL;5774--m_BlocksFreeCount;5775m_BlocksFreeSize -= block->size;5776}57775778void BlockMetadata_TLSF::InsertFreeBlock(Block* block)5779{5780D3D12MA_ASSERT(block != m_NullBlock);5781D3D12MA_ASSERT(!block->IsFree() && "Cannot insert block twice!");57825783UINT8 memClass = SizeToMemoryClass(block->size);5784UINT16 secondIndex = SizeToSecondIndex(block->size, memClass);5785UINT32 index = GetListIndex(memClass, secondIndex);5786block->PrevFree() = NULL;5787block->NextFree() = m_FreeList[index];5788m_FreeList[index] = block;5789if (block->NextFree() != NULL)5790block->NextFree()->PrevFree() = block;5791else5792{5793m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;5794m_IsFreeBitmap |= 1UL << memClass;5795}5796++m_BlocksFreeCount;5797m_BlocksFreeSize += block->size;5798}57995800void BlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)5801{5802D3D12MA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!");5803D3D12MA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");58045805block->offset = prev->offset;5806block->size += prev->size;5807block->prevPhysical = prev->prevPhysical;5808if (block->prevPhysical)5809block->prevPhysical->nextPhysical = block;5810m_BlockAllocator.Free(prev);5811}58125813BlockMetadata_TLSF::Block* BlockMetadata_TLSF::FindFreeBlock(UINT64 size, UINT32& listIndex) const5814{5815UINT8 memoryClass = SizeToMemoryClass(size);5816UINT32 innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));5817if (!innerFreeMap)5818{5819// Check higher levels for avaiable blocks5820UINT32 freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));5821if (!freeMap)5822return NULL; // No more memory avaible58235824// Find lowest free region5825memoryClass = BitScanLSB(freeMap);5826innerFreeMap = m_InnerIsFreeBitmap[memoryClass];5827D3D12MA_ASSERT(innerFreeMap != 0);5828}5829// Find lowest free subregion5830listIndex = GetListIndex(memoryClass, BitScanLSB(innerFreeMap));5831return m_FreeList[listIndex];5832}58335834bool BlockMetadata_TLSF::CheckBlock(5835Block& block,5836UINT32 listIndex,5837UINT64 allocSize,5838UINT64 allocAlignment,5839AllocationRequest* pAllocationRequest)5840{5841D3D12MA_ASSERT(block.IsFree() && "Block is already taken!");58425843UINT64 alignedOffset = AlignUp(block.offset, allocAlignment);5844if (block.size < allocSize + alignedOffset - block.offset)5845return false;58465847// Alloc successful5848pAllocationRequest->allocHandle = (AllocHandle)█5849pAllocationRequest->size = allocSize - GetDebugMargin();5850pAllocationRequest->algorithmData = alignedOffset;58515852// Place block at the start of list if it's normal block5853if (listIndex != m_ListsCount && block.PrevFree())5854{5855block.PrevFree()->NextFree() = block.NextFree();5856if (block.NextFree())5857block.NextFree()->PrevFree() = block.PrevFree();5858block.PrevFree() = NULL;5859block.NextFree() = m_FreeList[listIndex];5860m_FreeList[listIndex] = █5861if (block.NextFree())5862block.NextFree()->PrevFree() = █5863}58645865return true;5866}5867#endif // _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS5868#endif // _D3D12MA_BLOCK_METADATA_TLSF58695870#ifndef _D3D12MA_MEMORY_BLOCK5871/*5872Represents a single block of device memory (heap).5873Base class for inheritance.5874Thread-safety: This class must be externally synchronized.5875*/5876class MemoryBlock5877{5878public:5879// Creates the ID3D12Heap.5880MemoryBlock(5881AllocatorPimpl* allocator,5882const D3D12_HEAP_PROPERTIES& heapProps,5883D3D12_HEAP_FLAGS heapFlags,5884UINT64 size,5885UINT id);5886virtual ~MemoryBlock();58875888const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }5889D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }5890UINT64 GetSize() const { return m_Size; }5891UINT GetId() const { return m_Id; }5892ID3D12Heap* GetHeap() const { return m_Heap; }58935894protected:5895AllocatorPimpl* const m_Allocator;5896const D3D12_HEAP_PROPERTIES m_HeapProps;5897const D3D12_HEAP_FLAGS m_HeapFlags;5898const UINT64 m_Size;5899const UINT m_Id;59005901HRESULT Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures);59025903private:5904ID3D12Heap* m_Heap = NULL;59055906D3D12MA_CLASS_NO_COPY(MemoryBlock)5907};5908#endif // _D3D12MA_MEMORY_BLOCK59095910#ifndef _D3D12MA_NORMAL_BLOCK5911/*5912Represents a single block of device memory (heap) with all the data about its5913regions (aka suballocations, Allocation), assigned and free.5914Thread-safety: This class must be externally synchronized.5915*/5916class NormalBlock : public MemoryBlock5917{5918public:5919BlockMetadata* m_pMetadata;59205921NormalBlock(5922AllocatorPimpl* allocator,5923BlockVector* blockVector,5924const D3D12_HEAP_PROPERTIES& heapProps,5925D3D12_HEAP_FLAGS heapFlags,5926UINT64 size,5927UINT id);5928virtual ~NormalBlock();59295930BlockVector* GetBlockVector() const { return m_BlockVector; }59315932// 'algorithm' should be one of the *_ALGORITHM_* flags in enums POOL_FLAGS or VIRTUAL_BLOCK_FLAGS5933HRESULT Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures);59345935// Validates all data structures inside this object. If not valid, returns false.5936bool Validate() const;59375938private:5939BlockVector* m_BlockVector;59405941D3D12MA_CLASS_NO_COPY(NormalBlock)5942};5943#endif // _D3D12MA_NORMAL_BLOCK59445945#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS5946struct CommittedAllocationListItemTraits5947{5948using ItemType = Allocation;59495950static ItemType* GetPrev(const ItemType* item)5951{5952D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5953return item->m_Committed.prev;5954}5955static ItemType* GetNext(const ItemType* item)5956{5957D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5958return item->m_Committed.next;5959}5960static ItemType*& AccessPrev(ItemType* item)5961{5962D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5963return item->m_Committed.prev;5964}5965static ItemType*& AccessNext(ItemType* item)5966{5967D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5968return item->m_Committed.next;5969}5970};5971#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS59725973#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST5974/*5975Stores linked list of Allocation objects that are of TYPE_COMMITTED or TYPE_HEAP.5976Thread-safe, synchronized internally.5977*/5978class CommittedAllocationList5979{5980public:5981CommittedAllocationList() = default;5982void Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool);5983~CommittedAllocationList();59845985D3D12_HEAP_TYPE GetHeapType() const { return m_HeapType; }5986PoolPimpl* GetPool() const { return m_Pool; }5987UINT GetMemorySegmentGroup(AllocatorPimpl* allocator) const;59885989void AddStatistics(Statistics& inoutStats);5990void AddDetailedStatistics(DetailedStatistics& inoutStats);5991// Writes JSON array with the list of allocations.5992void BuildStatsString(JsonWriter& json);59935994void Register(Allocation* alloc);5995void Unregister(Allocation* alloc);59965997private:5998using CommittedAllocationLinkedList = IntrusiveLinkedList<CommittedAllocationListItemTraits>;59996000bool m_UseMutex = true;6001D3D12_HEAP_TYPE m_HeapType = D3D12_HEAP_TYPE_CUSTOM;6002PoolPimpl* m_Pool = NULL;60036004D3D12MA_RW_MUTEX m_Mutex;6005CommittedAllocationLinkedList m_AllocationList;6006};6007#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST60086009#ifndef _D3D12M_COMMITTED_ALLOCATION_PARAMETERS6010struct CommittedAllocationParameters6011{6012CommittedAllocationList* m_List = NULL;6013D3D12_HEAP_PROPERTIES m_HeapProperties = {};6014D3D12_HEAP_FLAGS m_HeapFlags = D3D12_HEAP_FLAG_NONE;6015ID3D12ProtectedResourceSession* m_ProtectedSession = NULL;6016bool m_CanAlias = false;6017D3D12_RESIDENCY_PRIORITY m_ResidencyPriority = D3D12_RESIDENCY_PRIORITY_NONE;60186019bool IsValid() const { return m_List != NULL; }6020};6021#endif // _D3D12M_COMMITTED_ALLOCATION_PARAMETERS60226023// Simple variant data structure to hold all possible variations of ID3D12Device*::CreateCommittedResource* and ID3D12Device*::CreatePlacedResource* arguments6024struct CREATE_RESOURCE_PARAMS6025{6026CREATE_RESOURCE_PARAMS() = delete;6027CREATE_RESOURCE_PARAMS(6028const D3D12_RESOURCE_DESC* pResourceDesc,6029D3D12_RESOURCE_STATES InitialResourceState,6030const D3D12_CLEAR_VALUE* pOptimizedClearValue)6031: Variant(VARIANT_WITH_STATE)6032, pResourceDesc(pResourceDesc)6033, InitialResourceState(InitialResourceState)6034, pOptimizedClearValue(pOptimizedClearValue)6035{6036}6037#ifdef __ID3D12Device8_INTERFACE_DEFINED__6038CREATE_RESOURCE_PARAMS(6039const D3D12_RESOURCE_DESC1* pResourceDesc,6040D3D12_RESOURCE_STATES InitialResourceState,6041const D3D12_CLEAR_VALUE* pOptimizedClearValue)6042: Variant(VARIANT_WITH_STATE_AND_DESC1)6043, pResourceDesc1(pResourceDesc)6044, InitialResourceState(InitialResourceState)6045, pOptimizedClearValue(pOptimizedClearValue)6046{6047}6048#endif6049#ifdef __ID3D12Device10_INTERFACE_DEFINED__6050CREATE_RESOURCE_PARAMS(6051const D3D12_RESOURCE_DESC1* pResourceDesc,6052D3D12_BARRIER_LAYOUT InitialLayout,6053const D3D12_CLEAR_VALUE* pOptimizedClearValue,6054UINT32 NumCastableFormats,6055DXGI_FORMAT* pCastableFormats)6056: Variant(VARIANT_WITH_LAYOUT)6057, pResourceDesc1(pResourceDesc)6058, InitialLayout(InitialLayout)6059, pOptimizedClearValue(pOptimizedClearValue)6060, NumCastableFormats(NumCastableFormats)6061, pCastableFormats(pCastableFormats)6062{6063}6064#endif60656066enum VARIANT6067{6068VARIANT_INVALID = 0,6069VARIANT_WITH_STATE,6070VARIANT_WITH_STATE_AND_DESC1,6071VARIANT_WITH_LAYOUT6072};60736074VARIANT Variant = VARIANT_INVALID;60756076const D3D12_RESOURCE_DESC* GetResourceDesc() const6077{6078D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE);6079return pResourceDesc;6080}6081const D3D12_RESOURCE_DESC*& AccessResourceDesc()6082{6083D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE);6084return pResourceDesc;6085}6086const D3D12_RESOURCE_DESC* GetBaseResourceDesc() const6087{6088// D3D12_RESOURCE_DESC1 can be cast to D3D12_RESOURCE_DESC by discarding the new members at the end.6089return pResourceDesc;6090}6091D3D12_RESOURCE_STATES GetInitialResourceState() const6092{6093D3D12MA_ASSERT(Variant < VARIANT_WITH_LAYOUT);6094return InitialResourceState;6095}6096const D3D12_CLEAR_VALUE* GetOptimizedClearValue() const6097{6098return pOptimizedClearValue;6099}61006101#ifdef __ID3D12Device8_INTERFACE_DEFINED__6102const D3D12_RESOURCE_DESC1* GetResourceDesc1() const6103{6104D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1);6105return pResourceDesc1;6106}6107const D3D12_RESOURCE_DESC1*& AccessResourceDesc1()6108{6109D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1);6110return pResourceDesc1;6111}6112#endif61136114#ifdef __ID3D12Device10_INTERFACE_DEFINED__6115D3D12_BARRIER_LAYOUT GetInitialLayout() const6116{6117D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);6118return InitialLayout;6119}6120UINT32 GetNumCastableFormats() const6121{6122D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);6123return NumCastableFormats;6124}6125DXGI_FORMAT* GetCastableFormats() const6126{6127D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);6128return pCastableFormats;6129}6130#endif61316132private:6133union6134{6135const D3D12_RESOURCE_DESC* pResourceDesc;6136#ifdef __ID3D12Device8_INTERFACE_DEFINED__6137const D3D12_RESOURCE_DESC1* pResourceDesc1;6138#endif6139};6140union6141{6142D3D12_RESOURCE_STATES InitialResourceState;6143#ifdef __ID3D12Device10_INTERFACE_DEFINED__6144D3D12_BARRIER_LAYOUT InitialLayout;6145#endif6146};6147const D3D12_CLEAR_VALUE* pOptimizedClearValue;6148#ifdef __ID3D12Device10_INTERFACE_DEFINED__6149UINT32 NumCastableFormats;6150DXGI_FORMAT* pCastableFormats;6151#endif6152};61536154#ifndef _D3D12MA_BLOCK_VECTOR6155/*6156Sequence of NormalBlock. Represents memory blocks allocated for a specific6157heap type and possibly resource type (if only Tier 1 is supported).61586159Synchronized internally with a mutex.6160*/6161class BlockVector6162{6163friend class DefragmentationContextPimpl;6164D3D12MA_CLASS_NO_COPY(BlockVector)6165public:6166BlockVector(6167AllocatorPimpl* hAllocator,6168const D3D12_HEAP_PROPERTIES& heapProps,6169D3D12_HEAP_FLAGS heapFlags,6170UINT64 preferredBlockSize,6171size_t minBlockCount,6172size_t maxBlockCount,6173bool explicitBlockSize,6174UINT64 minAllocationAlignment,6175UINT32 algorithm,6176bool denyMsaaTextures,6177ID3D12ProtectedResourceSession* pProtectedSession,6178D3D12_RESIDENCY_PRIORITY residencyPriority);6179~BlockVector();6180D3D12_RESIDENCY_PRIORITY GetResidencyPriority() const { return m_ResidencyPriority; }61816182const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }6183D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }6184UINT64 GetPreferredBlockSize() const { return m_PreferredBlockSize; }6185UINT32 GetAlgorithm() const { return m_Algorithm; }6186bool DeniesMsaaTextures() const { return m_DenyMsaaTextures; }6187// To be used only while the m_Mutex is locked. Used during defragmentation.6188size_t GetBlockCount() const { return m_Blocks.size(); }6189// To be used only while the m_Mutex is locked. Used during defragmentation.6190NormalBlock* GetBlock(size_t index) const { return m_Blocks[index]; }6191D3D12MA_RW_MUTEX& GetMutex() { return m_Mutex; }61926193HRESULT CreateMinBlocks();6194bool IsEmpty();61956196HRESULT Allocate(6197UINT64 size,6198UINT64 alignment,6199const ALLOCATION_DESC& allocDesc,6200size_t allocationCount,6201Allocation** pAllocations);62026203void Free(Allocation* hAllocation);62046205HRESULT CreateResource(6206UINT64 size,6207UINT64 alignment,6208const ALLOCATION_DESC& allocDesc,6209const CREATE_RESOURCE_PARAMS& createParams,6210Allocation** ppAllocation,6211REFIID riidResource,6212void** ppvResource);62136214void AddStatistics(Statistics& inoutStats);6215void AddDetailedStatistics(DetailedStatistics& inoutStats);62166217void WriteBlockInfoToJson(JsonWriter& json);62186219private:6220AllocatorPimpl* const m_hAllocator;6221const D3D12_HEAP_PROPERTIES m_HeapProps;6222const D3D12_HEAP_FLAGS m_HeapFlags;6223const UINT64 m_PreferredBlockSize;6224const size_t m_MinBlockCount;6225const size_t m_MaxBlockCount;6226const bool m_ExplicitBlockSize;6227const UINT64 m_MinAllocationAlignment;6228const UINT32 m_Algorithm;6229const bool m_DenyMsaaTextures;6230ID3D12ProtectedResourceSession* const m_ProtectedSession;6231const D3D12_RESIDENCY_PRIORITY m_ResidencyPriority;6232/* There can be at most one allocation that is completely empty - a6233hysteresis to avoid pessimistic case of alternating creation and destruction6234of a ID3D12Heap. */6235bool m_HasEmptyBlock;6236D3D12MA_RW_MUTEX m_Mutex;6237// Incrementally sorted by sumFreeSize, ascending.6238Vector<NormalBlock*> m_Blocks;6239UINT m_NextBlockId;6240bool m_IncrementalSort = true;62416242// Disable incremental sorting when freeing allocations6243void SetIncrementalSort(bool val) { m_IncrementalSort = val; }62446245UINT64 CalcSumBlockSize() const;6246UINT64 CalcMaxBlockSize() const;62476248// Finds and removes given block from vector.6249void Remove(NormalBlock* pBlock);62506251// Performs single step in sorting m_Blocks. They may not be fully sorted6252// after this call.6253void IncrementallySortBlocks();6254void SortByFreeSize();62556256HRESULT AllocatePage(6257UINT64 size,6258UINT64 alignment,6259const ALLOCATION_DESC& allocDesc,6260Allocation** pAllocation);62616262HRESULT AllocateFromBlock(6263NormalBlock* pBlock,6264UINT64 size,6265UINT64 alignment,6266ALLOCATION_FLAGS allocFlags,6267void* pPrivateData,6268UINT32 strategy,6269Allocation** pAllocation);62706271HRESULT CommitAllocationRequest(6272AllocationRequest& allocRequest,6273NormalBlock* pBlock,6274UINT64 size,6275UINT64 alignment,6276void* pPrivateData,6277Allocation** pAllocation);62786279HRESULT CreateBlock(6280UINT64 blockSize,6281size_t* pNewBlockIndex);6282};6283#endif // _D3D12MA_BLOCK_VECTOR62846285#ifndef _D3D12MA_CURRENT_BUDGET_DATA6286class CurrentBudgetData6287{6288public:6289bool ShouldUpdateBudget() const { return m_OperationsSinceBudgetFetch >= 30; }62906291void GetStatistics(Statistics& outStats, UINT group) const;6292void GetBudget(bool useMutex,6293UINT64* outLocalUsage, UINT64* outLocalBudget,6294UINT64* outNonLocalUsage, UINT64* outNonLocalBudget);62956296#if D3D12MA_DXGI_1_46297HRESULT UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex);6298#endif62996300void AddAllocation(UINT group, UINT64 allocationBytes);6301void RemoveAllocation(UINT group, UINT64 allocationBytes);63026303void AddBlock(UINT group, UINT64 blockBytes);6304void RemoveBlock(UINT group, UINT64 blockBytes);63056306private:6307D3D12MA_ATOMIC_UINT32 m_BlockCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6308D3D12MA_ATOMIC_UINT32 m_AllocationCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6309D3D12MA_ATOMIC_UINT64 m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6310D3D12MA_ATOMIC_UINT64 m_AllocationBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};63116312D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch = {0};6313D3D12MA_RW_MUTEX m_BudgetMutex;6314UINT64 m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6315UINT64 m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6316UINT64 m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6317};63186319#ifndef _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS6320void CurrentBudgetData::GetStatistics(Statistics& outStats, UINT group) const6321{6322outStats.BlockCount = m_BlockCount[group];6323outStats.AllocationCount = m_AllocationCount[group];6324outStats.BlockBytes = m_BlockBytes[group];6325outStats.AllocationBytes = m_AllocationBytes[group];6326}63276328void CurrentBudgetData::GetBudget(bool useMutex,6329UINT64* outLocalUsage, UINT64* outLocalBudget,6330UINT64* outNonLocalUsage, UINT64* outNonLocalBudget)6331{6332MutexLockRead lockRead(m_BudgetMutex, useMutex);63336334if (outLocalUsage)6335{6336const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];6337const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];6338const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];6339*outLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ?6340D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0;6341}6342if (outLocalBudget)6343*outLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];63446345if (outNonLocalUsage)6346{6347const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];6348const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];6349const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];6350*outNonLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ?6351D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0;6352}6353if (outNonLocalBudget)6354*outNonLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];6355}63566357#if D3D12MA_DXGI_1_46358HRESULT CurrentBudgetData::UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex)6359{6360D3D12MA_ASSERT(adapter3);63616362DXGI_QUERY_VIDEO_MEMORY_INFO infoLocal = {};6363DXGI_QUERY_VIDEO_MEMORY_INFO infoNonLocal = {};6364const HRESULT hrLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &infoLocal);6365const HRESULT hrNonLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &infoNonLocal);63666367if (SUCCEEDED(hrLocal) || SUCCEEDED(hrNonLocal))6368{6369MutexLockWrite lockWrite(m_BudgetMutex, useMutex);63706371if (SUCCEEDED(hrLocal))6372{6373m_D3D12Usage[0] = infoLocal.CurrentUsage;6374m_D3D12Budget[0] = infoLocal.Budget;6375}6376if (SUCCEEDED(hrNonLocal))6377{6378m_D3D12Usage[1] = infoNonLocal.CurrentUsage;6379m_D3D12Budget[1] = infoNonLocal.Budget;6380}63816382m_BlockBytesAtD3D12Fetch[0] = m_BlockBytes[0];6383m_BlockBytesAtD3D12Fetch[1] = m_BlockBytes[1];6384m_OperationsSinceBudgetFetch = 0;6385}63866387return FAILED(hrLocal) ? hrLocal : hrNonLocal;6388}6389#endif // #if D3D12MA_DXGI_1_463906391void CurrentBudgetData::AddAllocation(UINT group, UINT64 allocationBytes)6392{6393++m_AllocationCount[group];6394m_AllocationBytes[group] += allocationBytes;6395++m_OperationsSinceBudgetFetch;6396}63976398void CurrentBudgetData::RemoveAllocation(UINT group, UINT64 allocationBytes)6399{6400D3D12MA_ASSERT(m_AllocationBytes[group] >= allocationBytes);6401D3D12MA_ASSERT(m_AllocationCount[group] > 0);6402m_AllocationBytes[group] -= allocationBytes;6403--m_AllocationCount[group];6404++m_OperationsSinceBudgetFetch;6405}64066407void CurrentBudgetData::AddBlock(UINT group, UINT64 blockBytes)6408{6409++m_BlockCount[group];6410m_BlockBytes[group] += blockBytes;6411++m_OperationsSinceBudgetFetch;6412}64136414void CurrentBudgetData::RemoveBlock(UINT group, UINT64 blockBytes)6415{6416D3D12MA_ASSERT(m_BlockBytes[group] >= blockBytes);6417D3D12MA_ASSERT(m_BlockCount[group] > 0);6418m_BlockBytes[group] -= blockBytes;6419--m_BlockCount[group];6420++m_OperationsSinceBudgetFetch;6421}6422#endif // _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS6423#endif // _D3D12MA_CURRENT_BUDGET_DATA64246425#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL6426class DefragmentationContextPimpl6427{6428D3D12MA_CLASS_NO_COPY(DefragmentationContextPimpl)6429public:6430DefragmentationContextPimpl(6431AllocatorPimpl* hAllocator,6432const DEFRAGMENTATION_DESC& desc,6433BlockVector* poolVector);6434~DefragmentationContextPimpl();64356436void GetStats(DEFRAGMENTATION_STATS& outStats) { outStats = m_GlobalStats; }6437const ALLOCATION_CALLBACKS& GetAllocs() const { return m_Moves.GetAllocs(); }64386439HRESULT DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo);6440HRESULT DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo);64416442private:6443// Max number of allocations to ignore due to size constraints before ending single pass6444static const UINT8 MAX_ALLOCS_TO_IGNORE = 16;6445enum class CounterStatus { Pass, Ignore, End };64466447struct FragmentedBlock6448{6449UINT32 data;6450NormalBlock* block;6451};6452struct StateBalanced6453{6454UINT64 avgFreeSize = 0;6455UINT64 avgAllocSize = UINT64_MAX;6456};6457struct MoveAllocationData6458{6459UINT64 size;6460UINT64 alignment;6461ALLOCATION_FLAGS flags;6462DEFRAGMENTATION_MOVE move = {};6463};64646465const UINT64 m_MaxPassBytes;6466const UINT32 m_MaxPassAllocations;64676468Vector<DEFRAGMENTATION_MOVE> m_Moves;64696470UINT8 m_IgnoredAllocs = 0;6471UINT32 m_Algorithm;6472UINT32 m_BlockVectorCount;6473BlockVector* m_PoolBlockVector;6474BlockVector** m_pBlockVectors;6475size_t m_ImmovableBlockCount = 0;6476DEFRAGMENTATION_STATS m_GlobalStats = { 0 };6477DEFRAGMENTATION_STATS m_PassStats = { 0 };6478void* m_AlgorithmState = NULL;64796480static MoveAllocationData GetMoveData(AllocHandle handle, BlockMetadata* metadata);6481CounterStatus CheckCounters(UINT64 bytes);6482bool IncrementCounters(UINT64 bytes);6483bool ReallocWithinBlock(BlockVector& vector, NormalBlock* block);6484bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector);64856486bool ComputeDefragmentation(BlockVector& vector, size_t index);6487bool ComputeDefragmentation_Fast(BlockVector& vector);6488bool ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update);6489bool ComputeDefragmentation_Full(BlockVector& vector);64906491void UpdateVectorStatistics(BlockVector& vector, StateBalanced& state);6492};6493#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL64946495#ifndef _D3D12MA_POOL_PIMPL6496class PoolPimpl6497{6498friend class Allocator;6499friend struct PoolListItemTraits;6500public:6501PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc);6502~PoolPimpl();65036504AllocatorPimpl* GetAllocator() const { return m_Allocator; }6505const POOL_DESC& GetDesc() const { return m_Desc; }6506bool SupportsCommittedAllocations() const { return m_Desc.BlockSize == 0; }6507LPCWSTR GetName() const { return m_Name; }65086509BlockVector* GetBlockVector() { return m_BlockVector; }6510CommittedAllocationList* GetCommittedAllocationList() { return SupportsCommittedAllocations() ? &m_CommittedAllocations : NULL; }65116512HRESULT Init();6513void GetStatistics(Statistics& outStats);6514void CalculateStatistics(DetailedStatistics& outStats);6515void AddDetailedStatistics(DetailedStatistics& inoutStats);6516void SetName(LPCWSTR Name);65176518private:6519AllocatorPimpl* m_Allocator; // Externally owned object.6520POOL_DESC m_Desc;6521BlockVector* m_BlockVector; // Owned object.6522CommittedAllocationList m_CommittedAllocations;6523wchar_t* m_Name;6524PoolPimpl* m_PrevPool = NULL;6525PoolPimpl* m_NextPool = NULL;65266527void FreeName();6528};65296530struct PoolListItemTraits6531{6532using ItemType = PoolPimpl;6533static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }6534static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }6535static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }6536static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }6537};6538#endif // _D3D12MA_POOL_PIMPL653965406541#ifndef _D3D12MA_ALLOCATOR_PIMPL6542class AllocatorPimpl6543{6544friend class Allocator;6545friend class Pool;6546public:6547std::atomic_uint32_t m_RefCount = {1};6548CurrentBudgetData m_Budget;65496550AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc);6551~AllocatorPimpl();65526553ID3D12Device* GetDevice() const { return m_Device; }6554#ifdef __ID3D12Device1_INTERFACE_DEFINED__6555ID3D12Device1* GetDevice1() const { return m_Device1; }6556#endif6557#ifdef __ID3D12Device4_INTERFACE_DEFINED__6558ID3D12Device4* GetDevice4() const { return m_Device4; }6559#endif6560#ifdef __ID3D12Device8_INTERFACE_DEFINED__6561ID3D12Device8* GetDevice8() const { return m_Device8; }6562#endif6563// Shortcut for "Allocation Callbacks", because this function is called so often.6564const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }6565const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const { return m_D3D12Options; }6566BOOL IsUMA() const { return m_D3D12Architecture.UMA; }6567BOOL IsCacheCoherentUMA() const { return m_D3D12Architecture.CacheCoherentUMA; }6568bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; }6569bool UseMutex() const { return m_UseMutex; }6570AllocationObjectAllocator& GetAllocationObjectAllocator() { return m_AllocationObjectAllocator; }6571UINT GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }6572/*6573If SupportsResourceHeapTier2():65740: D3D12_HEAP_TYPE_DEFAULT65751: D3D12_HEAP_TYPE_UPLOAD65762: D3D12_HEAP_TYPE_READBACK6577else:65780: D3D12_HEAP_TYPE_DEFAULT + buffer65791: D3D12_HEAP_TYPE_DEFAULT + texture65802: D3D12_HEAP_TYPE_DEFAULT + texture RT or DS65813: D3D12_HEAP_TYPE_UPLOAD + buffer65824: D3D12_HEAP_TYPE_UPLOAD + texture65835: D3D12_HEAP_TYPE_UPLOAD + texture RT or DS65846: D3D12_HEAP_TYPE_READBACK + buffer65857: D3D12_HEAP_TYPE_READBACK + texture65868: D3D12_HEAP_TYPE_READBACK + texture RT or DS6587*/6588UINT GetDefaultPoolCount() const { return SupportsResourceHeapTier2() ? 3 : 9; }6589BlockVector** GetDefaultPools() { return m_BlockVectors; }65906591HRESULT Init(const ALLOCATOR_DESC& desc);6592bool HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const;6593UINT StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const;6594UINT HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const;6595UINT64 GetMemoryCapacity(UINT memorySegmentGroup) const;65966597HRESULT CreatePlacedResourceWrap(6598ID3D12Heap *pHeap,6599UINT64 HeapOffset,6600const CREATE_RESOURCE_PARAMS& createParams,6601REFIID riidResource,6602void** ppvResource);66036604HRESULT CreateResource(6605const ALLOCATION_DESC* pAllocDesc,6606const CREATE_RESOURCE_PARAMS& createParams,6607Allocation** ppAllocation,6608REFIID riidResource,6609void** ppvResource);66106611HRESULT CreateAliasingResource(6612Allocation* pAllocation,6613UINT64 AllocationLocalOffset,6614const CREATE_RESOURCE_PARAMS& createParams,6615REFIID riidResource,6616void** ppvResource);66176618HRESULT AllocateMemory(6619const ALLOCATION_DESC* pAllocDesc,6620const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,6621Allocation** ppAllocation);66226623// Unregisters allocation from the collection of dedicated allocations.6624// Allocation object must be deleted externally afterwards.6625void FreeCommittedMemory(Allocation* allocation);6626// Unregisters allocation from the collection of placed allocations.6627// Allocation object must be deleted externally afterwards.6628void FreePlacedMemory(Allocation* allocation);6629// Unregisters allocation from the collection of dedicated allocations and destroys associated heap.6630// Allocation object must be deleted externally afterwards.6631void FreeHeapMemory(Allocation* allocation);66326633void SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const;66346635void SetCurrentFrameIndex(UINT frameIndex);6636// For more deailed stats use outCustomHeaps to access statistics divided into L0 and L1 group6637void CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCustomHeaps[2] = NULL);66386639void GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget);6640void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType);66416642void BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap);6643void FreeStatsString(WCHAR* pStatsString);66446645private:6646using PoolList = IntrusiveLinkedList<PoolListItemTraits>;66476648const bool m_UseMutex;6649const bool m_AlwaysCommitted;6650const bool m_MsaaAlwaysCommitted;6651bool m_DefaultPoolsNotZeroed = false;6652ID3D12Device* m_Device; // AddRef6653#ifdef __ID3D12Device1_INTERFACE_DEFINED__6654ID3D12Device1* m_Device1 = NULL; // AddRef, optional6655#endif6656#ifdef __ID3D12Device4_INTERFACE_DEFINED__6657ID3D12Device4* m_Device4 = NULL; // AddRef, optional6658#endif6659#ifdef __ID3D12Device8_INTERFACE_DEFINED__6660ID3D12Device8* m_Device8 = NULL; // AddRef, optional6661#endif6662#ifdef __ID3D12Device10_INTERFACE_DEFINED__6663ID3D12Device10* m_Device10 = NULL; // AddRef, optional6664#endif6665IDXGIAdapter* m_Adapter; // AddRef6666#if D3D12MA_DXGI_1_46667IDXGIAdapter3* m_Adapter3 = NULL; // AddRef, optional6668#endif6669UINT64 m_PreferredBlockSize;6670ALLOCATION_CALLBACKS m_AllocationCallbacks;6671D3D12MA_ATOMIC_UINT32 m_CurrentFrameIndex;6672DXGI_ADAPTER_DESC m_AdapterDesc;6673D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options;6674D3D12_FEATURE_DATA_ARCHITECTURE m_D3D12Architecture;6675AllocationObjectAllocator m_AllocationObjectAllocator;66766677D3D12MA_RW_MUTEX m_PoolsMutex[HEAP_TYPE_COUNT];6678PoolList m_Pools[HEAP_TYPE_COUNT];6679// Default pools.6680BlockVector* m_BlockVectors[DEFAULT_POOL_MAX_COUNT];6681CommittedAllocationList m_CommittedAllocations[STANDARD_HEAP_TYPE_COUNT];66826683/*6684Heuristics that decides whether a resource should better be placed in its own,6685dedicated allocation (committed resource rather than placed resource).6686*/6687template<typename D3D12_RESOURCE_DESC_T>6688static bool PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc);66896690// Allocates and registers new committed resource with implicit heap, as dedicated allocation.6691// Creates and returns Allocation object and optionally D3D12 resource.6692HRESULT AllocateCommittedResource(6693const CommittedAllocationParameters& committedAllocParams,6694UINT64 resourceSize, bool withinBudget, void* pPrivateData,6695const CREATE_RESOURCE_PARAMS& createParams,6696Allocation** ppAllocation, REFIID riidResource, void** ppvResource);66976698// Allocates and registers new heap without any resources placed in it, as dedicated allocation.6699// Creates and returns Allocation object.6700HRESULT AllocateHeap(6701const CommittedAllocationParameters& committedAllocParams,6702const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget,6703void* pPrivateData, Allocation** ppAllocation);67046705template<typename D3D12_RESOURCE_DESC_T>6706HRESULT CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,6707const D3D12_RESOURCE_DESC_T* resDesc, // Optional6708BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted);67096710// Returns UINT32_MAX if index cannot be calculcated.6711UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const;6712void CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const;67136714// Registers Pool object in m_Pools.6715void RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);6716// Unregisters Pool object from m_Pools.6717void UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);67186719HRESULT UpdateD3D12Budget();67206721D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const;6722#ifdef __ID3D12Device8_INTERFACE_DEFINED__6723D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const;6724#endif67256726template<typename D3D12_RESOURCE_DESC_T>6727D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const;67286729bool NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size);67306731// Writes object { } with data of given budget.6732static void WriteBudgetToJson(JsonWriter& json, const Budget& budget);6733};67346735#ifndef _D3D12MA_ALLOCATOR_PIMPL_FUNCTINOS6736AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc)6737: m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0),6738m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0),6739m_MsaaAlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0),6740m_Device(desc.pDevice),6741m_Adapter(desc.pAdapter),6742m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE),6743m_AllocationCallbacks(allocationCallbacks),6744m_CurrentFrameIndex(0),6745// Below this line don't use allocationCallbacks but m_AllocationCallbacks!!!6746m_AllocationObjectAllocator(m_AllocationCallbacks)6747{6748// desc.pAllocationCallbacks intentionally ignored here, preprocessed by CreateAllocator.6749ZeroMemory(&m_D3D12Options, sizeof(m_D3D12Options));6750ZeroMemory(&m_D3D12Architecture, sizeof(m_D3D12Architecture));67516752ZeroMemory(m_BlockVectors, sizeof(m_BlockVectors));67536754for (UINT i = 0; i < STANDARD_HEAP_TYPE_COUNT; ++i)6755{6756m_CommittedAllocations[i].Init(6757m_UseMutex,6758IndexToStandardHeapType(i),6759NULL); // pool6760}67616762m_Device->AddRef();6763m_Adapter->AddRef();6764}67656766HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc)6767{6768#if D3D12MA_DXGI_1_46769desc.pAdapter->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Adapter3));6770#endif67716772#ifdef __ID3D12Device1_INTERFACE_DEFINED__6773m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device1));6774#endif67756776#ifdef __ID3D12Device4_INTERFACE_DEFINED__6777m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device4));6778#endif67796780#ifdef __ID3D12Device8_INTERFACE_DEFINED__6781m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device8));67826783if((desc.Flags & ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED) != 0)6784{6785D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = {};6786if(SUCCEEDED(m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, sizeof(options7))))6787{6788// DEFAULT_POOLS_NOT_ZEROED both supported and enabled by the user.6789m_DefaultPoolsNotZeroed = true;6790}6791}6792#endif67936794#ifdef __ID3D12Device10_INTERFACE_DEFINED__6795m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device10));6796#endif67976798HRESULT hr = m_Adapter->GetDesc(&m_AdapterDesc);6799if (FAILED(hr))6800{6801return hr;6802}68036804hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_D3D12Options, sizeof(m_D3D12Options));6805if (FAILED(hr))6806{6807return hr;6808}6809#ifdef D3D12MA_FORCE_RESOURCE_HEAP_TIER6810m_D3D12Options.ResourceHeapTier = (D3D12MA_FORCE_RESOURCE_HEAP_TIER);6811#endif68126813hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &m_D3D12Architecture, sizeof(m_D3D12Architecture));6814if (FAILED(hr))6815{6816m_D3D12Architecture.UMA = FALSE;6817m_D3D12Architecture.CacheCoherentUMA = FALSE;6818}68196820D3D12_HEAP_PROPERTIES heapProps = {};6821const UINT defaultPoolCount = GetDefaultPoolCount();6822for (UINT i = 0; i < defaultPoolCount; ++i)6823{6824D3D12_HEAP_FLAGS heapFlags;6825CalcDefaultPoolParams(heapProps.Type, heapFlags, i);68266827#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE6828if(m_DefaultPoolsNotZeroed)6829{6830heapFlags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;6831}6832#endif68336834m_BlockVectors[i] = D3D12MA_NEW(GetAllocs(), BlockVector)(6835this, // hAllocator6836heapProps, // heapType6837heapFlags, // heapFlags6838m_PreferredBlockSize,68390, // minBlockCount6840SIZE_MAX, // maxBlockCount6841false, // explicitBlockSize6842D3D12MA_DEBUG_ALIGNMENT, // minAllocationAlignment68430, // Default algorithm,6844m_MsaaAlwaysCommitted,6845NULL, // pProtectedSession6846D3D12_RESIDENCY_PRIORITY_NONE); // residencyPriority6847// No need to call m_pBlockVectors[i]->CreateMinBlocks here, becase minBlockCount is 0.6848}68496850#if D3D12MA_DXGI_1_46851UpdateD3D12Budget();6852#endif68536854return S_OK;6855}68566857AllocatorPimpl::~AllocatorPimpl()6858{6859#ifdef __ID3D12Device10_INTERFACE_DEFINED__6860SAFE_RELEASE(m_Device10);6861#endif6862#ifdef __ID3D12Device8_INTERFACE_DEFINED__6863SAFE_RELEASE(m_Device8);6864#endif6865#ifdef __ID3D12Device4_INTERFACE_DEFINED__6866SAFE_RELEASE(m_Device4);6867#endif6868#ifdef __ID3D12Device1_INTERFACE_DEFINED__6869SAFE_RELEASE(m_Device1);6870#endif6871#if D3D12MA_DXGI_1_46872SAFE_RELEASE(m_Adapter3);6873#endif6874SAFE_RELEASE(m_Adapter);6875SAFE_RELEASE(m_Device);68766877for (UINT i = DEFAULT_POOL_MAX_COUNT; i--; )6878{6879D3D12MA_DELETE(GetAllocs(), m_BlockVectors[i]);6880}68816882for (UINT i = HEAP_TYPE_COUNT; i--; )6883{6884if (!m_Pools[i].IsEmpty())6885{6886D3D12MA_ASSERT(0 && "Unfreed pools found!");6887}6888}6889}68906891bool AllocatorPimpl::HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const6892{6893if (SupportsResourceHeapTier2())6894{6895return true;6896}6897else6898{6899const bool allowBuffers = (flags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;6900const bool allowRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;6901const bool allowNonRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;6902const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);6903return allowedGroupCount == 1;6904}6905}69066907UINT AllocatorPimpl::StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const6908{6909D3D12MA_ASSERT(IsHeapTypeStandard(heapType));6910if (IsUMA())6911return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY;6912return heapType == D3D12_HEAP_TYPE_DEFAULT ?6913DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY;6914}69156916UINT AllocatorPimpl::HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const6917{6918if (IsUMA())6919return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY;6920if (heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_UNKNOWN)6921return StandardHeapTypeToMemorySegmentGroup(heapProps.Type);6922return heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_L1 ?6923DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY;6924}69256926UINT64 AllocatorPimpl::GetMemoryCapacity(UINT memorySegmentGroup) const6927{6928switch (memorySegmentGroup)6929{6930case DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY:6931return IsUMA() ?6932m_AdapterDesc.DedicatedVideoMemory + m_AdapterDesc.SharedSystemMemory : m_AdapterDesc.DedicatedVideoMemory;6933case DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY:6934return IsUMA() ? 0 : m_AdapterDesc.SharedSystemMemory;6935default:6936D3D12MA_ASSERT(0);6937return UINT64_MAX;6938}6939}69406941HRESULT AllocatorPimpl::CreatePlacedResourceWrap(6942ID3D12Heap *pHeap,6943UINT64 HeapOffset,6944const CREATE_RESOURCE_PARAMS& createParams,6945REFIID riidResource,6946void** ppvResource)6947{6948#ifdef __ID3D12Device10_INTERFACE_DEFINED__6949if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)6950{6951if (!m_Device10)6952{6953return E_NOINTERFACE;6954}6955return m_Device10->CreatePlacedResource2(pHeap, HeapOffset,6956createParams.GetResourceDesc1(), createParams.GetInitialLayout(),6957createParams.GetOptimizedClearValue(), createParams.GetNumCastableFormats(),6958createParams.GetCastableFormats(), riidResource, ppvResource);6959} else6960#endif6961#ifdef __ID3D12Device8_INTERFACE_DEFINED__6962if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)6963{6964if (!m_Device8)6965{6966return E_NOINTERFACE;6967}6968return m_Device8->CreatePlacedResource1(pHeap, HeapOffset,6969createParams.GetResourceDesc1(), createParams.GetInitialResourceState(),6970createParams.GetOptimizedClearValue(), riidResource, ppvResource);6971} else6972#endif6973if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)6974{6975return m_Device->CreatePlacedResource(pHeap, HeapOffset,6976createParams.GetResourceDesc(), createParams.GetInitialResourceState(),6977createParams.GetOptimizedClearValue(), riidResource, ppvResource);6978}6979else6980{6981D3D12MA_ASSERT(0);6982return E_INVALIDARG;6983}6984}698569866987HRESULT AllocatorPimpl::CreateResource(6988const ALLOCATION_DESC* pAllocDesc,6989const CREATE_RESOURCE_PARAMS& createParams,6990Allocation** ppAllocation,6991REFIID riidResource,6992void** ppvResource)6993{6994D3D12MA_ASSERT(pAllocDesc && createParams.GetBaseResourceDesc() && ppAllocation);69956996*ppAllocation = NULL;6997if (ppvResource)6998{6999*ppvResource = NULL;7000}70017002CREATE_RESOURCE_PARAMS finalCreateParams = createParams;7003D3D12_RESOURCE_DESC finalResourceDesc;7004#ifdef __ID3D12Device8_INTERFACE_DEFINED__7005D3D12_RESOURCE_DESC1 finalResourceDesc1;7006#endif7007D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo;7008if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)7009{7010finalResourceDesc = *createParams.GetResourceDesc();7011finalCreateParams.AccessResourceDesc() = &finalResourceDesc;7012resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);7013}7014#ifdef __ID3D12Device8_INTERFACE_DEFINED__7015else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7016{7017if (!m_Device8)7018{7019return E_NOINTERFACE;7020}7021finalResourceDesc1 = *createParams.GetResourceDesc1();7022finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;7023resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);7024}7025#endif7026#ifdef __ID3D12Device10_INTERFACE_DEFINED__7027else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)7028{7029if (!m_Device10)7030{7031return E_NOINTERFACE;7032}7033finalResourceDesc1 = *createParams.GetResourceDesc1();7034finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;7035resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);7036}7037#endif7038else7039{7040D3D12MA_ASSERT(0);7041return E_INVALIDARG;7042}7043D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));7044D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);70457046BlockVector* blockVector = NULL;7047CommittedAllocationParameters committedAllocationParams = {};7048bool preferCommitted = false;70497050HRESULT hr;7051#ifdef __ID3D12Device8_INTERFACE_DEFINED__7052if (createParams.Variant >= CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7053{7054hr = CalcAllocationParams<D3D12_RESOURCE_DESC1>(*pAllocDesc, resAllocInfo.SizeInBytes,7055createParams.GetResourceDesc1(),7056blockVector, committedAllocationParams, preferCommitted);7057}7058else7059#endif7060{7061hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, resAllocInfo.SizeInBytes,7062createParams.GetResourceDesc(),7063blockVector, committedAllocationParams, preferCommitted);7064}7065if (FAILED(hr))7066return hr;70677068const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0;7069hr = E_INVALIDARG;7070if (committedAllocationParams.IsValid() && preferCommitted)7071{7072hr = AllocateCommittedResource(committedAllocationParams,7073resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData,7074finalCreateParams, ppAllocation, riidResource, ppvResource);7075if (SUCCEEDED(hr))7076return hr;7077}7078if (blockVector != NULL)7079{7080hr = blockVector->CreateResource(resAllocInfo.SizeInBytes, resAllocInfo.Alignment,7081*pAllocDesc, finalCreateParams,7082ppAllocation, riidResource, ppvResource);7083if (SUCCEEDED(hr))7084return hr;7085}7086if (committedAllocationParams.IsValid() && !preferCommitted)7087{7088hr = AllocateCommittedResource(committedAllocationParams,7089resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData,7090finalCreateParams, ppAllocation, riidResource, ppvResource);7091if (SUCCEEDED(hr))7092return hr;7093}7094return hr;7095}70967097HRESULT AllocatorPimpl::AllocateMemory(7098const ALLOCATION_DESC* pAllocDesc,7099const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,7100Allocation** ppAllocation)7101{7102*ppAllocation = NULL;71037104BlockVector* blockVector = NULL;7105CommittedAllocationParameters committedAllocationParams = {};7106bool preferCommitted = false;7107HRESULT hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, pAllocInfo->SizeInBytes,7108NULL, // pResDesc7109blockVector, committedAllocationParams, preferCommitted);7110if (FAILED(hr))7111return hr;71127113const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0;7114hr = E_INVALIDARG;7115if (committedAllocationParams.IsValid() && preferCommitted)7116{7117hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation);7118if (SUCCEEDED(hr))7119return hr;7120}7121if (blockVector != NULL)7122{7123hr = blockVector->Allocate(pAllocInfo->SizeInBytes, pAllocInfo->Alignment,7124*pAllocDesc, 1, (Allocation**)ppAllocation);7125if (SUCCEEDED(hr))7126return hr;7127}7128if (committedAllocationParams.IsValid() && !preferCommitted)7129{7130hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation);7131if (SUCCEEDED(hr))7132return hr;7133}7134return hr;7135}71367137HRESULT AllocatorPimpl::CreateAliasingResource(7138Allocation* pAllocation,7139UINT64 AllocationLocalOffset,7140const CREATE_RESOURCE_PARAMS& createParams,7141REFIID riidResource,7142void** ppvResource)7143{7144*ppvResource = NULL;71457146CREATE_RESOURCE_PARAMS finalCreateParams = createParams;7147D3D12_RESOURCE_DESC finalResourceDesc;7148#ifdef __ID3D12Device8_INTERFACE_DEFINED__7149D3D12_RESOURCE_DESC1 finalResourceDesc1;7150#endif7151D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo;7152if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)7153{7154finalResourceDesc = *createParams.GetResourceDesc();7155finalCreateParams.AccessResourceDesc() = &finalResourceDesc;7156resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);7157}7158#ifdef __ID3D12Device8_INTERFACE_DEFINED__7159else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7160{7161if (!m_Device8)7162{7163return E_NOINTERFACE;7164}7165finalResourceDesc1 = *createParams.GetResourceDesc1();7166finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;7167resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);7168}7169#endif7170#ifdef __ID3D12Device10_INTERFACE_DEFINED__7171else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)7172{7173if (!m_Device10)7174{7175return E_NOINTERFACE;7176}7177finalResourceDesc1 = *createParams.GetResourceDesc1();7178finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;7179resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);7180}7181#endif7182else7183{7184D3D12MA_ASSERT(0);7185return E_INVALIDARG;7186}7187D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));7188D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);71897190ID3D12Heap* const existingHeap = pAllocation->GetHeap();7191const UINT64 existingOffset = pAllocation->GetOffset();7192const UINT64 existingSize = pAllocation->GetSize();7193const UINT64 newOffset = existingOffset + AllocationLocalOffset;71947195if (existingHeap == NULL ||7196AllocationLocalOffset + resAllocInfo.SizeInBytes > existingSize ||7197newOffset % resAllocInfo.Alignment != 0)7198{7199return E_INVALIDARG;7200}72017202return CreatePlacedResourceWrap(existingHeap, newOffset, finalCreateParams, riidResource, ppvResource);7203}72047205void AllocatorPimpl::FreeCommittedMemory(Allocation* allocation)7206{7207D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_COMMITTED);72087209CommittedAllocationList* const allocList = allocation->m_Committed.list;7210allocList->Unregister(allocation);72117212const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this);7213const UINT64 allocSize = allocation->GetSize();7214m_Budget.RemoveAllocation(memSegmentGroup, allocSize);7215m_Budget.RemoveBlock(memSegmentGroup, allocSize);7216}72177218void AllocatorPimpl::FreePlacedMemory(Allocation* allocation)7219{7220D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_PLACED);72217222NormalBlock* const block = allocation->m_Placed.block;7223D3D12MA_ASSERT(block);7224BlockVector* const blockVector = block->GetBlockVector();7225D3D12MA_ASSERT(blockVector);7226m_Budget.RemoveAllocation(HeapPropertiesToMemorySegmentGroup(block->GetHeapProperties()), allocation->GetSize());7227blockVector->Free(allocation);7228}72297230void AllocatorPimpl::FreeHeapMemory(Allocation* allocation)7231{7232D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_HEAP);72337234CommittedAllocationList* const allocList = allocation->m_Committed.list;7235allocList->Unregister(allocation);7236SAFE_RELEASE(allocation->m_Heap.heap);72377238const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this);7239const UINT64 allocSize = allocation->GetSize();7240m_Budget.RemoveAllocation(memSegmentGroup, allocSize);7241m_Budget.RemoveBlock(memSegmentGroup, allocSize);7242}72437244void AllocatorPimpl::SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const7245{7246#ifdef __ID3D12Device1_INTERFACE_DEFINED__7247if (priority != D3D12_RESIDENCY_PRIORITY_NONE && m_Device1)7248{7249// Intentionally ignoring the result.7250m_Device1->SetResidencyPriority(1, &obj, &priority);7251}7252#endif7253}72547255void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex)7256{7257m_CurrentFrameIndex.store(frameIndex);72587259#if D3D12MA_DXGI_1_47260UpdateD3D12Budget();7261#endif7262}72637264void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCustomHeaps[2])7265{7266// Init stats7267for (size_t i = 0; i < HEAP_TYPE_COUNT; i++)7268ClearDetailedStatistics(outStats.HeapType[i]);7269for (size_t i = 0; i < DXGI_MEMORY_SEGMENT_GROUP_COUNT; i++)7270ClearDetailedStatistics(outStats.MemorySegmentGroup[i]);7271ClearDetailedStatistics(outStats.Total);7272if (outCustomHeaps)7273{7274ClearDetailedStatistics(outCustomHeaps[0]);7275ClearDetailedStatistics(outCustomHeaps[1]);7276}72777278// Process default pools. 3 standard heap types only. Add them to outStats.HeapType[i].7279if (SupportsResourceHeapTier2())7280{7281// DEFAULT, UPLOAD, READBACK.7282for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)7283{7284BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex];7285D3D12MA_ASSERT(pBlockVector);7286pBlockVector->AddDetailedStatistics(outStats.HeapType[heapTypeIndex]);7287}7288}7289else7290{7291// DEFAULT, UPLOAD, READBACK.7292for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)7293{7294for (size_t heapSubType = 0; heapSubType < 3; ++heapSubType)7295{7296BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex * 3 + heapSubType];7297D3D12MA_ASSERT(pBlockVector);7298pBlockVector->AddDetailedStatistics(outStats.HeapType[heapTypeIndex]);7299}7300}7301}73027303// Sum them up to memory segment groups.7304AddDetailedStatistics(7305outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_DEFAULT)],7306outStats.HeapType[0]);7307AddDetailedStatistics(7308outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_UPLOAD)],7309outStats.HeapType[1]);7310AddDetailedStatistics(7311outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_READBACK)],7312outStats.HeapType[2]);73137314// Process custom pools.7315DetailedStatistics tmpStats;7316for (size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)7317{7318MutexLockRead lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);7319PoolList& poolList = m_Pools[heapTypeIndex];7320for (PoolPimpl* pool = poolList.Front(); pool != NULL; pool = poolList.GetNext(pool))7321{7322const D3D12_HEAP_PROPERTIES& poolHeapProps = pool->GetDesc().HeapProperties;7323ClearDetailedStatistics(tmpStats);7324pool->AddDetailedStatistics(tmpStats);7325AddDetailedStatistics(7326outStats.HeapType[heapTypeIndex], tmpStats);73277328UINT memorySegment = HeapPropertiesToMemorySegmentGroup(poolHeapProps);7329AddDetailedStatistics(7330outStats.MemorySegmentGroup[memorySegment], tmpStats);73317332if (outCustomHeaps)7333AddDetailedStatistics(outCustomHeaps[memorySegment], tmpStats);7334}7335}73367337// Process committed allocations. 3 standard heap types only.7338for (UINT heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)7339{7340ClearDetailedStatistics(tmpStats);7341m_CommittedAllocations[heapTypeIndex].AddDetailedStatistics(tmpStats);7342AddDetailedStatistics(7343outStats.HeapType[heapTypeIndex], tmpStats);7344AddDetailedStatistics(7345outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(IndexToStandardHeapType(heapTypeIndex))], tmpStats);7346}73477348// Sum up memory segment groups to totals.7349AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[0]);7350AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[1]);73517352D3D12MA_ASSERT(outStats.Total.Stats.BlockCount ==7353outStats.MemorySegmentGroup[0].Stats.BlockCount + outStats.MemorySegmentGroup[1].Stats.BlockCount);7354D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount ==7355outStats.MemorySegmentGroup[0].Stats.AllocationCount + outStats.MemorySegmentGroup[1].Stats.AllocationCount);7356D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes ==7357outStats.MemorySegmentGroup[0].Stats.BlockBytes + outStats.MemorySegmentGroup[1].Stats.BlockBytes);7358D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes ==7359outStats.MemorySegmentGroup[0].Stats.AllocationBytes + outStats.MemorySegmentGroup[1].Stats.AllocationBytes);7360D3D12MA_ASSERT(outStats.Total.UnusedRangeCount ==7361outStats.MemorySegmentGroup[0].UnusedRangeCount + outStats.MemorySegmentGroup[1].UnusedRangeCount);73627363D3D12MA_ASSERT(outStats.Total.Stats.BlockCount ==7364outStats.HeapType[0].Stats.BlockCount + outStats.HeapType[1].Stats.BlockCount +7365outStats.HeapType[2].Stats.BlockCount + outStats.HeapType[3].Stats.BlockCount);7366D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount ==7367outStats.HeapType[0].Stats.AllocationCount + outStats.HeapType[1].Stats.AllocationCount +7368outStats.HeapType[2].Stats.AllocationCount + outStats.HeapType[3].Stats.AllocationCount);7369D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes ==7370outStats.HeapType[0].Stats.BlockBytes + outStats.HeapType[1].Stats.BlockBytes +7371outStats.HeapType[2].Stats.BlockBytes + outStats.HeapType[3].Stats.BlockBytes);7372D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes ==7373outStats.HeapType[0].Stats.AllocationBytes + outStats.HeapType[1].Stats.AllocationBytes +7374outStats.HeapType[2].Stats.AllocationBytes + outStats.HeapType[3].Stats.AllocationBytes);7375D3D12MA_ASSERT(outStats.Total.UnusedRangeCount ==7376outStats.HeapType[0].UnusedRangeCount + outStats.HeapType[1].UnusedRangeCount +7377outStats.HeapType[2].UnusedRangeCount + outStats.HeapType[3].UnusedRangeCount);7378}73797380void AllocatorPimpl::GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget)7381{7382if (outLocalBudget)7383m_Budget.GetStatistics(outLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY);7384if (outNonLocalBudget)7385m_Budget.GetStatistics(outNonLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY);73867387#if D3D12MA_DXGI_1_47388if (m_Adapter3)7389{7390if (!m_Budget.ShouldUpdateBudget())7391{7392m_Budget.GetBudget(m_UseMutex,7393outLocalBudget ? &outLocalBudget->UsageBytes : NULL,7394outLocalBudget ? &outLocalBudget->BudgetBytes : NULL,7395outNonLocalBudget ? &outNonLocalBudget->UsageBytes : NULL,7396outNonLocalBudget ? &outNonLocalBudget->BudgetBytes : NULL);7397}7398else7399{7400UpdateD3D12Budget();7401GetBudget(outLocalBudget, outNonLocalBudget); // Recursion7402}7403}7404else7405#endif7406{7407if (outLocalBudget)7408{7409outLocalBudget->UsageBytes = outLocalBudget->Stats.BlockBytes;7410outLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY) * 8 / 10; // 80% heuristics.7411}7412if (outNonLocalBudget)7413{7414outNonLocalBudget->UsageBytes = outNonLocalBudget->Stats.BlockBytes;7415outNonLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY) * 8 / 10; // 80% heuristics.7416}7417}7418}74197420void AllocatorPimpl::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType)7421{7422switch (heapType)7423{7424case D3D12_HEAP_TYPE_DEFAULT:7425GetBudget(&outBudget, NULL);7426break;7427case D3D12_HEAP_TYPE_UPLOAD:7428case D3D12_HEAP_TYPE_READBACK:7429GetBudget(NULL, &outBudget);7430break;7431default: D3D12MA_ASSERT(0);7432}7433}74347435void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)7436{7437StringBuilder sb(GetAllocs());7438{7439Budget localBudget = {}, nonLocalBudget = {};7440GetBudget(&localBudget, &nonLocalBudget);74417442TotalStatistics stats;7443DetailedStatistics customHeaps[2];7444CalculateStatistics(stats, customHeaps);74457446JsonWriter json(GetAllocs(), sb);7447json.BeginObject();7448{7449json.WriteString(L"General");7450json.BeginObject();7451{7452json.WriteString(L"API");7453json.WriteString(L"Direct3D 12");74547455json.WriteString(L"GPU");7456json.WriteString(m_AdapterDesc.Description);74577458json.WriteString(L"DedicatedVideoMemory");7459json.WriteNumber((UINT64)m_AdapterDesc.DedicatedVideoMemory);7460json.WriteString(L"DedicatedSystemMemory");7461json.WriteNumber((UINT64)m_AdapterDesc.DedicatedSystemMemory);7462json.WriteString(L"SharedSystemMemory");7463json.WriteNumber((UINT64)m_AdapterDesc.SharedSystemMemory);74647465json.WriteString(L"ResourceHeapTier");7466json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceHeapTier));74677468json.WriteString(L"ResourceBindingTier");7469json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceBindingTier));74707471json.WriteString(L"TiledResourcesTier");7472json.WriteNumber(static_cast<UINT>(m_D3D12Options.TiledResourcesTier));74737474json.WriteString(L"TileBasedRenderer");7475json.WriteBool(m_D3D12Architecture.TileBasedRenderer);74767477json.WriteString(L"UMA");7478json.WriteBool(m_D3D12Architecture.UMA);7479json.WriteString(L"CacheCoherentUMA");7480json.WriteBool(m_D3D12Architecture.CacheCoherentUMA);7481}7482json.EndObject();7483}7484{7485json.WriteString(L"Total");7486json.AddDetailedStatisticsInfoObject(stats.Total);7487}7488{7489json.WriteString(L"MemoryInfo");7490json.BeginObject();7491{7492json.WriteString(L"L0");7493json.BeginObject();7494{7495json.WriteString(L"Budget");7496WriteBudgetToJson(json, IsUMA() ? localBudget : nonLocalBudget); // When UMA device only L0 present as local74977498json.WriteString(L"Stats");7499json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[!IsUMA()]);75007501json.WriteString(L"MemoryPools");7502json.BeginObject();7503{7504if (IsUMA())7505{7506json.WriteString(L"DEFAULT");7507json.BeginObject();7508{7509json.WriteString(L"Stats");7510json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);7511}7512json.EndObject();7513}7514json.WriteString(L"UPLOAD");7515json.BeginObject();7516{7517json.WriteString(L"Stats");7518json.AddDetailedStatisticsInfoObject(stats.HeapType[1]);7519}7520json.EndObject();75217522json.WriteString(L"READBACK");7523json.BeginObject();7524{7525json.WriteString(L"Stats");7526json.AddDetailedStatisticsInfoObject(stats.HeapType[2]);7527}7528json.EndObject();75297530json.WriteString(L"CUSTOM");7531json.BeginObject();7532{7533json.WriteString(L"Stats");7534json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]);7535}7536json.EndObject();7537}7538json.EndObject();7539}7540json.EndObject();7541if (!IsUMA())7542{7543json.WriteString(L"L1");7544json.BeginObject();7545{7546json.WriteString(L"Budget");7547WriteBudgetToJson(json, localBudget);75487549json.WriteString(L"Stats");7550json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[0]);75517552json.WriteString(L"MemoryPools");7553json.BeginObject();7554{7555json.WriteString(L"DEFAULT");7556json.BeginObject();7557{7558json.WriteString(L"Stats");7559json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);7560}7561json.EndObject();75627563json.WriteString(L"CUSTOM");7564json.BeginObject();7565{7566json.WriteString(L"Stats");7567json.AddDetailedStatisticsInfoObject(customHeaps[0]);7568}7569json.EndObject();7570}7571json.EndObject();7572}7573json.EndObject();7574}7575}7576json.EndObject();7577}75787579if (detailedMap)7580{7581const auto writeHeapInfo = [&](BlockVector* blockVector, CommittedAllocationList* committedAllocs, bool customHeap)7582{7583D3D12MA_ASSERT(blockVector);75847585D3D12_HEAP_FLAGS flags = blockVector->GetHeapFlags();7586json.WriteString(L"Flags");7587json.BeginArray(true);7588{7589if (flags & D3D12_HEAP_FLAG_SHARED)7590json.WriteString(L"HEAP_FLAG_SHARED");7591if (flags & D3D12_HEAP_FLAG_ALLOW_DISPLAY)7592json.WriteString(L"HEAP_FLAG_ALLOW_DISPLAY");7593if (flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER)7594json.WriteString(L"HEAP_FLAG_CROSS_ADAPTER");7595if (flags & D3D12_HEAP_FLAG_HARDWARE_PROTECTED)7596json.WriteString(L"HEAP_FLAG_HARDWARE_PROTECTED");7597if (flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH)7598json.WriteString(L"HEAP_FLAG_ALLOW_WRITE_WATCH");7599if (flags & D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS)7600json.WriteString(L"HEAP_FLAG_ALLOW_SHADER_ATOMICS");7601#ifdef __ID3D12Device8_INTERFACE_DEFINED__7602if (flags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT)7603json.WriteString(L"HEAP_FLAG_CREATE_NOT_RESIDENT");7604if (flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED)7605json.WriteString(L"HEAP_FLAG_CREATE_NOT_ZEROED");7606#endif76077608if (flags & D3D12_HEAP_FLAG_DENY_BUFFERS)7609json.WriteString(L"HEAP_FLAG_DENY_BUFFERS");7610if (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES)7611json.WriteString(L"HEAP_FLAG_DENY_RT_DS_TEXTURES");7612if (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES)7613json.WriteString(L"HEAP_FLAG_DENY_NON_RT_DS_TEXTURES");76147615flags &= ~(D3D12_HEAP_FLAG_SHARED7616| D3D12_HEAP_FLAG_DENY_BUFFERS7617| D3D12_HEAP_FLAG_ALLOW_DISPLAY7618| D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER7619| D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES7620| D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES7621| D3D12_HEAP_FLAG_HARDWARE_PROTECTED7622| D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH7623| D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS);7624#ifdef __ID3D12Device8_INTERFACE_DEFINED__7625flags &= ~(D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT7626| D3D12_HEAP_FLAG_CREATE_NOT_ZEROED);7627#endif7628if (flags != 0)7629json.WriteNumber((UINT)flags);76307631if (customHeap)7632{7633const D3D12_HEAP_PROPERTIES& properties = blockVector->GetHeapProperties();7634switch (properties.MemoryPoolPreference)7635{7636default:7637D3D12MA_ASSERT(0);7638case D3D12_MEMORY_POOL_UNKNOWN:7639json.WriteString(L"MEMORY_POOL_UNKNOWN");7640break;7641case D3D12_MEMORY_POOL_L0:7642json.WriteString(L"MEMORY_POOL_L0");7643break;7644case D3D12_MEMORY_POOL_L1:7645json.WriteString(L"MEMORY_POOL_L1");7646break;7647}7648switch (properties.CPUPageProperty)7649{7650default:7651D3D12MA_ASSERT(0);7652case D3D12_CPU_PAGE_PROPERTY_UNKNOWN:7653json.WriteString(L"CPU_PAGE_PROPERTY_UNKNOWN");7654break;7655case D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE:7656json.WriteString(L"CPU_PAGE_PROPERTY_NOT_AVAILABLE");7657break;7658case D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE:7659json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_COMBINE");7660break;7661case D3D12_CPU_PAGE_PROPERTY_WRITE_BACK:7662json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_BACK");7663break;7664}7665}7666}7667json.EndArray();76687669json.WriteString(L"PreferredBlockSize");7670json.WriteNumber(blockVector->GetPreferredBlockSize());76717672json.WriteString(L"Blocks");7673blockVector->WriteBlockInfoToJson(json);76747675json.WriteString(L"DedicatedAllocations");7676json.BeginArray();7677if (committedAllocs)7678committedAllocs->BuildStatsString(json);7679json.EndArray();7680};76817682json.WriteString(L"DefaultPools");7683json.BeginObject();7684{7685if (SupportsResourceHeapTier2())7686{7687for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)7688{7689json.WriteString(StandardHeapTypeNames[heapType]);7690json.BeginObject();7691writeHeapInfo(m_BlockVectors[heapType], m_CommittedAllocations + heapType, false);7692json.EndObject();7693}7694}7695else7696{7697for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)7698{7699for (uint8_t heapSubType = 0; heapSubType < 3; ++heapSubType)7700{7701static const WCHAR* const heapSubTypeName[] = {7702L" - Buffers",7703L" - Textures",7704L" - Textures RT/DS",7705};7706json.BeginString(StandardHeapTypeNames[heapType]);7707json.EndString(heapSubTypeName[heapSubType]);77087709json.BeginObject();7710writeHeapInfo(m_BlockVectors[heapType * 3 + heapSubType], m_CommittedAllocations + heapType, false);7711json.EndObject();7712}7713}7714}7715}7716json.EndObject();77177718json.WriteString(L"CustomPools");7719json.BeginObject();7720for (uint8_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)7721{7722MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex);7723auto* item = m_Pools[heapTypeIndex].Front();7724if (item != NULL)7725{7726size_t index = 0;7727json.WriteString(HeapTypeNames[heapTypeIndex]);7728json.BeginArray();7729do7730{7731json.BeginObject();7732json.WriteString(L"Name");7733json.BeginString();7734json.ContinueString(index++);7735if (item->GetName())7736{7737json.ContinueString(L" - ");7738json.ContinueString(item->GetName());7739}7740json.EndString();77417742writeHeapInfo(item->GetBlockVector(), item->GetCommittedAllocationList(), heapTypeIndex == 3);7743json.EndObject();7744} while ((item = PoolList::GetNext(item)) != NULL);7745json.EndArray();7746}7747}7748json.EndObject();7749}7750json.EndObject();7751}77527753const size_t length = sb.GetLength();7754WCHAR* result = AllocateArray<WCHAR>(GetAllocs(), length + 2);7755result[0] = 0xFEFF;7756memcpy(result + 1, sb.GetData(), length * sizeof(WCHAR));7757result[length + 1] = L'\0';7758*ppStatsString = result;7759}77607761void AllocatorPimpl::FreeStatsString(WCHAR* pStatsString)7762{7763D3D12MA_ASSERT(pStatsString);7764Free(GetAllocs(), pStatsString);7765}77667767template<typename D3D12_RESOURCE_DESC_T>7768bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc)7769{7770// Intentional. It may change in the future.7771return false;7772}77737774HRESULT AllocatorPimpl::AllocateCommittedResource(7775const CommittedAllocationParameters& committedAllocParams,7776UINT64 resourceSize, bool withinBudget, void* pPrivateData,7777const CREATE_RESOURCE_PARAMS& createParams,7778Allocation** ppAllocation, REFIID riidResource, void** ppvResource)7779{7780D3D12MA_ASSERT(committedAllocParams.IsValid());77817782HRESULT hr;7783ID3D12Resource* res = NULL;7784// Allocate aliasing memory with explicit heap7785if (committedAllocParams.m_CanAlias)7786{7787D3D12_RESOURCE_ALLOCATION_INFO heapAllocInfo = {};7788heapAllocInfo.SizeInBytes = resourceSize;7789heapAllocInfo.Alignment = HeapFlagsToAlignment(committedAllocParams.m_HeapFlags, m_MsaaAlwaysCommitted);7790hr = AllocateHeap(committedAllocParams, heapAllocInfo, withinBudget, pPrivateData, ppAllocation);7791if (SUCCEEDED(hr))7792{7793hr = CreatePlacedResourceWrap((*ppAllocation)->GetHeap(), 0,7794createParams, D3D12MA_IID_PPV_ARGS(&res));7795if (SUCCEEDED(hr))7796{7797if (ppvResource != NULL)7798hr = res->QueryInterface(riidResource, ppvResource);7799if (SUCCEEDED(hr))7800{7801(*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc());7802return hr;7803}7804res->Release();7805}7806FreeHeapMemory(*ppAllocation);7807}7808return hr;7809}78107811if (withinBudget &&7812!NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, resourceSize))7813{7814return E_OUTOFMEMORY;7815}78167817/* D3D12 ERROR:7818* ID3D12Device::CreateCommittedResource:7819* When creating a committed resource, D3D12_HEAP_FLAGS must not have either7820* D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES,7821* D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES,7822* nor D3D12_HEAP_FLAG_DENY_BUFFERS set.7823* These flags will be set automatically to correspond with the committed resource type.7824*7825* [ STATE_CREATION ERROR #640: CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS]7826*/78277828#ifdef __ID3D12Device10_INTERFACE_DEFINED__7829if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)7830{7831if (!m_Device10)7832{7833return E_NOINTERFACE;7834}7835hr = m_Device10->CreateCommittedResource3(7836&committedAllocParams.m_HeapProperties,7837committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7838createParams.GetResourceDesc1(), createParams.GetInitialLayout(),7839createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7840createParams.GetNumCastableFormats(), createParams.GetCastableFormats(),7841D3D12MA_IID_PPV_ARGS(&res));7842} else7843#endif7844#ifdef __ID3D12Device8_INTERFACE_DEFINED__7845if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7846{7847if (!m_Device8)7848{7849return E_NOINTERFACE;7850}7851hr = m_Device8->CreateCommittedResource2(7852&committedAllocParams.m_HeapProperties,7853committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7854createParams.GetResourceDesc1(), createParams.GetInitialResourceState(),7855createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7856D3D12MA_IID_PPV_ARGS(&res));7857} else7858#endif7859if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)7860{7861#ifdef __ID3D12Device4_INTERFACE_DEFINED__7862if (m_Device4)7863{7864hr = m_Device4->CreateCommittedResource1(7865&committedAllocParams.m_HeapProperties,7866committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7867createParams.GetResourceDesc(), createParams.GetInitialResourceState(),7868createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7869D3D12MA_IID_PPV_ARGS(&res));7870}7871else7872#endif7873{7874if (committedAllocParams.m_ProtectedSession == NULL)7875{7876hr = m_Device->CreateCommittedResource(7877&committedAllocParams.m_HeapProperties,7878committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7879createParams.GetResourceDesc(), createParams.GetInitialResourceState(),7880createParams.GetOptimizedClearValue(), D3D12MA_IID_PPV_ARGS(&res));7881}7882else7883hr = E_NOINTERFACE;7884}7885}7886else7887{7888D3D12MA_ASSERT(0);7889return E_INVALIDARG;7890}78917892if (SUCCEEDED(hr))7893{7894SetResidencyPriority(res, committedAllocParams.m_ResidencyPriority);78957896if (ppvResource != NULL)7897{7898hr = res->QueryInterface(riidResource, ppvResource);7899}7900if (SUCCEEDED(hr))7901{7902BOOL wasZeroInitialized = TRUE;7903#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE7904if((committedAllocParams.m_HeapFlags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) != 0)7905{7906wasZeroInitialized = FALSE;7907}7908#endif79097910Allocation* alloc = m_AllocationObjectAllocator.Allocate(7911this, resourceSize, createParams.GetBaseResourceDesc()->Alignment, wasZeroInitialized);7912alloc->InitCommitted(committedAllocParams.m_List);7913alloc->SetResourcePointer(res, createParams.GetBaseResourceDesc());7914alloc->SetPrivateData(pPrivateData);79157916*ppAllocation = alloc;79177918committedAllocParams.m_List->Register(alloc);79197920const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties);7921m_Budget.AddBlock(memSegmentGroup, resourceSize);7922m_Budget.AddAllocation(memSegmentGroup, resourceSize);7923}7924else7925{7926res->Release();7927}7928}7929return hr;7930}79317932HRESULT AllocatorPimpl::AllocateHeap(7933const CommittedAllocationParameters& committedAllocParams,7934const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget,7935void* pPrivateData, Allocation** ppAllocation)7936{7937D3D12MA_ASSERT(committedAllocParams.IsValid());79387939*ppAllocation = nullptr;79407941if (withinBudget &&7942!NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, allocInfo.SizeInBytes))7943{7944return E_OUTOFMEMORY;7945}79467947D3D12_HEAP_DESC heapDesc = {};7948heapDesc.SizeInBytes = allocInfo.SizeInBytes;7949heapDesc.Properties = committedAllocParams.m_HeapProperties;7950heapDesc.Alignment = allocInfo.Alignment;7951heapDesc.Flags = committedAllocParams.m_HeapFlags;79527953HRESULT hr;7954ID3D12Heap* heap = nullptr;7955#ifdef __ID3D12Device4_INTERFACE_DEFINED__7956if (m_Device4)7957hr = m_Device4->CreateHeap1(&heapDesc, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&heap));7958else7959#endif7960{7961if (committedAllocParams.m_ProtectedSession == NULL)7962hr = m_Device->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&heap));7963else7964hr = E_NOINTERFACE;7965}79667967if (SUCCEEDED(hr))7968{7969SetResidencyPriority(heap, committedAllocParams.m_ResidencyPriority);79707971BOOL wasZeroInitialized = TRUE;7972#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE7973if((heapDesc.Flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) != 0)7974{7975wasZeroInitialized = FALSE;7976}7977#endif79787979(*ppAllocation) = m_AllocationObjectAllocator.Allocate(this, allocInfo.SizeInBytes, allocInfo.Alignment, wasZeroInitialized);7980(*ppAllocation)->InitHeap(committedAllocParams.m_List, heap);7981(*ppAllocation)->SetPrivateData(pPrivateData);7982committedAllocParams.m_List->Register(*ppAllocation);79837984const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties);7985m_Budget.AddBlock(memSegmentGroup, allocInfo.SizeInBytes);7986m_Budget.AddAllocation(memSegmentGroup, allocInfo.SizeInBytes);7987}7988return hr;7989}79907991template<typename D3D12_RESOURCE_DESC_T>7992HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,7993const D3D12_RESOURCE_DESC_T* resDesc,7994BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted)7995{7996outBlockVector = NULL;7997outCommittedAllocationParams = CommittedAllocationParameters();7998outPreferCommitted = false;79998000bool msaaAlwaysCommitted;8001if (allocDesc.CustomPool != NULL)8002{8003PoolPimpl* const pool = allocDesc.CustomPool->m_Pimpl;80048005msaaAlwaysCommitted = pool->GetBlockVector()->DeniesMsaaTextures();8006outBlockVector = pool->GetBlockVector();80078008const auto& desc = pool->GetDesc();8009outCommittedAllocationParams.m_ProtectedSession = desc.pProtectedSession;8010outCommittedAllocationParams.m_HeapProperties = desc.HeapProperties;8011outCommittedAllocationParams.m_HeapFlags = desc.HeapFlags;8012outCommittedAllocationParams.m_List = pool->GetCommittedAllocationList();8013outCommittedAllocationParams.m_ResidencyPriority = pool->GetDesc().ResidencyPriority;8014}8015else8016{8017if (!IsHeapTypeStandard(allocDesc.HeapType))8018{8019return E_INVALIDARG;8020}8021msaaAlwaysCommitted = m_MsaaAlwaysCommitted;80228023outCommittedAllocationParams.m_HeapProperties = StandardHeapTypeToHeapProperties(allocDesc.HeapType);8024outCommittedAllocationParams.m_HeapFlags = allocDesc.ExtraHeapFlags;8025outCommittedAllocationParams.m_List = &m_CommittedAllocations[StandardHeapTypeToIndex(allocDesc.HeapType)];8026// outCommittedAllocationParams.m_ResidencyPriority intentionally left with default value.80278028const ResourceClass resourceClass = (resDesc != NULL) ?8029ResourceDescToResourceClass(*resDesc) : HeapFlagsToResourceClass(allocDesc.ExtraHeapFlags);8030const UINT defaultPoolIndex = CalcDefaultPoolIndex(allocDesc, resourceClass);8031if (defaultPoolIndex != UINT32_MAX)8032{8033outBlockVector = m_BlockVectors[defaultPoolIndex];8034const UINT64 preferredBlockSize = outBlockVector->GetPreferredBlockSize();8035if (allocSize > preferredBlockSize)8036{8037outBlockVector = NULL;8038}8039else if (allocSize > preferredBlockSize / 2)8040{8041// Heuristics: Allocate committed memory if requested size if greater than half of preferred block size.8042outPreferCommitted = true;8043}8044}80458046const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS;8047if (outBlockVector != NULL && extraHeapFlags != 0)8048{8049outBlockVector = NULL;8050}8051}80528053if ((allocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0 ||8054m_AlwaysCommitted)8055{8056outBlockVector = NULL;8057}8058if ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) != 0)8059{8060outCommittedAllocationParams.m_List = NULL;8061}8062outCommittedAllocationParams.m_CanAlias = allocDesc.Flags & ALLOCATION_FLAG_CAN_ALIAS;80638064if (resDesc != NULL)8065{8066if (resDesc->SampleDesc.Count > 1 && msaaAlwaysCommitted)8067outBlockVector = NULL;8068if (!outPreferCommitted && PrefersCommittedAllocation(*resDesc))8069outPreferCommitted = true;8070}80718072return (outBlockVector != NULL || outCommittedAllocationParams.m_List != NULL) ? S_OK : E_INVALIDARG;8073}80748075UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const8076{8077D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS;80788079#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE8080// If allocator was created with ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED, also ignore8081// D3D12_HEAP_FLAG_CREATE_NOT_ZEROED.8082if(m_DefaultPoolsNotZeroed)8083{8084extraHeapFlags &= ~D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;8085}8086#endif80878088if (extraHeapFlags != 0)8089{8090return UINT32_MAX;8091}80928093UINT poolIndex = UINT_MAX;8094switch (allocDesc.HeapType)8095{8096case D3D12_HEAP_TYPE_DEFAULT: poolIndex = 0; break;8097case D3D12_HEAP_TYPE_UPLOAD: poolIndex = 1; break;8098case D3D12_HEAP_TYPE_READBACK: poolIndex = 2; break;8099default: D3D12MA_ASSERT(0);8100}81018102if (SupportsResourceHeapTier2())8103return poolIndex;8104else8105{8106switch (resourceClass)8107{8108case ResourceClass::Buffer:8109return poolIndex * 3;8110case ResourceClass::Non_RT_DS_Texture:8111return poolIndex * 3 + 1;8112case ResourceClass::RT_DS_Texture:8113return poolIndex * 3 + 2;8114default:8115return UINT32_MAX;8116}8117}8118}81198120void AllocatorPimpl::CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const8121{8122outHeapType = D3D12_HEAP_TYPE_DEFAULT;8123outHeapFlags = D3D12_HEAP_FLAG_NONE;81248125if (!SupportsResourceHeapTier2())8126{8127switch (index % 3)8128{8129case 0:8130outHeapFlags = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;8131break;8132case 1:8133outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;8134break;8135case 2:8136outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;8137break;8138}81398140index /= 3;8141}81428143switch (index)8144{8145case 0:8146outHeapType = D3D12_HEAP_TYPE_DEFAULT;8147break;8148case 1:8149outHeapType = D3D12_HEAP_TYPE_UPLOAD;8150break;8151case 2:8152outHeapType = D3D12_HEAP_TYPE_READBACK;8153break;8154default:8155D3D12MA_ASSERT(0);8156}8157}81588159void AllocatorPimpl::RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)8160{8161const UINT heapTypeIndex = (UINT)heapType - 1;81628163MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);8164m_Pools[heapTypeIndex].PushBack(pool->m_Pimpl);8165}81668167void AllocatorPimpl::UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)8168{8169const UINT heapTypeIndex = (UINT)heapType - 1;81708171MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);8172m_Pools[heapTypeIndex].Remove(pool->m_Pimpl);8173}81748175HRESULT AllocatorPimpl::UpdateD3D12Budget()8176{8177#if D3D12MA_DXGI_1_48178if (m_Adapter3)8179return m_Budget.UpdateBudget(m_Adapter3, m_UseMutex);8180else8181return E_NOINTERFACE;8182#else8183return S_OK;8184#endif8185}81868187D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const8188{8189return m_Device->GetResourceAllocationInfo(0, 1, &resourceDesc);8190}81918192#ifdef __ID3D12Device8_INTERFACE_DEFINED__8193D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const8194{8195D3D12MA_ASSERT(m_Device8 != NULL);8196D3D12_RESOURCE_ALLOCATION_INFO1 info1Unused;8197return m_Device8->GetResourceAllocationInfo2(0, 1, &resourceDesc, &info1Unused);8198}8199#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__82008201template<typename D3D12_RESOURCE_DESC_T>8202D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const8203{8204#ifdef __ID3D12Device1_INTERFACE_DEFINED__8205/* Optional optimization: Microsoft documentation says:8206https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getresourceallocationinfo82078208Your application can forgo using GetResourceAllocationInfo for buffer resources8209(D3D12_RESOURCE_DIMENSION_BUFFER). Buffers have the same size on all adapters,8210which is merely the smallest multiple of 64KB that's greater or equal to8211D3D12_RESOURCE_DESC::Width.8212*/8213if (inOutResourceDesc.Alignment == 0 &&8214inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)8215{8216return {8217AlignUp<UINT64>(inOutResourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), // SizeInBytes8218D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT }; // Alignment8219}8220#endif // #ifdef __ID3D12Device1_INTERFACE_DEFINED__82218222#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT8223if (inOutResourceDesc.Alignment == 0 &&8224inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&8225(inOutResourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 08226#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT == 18227&& CanUseSmallAlignment(inOutResourceDesc)8228#endif8229)8230{8231/*8232The algorithm here is based on Microsoft sample: "Small Resources Sample"8233https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12SmallResources8234*/8235const UINT64 smallAlignmentToTry = inOutResourceDesc.SampleDesc.Count > 1 ?8236D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT :8237D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;8238inOutResourceDesc.Alignment = smallAlignmentToTry;8239const D3D12_RESOURCE_ALLOCATION_INFO smallAllocInfo = GetResourceAllocationInfoNative(inOutResourceDesc);8240// Check if alignment requested has been granted.8241if (smallAllocInfo.Alignment == smallAlignmentToTry)8242{8243return smallAllocInfo;8244}8245inOutResourceDesc.Alignment = 0; // Restore original8246}8247#endif // #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT82488249return GetResourceAllocationInfoNative(inOutResourceDesc);8250}82518252bool AllocatorPimpl::NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size)8253{8254Budget budget = {};8255GetBudgetForHeapType(budget, heapType);8256return budget.UsageBytes + size <= budget.BudgetBytes;8257}82588259void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget)8260{8261json.BeginObject();8262{8263json.WriteString(L"BudgetBytes");8264json.WriteNumber(budget.BudgetBytes);8265json.WriteString(L"UsageBytes");8266json.WriteNumber(budget.UsageBytes);8267}8268json.EndObject();8269}82708271#endif // _D3D12MA_ALLOCATOR_PIMPL8272#endif // _D3D12MA_ALLOCATOR_PIMPL82738274#ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL8275class VirtualBlockPimpl8276{8277public:8278const ALLOCATION_CALLBACKS m_AllocationCallbacks;8279const UINT64 m_Size;8280BlockMetadata* m_Metadata;82818282VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc);8283~VirtualBlockPimpl();8284};82858286#ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS8287VirtualBlockPimpl::VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc)8288: m_AllocationCallbacks(allocationCallbacks), m_Size(desc.Size)8289{8290switch (desc.Flags & VIRTUAL_BLOCK_FLAG_ALGORITHM_MASK)8291{8292case VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR:8293m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_Linear)(&m_AllocationCallbacks, true);8294break;8295default:8296D3D12MA_ASSERT(0);8297case 0:8298m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_TLSF)(&m_AllocationCallbacks, true);8299break;8300}8301m_Metadata->Init(m_Size);8302}83038304VirtualBlockPimpl::~VirtualBlockPimpl()8305{8306D3D12MA_DELETE(m_AllocationCallbacks, m_Metadata);8307}8308#endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS8309#endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL831083118312#ifndef _D3D12MA_MEMORY_BLOCK_FUNCTIONS8313MemoryBlock::MemoryBlock(8314AllocatorPimpl* allocator,8315const D3D12_HEAP_PROPERTIES& heapProps,8316D3D12_HEAP_FLAGS heapFlags,8317UINT64 size,8318UINT id)8319: m_Allocator(allocator),8320m_HeapProps(heapProps),8321m_HeapFlags(heapFlags),8322m_Size(size),8323m_Id(id) {}83248325MemoryBlock::~MemoryBlock()8326{8327if (m_Heap)8328{8329m_Heap->Release();8330m_Allocator->m_Budget.RemoveBlock(8331m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size);8332}8333}83348335HRESULT MemoryBlock::Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures)8336{8337D3D12MA_ASSERT(m_Heap == NULL && m_Size > 0);83388339D3D12_HEAP_DESC heapDesc = {};8340heapDesc.SizeInBytes = m_Size;8341heapDesc.Properties = m_HeapProps;8342heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags, denyMsaaTextures);8343heapDesc.Flags = m_HeapFlags;83448345HRESULT hr;8346#ifdef __ID3D12Device4_INTERFACE_DEFINED__8347ID3D12Device4* const device4 = m_Allocator->GetDevice4();8348if (device4)8349hr = m_Allocator->GetDevice4()->CreateHeap1(&heapDesc, pProtectedSession, D3D12MA_IID_PPV_ARGS(&m_Heap));8350else8351#endif8352{8353if (pProtectedSession == NULL)8354hr = m_Allocator->GetDevice()->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&m_Heap));8355else8356hr = E_NOINTERFACE;8357}83588359if (SUCCEEDED(hr))8360{8361m_Allocator->m_Budget.AddBlock(8362m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size);8363}8364return hr;8365}8366#endif // _D3D12MA_MEMORY_BLOCK_FUNCTIONS83678368#ifndef _D3D12MA_NORMAL_BLOCK_FUNCTIONS8369NormalBlock::NormalBlock(8370AllocatorPimpl* allocator,8371BlockVector* blockVector,8372const D3D12_HEAP_PROPERTIES& heapProps,8373D3D12_HEAP_FLAGS heapFlags,8374UINT64 size,8375UINT id)8376: MemoryBlock(allocator, heapProps, heapFlags, size, id),8377m_pMetadata(NULL),8378m_BlockVector(blockVector) {}83798380NormalBlock::~NormalBlock()8381{8382if (m_pMetadata != NULL)8383{8384// Define macro D3D12MA_DEBUG_LOG to receive the list of the unfreed allocations.8385if (!m_pMetadata->IsEmpty())8386m_pMetadata->DebugLogAllAllocations();83878388// THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY!8389// Hitting it means you have some memory leak - unreleased Allocation objects.8390D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");83918392D3D12MA_DELETE(m_Allocator->GetAllocs(), m_pMetadata);8393}8394}83958396HRESULT NormalBlock::Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures)8397{8398HRESULT hr = MemoryBlock::Init(pProtectedSession, denyMsaaTextures);8399if (FAILED(hr))8400{8401return hr;8402}84038404switch (algorithm)8405{8406case POOL_FLAG_ALGORITHM_LINEAR:8407m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_Linear)(&m_Allocator->GetAllocs(), false);8408break;8409default:8410D3D12MA_ASSERT(0);8411case 0:8412m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_TLSF)(&m_Allocator->GetAllocs(), false);8413break;8414}8415m_pMetadata->Init(m_Size);84168417return hr;8418}84198420bool NormalBlock::Validate() const8421{8422D3D12MA_VALIDATE(GetHeap() &&8423m_pMetadata &&8424m_pMetadata->GetSize() != 0 &&8425m_pMetadata->GetSize() == GetSize());8426return m_pMetadata->Validate();8427}8428#endif // _D3D12MA_NORMAL_BLOCK_FUNCTIONS84298430#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS8431void CommittedAllocationList::Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool)8432{8433m_UseMutex = useMutex;8434m_HeapType = heapType;8435m_Pool = pool;8436}84378438CommittedAllocationList::~CommittedAllocationList()8439{8440if (!m_AllocationList.IsEmpty())8441{8442D3D12MA_ASSERT(0 && "Unfreed committed allocations found!");8443}8444}84458446UINT CommittedAllocationList::GetMemorySegmentGroup(AllocatorPimpl* allocator) const8447{8448if (m_Pool)8449return allocator->HeapPropertiesToMemorySegmentGroup(m_Pool->GetDesc().HeapProperties);8450else8451return allocator->StandardHeapTypeToMemorySegmentGroup(m_HeapType);8452}84538454void CommittedAllocationList::AddStatistics(Statistics& inoutStats)8455{8456MutexLockRead lock(m_Mutex, m_UseMutex);84578458for (Allocation* alloc = m_AllocationList.Front();8459alloc != NULL; alloc = m_AllocationList.GetNext(alloc))8460{8461const UINT64 size = alloc->GetSize();8462inoutStats.BlockCount++;8463inoutStats.AllocationCount++;8464inoutStats.BlockBytes += size;8465inoutStats.AllocationBytes += size;8466}8467}84688469void CommittedAllocationList::AddDetailedStatistics(DetailedStatistics& inoutStats)8470{8471MutexLockRead lock(m_Mutex, m_UseMutex);84728473for (Allocation* alloc = m_AllocationList.Front();8474alloc != NULL; alloc = m_AllocationList.GetNext(alloc))8475{8476const UINT64 size = alloc->GetSize();8477inoutStats.Stats.BlockCount++;8478inoutStats.Stats.BlockBytes += size;8479AddDetailedStatisticsAllocation(inoutStats, size);8480}8481}84828483void CommittedAllocationList::BuildStatsString(JsonWriter& json)8484{8485MutexLockRead lock(m_Mutex, m_UseMutex);84868487for (Allocation* alloc = m_AllocationList.Front();8488alloc != NULL; alloc = m_AllocationList.GetNext(alloc))8489{8490json.BeginObject(true);8491json.AddAllocationToObject(*alloc);8492json.EndObject();8493}8494}84958496void CommittedAllocationList::Register(Allocation* alloc)8497{8498MutexLockWrite lock(m_Mutex, m_UseMutex);8499m_AllocationList.PushBack(alloc);8500}85018502void CommittedAllocationList::Unregister(Allocation* alloc)8503{8504MutexLockWrite lock(m_Mutex, m_UseMutex);8505m_AllocationList.Remove(alloc);8506}8507#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS85088509#ifndef _D3D12MA_BLOCK_VECTOR_FUNCTIONS8510BlockVector::BlockVector(8511AllocatorPimpl* hAllocator,8512const D3D12_HEAP_PROPERTIES& heapProps,8513D3D12_HEAP_FLAGS heapFlags,8514UINT64 preferredBlockSize,8515size_t minBlockCount,8516size_t maxBlockCount,8517bool explicitBlockSize,8518UINT64 minAllocationAlignment,8519UINT32 algorithm,8520bool denyMsaaTextures,8521ID3D12ProtectedResourceSession* pProtectedSession,8522D3D12_RESIDENCY_PRIORITY residencyPriority)8523: m_hAllocator(hAllocator),8524m_HeapProps(heapProps),8525m_HeapFlags(heapFlags),8526m_PreferredBlockSize(preferredBlockSize),8527m_MinBlockCount(minBlockCount),8528m_MaxBlockCount(maxBlockCount),8529m_ExplicitBlockSize(explicitBlockSize),8530m_MinAllocationAlignment(minAllocationAlignment),8531m_Algorithm(algorithm),8532m_DenyMsaaTextures(denyMsaaTextures),8533m_ProtectedSession(pProtectedSession),8534m_ResidencyPriority(residencyPriority),8535m_HasEmptyBlock(false),8536m_Blocks(hAllocator->GetAllocs()),8537m_NextBlockId(0) {}85388539BlockVector::~BlockVector()8540{8541for (size_t i = m_Blocks.size(); i--; )8542{8543D3D12MA_DELETE(m_hAllocator->GetAllocs(), m_Blocks[i]);8544}8545}85468547HRESULT BlockVector::CreateMinBlocks()8548{8549for (size_t i = 0; i < m_MinBlockCount; ++i)8550{8551HRESULT hr = CreateBlock(m_PreferredBlockSize, NULL);8552if (FAILED(hr))8553{8554return hr;8555}8556}8557return S_OK;8558}85598560bool BlockVector::IsEmpty()8561{8562MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());8563return m_Blocks.empty();8564}85658566HRESULT BlockVector::Allocate(8567UINT64 size,8568UINT64 alignment,8569const ALLOCATION_DESC& allocDesc,8570size_t allocationCount,8571Allocation** pAllocations)8572{8573size_t allocIndex;8574HRESULT hr = S_OK;85758576{8577MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());8578for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)8579{8580hr = AllocatePage(8581size,8582alignment,8583allocDesc,8584pAllocations + allocIndex);8585if (FAILED(hr))8586{8587break;8588}8589}8590}85918592if (FAILED(hr))8593{8594// Free all already created allocations.8595while (allocIndex--)8596{8597Free(pAllocations[allocIndex]);8598}8599ZeroMemory(pAllocations, sizeof(Allocation*) * allocationCount);8600}86018602return hr;8603}86048605void BlockVector::Free(Allocation* hAllocation)8606{8607NormalBlock* pBlockToDelete = NULL;86088609bool budgetExceeded = false;8610if (IsHeapTypeStandard(m_HeapProps.Type))8611{8612Budget budget = {};8613m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type);8614budgetExceeded = budget.UsageBytes >= budget.BudgetBytes;8615}86168617// Scope for lock.8618{8619MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());86208621NormalBlock* pBlock = hAllocation->m_Placed.block;86228623pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());8624D3D12MA_HEAVY_ASSERT(pBlock->Validate());86258626const size_t blockCount = m_Blocks.size();8627// pBlock became empty after this deallocation.8628if (pBlock->m_pMetadata->IsEmpty())8629{8630// Already has empty Allocation. We don't want to have two, so delete this one.8631if ((m_HasEmptyBlock || budgetExceeded) &&8632blockCount > m_MinBlockCount)8633{8634pBlockToDelete = pBlock;8635Remove(pBlock);8636}8637// We now have first empty block.8638else8639{8640m_HasEmptyBlock = true;8641}8642}8643// pBlock didn't become empty, but we have another empty block - find and free that one.8644// (This is optional, heuristics.)8645else if (m_HasEmptyBlock && blockCount > m_MinBlockCount)8646{8647NormalBlock* pLastBlock = m_Blocks.back();8648if (pLastBlock->m_pMetadata->IsEmpty())8649{8650pBlockToDelete = pLastBlock;8651m_Blocks.pop_back();8652m_HasEmptyBlock = false;8653}8654}86558656IncrementallySortBlocks();8657}86588659// Destruction of a free Allocation. Deferred until this point, outside of mutex8660// lock, for performance reason.8661if (pBlockToDelete != NULL)8662{8663D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlockToDelete);8664}8665}86668667HRESULT BlockVector::CreateResource(8668UINT64 size,8669UINT64 alignment,8670const ALLOCATION_DESC& allocDesc,8671const CREATE_RESOURCE_PARAMS& createParams,8672Allocation** ppAllocation,8673REFIID riidResource,8674void** ppvResource)8675{8676HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation);8677if (SUCCEEDED(hr))8678{8679ID3D12Resource* res = NULL;8680hr = m_hAllocator->CreatePlacedResourceWrap(8681(*ppAllocation)->m_Placed.block->GetHeap(),8682(*ppAllocation)->GetOffset(),8683createParams,8684D3D12MA_IID_PPV_ARGS(&res));8685if (SUCCEEDED(hr))8686{8687if (ppvResource != NULL)8688{8689hr = res->QueryInterface(riidResource, ppvResource);8690}8691if (SUCCEEDED(hr))8692{8693(*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc());8694}8695else8696{8697res->Release();8698SAFE_RELEASE(*ppAllocation);8699}8700}8701else8702{8703SAFE_RELEASE(*ppAllocation);8704}8705}8706return hr;8707}87088709void BlockVector::AddStatistics(Statistics& inoutStats)8710{8711MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());87128713for (size_t i = 0; i < m_Blocks.size(); ++i)8714{8715const NormalBlock* const pBlock = m_Blocks[i];8716D3D12MA_ASSERT(pBlock);8717D3D12MA_HEAVY_ASSERT(pBlock->Validate());8718pBlock->m_pMetadata->AddStatistics(inoutStats);8719}8720}87218722void BlockVector::AddDetailedStatistics(DetailedStatistics& inoutStats)8723{8724MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());87258726for (size_t i = 0; i < m_Blocks.size(); ++i)8727{8728const NormalBlock* const pBlock = m_Blocks[i];8729D3D12MA_ASSERT(pBlock);8730D3D12MA_HEAVY_ASSERT(pBlock->Validate());8731pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);8732}8733}87348735void BlockVector::WriteBlockInfoToJson(JsonWriter& json)8736{8737MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());87388739json.BeginObject();87408741for (size_t i = 0, count = m_Blocks.size(); i < count; ++i)8742{8743const NormalBlock* const pBlock = m_Blocks[i];8744D3D12MA_ASSERT(pBlock);8745D3D12MA_HEAVY_ASSERT(pBlock->Validate());8746json.BeginString();8747json.ContinueString(pBlock->GetId());8748json.EndString();87498750json.BeginObject();8751pBlock->m_pMetadata->WriteAllocationInfoToJson(json);8752json.EndObject();8753}87548755json.EndObject();8756}87578758UINT64 BlockVector::CalcSumBlockSize() const8759{8760UINT64 result = 0;8761for (size_t i = m_Blocks.size(); i--; )8762{8763result += m_Blocks[i]->m_pMetadata->GetSize();8764}8765return result;8766}87678768UINT64 BlockVector::CalcMaxBlockSize() const8769{8770UINT64 result = 0;8771for (size_t i = m_Blocks.size(); i--; )8772{8773result = D3D12MA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());8774if (result >= m_PreferredBlockSize)8775{8776break;8777}8778}8779return result;8780}87818782void BlockVector::Remove(NormalBlock* pBlock)8783{8784for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)8785{8786if (m_Blocks[blockIndex] == pBlock)8787{8788m_Blocks.remove(blockIndex);8789return;8790}8791}8792D3D12MA_ASSERT(0);8793}87948795void BlockVector::IncrementallySortBlocks()8796{8797if (!m_IncrementalSort)8798return;8799// Bubble sort only until first swap.8800for (size_t i = 1; i < m_Blocks.size(); ++i)8801{8802if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())8803{8804D3D12MA_SWAP(m_Blocks[i - 1], m_Blocks[i]);8805return;8806}8807}8808}88098810void BlockVector::SortByFreeSize()8811{8812D3D12MA_SORT(m_Blocks.begin(), m_Blocks.end(),8813[](auto* b1, auto* b2)8814{8815return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();8816});8817}88188819HRESULT BlockVector::AllocatePage(8820UINT64 size,8821UINT64 alignment,8822const ALLOCATION_DESC& allocDesc,8823Allocation** pAllocation)8824{8825// Early reject: requested allocation size is larger that maximum block size for this block vector.8826if (size + D3D12MA_DEBUG_MARGIN > m_PreferredBlockSize)8827{8828return E_OUTOFMEMORY;8829}88308831UINT64 freeMemory = UINT64_MAX;8832if (IsHeapTypeStandard(m_HeapProps.Type))8833{8834Budget budget = {};8835m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type);8836freeMemory = (budget.UsageBytes < budget.BudgetBytes) ? (budget.BudgetBytes - budget.UsageBytes) : 0;8837}88388839const bool canCreateNewBlock =8840((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0) &&8841(m_Blocks.size() < m_MaxBlockCount) &&8842// Even if we don't have to stay within budget with this allocation, when the8843// budget would be exceeded, we don't want to allocate new blocks, but always8844// create resources as committed.8845freeMemory >= size;88468847// 1. Search existing allocations8848{8849// Forward order in m_Blocks - prefer blocks with smallest amount of free space.8850for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)8851{8852NormalBlock* const pCurrBlock = m_Blocks[blockIndex];8853D3D12MA_ASSERT(pCurrBlock);8854HRESULT hr = AllocateFromBlock(8855pCurrBlock,8856size,8857alignment,8858allocDesc.Flags,8859allocDesc.pPrivateData,8860allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK,8861pAllocation);8862if (SUCCEEDED(hr))8863{8864return hr;8865}8866}8867}88688869// 2. Try to create new block.8870if (canCreateNewBlock)8871{8872// Calculate optimal size for new block.8873UINT64 newBlockSize = m_PreferredBlockSize;8874UINT newBlockSizeShift = 0;88758876if (!m_ExplicitBlockSize)8877{8878// Allocate 1/8, 1/4, 1/2 as first blocks.8879const UINT64 maxExistingBlockSize = CalcMaxBlockSize();8880for (UINT i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)8881{8882const UINT64 smallerNewBlockSize = newBlockSize / 2;8883if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)8884{8885newBlockSize = smallerNewBlockSize;8886++newBlockSizeShift;8887}8888else8889{8890break;8891}8892}8893}88948895size_t newBlockIndex = 0;8896HRESULT hr = newBlockSize <= freeMemory ?8897CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;8898// Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.8899if (!m_ExplicitBlockSize)8900{8901while (FAILED(hr) && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)8902{8903const UINT64 smallerNewBlockSize = newBlockSize / 2;8904if (smallerNewBlockSize >= size)8905{8906newBlockSize = smallerNewBlockSize;8907++newBlockSizeShift;8908hr = newBlockSize <= freeMemory ?8909CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;8910}8911else8912{8913break;8914}8915}8916}89178918if (SUCCEEDED(hr))8919{8920NormalBlock* const pBlock = m_Blocks[newBlockIndex];8921D3D12MA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);89228923hr = AllocateFromBlock(8924pBlock,8925size,8926alignment,8927allocDesc.Flags,8928allocDesc.pPrivateData,8929allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK,8930pAllocation);8931if (SUCCEEDED(hr))8932{8933return hr;8934}8935else8936{8937// Allocation from new block failed, possibly due to D3D12MA_DEBUG_MARGIN or alignment.8938return E_OUTOFMEMORY;8939}8940}8941}89428943return E_OUTOFMEMORY;8944}89458946HRESULT BlockVector::AllocateFromBlock(8947NormalBlock* pBlock,8948UINT64 size,8949UINT64 alignment,8950ALLOCATION_FLAGS allocFlags,8951void* pPrivateData,8952UINT32 strategy,8953Allocation** pAllocation)8954{8955alignment = D3D12MA_MAX(alignment, m_MinAllocationAlignment);89568957AllocationRequest currRequest = {};8958if (pBlock->m_pMetadata->CreateAllocationRequest(8959size,8960alignment,8961allocFlags & ALLOCATION_FLAG_UPPER_ADDRESS,8962strategy,8963&currRequest))8964{8965return CommitAllocationRequest(currRequest, pBlock, size, alignment, pPrivateData, pAllocation);8966}8967return E_OUTOFMEMORY;8968}89698970HRESULT BlockVector::CommitAllocationRequest(8971AllocationRequest& allocRequest,8972NormalBlock* pBlock,8973UINT64 size,8974UINT64 alignment,8975void* pPrivateData,8976Allocation** pAllocation)8977{8978// We no longer have an empty Allocation.8979if (pBlock->m_pMetadata->IsEmpty())8980m_HasEmptyBlock = false;89818982*pAllocation = m_hAllocator->GetAllocationObjectAllocator().Allocate(m_hAllocator, size, alignment, allocRequest.zeroInitialized);8983pBlock->m_pMetadata->Alloc(allocRequest, size, *pAllocation);89848985(*pAllocation)->InitPlaced(allocRequest.allocHandle, pBlock);8986(*pAllocation)->SetPrivateData(pPrivateData);89878988D3D12MA_HEAVY_ASSERT(pBlock->Validate());8989m_hAllocator->m_Budget.AddAllocation(m_hAllocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), size);89908991return S_OK;8992}89938994HRESULT BlockVector::CreateBlock(8995UINT64 blockSize,8996size_t* pNewBlockIndex)8997{8998NormalBlock* const pBlock = D3D12MA_NEW(m_hAllocator->GetAllocs(), NormalBlock)(8999m_hAllocator,9000this,9001m_HeapProps,9002m_HeapFlags,9003blockSize,9004m_NextBlockId++);9005HRESULT hr = pBlock->Init(m_Algorithm, m_ProtectedSession, m_DenyMsaaTextures);9006if (FAILED(hr))9007{9008D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlock);9009return hr;9010}90119012m_hAllocator->SetResidencyPriority(pBlock->GetHeap(), m_ResidencyPriority);90139014m_Blocks.push_back(pBlock);9015if (pNewBlockIndex != NULL)9016{9017*pNewBlockIndex = m_Blocks.size() - 1;9018}90199020return hr;9021}9022#endif // _D3D12MA_BLOCK_VECTOR_FUNCTIONS90239024#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS9025DefragmentationContextPimpl::DefragmentationContextPimpl(9026AllocatorPimpl* hAllocator,9027const DEFRAGMENTATION_DESC& desc,9028BlockVector* poolVector)9029: m_MaxPassBytes(desc.MaxBytesPerPass == 0 ? UINT64_MAX : desc.MaxBytesPerPass),9030m_MaxPassAllocations(desc.MaxAllocationsPerPass == 0 ? UINT32_MAX : desc.MaxAllocationsPerPass),9031m_Moves(hAllocator->GetAllocs())9032{9033m_Algorithm = desc.Flags & DEFRAGMENTATION_FLAG_ALGORITHM_MASK;90349035if (poolVector != NULL)9036{9037m_BlockVectorCount = 1;9038m_PoolBlockVector = poolVector;9039m_pBlockVectors = &m_PoolBlockVector;9040m_PoolBlockVector->SetIncrementalSort(false);9041m_PoolBlockVector->SortByFreeSize();9042}9043else9044{9045m_BlockVectorCount = hAllocator->GetDefaultPoolCount();9046m_PoolBlockVector = NULL;9047m_pBlockVectors = hAllocator->GetDefaultPools();9048for (UINT32 i = 0; i < m_BlockVectorCount; ++i)9049{9050BlockVector* vector = m_pBlockVectors[i];9051if (vector != NULL)9052{9053vector->SetIncrementalSort(false);9054vector->SortByFreeSize();9055}9056}9057}90589059switch (m_Algorithm)9060{9061case 0: // Default algorithm9062m_Algorithm = DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED;9063case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:9064{9065m_AlgorithmState = D3D12MA_NEW_ARRAY(hAllocator->GetAllocs(), StateBalanced, m_BlockVectorCount);9066break;9067}9068}9069}90709071DefragmentationContextPimpl::~DefragmentationContextPimpl()9072{9073if (m_PoolBlockVector != NULL)9074m_PoolBlockVector->SetIncrementalSort(true);9075else9076{9077for (UINT32 i = 0; i < m_BlockVectorCount; ++i)9078{9079BlockVector* vector = m_pBlockVectors[i];9080if (vector != NULL)9081vector->SetIncrementalSort(true);9082}9083}90849085if (m_AlgorithmState)9086{9087switch (m_Algorithm)9088{9089case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:9090D3D12MA_DELETE_ARRAY(m_Moves.GetAllocs(), reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);9091break;9092default:9093D3D12MA_ASSERT(0);9094}9095}9096}90979098HRESULT DefragmentationContextPimpl::DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo)9099{9100if (m_PoolBlockVector != NULL)9101{9102MutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->m_hAllocator->UseMutex());91039104if (m_PoolBlockVector->GetBlockCount() > 1)9105ComputeDefragmentation(*m_PoolBlockVector, 0);9106else if (m_PoolBlockVector->GetBlockCount() == 1)9107ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));91089109// Setup index into block vector9110for (size_t i = 0; i < m_Moves.size(); ++i)9111m_Moves[i].pDstTmpAllocation->SetPrivateData(0);9112}9113else9114{9115for (UINT32 i = 0; i < m_BlockVectorCount; ++i)9116{9117if (m_pBlockVectors[i] != NULL)9118{9119MutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->m_hAllocator->UseMutex());91209121bool end = false;9122size_t movesOffset = m_Moves.size();9123if (m_pBlockVectors[i]->GetBlockCount() > 1)9124{9125end = ComputeDefragmentation(*m_pBlockVectors[i], i);9126}9127else if (m_pBlockVectors[i]->GetBlockCount() == 1)9128{9129end = ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0));9130}91319132// Setup index into block vector9133for (; movesOffset < m_Moves.size(); ++movesOffset)9134m_Moves[movesOffset].pDstTmpAllocation->SetPrivateData(reinterpret_cast<void*>(static_cast<uintptr_t>(i)));91359136if (end)9137break;9138}9139}9140}91419142moveInfo.MoveCount = static_cast<UINT32>(m_Moves.size());9143if (moveInfo.MoveCount > 0)9144{9145moveInfo.pMoves = m_Moves.data();9146return S_FALSE;9147}91489149moveInfo.pMoves = NULL;9150return S_OK;9151}91529153HRESULT DefragmentationContextPimpl::DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo)9154{9155D3D12MA_ASSERT(moveInfo.MoveCount > 0 ? moveInfo.pMoves != NULL : true);91569157HRESULT result = S_OK;9158Vector<FragmentedBlock> immovableBlocks(m_Moves.GetAllocs());91599160for (uint32_t i = 0; i < moveInfo.MoveCount; ++i)9161{9162DEFRAGMENTATION_MOVE& move = moveInfo.pMoves[i];9163size_t prevCount = 0, currentCount = 0;9164UINT64 freedBlockSize = 0;91659166UINT32 vectorIndex;9167BlockVector* vector;9168if (m_PoolBlockVector != NULL)9169{9170vectorIndex = 0;9171vector = m_PoolBlockVector;9172}9173else9174{9175vectorIndex = static_cast<UINT32>(reinterpret_cast<uintptr_t>(move.pDstTmpAllocation->GetPrivateData()));9176vector = m_pBlockVectors[vectorIndex];9177D3D12MA_ASSERT(vector != NULL);9178}91799180switch (move.Operation)9181{9182case DEFRAGMENTATION_MOVE_OPERATION_COPY:9183{9184move.pSrcAllocation->SwapBlockAllocation(move.pDstTmpAllocation);91859186// Scope for locks, Free have it's own lock9187{9188MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9189prevCount = vector->GetBlockCount();9190freedBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize();9191}9192move.pDstTmpAllocation->Release();9193{9194MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9195currentCount = vector->GetBlockCount();9196}91979198result = S_FALSE;9199break;9200}9201case DEFRAGMENTATION_MOVE_OPERATION_IGNORE:9202{9203m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize();9204--m_PassStats.AllocationsMoved;9205move.pDstTmpAllocation->Release();92069207NormalBlock* newBlock = move.pSrcAllocation->GetBlock();9208bool notPresent = true;9209for (const FragmentedBlock& block : immovableBlocks)9210{9211if (block.block == newBlock)9212{9213notPresent = false;9214break;9215}9216}9217if (notPresent)9218immovableBlocks.push_back({ vectorIndex, newBlock });9219break;9220}9221case DEFRAGMENTATION_MOVE_OPERATION_DESTROY:9222{9223m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize();9224--m_PassStats.AllocationsMoved;9225// Scope for locks, Free have it's own lock9226{9227MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9228prevCount = vector->GetBlockCount();9229freedBlockSize = move.pSrcAllocation->GetBlock()->m_pMetadata->GetSize();9230}9231move.pSrcAllocation->Release();9232{9233MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9234currentCount = vector->GetBlockCount();9235}9236freedBlockSize *= prevCount - currentCount;92379238UINT64 dstBlockSize;9239{9240MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9241dstBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize();9242}9243move.pDstTmpAllocation->Release();9244{9245MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9246freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());9247currentCount = vector->GetBlockCount();9248}92499250result = S_FALSE;9251break;9252}9253default:9254D3D12MA_ASSERT(0);9255}92569257if (prevCount > currentCount)9258{9259size_t freedBlocks = prevCount - currentCount;9260m_PassStats.HeapsFreed += static_cast<UINT32>(freedBlocks);9261m_PassStats.BytesFreed += freedBlockSize;9262}9263}9264moveInfo.MoveCount = 0;9265moveInfo.pMoves = NULL;9266m_Moves.clear();92679268// Update stats9269m_GlobalStats.AllocationsMoved += m_PassStats.AllocationsMoved;9270m_GlobalStats.BytesFreed += m_PassStats.BytesFreed;9271m_GlobalStats.BytesMoved += m_PassStats.BytesMoved;9272m_GlobalStats.HeapsFreed += m_PassStats.HeapsFreed;9273m_PassStats = { 0 };92749275// Move blocks with immovable allocations according to algorithm9276if (immovableBlocks.size() > 0)9277{9278// Move to the begining9279for (const FragmentedBlock& block : immovableBlocks)9280{9281BlockVector* vector = m_pBlockVectors[block.data];9282MutexLockWrite lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());92839284for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)9285{9286if (vector->GetBlock(i) == block.block)9287{9288D3D12MA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);9289break;9290}9291}9292}9293}9294return result;9295}92969297bool DefragmentationContextPimpl::ComputeDefragmentation(BlockVector& vector, size_t index)9298{9299switch (m_Algorithm)9300{9301case DEFRAGMENTATION_FLAG_ALGORITHM_FAST:9302return ComputeDefragmentation_Fast(vector);9303default:9304D3D12MA_ASSERT(0);9305case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:9306return ComputeDefragmentation_Balanced(vector, index, true);9307case DEFRAGMENTATION_FLAG_ALGORITHM_FULL:9308return ComputeDefragmentation_Full(vector);9309}9310}93119312DefragmentationContextPimpl::MoveAllocationData DefragmentationContextPimpl::GetMoveData(9313AllocHandle handle, BlockMetadata* metadata)9314{9315MoveAllocationData moveData;9316moveData.move.pSrcAllocation = (Allocation*)metadata->GetAllocationPrivateData(handle);9317moveData.size = moveData.move.pSrcAllocation->GetSize();9318moveData.alignment = moveData.move.pSrcAllocation->GetAlignment();9319moveData.flags = ALLOCATION_FLAG_NONE;93209321return moveData;9322}93239324DefragmentationContextPimpl::CounterStatus DefragmentationContextPimpl::CheckCounters(UINT64 bytes)9325{9326// Ignore allocation if will exceed max size for copy9327if (m_PassStats.BytesMoved + bytes > m_MaxPassBytes)9328{9329if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)9330return CounterStatus::Ignore;9331else9332return CounterStatus::End;9333}9334return CounterStatus::Pass;9335}93369337bool DefragmentationContextPimpl::IncrementCounters(UINT64 bytes)9338{9339m_PassStats.BytesMoved += bytes;9340// Early return when max found9341if (++m_PassStats.AllocationsMoved >= m_MaxPassAllocations || m_PassStats.BytesMoved >= m_MaxPassBytes)9342{9343D3D12MA_ASSERT((m_PassStats.AllocationsMoved == m_MaxPassAllocations ||9344m_PassStats.BytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!");9345return true;9346}9347return false;9348}93499350bool DefragmentationContextPimpl::ReallocWithinBlock(BlockVector& vector, NormalBlock* block)9351{9352BlockMetadata* metadata = block->m_pMetadata;93539354for (AllocHandle handle = metadata->GetAllocationListBegin();9355handle != (AllocHandle)0;9356handle = metadata->GetNextAllocation(handle))9357{9358MoveAllocationData moveData = GetMoveData(handle, metadata);9359// Ignore newly created allocations by defragmentation algorithm9360if (moveData.move.pSrcAllocation->GetPrivateData() == this)9361continue;9362switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9363{9364case CounterStatus::Ignore:9365continue;9366case CounterStatus::End:9367return true;9368default:9369D3D12MA_ASSERT(0);9370case CounterStatus::Pass:9371break;9372}93739374UINT64 offset = moveData.move.pSrcAllocation->GetOffset();9375if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)9376{9377AllocationRequest request = {};9378if (metadata->CreateAllocationRequest(9379moveData.size,9380moveData.alignment,9381false,9382ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,9383&request))9384{9385if (metadata->GetAllocationOffset(request.allocHandle) < offset)9386{9387if (SUCCEEDED(vector.CommitAllocationRequest(9388request,9389block,9390moveData.size,9391moveData.alignment,9392this,9393&moveData.move.pDstTmpAllocation)))9394{9395m_Moves.push_back(moveData.move);9396if (IncrementCounters(moveData.size))9397return true;9398}9399}9400}9401}9402}9403return false;9404}94059406bool DefragmentationContextPimpl::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector)9407{9408for (; start < end; ++start)9409{9410NormalBlock* dstBlock = vector.GetBlock(start);9411if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)9412{9413if (SUCCEEDED(vector.AllocateFromBlock(dstBlock,9414data.size,9415data.alignment,9416data.flags,9417this,94180,9419&data.move.pDstTmpAllocation)))9420{9421m_Moves.push_back(data.move);9422if (IncrementCounters(data.size))9423return true;9424break;9425}9426}9427}9428return false;9429}94309431bool DefragmentationContextPimpl::ComputeDefragmentation_Fast(BlockVector& vector)9432{9433// Move only between blocks94349435// Go through allocations in last blocks and try to fit them inside first ones9436for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)9437{9438BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;94399440for (AllocHandle handle = metadata->GetAllocationListBegin();9441handle != (AllocHandle)0;9442handle = metadata->GetNextAllocation(handle))9443{9444MoveAllocationData moveData = GetMoveData(handle, metadata);9445// Ignore newly created allocations by defragmentation algorithm9446if (moveData.move.pSrcAllocation->GetPrivateData() == this)9447continue;9448switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9449{9450case CounterStatus::Ignore:9451continue;9452case CounterStatus::End:9453return true;9454default:9455D3D12MA_ASSERT(0);9456case CounterStatus::Pass:9457break;9458}94599460// Check all previous blocks for free space9461if (AllocInOtherBlock(0, i, moveData, vector))9462return true;9463}9464}9465return false;9466}94679468bool DefragmentationContextPimpl::ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update)9469{9470// Go over every allocation and try to fit it in previous blocks at lowest offsets,9471// if not possible: realloc within single block to minimize offset (exclude offset == 0),9472// but only if there are noticable gaps between them (some heuristic, ex. average size of allocation in block)9473D3D12MA_ASSERT(m_AlgorithmState != NULL);94749475StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];9476if (update && vectorState.avgAllocSize == UINT64_MAX)9477UpdateVectorStatistics(vector, vectorState);94789479const size_t startMoveCount = m_Moves.size();9480UINT64 minimalFreeRegion = vectorState.avgFreeSize / 2;9481for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)9482{9483NormalBlock* block = vector.GetBlock(i);9484BlockMetadata* metadata = block->m_pMetadata;9485UINT64 prevFreeRegionSize = 0;94869487for (AllocHandle handle = metadata->GetAllocationListBegin();9488handle != (AllocHandle)0;9489handle = metadata->GetNextAllocation(handle))9490{9491MoveAllocationData moveData = GetMoveData(handle, metadata);9492// Ignore newly created allocations by defragmentation algorithm9493if (moveData.move.pSrcAllocation->GetPrivateData() == this)9494continue;9495switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9496{9497case CounterStatus::Ignore:9498continue;9499case CounterStatus::End:9500return true;9501default:9502D3D12MA_ASSERT(0);9503case CounterStatus::Pass:9504break;9505}95069507// Check all previous blocks for free space9508const size_t prevMoveCount = m_Moves.size();9509if (AllocInOtherBlock(0, i, moveData, vector))9510return true;95119512UINT64 nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);9513// If no room found then realloc within block for lower offset9514UINT64 offset = moveData.move.pSrcAllocation->GetOffset();9515if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)9516{9517// Check if realloc will make sense9518if (prevFreeRegionSize >= minimalFreeRegion ||9519nextFreeRegionSize >= minimalFreeRegion ||9520moveData.size <= vectorState.avgFreeSize ||9521moveData.size <= vectorState.avgAllocSize)9522{9523AllocationRequest request = {};9524if (metadata->CreateAllocationRequest(9525moveData.size,9526moveData.alignment,9527false,9528ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,9529&request))9530{9531if (metadata->GetAllocationOffset(request.allocHandle) < offset)9532{9533if (SUCCEEDED(vector.CommitAllocationRequest(9534request,9535block,9536moveData.size,9537moveData.alignment,9538this,9539&moveData.move.pDstTmpAllocation)))9540{9541m_Moves.push_back(moveData.move);9542if (IncrementCounters(moveData.size))9543return true;9544}9545}9546}9547}9548}9549prevFreeRegionSize = nextFreeRegionSize;9550}9551}95529553// No moves perfomed, update statistics to current vector state9554if (startMoveCount == m_Moves.size() && !update)9555{9556vectorState.avgAllocSize = UINT64_MAX;9557return ComputeDefragmentation_Balanced(vector, index, false);9558}9559return false;9560}95619562bool DefragmentationContextPimpl::ComputeDefragmentation_Full(BlockVector& vector)9563{9564// Go over every allocation and try to fit it in previous blocks at lowest offsets,9565// if not possible: realloc within single block to minimize offset (exclude offset == 0)95669567for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)9568{9569NormalBlock* block = vector.GetBlock(i);9570BlockMetadata* metadata = block->m_pMetadata;95719572for (AllocHandle handle = metadata->GetAllocationListBegin();9573handle != (AllocHandle)0;9574handle = metadata->GetNextAllocation(handle))9575{9576MoveAllocationData moveData = GetMoveData(handle, metadata);9577// Ignore newly created allocations by defragmentation algorithm9578if (moveData.move.pSrcAllocation->GetPrivateData() == this)9579continue;9580switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9581{9582case CounterStatus::Ignore:9583continue;9584case CounterStatus::End:9585return true;9586default:9587D3D12MA_ASSERT(0);9588case CounterStatus::Pass:9589break;9590}95919592// Check all previous blocks for free space9593const size_t prevMoveCount = m_Moves.size();9594if (AllocInOtherBlock(0, i, moveData, vector))9595return true;95969597// If no room found then realloc within block for lower offset9598UINT64 offset = moveData.move.pSrcAllocation->GetOffset();9599if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)9600{9601AllocationRequest request = {};9602if (metadata->CreateAllocationRequest(9603moveData.size,9604moveData.alignment,9605false,9606ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,9607&request))9608{9609if (metadata->GetAllocationOffset(request.allocHandle) < offset)9610{9611if (SUCCEEDED(vector.CommitAllocationRequest(9612request,9613block,9614moveData.size,9615moveData.alignment,9616this,9617&moveData.move.pDstTmpAllocation)))9618{9619m_Moves.push_back(moveData.move);9620if (IncrementCounters(moveData.size))9621return true;9622}9623}9624}9625}9626}9627}9628return false;9629}96309631void DefragmentationContextPimpl::UpdateVectorStatistics(BlockVector& vector, StateBalanced& state)9632{9633size_t allocCount = 0;9634size_t freeCount = 0;9635state.avgFreeSize = 0;9636state.avgAllocSize = 0;96379638for (size_t i = 0; i < vector.GetBlockCount(); ++i)9639{9640BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;96419642allocCount += metadata->GetAllocationCount();9643freeCount += metadata->GetFreeRegionsCount();9644state.avgFreeSize += metadata->GetSumFreeSize();9645state.avgAllocSize += metadata->GetSize();9646}96479648state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;9649state.avgFreeSize /= freeCount;9650}9651#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS96529653#ifndef _D3D12MA_POOL_PIMPL_FUNCTIONS9654PoolPimpl::PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc)9655: m_Allocator(allocator),9656m_Desc(desc),9657m_BlockVector(NULL),9658m_Name(NULL)9659{9660const bool explicitBlockSize = desc.BlockSize != 0;9661const UINT64 preferredBlockSize = explicitBlockSize ? desc.BlockSize : D3D12MA_DEFAULT_BLOCK_SIZE;9662UINT maxBlockCount = desc.MaxBlockCount != 0 ? desc.MaxBlockCount : UINT_MAX;96639664#ifndef __ID3D12Device4_INTERFACE_DEFINED__9665D3D12MA_ASSERT(m_Desc.pProtectedSession == NULL);9666#endif96679668m_BlockVector = D3D12MA_NEW(allocator->GetAllocs(), BlockVector)(9669allocator, desc.HeapProperties, desc.HeapFlags,9670preferredBlockSize,9671desc.MinBlockCount, maxBlockCount,9672explicitBlockSize,9673D3D12MA_MAX(desc.MinAllocationAlignment, (UINT64)D3D12MA_DEBUG_ALIGNMENT),9674(desc.Flags & POOL_FLAG_ALGORITHM_MASK) != 0,9675(desc.Flags & POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0,9676desc.pProtectedSession,9677desc.ResidencyPriority);9678}96799680PoolPimpl::~PoolPimpl()9681{9682D3D12MA_ASSERT(m_PrevPool == NULL && m_NextPool == NULL);9683FreeName();9684D3D12MA_DELETE(m_Allocator->GetAllocs(), m_BlockVector);9685}96869687HRESULT PoolPimpl::Init()9688{9689m_CommittedAllocations.Init(m_Allocator->UseMutex(), m_Desc.HeapProperties.Type, this);9690return m_BlockVector->CreateMinBlocks();9691}96929693void PoolPimpl::GetStatistics(Statistics& outStats)9694{9695ClearStatistics(outStats);9696m_BlockVector->AddStatistics(outStats);9697m_CommittedAllocations.AddStatistics(outStats);9698}96999700void PoolPimpl::CalculateStatistics(DetailedStatistics& outStats)9701{9702ClearDetailedStatistics(outStats);9703AddDetailedStatistics(outStats);9704}97059706void PoolPimpl::AddDetailedStatistics(DetailedStatistics& inoutStats)9707{9708m_BlockVector->AddDetailedStatistics(inoutStats);9709m_CommittedAllocations.AddDetailedStatistics(inoutStats);9710}97119712void PoolPimpl::SetName(LPCWSTR Name)9713{9714FreeName();97159716if (Name)9717{9718const size_t nameCharCount = wcslen(Name) + 1;9719m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);9720memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));9721}9722}97239724void PoolPimpl::FreeName()9725{9726if (m_Name)9727{9728const size_t nameCharCount = wcslen(m_Name) + 1;9729D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);9730m_Name = NULL;9731}9732}9733#endif // _D3D12MA_POOL_PIMPL_FUNCTIONS973497359736#ifndef _D3D12MA_PUBLIC_INTERFACE9737HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)9738{9739if (!pDesc || !ppAllocator || !pDesc->pDevice || !pDesc->pAdapter ||9740!(pDesc->PreferredBlockSize == 0 || (pDesc->PreferredBlockSize >= 16 && pDesc->PreferredBlockSize < 0x10000000000ull)))9741{9742D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateAllocator.");9743return E_INVALIDARG;9744}97459746D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK97479748ALLOCATION_CALLBACKS allocationCallbacks;9749SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);97509751*ppAllocator = D3D12MA_NEW(allocationCallbacks, Allocator)(allocationCallbacks, *pDesc);9752HRESULT hr = (*ppAllocator)->m_Pimpl->Init(*pDesc);9753if (FAILED(hr))9754{9755D3D12MA_DELETE(allocationCallbacks, *ppAllocator);9756*ppAllocator = NULL;9757}9758return hr;9759}97609761HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock)9762{9763if (!pDesc || !ppVirtualBlock)9764{9765D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateVirtualBlock.");9766return E_INVALIDARG;9767}97689769D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK97709771ALLOCATION_CALLBACKS allocationCallbacks;9772SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);97739774*ppVirtualBlock = D3D12MA_NEW(allocationCallbacks, VirtualBlock)(allocationCallbacks, *pDesc);9775return S_OK;9776}97779778#ifndef _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS9779HRESULT STDMETHODCALLTYPE IUnknownImpl::QueryInterface(REFIID riid, void** ppvObject)9780{9781if (ppvObject == NULL)9782return E_POINTER;9783if (riid == IID_IUnknown)9784{9785++m_RefCount;9786*ppvObject = this;9787return S_OK;9788}9789*ppvObject = NULL;9790return E_NOINTERFACE;9791}97929793ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef()9794{9795return ++m_RefCount;9796}97979798ULONG STDMETHODCALLTYPE IUnknownImpl::Release()9799{9800D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK98019802const uint32_t newRefCount = --m_RefCount;9803if (newRefCount == 0)9804ReleaseThis();9805return newRefCount;9806}9807#endif // _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS98089809#ifndef _D3D12MA_ALLOCATION_FUNCTIONS9810void Allocation::PackedData::SetType(Type type)9811{9812const UINT u = (UINT)type;9813D3D12MA_ASSERT(u < (1u << 2));9814m_Type = u;9815}98169817void Allocation::PackedData::SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension)9818{9819const UINT u = (UINT)resourceDimension;9820D3D12MA_ASSERT(u < (1u << 3));9821m_ResourceDimension = u;9822}98239824void Allocation::PackedData::SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags)9825{9826const UINT u = (UINT)resourceFlags;9827D3D12MA_ASSERT(u < (1u << 24));9828m_ResourceFlags = u;9829}98309831void Allocation::PackedData::SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout)9832{9833const UINT u = (UINT)textureLayout;9834D3D12MA_ASSERT(u < (1u << 9));9835m_TextureLayout = u;9836}98379838UINT64 Allocation::GetOffset() const9839{9840switch (m_PackedData.GetType())9841{9842case TYPE_COMMITTED:9843case TYPE_HEAP:9844return 0;9845case TYPE_PLACED:9846return m_Placed.block->m_pMetadata->GetAllocationOffset(m_Placed.allocHandle);9847default:9848D3D12MA_ASSERT(0);9849return 0;9850}9851}98529853void Allocation::SetResource(ID3D12Resource* pResource)9854{9855if (pResource != m_Resource)9856{9857if (m_Resource)9858m_Resource->Release();9859m_Resource = pResource;9860if (m_Resource)9861m_Resource->AddRef();9862}9863}98649865ID3D12Heap* Allocation::GetHeap() const9866{9867switch (m_PackedData.GetType())9868{9869case TYPE_COMMITTED:9870return NULL;9871case TYPE_PLACED:9872return m_Placed.block->GetHeap();9873case TYPE_HEAP:9874return m_Heap.heap;9875default:9876D3D12MA_ASSERT(0);9877return 0;9878}9879}98809881void Allocation::SetName(LPCWSTR Name)9882{9883FreeName();98849885if (Name)9886{9887const size_t nameCharCount = wcslen(Name) + 1;9888m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);9889memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));9890}9891}98929893void Allocation::ReleaseThis()9894{9895if (this == NULL)9896{9897return;9898}98999900SAFE_RELEASE(m_Resource);99019902switch (m_PackedData.GetType())9903{9904case TYPE_COMMITTED:9905m_Allocator->FreeCommittedMemory(this);9906break;9907case TYPE_PLACED:9908m_Allocator->FreePlacedMemory(this);9909break;9910case TYPE_HEAP:9911m_Allocator->FreeHeapMemory(this);9912break;9913}99149915FreeName();99169917m_Allocator->GetAllocationObjectAllocator().Free(this);9918}99199920Allocation::Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, BOOL wasZeroInitialized)9921: m_Allocator{ allocator },9922m_Size{ size },9923m_Alignment{ alignment },9924m_Resource{ NULL },9925m_pPrivateData{ NULL },9926m_Name{ NULL }9927{9928D3D12MA_ASSERT(allocator);99299930m_PackedData.SetType(TYPE_COUNT);9931m_PackedData.SetResourceDimension(D3D12_RESOURCE_DIMENSION_UNKNOWN);9932m_PackedData.SetResourceFlags(D3D12_RESOURCE_FLAG_NONE);9933m_PackedData.SetTextureLayout(D3D12_TEXTURE_LAYOUT_UNKNOWN);9934m_PackedData.SetWasZeroInitialized(wasZeroInitialized);9935}99369937void Allocation::InitCommitted(CommittedAllocationList* list)9938{9939m_PackedData.SetType(TYPE_COMMITTED);9940m_Committed.list = list;9941m_Committed.prev = NULL;9942m_Committed.next = NULL;9943}99449945void Allocation::InitPlaced(AllocHandle allocHandle, NormalBlock* block)9946{9947m_PackedData.SetType(TYPE_PLACED);9948m_Placed.allocHandle = allocHandle;9949m_Placed.block = block;9950}99519952void Allocation::InitHeap(CommittedAllocationList* list, ID3D12Heap* heap)9953{9954m_PackedData.SetType(TYPE_HEAP);9955m_Heap.list = list;9956m_Committed.prev = NULL;9957m_Committed.next = NULL;9958m_Heap.heap = heap;9959}99609961void Allocation::SwapBlockAllocation(Allocation* allocation)9962{9963D3D12MA_ASSERT(allocation != NULL);9964D3D12MA_ASSERT(m_PackedData.GetType() == TYPE_PLACED);9965D3D12MA_ASSERT(allocation->m_PackedData.GetType() == TYPE_PLACED);99669967D3D12MA_SWAP(m_Resource, allocation->m_Resource);9968m_PackedData.SetWasZeroInitialized(allocation->m_PackedData.WasZeroInitialized());9969m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, allocation);9970D3D12MA_SWAP(m_Placed, allocation->m_Placed);9971m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, this);9972}99739974AllocHandle Allocation::GetAllocHandle() const9975{9976switch (m_PackedData.GetType())9977{9978case TYPE_COMMITTED:9979case TYPE_HEAP:9980return (AllocHandle)0;9981case TYPE_PLACED:9982return m_Placed.allocHandle;9983default:9984D3D12MA_ASSERT(0);9985return (AllocHandle)0;9986}9987}99889989NormalBlock* Allocation::GetBlock()9990{9991switch (m_PackedData.GetType())9992{9993case TYPE_COMMITTED:9994case TYPE_HEAP:9995return NULL;9996case TYPE_PLACED:9997return m_Placed.block;9998default:9999D3D12MA_ASSERT(0);10000return NULL;10001}10002}1000310004template<typename D3D12_RESOURCE_DESC_T>10005void Allocation::SetResourcePointer(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc)10006{10007D3D12MA_ASSERT(m_Resource == NULL && pResourceDesc);10008m_Resource = resource;10009m_PackedData.SetResourceDimension(pResourceDesc->Dimension);10010m_PackedData.SetResourceFlags(pResourceDesc->Flags);10011m_PackedData.SetTextureLayout(pResourceDesc->Layout);10012}1001310014void Allocation::FreeName()10015{10016if (m_Name)10017{10018const size_t nameCharCount = wcslen(m_Name) + 1;10019D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);10020m_Name = NULL;10021}10022}10023#endif // _D3D12MA_ALLOCATION_FUNCTIONS1002410025#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS10026HRESULT DefragmentationContext::BeginPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo)10027{10028D3D12MA_ASSERT(pPassInfo);10029return m_Pimpl->DefragmentPassBegin(*pPassInfo);10030}1003110032HRESULT DefragmentationContext::EndPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo)10033{10034D3D12MA_ASSERT(pPassInfo);10035return m_Pimpl->DefragmentPassEnd(*pPassInfo);10036}1003710038void DefragmentationContext::GetStats(DEFRAGMENTATION_STATS* pStats)10039{10040D3D12MA_ASSERT(pStats);10041m_Pimpl->GetStats(*pStats);10042}1004310044void DefragmentationContext::ReleaseThis()10045{10046if (this == NULL)10047{10048return;10049}1005010051D3D12MA_DELETE(m_Pimpl->GetAllocs(), this);10052}1005310054DefragmentationContext::DefragmentationContext(AllocatorPimpl* allocator,10055const DEFRAGMENTATION_DESC& desc,10056BlockVector* poolVector)10057: m_Pimpl(D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContextPimpl)(allocator, desc, poolVector)) {}1005810059DefragmentationContext::~DefragmentationContext()10060{10061D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);10062}10063#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS1006410065#ifndef _D3D12MA_POOL_FUNCTIONS10066POOL_DESC Pool::GetDesc() const10067{10068return m_Pimpl->GetDesc();10069}1007010071void Pool::GetStatistics(Statistics* pStats)10072{10073D3D12MA_ASSERT(pStats);10074D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10075m_Pimpl->GetStatistics(*pStats);10076}1007710078void Pool::CalculateStatistics(DetailedStatistics* pStats)10079{10080D3D12MA_ASSERT(pStats);10081D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10082m_Pimpl->CalculateStatistics(*pStats);10083}1008410085void Pool::SetName(LPCWSTR Name)10086{10087D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10088m_Pimpl->SetName(Name);10089}1009010091LPCWSTR Pool::GetName() const10092{10093return m_Pimpl->GetName();10094}1009510096HRESULT Pool::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext)10097{10098D3D12MA_ASSERT(pDesc && ppContext);1009910100// Check for support10101if (m_Pimpl->GetBlockVector()->GetAlgorithm() & POOL_FLAG_ALGORITHM_LINEAR)10102return E_NOINTERFACE;1010310104AllocatorPimpl* allocator = m_Pimpl->GetAllocator();10105*ppContext = D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContext)(allocator, *pDesc, m_Pimpl->GetBlockVector());10106return S_OK;10107}1010810109void Pool::ReleaseThis()10110{10111if (this == NULL)10112{10113return;10114}1011510116D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), this);10117}1011810119Pool::Pool(Allocator* allocator, const POOL_DESC& desc)10120: m_Pimpl(D3D12MA_NEW(allocator->m_Pimpl->GetAllocs(), PoolPimpl)(allocator->m_Pimpl, desc)) {}1012110122Pool::~Pool()10123{10124m_Pimpl->GetAllocator()->UnregisterPool(this, m_Pimpl->GetDesc().HeapProperties.Type);1012510126D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), m_Pimpl);10127}10128#endif // _D3D12MA_POOL_FUNCTIONS1012910130#ifndef _D3D12MA_ALLOCATOR_FUNCTIONS10131const D3D12_FEATURE_DATA_D3D12_OPTIONS& Allocator::GetD3D12Options() const10132{10133return m_Pimpl->GetD3D12Options();10134}1013510136BOOL Allocator::IsUMA() const10137{10138return m_Pimpl->IsUMA();10139}1014010141BOOL Allocator::IsCacheCoherentUMA() const10142{10143return m_Pimpl->IsCacheCoherentUMA();10144}1014510146UINT64 Allocator::GetMemoryCapacity(UINT memorySegmentGroup) const10147{10148return m_Pimpl->GetMemoryCapacity(memorySegmentGroup);10149}1015010151HRESULT Allocator::CreateResource(10152const ALLOCATION_DESC* pAllocDesc,10153const D3D12_RESOURCE_DESC* pResourceDesc,10154D3D12_RESOURCE_STATES InitialResourceState,10155const D3D12_CLEAR_VALUE* pOptimizedClearValue,10156Allocation** ppAllocation,10157REFIID riidResource,10158void** ppvResource)10159{10160if (!pAllocDesc || !pResourceDesc || !ppAllocation)10161{10162D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource.");10163return E_INVALIDARG;10164}10165D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10166return m_Pimpl->CreateResource(10167pAllocDesc,10168CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),10169ppAllocation,10170riidResource,10171ppvResource);10172}1017310174#ifdef __ID3D12Device8_INTERFACE_DEFINED__10175HRESULT Allocator::CreateResource2(10176const ALLOCATION_DESC* pAllocDesc,10177const D3D12_RESOURCE_DESC1* pResourceDesc,10178D3D12_RESOURCE_STATES InitialResourceState,10179const D3D12_CLEAR_VALUE* pOptimizedClearValue,10180Allocation** ppAllocation,10181REFIID riidResource,10182void** ppvResource)10183{10184if (!pAllocDesc || !pResourceDesc || !ppAllocation)10185{10186D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource2.");10187return E_INVALIDARG;10188}10189D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10190return m_Pimpl->CreateResource(10191pAllocDesc,10192CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),10193ppAllocation,10194riidResource,10195ppvResource);10196}10197#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__1019810199#ifdef __ID3D12Device10_INTERFACE_DEFINED__10200HRESULT Allocator::CreateResource3(10201const ALLOCATION_DESC* pAllocDesc,10202const D3D12_RESOURCE_DESC1* pResourceDesc,10203D3D12_BARRIER_LAYOUT InitialLayout,10204const D3D12_CLEAR_VALUE* pOptimizedClearValue,10205UINT32 NumCastableFormats,10206DXGI_FORMAT* pCastableFormats,10207Allocation** ppAllocation,10208REFIID riidResource,10209void** ppvResource)10210{10211if (!pAllocDesc || !pResourceDesc || !ppAllocation)10212{10213D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource3.");10214return E_INVALIDARG;10215}10216D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10217return m_Pimpl->CreateResource(10218pAllocDesc,10219CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats),10220ppAllocation,10221riidResource,10222ppvResource);10223}10224#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__1022510226HRESULT Allocator::AllocateMemory(10227const ALLOCATION_DESC* pAllocDesc,10228const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,10229Allocation** ppAllocation)10230{10231if (!ValidateAllocateMemoryParameters(pAllocDesc, pAllocInfo, ppAllocation))10232{10233D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::AllocateMemory.");10234return E_INVALIDARG;10235}10236D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10237return m_Pimpl->AllocateMemory(pAllocDesc, pAllocInfo, ppAllocation);10238}1023910240HRESULT Allocator::CreateAliasingResource(10241Allocation* pAllocation,10242UINT64 AllocationLocalOffset,10243const D3D12_RESOURCE_DESC* pResourceDesc,10244D3D12_RESOURCE_STATES InitialResourceState,10245const D3D12_CLEAR_VALUE* pOptimizedClearValue,10246REFIID riidResource,10247void** ppvResource)10248{10249if (!pAllocation || !pResourceDesc || !ppvResource)10250{10251D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");10252return E_INVALIDARG;10253}10254D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10255return m_Pimpl->CreateAliasingResource(10256pAllocation,10257AllocationLocalOffset,10258CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),10259riidResource,10260ppvResource);10261}1026210263#ifdef __ID3D12Device8_INTERFACE_DEFINED__10264HRESULT Allocator::CreateAliasingResource1(10265Allocation* pAllocation,10266UINT64 AllocationLocalOffset,10267const D3D12_RESOURCE_DESC1* pResourceDesc,10268D3D12_RESOURCE_STATES InitialResourceState,10269const D3D12_CLEAR_VALUE* pOptimizedClearValue,10270REFIID riidResource,10271void** ppvResource)10272{10273if (!pAllocation || !pResourceDesc || !ppvResource)10274{10275D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");10276return E_INVALIDARG;10277}10278D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10279return m_Pimpl->CreateAliasingResource(10280pAllocation,10281AllocationLocalOffset,10282CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),10283riidResource,10284ppvResource);10285}10286#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__1028710288#ifdef __ID3D12Device10_INTERFACE_DEFINED__10289HRESULT Allocator::CreateAliasingResource2(10290Allocation* pAllocation,10291UINT64 AllocationLocalOffset,10292const D3D12_RESOURCE_DESC1* pResourceDesc,10293D3D12_BARRIER_LAYOUT InitialLayout,10294const D3D12_CLEAR_VALUE* pOptimizedClearValue,10295UINT32 NumCastableFormats,10296DXGI_FORMAT* pCastableFormats,10297REFIID riidResource,10298void** ppvResource)10299{10300if (!pAllocation || !pResourceDesc || !ppvResource)10301{10302D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");10303return E_INVALIDARG;10304}10305D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10306return m_Pimpl->CreateAliasingResource(10307pAllocation,10308AllocationLocalOffset,10309CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats),10310riidResource,10311ppvResource);10312}10313#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__1031410315HRESULT Allocator::CreatePool(10316const POOL_DESC* pPoolDesc,10317Pool** ppPool)10318{10319if (!pPoolDesc || !ppPool ||10320(pPoolDesc->MaxBlockCount > 0 && pPoolDesc->MaxBlockCount < pPoolDesc->MinBlockCount) ||10321(pPoolDesc->MinAllocationAlignment > 0 && !IsPow2(pPoolDesc->MinAllocationAlignment)))10322{10323D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreatePool.");10324return E_INVALIDARG;10325}10326if (!m_Pimpl->HeapFlagsFulfillResourceHeapTier(pPoolDesc->HeapFlags))10327{10328D3D12MA_ASSERT(0 && "Invalid pPoolDesc->HeapFlags passed to Allocator::CreatePool. Did you forget to handle ResourceHeapTier=1?");10329return E_INVALIDARG;10330}10331D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10332* ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc);10333HRESULT hr = (*ppPool)->m_Pimpl->Init();10334if (SUCCEEDED(hr))10335{10336m_Pimpl->RegisterPool(*ppPool, pPoolDesc->HeapProperties.Type);10337}10338else10339{10340D3D12MA_DELETE(m_Pimpl->GetAllocs(), *ppPool);10341*ppPool = NULL;10342}10343return hr;10344}1034510346void Allocator::SetCurrentFrameIndex(UINT frameIndex)10347{10348D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10349m_Pimpl->SetCurrentFrameIndex(frameIndex);10350}1035110352void Allocator::GetBudget(Budget* pLocalBudget, Budget* pNonLocalBudget)10353{10354if (pLocalBudget == NULL && pNonLocalBudget == NULL)10355{10356return;10357}10358D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10359m_Pimpl->GetBudget(pLocalBudget, pNonLocalBudget);10360}1036110362void Allocator::CalculateStatistics(TotalStatistics* pStats)10363{10364D3D12MA_ASSERT(pStats);10365D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10366m_Pimpl->CalculateStatistics(*pStats);10367}1036810369void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const10370{10371D3D12MA_ASSERT(ppStatsString);10372D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10373m_Pimpl->BuildStatsString(ppStatsString, DetailedMap);10374}1037510376void Allocator::FreeStatsString(WCHAR* pStatsString) const10377{10378if (pStatsString != NULL)10379{10380D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10381m_Pimpl->FreeStatsString(pStatsString);10382}10383}1038410385void Allocator::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext)10386{10387D3D12MA_ASSERT(pDesc && ppContext);1038810389*ppContext = D3D12MA_NEW(m_Pimpl->GetAllocs(), DefragmentationContext)(m_Pimpl, *pDesc, NULL);10390}1039110392void Allocator::ReleaseThis()10393{10394// Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.10395const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->GetAllocs();10396D3D12MA_DELETE(allocationCallbacksCopy, this);10397}1039810399Allocator::Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc)10400: m_Pimpl(D3D12MA_NEW(allocationCallbacks, AllocatorPimpl)(allocationCallbacks, desc)) {}1040110402Allocator::~Allocator()10403{10404D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);10405}10406#endif // _D3D12MA_ALLOCATOR_FUNCTIONS1040710408#ifndef _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS10409BOOL VirtualBlock::IsEmpty() const10410{10411D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10412return m_Pimpl->m_Metadata->IsEmpty() ? TRUE : FALSE;10413}1041410415void VirtualBlock::GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOCATION_INFO* pInfo) const10416{10417D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0 && pInfo);1041810419D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10420m_Pimpl->m_Metadata->GetAllocationInfo(allocation.AllocHandle, *pInfo);10421}1042210423HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, VirtualAllocation* pAllocation, UINT64* pOffset)10424{10425if (!pDesc || !pAllocation || pDesc->Size == 0 || !IsPow2(pDesc->Alignment))10426{10427D3D12MA_ASSERT(0 && "Invalid arguments passed to VirtualBlock::Allocate.");10428return E_INVALIDARG;10429}1043010431D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK1043210433const UINT64 alignment = pDesc->Alignment != 0 ? pDesc->Alignment : 1;10434AllocationRequest allocRequest = {};10435if (m_Pimpl->m_Metadata->CreateAllocationRequest(10436pDesc->Size,10437alignment,10438pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_UPPER_ADDRESS,10439pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_STRATEGY_MASK,10440&allocRequest))10441{10442m_Pimpl->m_Metadata->Alloc(allocRequest, pDesc->Size, pDesc->pPrivateData);10443D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10444pAllocation->AllocHandle = allocRequest.allocHandle;1044510446if (pOffset)10447*pOffset = m_Pimpl->m_Metadata->GetAllocationOffset(allocRequest.allocHandle);10448return S_OK;10449}1045010451pAllocation->AllocHandle = (AllocHandle)0;10452if (pOffset)10453*pOffset = UINT64_MAX;1045410455return E_OUTOFMEMORY;10456}1045710458void VirtualBlock::FreeAllocation(VirtualAllocation allocation)10459{10460if (allocation.AllocHandle == (AllocHandle)0)10461return;1046210463D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK1046410465m_Pimpl->m_Metadata->Free(allocation.AllocHandle);10466D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10467}1046810469void VirtualBlock::Clear()10470{10471D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK1047210473m_Pimpl->m_Metadata->Clear();10474D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10475}1047610477void VirtualBlock::SetAllocationPrivateData(VirtualAllocation allocation, void* pPrivateData)10478{10479D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0);1048010481D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10482m_Pimpl->m_Metadata->SetAllocationPrivateData(allocation.AllocHandle, pPrivateData);10483}1048410485void VirtualBlock::GetStatistics(Statistics* pStats) const10486{10487D3D12MA_ASSERT(pStats);10488D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10489D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10490ClearStatistics(*pStats);10491m_Pimpl->m_Metadata->AddStatistics(*pStats);10492}1049310494void VirtualBlock::CalculateStatistics(DetailedStatistics* pStats) const10495{10496D3D12MA_ASSERT(pStats);10497D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10498D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10499ClearDetailedStatistics(*pStats);10500m_Pimpl->m_Metadata->AddDetailedStatistics(*pStats);10501}1050210503void VirtualBlock::BuildStatsString(WCHAR** ppStatsString) const10504{10505D3D12MA_ASSERT(ppStatsString);1050610507D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK1050810509StringBuilder sb(m_Pimpl->m_AllocationCallbacks);10510{10511JsonWriter json(m_Pimpl->m_AllocationCallbacks, sb);10512D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10513json.BeginObject();10514m_Pimpl->m_Metadata->WriteAllocationInfoToJson(json);10515json.EndObject();10516} // Scope for JsonWriter1051710518const size_t length = sb.GetLength();10519WCHAR* result = AllocateArray<WCHAR>(m_Pimpl->m_AllocationCallbacks, length + 1);10520memcpy(result, sb.GetData(), length * sizeof(WCHAR));10521result[length] = L'\0';10522*ppStatsString = result;10523}1052410525void VirtualBlock::FreeStatsString(WCHAR* pStatsString) const10526{10527if (pStatsString != NULL)10528{10529D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10530D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString);10531}10532}1053310534void VirtualBlock::ReleaseThis()10535{10536// Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.10537const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->m_AllocationCallbacks;10538D3D12MA_DELETE(allocationCallbacksCopy, this);10539}1054010541VirtualBlock::VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc)10542: m_Pimpl(D3D12MA_NEW(allocationCallbacks, VirtualBlockPimpl)(allocationCallbacks, desc)) {}1054310544VirtualBlock::~VirtualBlock()10545{10546// THIS IS AN IMPORTANT ASSERT!10547// Hitting it means you have some memory leak - unreleased allocations in this virtual block.10548D3D12MA_ASSERT(m_Pimpl->m_Metadata->IsEmpty() && "Some allocations were not freed before destruction of this virtual block!");1054910550D3D12MA_DELETE(m_Pimpl->m_AllocationCallbacks, m_Pimpl);10551}10552#endif // _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS10553#endif // _D3D12MA_PUBLIC_INTERFACE10554} // namespace D3D12MA105551055610557