Path: blob/master/thirdparty/d3d12ma/D3D12MemAlloc.cpp
21138 views
//1// Copyright (c) 2019-2025 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// Includes needed for MinGW - see #71.36#ifndef _MSC_VER37#include <guiddef.h>38// guiddef.h must be included first.39#include <dxguids.h>40#endif4142////////////////////////////////////////////////////////////////////////////////43////////////////////////////////////////////////////////////////////////////////44//45// Configuration Begin46//47////////////////////////////////////////////////////////////////////////////////48////////////////////////////////////////////////////////////////////////////////49#ifndef _D3D12MA_CONFIGURATION5051#ifdef _WIN3252#if !defined(WINVER) || WINVER < 0x060053#error Required at least WinAPI version supporting: client = Windows Vista, server = Windows Server 2008.54#endif55#endif5657#ifndef D3D12MA_SORT58#define D3D12MA_SORT(beg, end, cmp) std::sort(beg, end, cmp)59#endif6061#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED62#include <dxgi.h>63#if D3D12MA_DXGI_1_464#include <dxgi1_4.h>65#endif66#endif6768#ifndef D3D12MA_ASSERT69#include <cassert>70#define D3D12MA_ASSERT(cond) assert(cond)71#endif7273// Assert that will be called very often, like inside data structures e.g. operator[].74// Making it non-empty can make program slow.75#ifndef D3D12MA_HEAVY_ASSERT76#ifdef _DEBUG77#define D3D12MA_HEAVY_ASSERT(expr) //D3D12MA_ASSERT(expr)78#else79#define D3D12MA_HEAVY_ASSERT(expr)80#endif81#endif8283#ifndef D3D12MA_DEBUG_ALIGNMENT84/*85Minimum alignment of all allocations, in bytes.86Set to more than 1 for debugging purposes only. Must be power of two.87*/88#define D3D12MA_DEBUG_ALIGNMENT (1)89#endif9091#ifndef D3D12MA_DEBUG_MARGIN92// Minimum margin before and after every allocation, in bytes.93// Set nonzero for debugging purposes only.94#define D3D12MA_DEBUG_MARGIN (0)95#endif9697#ifndef D3D12MA_DEBUG_GLOBAL_MUTEX98/*99Set this to 1 for debugging purposes only, to enable single mutex protecting all100entry calls to the library. Can be useful for debugging multithreading issues.101*/102#define D3D12MA_DEBUG_GLOBAL_MUTEX (0)103#endif104105/*106Define this macro for debugging purposes only to force specific D3D12_RESOURCE_HEAP_TIER,107especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs.108*/109//#define D3D12MA_FORCE_RESOURCE_HEAP_TIER D3D12_RESOURCE_HEAP_TIER_1110111#ifndef D3D12MA_DEFAULT_BLOCK_SIZE112/// Default size of a block allocated as single ID3D12Heap.113#define D3D12MA_DEFAULT_BLOCK_SIZE (64ull * 1024 * 1024)114#endif115116#ifndef D3D12MA_OPTIONS16_SUPPORTED117#if D3D12_SDK_VERSION >= 610118#define D3D12MA_OPTIONS16_SUPPORTED 1119#else120#define D3D12MA_OPTIONS16_SUPPORTED 0121#endif122#endif123124#ifndef D3D12MA_DEBUG_LOG125#define D3D12MA_DEBUG_LOG(format, ...)126/*127#define D3D12MA_DEBUG_LOG(format, ...) do { \128wprintf(format, __VA_ARGS__); \129wprintf(L"\n"); \130} while(false)131*/132#endif133134#endif // _D3D12MA_CONFIGURATION135////////////////////////////////////////////////////////////////////////////////136////////////////////////////////////////////////////////////////////////////////137//138// Configuration End139//140////////////////////////////////////////////////////////////////////////////////141////////////////////////////////////////////////////////////////////////////////142143#define D3D12MA_IID_PPV_ARGS(ppType) __uuidof(**(ppType)), reinterpret_cast<void**>(ppType)144145namespace D3D12MA146{147static constexpr UINT HEAP_TYPE_COUNT = 5;148static constexpr UINT STANDARD_HEAP_TYPE_COUNT = 4; // Only DEFAULT, UPLOAD, READBACK, GPU_UPLOAD.149static constexpr UINT DEFAULT_POOL_MAX_COUNT = STANDARD_HEAP_TYPE_COUNT * 3;150static const UINT NEW_BLOCK_SIZE_SHIFT_MAX = 3;151// Minimum size of a free suballocation to register it in the free suballocation collection.152static const UINT64 MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;153154static const WCHAR* const HeapTypeNames[] =155{156L"DEFAULT",157L"UPLOAD",158L"READBACK",159L"CUSTOM",160L"GPU_UPLOAD",161};162static const WCHAR* const StandardHeapTypeNames[] =163{164L"DEFAULT",165L"UPLOAD",166L"READBACK",167L"GPU_UPLOAD",168};169170static const D3D12_HEAP_FLAGS RESOURCE_CLASS_HEAP_FLAGS =171D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;172173static const D3D12_RESIDENCY_PRIORITY D3D12_RESIDENCY_PRIORITY_NONE = D3D12_RESIDENCY_PRIORITY(0);174175static const D3D12_HEAP_TYPE D3D12_HEAP_TYPE_GPU_UPLOAD_COPY = (D3D12_HEAP_TYPE)5;176177#ifndef _D3D12MA_ENUM_DECLARATIONS178179// Local copy of this enum, as it is provided only by <dxgi1_4.h>, so it may not be available.180enum DXGI_MEMORY_SEGMENT_GROUP_COPY181{182DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY = 0,183DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY = 1,184DXGI_MEMORY_SEGMENT_GROUP_COUNT185};186187enum class ResourceClass188{189Unknown, Buffer, Non_RT_DS_Texture, RT_DS_Texture190};191192enum SuballocationType193{194SUBALLOCATION_TYPE_FREE = 0,195SUBALLOCATION_TYPE_ALLOCATION = 1,196};197198#endif // _D3D12MA_ENUM_DECLARATIONS199200201#ifndef _D3D12MA_FUNCTIONS202203static void* DefaultAllocate(size_t Size, size_t Alignment, void* /*pPrivateData*/)204{205#ifdef _WIN32206return _aligned_malloc(Size, Alignment);207#else208return aligned_alloc(Alignment, Size);209#endif210}211static void DefaultFree(void* pMemory, void* /*pPrivateData*/)212{213#ifdef _WIN32214return _aligned_free(pMemory);215#else216return free(pMemory);217#endif218}219220static void* Malloc(const ALLOCATION_CALLBACKS& allocs, size_t size, size_t alignment)221{222void* const result = (*allocs.pAllocate)(size, alignment, allocs.pPrivateData);223D3D12MA_ASSERT(result);224return result;225}226static void Free(const ALLOCATION_CALLBACKS& allocs, void* memory)227{228(*allocs.pFree)(memory, allocs.pPrivateData);229}230231template<typename T>232static T* Allocate(const ALLOCATION_CALLBACKS& allocs)233{234return (T*)Malloc(allocs, sizeof(T), __alignof(T));235}236template<typename T>237static T* AllocateArray(const ALLOCATION_CALLBACKS& allocs, size_t count)238{239return (T*)Malloc(allocs, sizeof(T) * count, __alignof(T));240}241242#define D3D12MA_NEW(allocs, type) new(D3D12MA::Allocate<type>(allocs))(type)243#define D3D12MA_NEW_ARRAY(allocs, type, count) new(D3D12MA::AllocateArray<type>((allocs), (count)))(type)244245template<typename T>246void D3D12MA_DELETE(const ALLOCATION_CALLBACKS& allocs, T* memory)247{248if (memory)249{250memory->~T();251Free(allocs, memory);252}253}254template<typename T>255void D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS& allocs, T* memory, size_t count)256{257if (memory)258{259for (size_t i = count; i--; )260{261memory[i].~T();262}263Free(allocs, memory);264}265}266267static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATION_CALLBACKS* allocationCallbacks)268{269if (allocationCallbacks)270{271outAllocs = *allocationCallbacks;272D3D12MA_ASSERT(outAllocs.pAllocate != NULL && outAllocs.pFree != NULL);273}274else275{276outAllocs.pAllocate = &DefaultAllocate;277outAllocs.pFree = &DefaultFree;278outAllocs.pPrivateData = NULL;279}280}281282#define SAFE_RELEASE(ptr) do { if(ptr) { (ptr)->Release(); (ptr) = NULL; } } while(false)283284#define D3D12MA_VALIDATE(cond) do { if(!(cond)) { \285D3D12MA_ASSERT(0 && "Validation failed: " #cond); \286return false; \287} } while(false)288289template<typename T>290static T D3D12MA_MIN(const T& a, const T& b) { return a <= b ? a : b; }291template<typename T>292static T D3D12MA_MAX(const T& a, const T& b) { return a <= b ? b : a; }293294template<typename T>295static void D3D12MA_SWAP(T& a, T& b) { T tmp = a; a = b; b = tmp; }296297// Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX298static UINT8 BitScanLSB(UINT64 mask)299{300#if defined(_MSC_VER) && defined(_WIN64)301unsigned long pos;302if (_BitScanForward64(&pos, mask))303return static_cast<UINT8>(pos);304return UINT8_MAX;305#elif defined __GNUC__ || defined __clang__306return static_cast<UINT8>(__builtin_ffsll(mask)) - 1U;307#else308UINT8 pos = 0;309UINT64 bit = 1;310do311{312if (mask & bit)313return pos;314bit <<= 1;315} while (pos++ < 63);316return UINT8_MAX;317#endif318}319// Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX320static UINT8 BitScanLSB(UINT32 mask)321{322#ifdef _MSC_VER323unsigned long pos;324if (_BitScanForward(&pos, mask))325return static_cast<UINT8>(pos);326return UINT8_MAX;327#elif defined __GNUC__ || defined __clang__328return static_cast<UINT8>(__builtin_ffs(mask)) - 1U;329#else330UINT8 pos = 0;331UINT32 bit = 1;332do333{334if (mask & bit)335return pos;336bit <<= 1;337} while (pos++ < 31);338return UINT8_MAX;339#endif340}341342// Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX343static UINT8 BitScanMSB(UINT64 mask)344{345#if defined(_MSC_VER) && defined(_WIN64)346unsigned long pos;347if (_BitScanReverse64(&pos, mask))348return static_cast<UINT8>(pos);349#elif defined __GNUC__ || defined __clang__350if (mask)351return 63 - static_cast<UINT8>(__builtin_clzll(mask));352#else353UINT8 pos = 63;354UINT64 bit = 1ULL << 63;355do356{357if (mask & bit)358return pos;359bit >>= 1;360} while (pos-- > 0);361#endif362return UINT8_MAX;363}364// Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX365static UINT8 BitScanMSB(UINT32 mask)366{367#ifdef _MSC_VER368unsigned long pos;369if (_BitScanReverse(&pos, mask))370return static_cast<UINT8>(pos);371#elif defined __GNUC__ || defined __clang__372if (mask)373return 31 - static_cast<UINT8>(__builtin_clz(mask));374#else375UINT8 pos = 31;376UINT32 bit = 1UL << 31;377do378{379if (mask & bit)380return pos;381bit >>= 1;382} while (pos-- > 0);383#endif384return UINT8_MAX;385}386387/*388Returns true if given number is a power of two.389T must be unsigned integer number or signed integer but always nonnegative.390For 0 returns true.391*/392template <typename T>393static bool IsPow2(T x) { return (x & (x - 1)) == 0; }394395// Aligns given value up to nearest multiply of align value. For example: AlignUp(11, 8) = 16.396// Use types like UINT, uint64_t as T.397template <typename T>398static T AlignUp(T val, T alignment)399{400D3D12MA_HEAVY_ASSERT(IsPow2(alignment));401return (val + alignment - 1) & ~(alignment - 1);402}403// Aligns given value down to nearest multiply of align value. For example: AlignUp(11, 8) = 8.404// Use types like UINT, uint64_t as T.405template <typename T>406static T AlignDown(T val, T alignment)407{408D3D12MA_HEAVY_ASSERT(IsPow2(alignment));409return val & ~(alignment - 1);410}411412// Division with mathematical rounding to nearest number.413template <typename T>414static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; }415template <typename T>416static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; }417418static WCHAR HexDigitToChar(UINT8 digit)419{420if(digit < 10)421return L'0' + digit;422else423return L'A' + (digit - 10);424}425426/*427Performs binary search and returns iterator to first element that is greater or428equal to `key`, according to comparison `cmp`.429430Cmp should return true if first argument is less than second argument.431432Returned value is the found element, if present in the collection or place where433new element with value (key) should be inserted.434*/435template <typename CmpLess, typename IterT, typename KeyT>436static IterT BinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)437{438size_t down = 0, up = (end - beg);439while (down < up)440{441const size_t mid = (down + up) / 2;442if (cmp(*(beg + mid), key))443{444down = mid + 1;445}446else447{448up = mid;449}450}451return beg + down;452}453454/*455Performs binary search and returns iterator to an element that is equal to `key`,456according to comparison `cmp`.457458Cmp should return true if first argument is less than second argument.459460Returned value is the found element, if present in the collection or end if not461found.462*/463template<typename CmpLess, typename IterT, typename KeyT>464static IterT BinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)465{466IterT it = BinaryFindFirstNotLess<CmpLess, IterT, KeyT>(beg, end, value, cmp);467if (it == end ||468(!cmp(*it, value) && !cmp(value, *it)))469{470return it;471}472return end;473}474475static UINT StandardHeapTypeToIndex(D3D12_HEAP_TYPE type)476{477switch (type)478{479case D3D12_HEAP_TYPE_DEFAULT: return 0;480case D3D12_HEAP_TYPE_UPLOAD: return 1;481case D3D12_HEAP_TYPE_READBACK: return 2;482case D3D12_HEAP_TYPE_GPU_UPLOAD_COPY: return 3;483default: D3D12MA_ASSERT(0); return UINT_MAX;484}485}486487static D3D12_HEAP_TYPE IndexToStandardHeapType(UINT heapTypeIndex)488{489switch(heapTypeIndex)490{491case 0: return D3D12_HEAP_TYPE_DEFAULT;492case 1: return D3D12_HEAP_TYPE_UPLOAD;493case 2: return D3D12_HEAP_TYPE_READBACK;494case 3: return D3D12_HEAP_TYPE_GPU_UPLOAD_COPY;495default: D3D12MA_ASSERT(0); return D3D12_HEAP_TYPE_CUSTOM;496}497}498499static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags, bool denyMsaaTextures)500{501/*502Documentation of D3D12_HEAP_DESC structure says:503504- D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT defined as 64KB.505- D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT defined as 4MB. An506application must decide whether the heap will contain multi-sample507anti-aliasing (MSAA), in which case, the application must choose [this flag].508509https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_heap_desc510*/511512if (denyMsaaTextures)513return D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;514515const D3D12_HEAP_FLAGS denyAllTexturesFlags =516D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;517const bool canContainAnyTextures =518(flags & denyAllTexturesFlags) != denyAllTexturesFlags;519return canContainAnyTextures ?520D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;521}522523static ResourceClass HeapFlagsToResourceClass(D3D12_HEAP_FLAGS heapFlags)524{525const bool allowBuffers = (heapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;526const bool allowRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;527const bool allowNonRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;528529const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);530if (allowedGroupCount != 1)531return ResourceClass::Unknown;532533if (allowRtDsTextures)534return ResourceClass::RT_DS_Texture;535if (allowNonRtDsTextures)536return ResourceClass::Non_RT_DS_Texture;537return ResourceClass::Buffer;538}539540static bool IsHeapTypeStandard(D3D12_HEAP_TYPE type)541{542return type == D3D12_HEAP_TYPE_DEFAULT ||543type == D3D12_HEAP_TYPE_UPLOAD ||544type == D3D12_HEAP_TYPE_READBACK ||545type == D3D12_HEAP_TYPE_GPU_UPLOAD_COPY;546}547548static D3D12_HEAP_PROPERTIES StandardHeapTypeToHeapProperties(D3D12_HEAP_TYPE type)549{550D3D12MA_ASSERT(IsHeapTypeStandard(type));551D3D12_HEAP_PROPERTIES result = {};552result.Type = type;553return result;554}555556static bool IsFormatCompressed(DXGI_FORMAT format)557{558switch (format)559{560case DXGI_FORMAT_BC1_TYPELESS:561case DXGI_FORMAT_BC1_UNORM:562case DXGI_FORMAT_BC1_UNORM_SRGB:563case DXGI_FORMAT_BC2_TYPELESS:564case DXGI_FORMAT_BC2_UNORM:565case DXGI_FORMAT_BC2_UNORM_SRGB:566case DXGI_FORMAT_BC3_TYPELESS:567case DXGI_FORMAT_BC3_UNORM:568case DXGI_FORMAT_BC3_UNORM_SRGB:569case DXGI_FORMAT_BC4_TYPELESS:570case DXGI_FORMAT_BC4_UNORM:571case DXGI_FORMAT_BC4_SNORM:572case DXGI_FORMAT_BC5_TYPELESS:573case DXGI_FORMAT_BC5_UNORM:574case DXGI_FORMAT_BC5_SNORM:575case DXGI_FORMAT_BC6H_TYPELESS:576case DXGI_FORMAT_BC6H_UF16:577case DXGI_FORMAT_BC6H_SF16:578case DXGI_FORMAT_BC7_TYPELESS:579case DXGI_FORMAT_BC7_UNORM:580case DXGI_FORMAT_BC7_UNORM_SRGB:581return true;582default:583return false;584}585}586587// Only some formats are supported. For others it returns 0.588static UINT GetBitsPerPixel(DXGI_FORMAT format)589{590switch (format)591{592case DXGI_FORMAT_R32G32B32A32_TYPELESS:593case DXGI_FORMAT_R32G32B32A32_FLOAT:594case DXGI_FORMAT_R32G32B32A32_UINT:595case DXGI_FORMAT_R32G32B32A32_SINT:596return 128;597case DXGI_FORMAT_R32G32B32_TYPELESS:598case DXGI_FORMAT_R32G32B32_FLOAT:599case DXGI_FORMAT_R32G32B32_UINT:600case DXGI_FORMAT_R32G32B32_SINT:601return 96;602case DXGI_FORMAT_R16G16B16A16_TYPELESS:603case DXGI_FORMAT_R16G16B16A16_FLOAT:604case DXGI_FORMAT_R16G16B16A16_UNORM:605case DXGI_FORMAT_R16G16B16A16_UINT:606case DXGI_FORMAT_R16G16B16A16_SNORM:607case DXGI_FORMAT_R16G16B16A16_SINT:608return 64;609case DXGI_FORMAT_R32G32_TYPELESS:610case DXGI_FORMAT_R32G32_FLOAT:611case DXGI_FORMAT_R32G32_UINT:612case DXGI_FORMAT_R32G32_SINT:613return 64;614case DXGI_FORMAT_R32G8X24_TYPELESS:615case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:616case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:617case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:618return 64;619case DXGI_FORMAT_R10G10B10A2_TYPELESS:620case DXGI_FORMAT_R10G10B10A2_UNORM:621case DXGI_FORMAT_R10G10B10A2_UINT:622case DXGI_FORMAT_R11G11B10_FLOAT:623return 32;624case DXGI_FORMAT_R8G8B8A8_TYPELESS:625case DXGI_FORMAT_R8G8B8A8_UNORM:626case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:627case DXGI_FORMAT_R8G8B8A8_UINT:628case DXGI_FORMAT_R8G8B8A8_SNORM:629case DXGI_FORMAT_R8G8B8A8_SINT:630return 32;631case DXGI_FORMAT_R16G16_TYPELESS:632case DXGI_FORMAT_R16G16_FLOAT:633case DXGI_FORMAT_R16G16_UNORM:634case DXGI_FORMAT_R16G16_UINT:635case DXGI_FORMAT_R16G16_SNORM:636case DXGI_FORMAT_R16G16_SINT:637return 32;638case DXGI_FORMAT_R32_TYPELESS:639case DXGI_FORMAT_D32_FLOAT:640case DXGI_FORMAT_R32_FLOAT:641case DXGI_FORMAT_R32_UINT:642case DXGI_FORMAT_R32_SINT:643return 32;644case DXGI_FORMAT_R24G8_TYPELESS:645case DXGI_FORMAT_D24_UNORM_S8_UINT:646case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:647case DXGI_FORMAT_X24_TYPELESS_G8_UINT:648return 32;649case DXGI_FORMAT_R8G8_TYPELESS:650case DXGI_FORMAT_R8G8_UNORM:651case DXGI_FORMAT_R8G8_UINT:652case DXGI_FORMAT_R8G8_SNORM:653case DXGI_FORMAT_R8G8_SINT:654return 16;655case DXGI_FORMAT_R16_TYPELESS:656case DXGI_FORMAT_R16_FLOAT:657case DXGI_FORMAT_D16_UNORM:658case DXGI_FORMAT_R16_UNORM:659case DXGI_FORMAT_R16_UINT:660case DXGI_FORMAT_R16_SNORM:661case DXGI_FORMAT_R16_SINT:662return 16;663case DXGI_FORMAT_R8_TYPELESS:664case DXGI_FORMAT_R8_UNORM:665case DXGI_FORMAT_R8_UINT:666case DXGI_FORMAT_R8_SNORM:667case DXGI_FORMAT_R8_SINT:668case DXGI_FORMAT_A8_UNORM:669return 8;670case DXGI_FORMAT_BC1_TYPELESS:671case DXGI_FORMAT_BC1_UNORM:672case DXGI_FORMAT_BC1_UNORM_SRGB:673return 4;674case DXGI_FORMAT_BC2_TYPELESS:675case DXGI_FORMAT_BC2_UNORM:676case DXGI_FORMAT_BC2_UNORM_SRGB:677return 8;678case DXGI_FORMAT_BC3_TYPELESS:679case DXGI_FORMAT_BC3_UNORM:680case DXGI_FORMAT_BC3_UNORM_SRGB:681return 8;682case DXGI_FORMAT_BC4_TYPELESS:683case DXGI_FORMAT_BC4_UNORM:684case DXGI_FORMAT_BC4_SNORM:685return 4;686case DXGI_FORMAT_BC5_TYPELESS:687case DXGI_FORMAT_BC5_UNORM:688case DXGI_FORMAT_BC5_SNORM:689return 8;690case DXGI_FORMAT_BC6H_TYPELESS:691case DXGI_FORMAT_BC6H_UF16:692case DXGI_FORMAT_BC6H_SF16:693return 8;694case DXGI_FORMAT_BC7_TYPELESS:695case DXGI_FORMAT_BC7_UNORM:696case DXGI_FORMAT_BC7_UNORM_SRGB:697return 8;698default:699return 0;700}701}702703template<typename D3D12_RESOURCE_DESC_T>704static ResourceClass ResourceDescToResourceClass(const D3D12_RESOURCE_DESC_T& resDesc)705{706if (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)707return ResourceClass::Buffer;708// Else: it's surely a texture.709const bool isRenderTargetOrDepthStencil =710(resDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;711return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture;712}713714// This algorithm is overly conservative.715template<typename D3D12_RESOURCE_DESC_T>716static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc)717{718if (resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)719return false;720if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0)721return false;722if (resourceDesc.SampleDesc.Count > 1)723return false;724if (resourceDesc.DepthOrArraySize != 1)725return false;726727UINT sizeX = (UINT)resourceDesc.Width;728UINT sizeY = resourceDesc.Height;729UINT bitsPerPixel = GetBitsPerPixel(resourceDesc.Format);730if (bitsPerPixel == 0)731return false;732733if (IsFormatCompressed(resourceDesc.Format))734{735sizeX = DivideRoundingUp(sizeX, 4u);736sizeY = DivideRoundingUp(sizeY, 4u);737bitsPerPixel *= 16;738}739740UINT tileSizeX = 0, tileSizeY = 0;741switch (bitsPerPixel)742{743case 8: tileSizeX = 64; tileSizeY = 64; break;744case 16: tileSizeX = 64; tileSizeY = 32; break;745case 32: tileSizeX = 32; tileSizeY = 32; break;746case 64: tileSizeX = 32; tileSizeY = 16; break;747case 128: tileSizeX = 16; tileSizeY = 16; break;748default: return false;749}750751const UINT tileCount = DivideRoundingUp(sizeX, tileSizeX) * DivideRoundingUp(sizeY, tileSizeY);752return tileCount <= 16;753}754755static bool ValidateAllocateMemoryParameters(756const ALLOCATION_DESC* pAllocDesc,757const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,758Allocation** ppAllocation)759{760return pAllocDesc &&761pAllocInfo &&762ppAllocation &&763(pAllocInfo->Alignment == 0 ||764pAllocInfo->Alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT ||765pAllocInfo->Alignment == D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT) &&766pAllocInfo->SizeInBytes != 0 &&767pAllocInfo->SizeInBytes % (64ull * 1024) == 0;768}769770#endif // _D3D12MA_FUNCTIONS771772#ifndef _D3D12MA_STATISTICS_FUNCTIONS773774static void ClearStatistics(Statistics& outStats)775{776outStats.BlockCount = 0;777outStats.AllocationCount = 0;778outStats.BlockBytes = 0;779outStats.AllocationBytes = 0;780}781782static void ClearDetailedStatistics(DetailedStatistics& outStats)783{784ClearStatistics(outStats.Stats);785outStats.UnusedRangeCount = 0;786outStats.AllocationSizeMin = UINT64_MAX;787outStats.AllocationSizeMax = 0;788outStats.UnusedRangeSizeMin = UINT64_MAX;789outStats.UnusedRangeSizeMax = 0;790}791792static void AddStatistics(Statistics& inoutStats, const Statistics& src)793{794inoutStats.BlockCount += src.BlockCount;795inoutStats.AllocationCount += src.AllocationCount;796inoutStats.BlockBytes += src.BlockBytes;797inoutStats.AllocationBytes += src.AllocationBytes;798}799800static void AddDetailedStatistics(DetailedStatistics& inoutStats, const DetailedStatistics& src)801{802AddStatistics(inoutStats.Stats, src.Stats);803inoutStats.UnusedRangeCount += src.UnusedRangeCount;804inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, src.AllocationSizeMin);805inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, src.AllocationSizeMax);806inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, src.UnusedRangeSizeMin);807inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, src.UnusedRangeSizeMax);808}809810static void AddDetailedStatisticsAllocation(DetailedStatistics& inoutStats, UINT64 size)811{812inoutStats.Stats.AllocationCount++;813inoutStats.Stats.AllocationBytes += size;814inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, size);815inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, size);816}817818static void AddDetailedStatisticsUnusedRange(DetailedStatistics& inoutStats, UINT64 size)819{820inoutStats.UnusedRangeCount++;821inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, size);822inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, size);823}824825#endif // _D3D12MA_STATISTICS_FUNCTIONS826827828#ifndef _D3D12MA_MUTEX829830#ifndef D3D12MA_MUTEX831class Mutex832{833public:834void Lock() { m_Mutex.lock(); }835void Unlock() { m_Mutex.unlock(); }836837private:838std::mutex m_Mutex;839};840#define D3D12MA_MUTEX Mutex841#endif842843#ifndef D3D12MA_RW_MUTEX844#ifdef _WIN32845class RWMutex846{847public:848RWMutex() { InitializeSRWLock(&m_Lock); }849void LockRead() { AcquireSRWLockShared(&m_Lock); }850void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }851void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }852void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }853854private:855SRWLOCK m_Lock;856};857#else // #ifdef _WIN32858class RWMutex859{860public:861RWMutex() {}862void LockRead() { m_Mutex.lock_shared(); }863void UnlockRead() { m_Mutex.unlock_shared(); }864void LockWrite() { m_Mutex.lock(); }865void UnlockWrite() { m_Mutex.unlock(); }866867private:868std::shared_timed_mutex m_Mutex;869};870#endif // #ifdef _WIN32871#define D3D12MA_RW_MUTEX RWMutex872#endif // #ifndef D3D12MA_RW_MUTEX873874// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).875struct MutexLock876{877D3D12MA_CLASS_NO_COPY(MutexLock);878public:879MutexLock(D3D12MA_MUTEX& mutex, bool useMutex = true) :880m_pMutex(useMutex ? &mutex : NULL)881{882if (m_pMutex) m_pMutex->Lock();883}884~MutexLock() { if (m_pMutex) m_pMutex->Unlock(); }885886private:887D3D12MA_MUTEX* m_pMutex;888};889890// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.891struct MutexLockRead892{893D3D12MA_CLASS_NO_COPY(MutexLockRead);894public:895MutexLockRead(D3D12MA_RW_MUTEX& mutex, bool useMutex)896: m_pMutex(useMutex ? &mutex : NULL)897{898if(m_pMutex)899{900m_pMutex->LockRead();901}902}903~MutexLockRead() { if (m_pMutex) m_pMutex->UnlockRead(); }904905private:906D3D12MA_RW_MUTEX* m_pMutex;907};908909// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.910struct MutexLockWrite911{912D3D12MA_CLASS_NO_COPY(MutexLockWrite);913public:914MutexLockWrite(D3D12MA_RW_MUTEX& mutex, bool useMutex)915: m_pMutex(useMutex ? &mutex : NULL)916{917if (m_pMutex) m_pMutex->LockWrite();918}919~MutexLockWrite() { if (m_pMutex) m_pMutex->UnlockWrite(); }920921private:922D3D12MA_RW_MUTEX* m_pMutex;923};924925#if D3D12MA_DEBUG_GLOBAL_MUTEX926static D3D12MA_MUTEX g_DebugGlobalMutex;927#define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK MutexLock debugGlobalMutexLock(g_DebugGlobalMutex, true);928#else929#define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK930#endif931#endif // _D3D12MA_MUTEX932933#ifndef _D3D12MA_VECTOR934/*935Dynamically resizing continuous array. Class with interface similar to std::vector.936T must be POD because constructors and destructors are not called and memcpy is937used for these objects.938*/939template<typename T>940class Vector941{942public:943using value_type = T;944using iterator = T*;945using const_iterator = const T*;946947// allocationCallbacks externally owned, must outlive this object.948Vector(const ALLOCATION_CALLBACKS& allocationCallbacks);949Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks);950Vector(const Vector<T>& src);951~Vector();952953const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }954bool empty() const { return m_Count == 0; }955size_t size() const { return m_Count; }956T* data() { return m_pArray; }957const T* data() const { return m_pArray; }958void clear(bool freeMemory = false) { resize(0, freeMemory); }959960iterator begin() { return m_pArray; }961iterator end() { return m_pArray + m_Count; }962const_iterator cbegin() const { return m_pArray; }963const_iterator cend() const { return m_pArray + m_Count; }964const_iterator begin() const { return cbegin(); }965const_iterator end() const { return cend(); }966967void push_front(const T& src) { insert(0, src); }968void push_back(const T& src);969void pop_front();970void pop_back();971972T& front();973T& back();974const T& front() const;975const T& back() const;976977void reserve(size_t newCapacity, bool freeMemory = false);978void resize(size_t newCount, bool freeMemory = false);979void insert(size_t index, const T& src);980void remove(size_t index);981982template<typename CmpLess>983size_t InsertSorted(const T& value, const CmpLess& cmp);984template<typename CmpLess>985bool RemoveSorted(const T& value, const CmpLess& cmp);986987Vector& operator=(const Vector<T>& rhs);988T& operator[](size_t index);989const T& operator[](size_t index) const;990991private:992const ALLOCATION_CALLBACKS& m_AllocationCallbacks;993T* m_pArray;994size_t m_Count;995size_t m_Capacity;996};997998#ifndef _D3D12MA_VECTOR_FUNCTIONS999template<typename T>1000Vector<T>::Vector(const ALLOCATION_CALLBACKS& allocationCallbacks)1001: m_AllocationCallbacks(allocationCallbacks),1002m_pArray(NULL),1003m_Count(0),1004m_Capacity(0) {}10051006template<typename T>1007Vector<T>::Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks)1008: m_AllocationCallbacks(allocationCallbacks),1009m_pArray(count ? AllocateArray<T>(allocationCallbacks, count) : NULL),1010m_Count(count),1011m_Capacity(count) {}10121013template<typename T>1014Vector<T>::Vector(const Vector<T>& src)1015: m_AllocationCallbacks(src.m_AllocationCallbacks),1016m_pArray(src.m_Count ? AllocateArray<T>(src.m_AllocationCallbacks, src.m_Count) : NULL),1017m_Count(src.m_Count),1018m_Capacity(src.m_Count)1019{1020if (m_Count > 0)1021{1022memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));1023}1024}10251026template<typename T>1027Vector<T>::~Vector()1028{1029Free(m_AllocationCallbacks, m_pArray);1030}10311032template<typename T>1033void Vector<T>::push_back(const T& src)1034{1035const size_t newIndex = size();1036resize(newIndex + 1);1037m_pArray[newIndex] = src;1038}10391040template<typename T>1041void Vector<T>::pop_front()1042{1043D3D12MA_HEAVY_ASSERT(m_Count > 0);1044remove(0);1045}10461047template<typename T>1048void Vector<T>::pop_back()1049{1050D3D12MA_HEAVY_ASSERT(m_Count > 0);1051resize(size() - 1);1052}10531054template<typename T>1055T& Vector<T>::front()1056{1057D3D12MA_HEAVY_ASSERT(m_Count > 0);1058return m_pArray[0];1059}10601061template<typename T>1062T& Vector<T>::back()1063{1064D3D12MA_HEAVY_ASSERT(m_Count > 0);1065return m_pArray[m_Count - 1];1066}10671068template<typename T>1069const T& Vector<T>::front() const1070{1071D3D12MA_HEAVY_ASSERT(m_Count > 0);1072return m_pArray[0];1073}10741075template<typename T>1076const T& Vector<T>::back() const1077{1078D3D12MA_HEAVY_ASSERT(m_Count > 0);1079return m_pArray[m_Count - 1];1080}10811082template<typename T>1083void Vector<T>::reserve(size_t newCapacity, bool freeMemory)1084{1085newCapacity = D3D12MA_MAX(newCapacity, m_Count);10861087if ((newCapacity < m_Capacity) && !freeMemory)1088{1089newCapacity = m_Capacity;1090}10911092if (newCapacity != m_Capacity)1093{1094T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;1095if (m_Count != 0)1096{1097memcpy(newArray, m_pArray, m_Count * sizeof(T));1098}1099Free(m_AllocationCallbacks, m_pArray);1100m_Capacity = newCapacity;1101m_pArray = newArray;1102}1103}11041105template<typename T>1106void Vector<T>::resize(size_t newCount, bool freeMemory)1107{1108size_t newCapacity = m_Capacity;1109if (newCount > m_Capacity)1110{1111newCapacity = D3D12MA_MAX(newCount, D3D12MA_MAX(m_Capacity * 3 / 2, (size_t)8));1112}1113else if (freeMemory)1114{1115newCapacity = newCount;1116}11171118if (newCapacity != m_Capacity)1119{1120T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;1121const size_t elementsToCopy = D3D12MA_MIN(m_Count, newCount);1122if (elementsToCopy != 0)1123{1124memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));1125}1126Free(m_AllocationCallbacks, m_pArray);1127m_Capacity = newCapacity;1128m_pArray = newArray;1129}11301131m_Count = newCount;1132}11331134template<typename T>1135void Vector<T>::insert(size_t index, const T& src)1136{1137D3D12MA_HEAVY_ASSERT(index <= m_Count);1138const size_t oldCount = size();1139resize(oldCount + 1);1140if (index < oldCount)1141{1142memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));1143}1144m_pArray[index] = src;1145}11461147template<typename T>1148void Vector<T>::remove(size_t index)1149{1150D3D12MA_HEAVY_ASSERT(index < m_Count);1151const size_t oldCount = size();1152if (index < oldCount - 1)1153{1154memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));1155}1156resize(oldCount - 1);1157}11581159template<typename T> template<typename CmpLess>1160size_t Vector<T>::InsertSorted(const T& value, const CmpLess& cmp)1161{1162const size_t indexToInsert = BinaryFindFirstNotLess<CmpLess, iterator, T>(1163m_pArray,1164m_pArray + m_Count,1165value,1166cmp) - m_pArray;1167insert(indexToInsert, value);1168return indexToInsert;1169}11701171template<typename T> template<typename CmpLess>1172bool Vector<T>::RemoveSorted(const T& value, const CmpLess& cmp)1173{1174const iterator it = BinaryFindFirstNotLess(1175m_pArray,1176m_pArray + m_Count,1177value,1178cmp);1179if ((it != end()) && !cmp(*it, value) && !cmp(value, *it))1180{1181size_t indexToRemove = it - begin();1182remove(indexToRemove);1183return true;1184}1185return false;1186}11871188template<typename T>1189Vector<T>& Vector<T>::operator=(const Vector<T>& rhs)1190{1191if (&rhs != this)1192{1193resize(rhs.m_Count);1194if (m_Count != 0)1195{1196memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));1197}1198}1199return *this;1200}12011202template<typename T>1203T& Vector<T>::operator[](size_t index)1204{1205D3D12MA_HEAVY_ASSERT(index < m_Count);1206return m_pArray[index];1207}12081209template<typename T>1210const T& Vector<T>::operator[](size_t index) const1211{1212D3D12MA_HEAVY_ASSERT(index < m_Count);1213return m_pArray[index];1214}1215#endif // _D3D12MA_VECTOR_FUNCTIONS1216#endif // _D3D12MA_VECTOR12171218#ifndef _D3D12MA_STRING_BUILDER1219class StringBuilder1220{1221public:1222StringBuilder(const ALLOCATION_CALLBACKS& allocationCallbacks) : m_Data(allocationCallbacks) {}12231224size_t GetLength() const { return m_Data.size(); }1225LPCWSTR GetData() const { return m_Data.data(); }12261227void Add(WCHAR ch) { m_Data.push_back(ch); }1228void Add(LPCWSTR str);1229void AddNewLine() { Add(L'\n'); }1230void AddNumber(UINT num);1231void AddNumber(UINT64 num);1232void AddPointer(const void* ptr);12331234private:1235Vector<WCHAR> m_Data;1236};12371238#ifndef _D3D12MA_STRING_BUILDER_FUNCTIONS1239void StringBuilder::Add(LPCWSTR str)1240{1241const size_t len = wcslen(str);1242if (len > 0)1243{1244const size_t oldCount = m_Data.size();1245m_Data.resize(oldCount + len);1246memcpy(m_Data.data() + oldCount, str, len * sizeof(WCHAR));1247}1248}12491250void StringBuilder::AddNumber(UINT num)1251{1252WCHAR buf[11];1253buf[10] = L'\0';1254WCHAR *p = &buf[10];1255do1256{1257*--p = L'0' + (num % 10);1258num /= 10;1259}1260while (num);1261Add(p);1262}12631264void StringBuilder::AddNumber(UINT64 num)1265{1266WCHAR buf[21];1267buf[20] = L'\0';1268WCHAR *p = &buf[20];1269do1270{1271*--p = L'0' + (num % 10);1272num /= 10;1273}1274while (num);1275Add(p);1276}12771278void StringBuilder::AddPointer(const void* ptr)1279{1280WCHAR buf[21];1281uintptr_t num = (uintptr_t)ptr;1282buf[20] = L'\0';1283WCHAR *p = &buf[20];1284do1285{1286*--p = HexDigitToChar((UINT8)(num & 0xF));1287num >>= 4;1288}1289while (num);1290Add(p);1291}12921293#endif // _D3D12MA_STRING_BUILDER_FUNCTIONS1294#endif // _D3D12MA_STRING_BUILDER12951296#ifndef _D3D12MA_JSON_WRITER1297/*1298Allows to conveniently build a correct JSON document to be written to the1299StringBuilder passed to the constructor.1300*/1301class JsonWriter1302{1303public:1304// stringBuilder - string builder to write the document to. Must remain alive for the whole lifetime of this object.1305JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder);1306~JsonWriter();13071308// Begins object by writing "{".1309// Inside an object, you must call pairs of WriteString and a value, e.g.:1310// j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();1311// Will write: { "A": 1, "B": 2 }1312void BeginObject(bool singleLine = false);1313// Ends object by writing "}".1314void EndObject();13151316// Begins array by writing "[".1317// Inside an array, you can write a sequence of any values.1318void BeginArray(bool singleLine = false);1319// Ends array by writing "[".1320void EndArray();13211322// Writes a string value inside "".1323// pStr can contain any UTF-16 characters, including '"', new line etc. - they will be properly escaped.1324void WriteString(LPCWSTR pStr);13251326// Begins writing a string value.1327// Call BeginString, ContinueString, ContinueString, ..., EndString instead of1328// WriteString to conveniently build the string content incrementally, made of1329// parts including numbers.1330void BeginString(LPCWSTR pStr = NULL);1331// Posts next part of an open string.1332void ContinueString(LPCWSTR pStr);1333// Posts next part of an open string. The number is converted to decimal characters.1334void ContinueString(UINT num);1335void ContinueString(UINT64 num);1336void ContinueString_Pointer(const void* ptr);1337// Posts next part of an open string. Pointer value is converted to characters1338// using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad001339// void ContinueString_Pointer(const void* ptr);1340// Ends writing a string value by writing '"'.1341void EndString(LPCWSTR pStr = NULL);13421343// Writes a number value.1344void WriteNumber(UINT num);1345void WriteNumber(UINT64 num);1346// Writes a boolean value - false or true.1347void WriteBool(bool b);1348// Writes a null value.1349void WriteNull();13501351void AddAllocationToObject(const Allocation& alloc);1352void AddDetailedStatisticsInfoObject(const DetailedStatistics& stats);13531354private:1355static const WCHAR* const INDENT;13561357enum CollectionType1358{1359COLLECTION_TYPE_OBJECT,1360COLLECTION_TYPE_ARRAY,1361};1362struct StackItem1363{1364CollectionType type;1365UINT valueCount;1366bool singleLineMode;1367};13681369StringBuilder& m_SB;1370Vector<StackItem> m_Stack;1371bool m_InsideString;13721373void BeginValue(bool isString);1374void WriteIndent(bool oneLess = false);1375};13761377#ifndef _D3D12MA_JSON_WRITER_FUNCTIONS1378const WCHAR* const JsonWriter::INDENT = L" ";13791380JsonWriter::JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder)1381: m_SB(stringBuilder),1382m_Stack(allocationCallbacks),1383m_InsideString(false) {}13841385JsonWriter::~JsonWriter()1386{1387D3D12MA_ASSERT(!m_InsideString);1388D3D12MA_ASSERT(m_Stack.empty());1389}13901391void JsonWriter::BeginObject(bool singleLine)1392{1393D3D12MA_ASSERT(!m_InsideString);13941395BeginValue(false);1396m_SB.Add(L'{');13971398StackItem stackItem;1399stackItem.type = COLLECTION_TYPE_OBJECT;1400stackItem.valueCount = 0;1401stackItem.singleLineMode = singleLine;1402m_Stack.push_back(stackItem);1403}14041405void JsonWriter::EndObject()1406{1407D3D12MA_ASSERT(!m_InsideString);1408D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);1409D3D12MA_ASSERT(m_Stack.back().valueCount % 2 == 0);14101411WriteIndent(true);1412m_SB.Add(L'}');14131414m_Stack.pop_back();1415}14161417void JsonWriter::BeginArray(bool singleLine)1418{1419D3D12MA_ASSERT(!m_InsideString);14201421BeginValue(false);1422m_SB.Add(L'[');14231424StackItem stackItem;1425stackItem.type = COLLECTION_TYPE_ARRAY;1426stackItem.valueCount = 0;1427stackItem.singleLineMode = singleLine;1428m_Stack.push_back(stackItem);1429}14301431void JsonWriter::EndArray()1432{1433D3D12MA_ASSERT(!m_InsideString);1434D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);14351436WriteIndent(true);1437m_SB.Add(L']');14381439m_Stack.pop_back();1440}14411442void JsonWriter::WriteString(LPCWSTR pStr)1443{1444BeginString(pStr);1445EndString();1446}14471448void JsonWriter::BeginString(LPCWSTR pStr)1449{1450D3D12MA_ASSERT(!m_InsideString);14511452BeginValue(true);1453m_InsideString = true;1454m_SB.Add(L'"');1455if (pStr != NULL)1456{1457ContinueString(pStr);1458}1459}14601461void JsonWriter::ContinueString(LPCWSTR pStr)1462{1463D3D12MA_ASSERT(m_InsideString);1464D3D12MA_ASSERT(pStr);14651466for (const WCHAR *p = pStr; *p; ++p)1467{1468// the strings we encode are assumed to be in UTF-16LE format, the native1469// windows wide character Unicode format. In this encoding Unicode code1470// points U+0000 to U+D7FF and U+E000 to U+FFFF are encoded in two bytes,1471// and everything else takes more than two bytes. We will reject any1472// multi wchar character encodings for simplicity.1473UINT val = (UINT)*p;1474D3D12MA_ASSERT(((val <= 0xD7FF) || (0xE000 <= val && val <= 0xFFFF)) &&1475"Character not currently supported.");1476switch (*p)1477{1478case L'"': m_SB.Add(L'\\'); m_SB.Add(L'"'); break;1479case L'\\': m_SB.Add(L'\\'); m_SB.Add(L'\\'); break;1480case L'/': m_SB.Add(L'\\'); m_SB.Add(L'/'); break;1481case L'\b': m_SB.Add(L'\\'); m_SB.Add(L'b'); break;1482case L'\f': m_SB.Add(L'\\'); m_SB.Add(L'f'); break;1483case L'\n': m_SB.Add(L'\\'); m_SB.Add(L'n'); break;1484case L'\r': m_SB.Add(L'\\'); m_SB.Add(L'r'); break;1485case L'\t': m_SB.Add(L'\\'); m_SB.Add(L't'); break;1486default:1487// conservatively use encoding \uXXXX for any Unicode character1488// requiring more than one byte.1489if (32 <= val && val < 256)1490m_SB.Add(*p);1491else1492{1493m_SB.Add(L'\\');1494m_SB.Add(L'u');1495for (UINT i = 0; i < 4; ++i)1496{1497UINT hexDigit = (val & 0xF000) >> 12;1498val <<= 4;1499if (hexDigit < 10)1500m_SB.Add(L'0' + (WCHAR)hexDigit);1501else1502m_SB.Add(L'A' + (WCHAR)hexDigit);1503}1504}1505break;1506}1507}1508}15091510void JsonWriter::ContinueString(UINT num)1511{1512D3D12MA_ASSERT(m_InsideString);1513m_SB.AddNumber(num);1514}15151516void JsonWriter::ContinueString(UINT64 num)1517{1518D3D12MA_ASSERT(m_InsideString);1519m_SB.AddNumber(num);1520}15211522void JsonWriter::ContinueString_Pointer(const void* ptr)1523{1524D3D12MA_ASSERT(m_InsideString);1525m_SB.AddPointer(ptr);1526}15271528void JsonWriter::EndString(LPCWSTR pStr)1529{1530D3D12MA_ASSERT(m_InsideString);15311532if (pStr)1533ContinueString(pStr);1534m_SB.Add(L'"');1535m_InsideString = false;1536}15371538void JsonWriter::WriteNumber(UINT num)1539{1540D3D12MA_ASSERT(!m_InsideString);1541BeginValue(false);1542m_SB.AddNumber(num);1543}15441545void JsonWriter::WriteNumber(UINT64 num)1546{1547D3D12MA_ASSERT(!m_InsideString);1548BeginValue(false);1549m_SB.AddNumber(num);1550}15511552void JsonWriter::WriteBool(bool b)1553{1554D3D12MA_ASSERT(!m_InsideString);1555BeginValue(false);1556if (b)1557m_SB.Add(L"true");1558else1559m_SB.Add(L"false");1560}15611562void JsonWriter::WriteNull()1563{1564D3D12MA_ASSERT(!m_InsideString);1565BeginValue(false);1566m_SB.Add(L"null");1567}15681569void JsonWriter::AddAllocationToObject(const Allocation& alloc)1570{1571WriteString(L"Type");1572switch (alloc.m_PackedData.GetResourceDimension()) {1573case D3D12_RESOURCE_DIMENSION_UNKNOWN:1574WriteString(L"UNKNOWN");1575break;1576case D3D12_RESOURCE_DIMENSION_BUFFER:1577WriteString(L"BUFFER");1578break;1579case D3D12_RESOURCE_DIMENSION_TEXTURE1D:1580WriteString(L"TEXTURE1D");1581break;1582case D3D12_RESOURCE_DIMENSION_TEXTURE2D:1583WriteString(L"TEXTURE2D");1584break;1585case D3D12_RESOURCE_DIMENSION_TEXTURE3D:1586WriteString(L"TEXTURE3D");1587break;1588default: D3D12MA_ASSERT(0); break;1589}15901591WriteString(L"Size");1592WriteNumber(alloc.GetSize());1593WriteString(L"Usage");1594WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags());15951596void* privateData = alloc.GetPrivateData();1597if (privateData)1598{1599WriteString(L"CustomData");1600BeginString();1601ContinueString_Pointer(privateData);1602EndString();1603}16041605LPCWSTR name = alloc.GetName();1606if (name != NULL)1607{1608WriteString(L"Name");1609WriteString(name);1610}1611if (alloc.m_PackedData.GetTextureLayout())1612{1613WriteString(L"Layout");1614WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout());1615}1616}16171618void JsonWriter::AddDetailedStatisticsInfoObject(const DetailedStatistics& stats)1619{1620BeginObject();16211622WriteString(L"BlockCount");1623WriteNumber(stats.Stats.BlockCount);1624WriteString(L"BlockBytes");1625WriteNumber(stats.Stats.BlockBytes);1626WriteString(L"AllocationCount");1627WriteNumber(stats.Stats.AllocationCount);1628WriteString(L"AllocationBytes");1629WriteNumber(stats.Stats.AllocationBytes);1630WriteString(L"UnusedRangeCount");1631WriteNumber(stats.UnusedRangeCount);16321633if (stats.Stats.AllocationCount > 1)1634{1635WriteString(L"AllocationSizeMin");1636WriteNumber(stats.AllocationSizeMin);1637WriteString(L"AllocationSizeMax");1638WriteNumber(stats.AllocationSizeMax);1639}1640if (stats.UnusedRangeCount > 1)1641{1642WriteString(L"UnusedRangeSizeMin");1643WriteNumber(stats.UnusedRangeSizeMin);1644WriteString(L"UnusedRangeSizeMax");1645WriteNumber(stats.UnusedRangeSizeMax);1646}1647EndObject();1648}16491650void JsonWriter::BeginValue(bool isString)1651{1652if (!m_Stack.empty())1653{1654StackItem& currItem = m_Stack.back();1655if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 0)1656{1657D3D12MA_ASSERT(isString);1658}16591660if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 1)1661{1662m_SB.Add(L':'); m_SB.Add(L' ');1663}1664else if (currItem.valueCount > 0)1665{1666m_SB.Add(L','); m_SB.Add(L' ');1667WriteIndent();1668}1669else1670{1671WriteIndent();1672}1673++currItem.valueCount;1674}1675}16761677void JsonWriter::WriteIndent(bool oneLess)1678{1679if (!m_Stack.empty() && !m_Stack.back().singleLineMode)1680{1681m_SB.AddNewLine();16821683size_t count = m_Stack.size();1684if (count > 0 && oneLess)1685{1686--count;1687}1688for (size_t i = 0; i < count; ++i)1689{1690m_SB.Add(INDENT);1691}1692}1693}1694#endif // _D3D12MA_JSON_WRITER_FUNCTIONS1695#endif // _D3D12MA_JSON_WRITER16961697#ifndef _D3D12MA_POOL_ALLOCATOR1698/*1699Allocator for objects of type T using a list of arrays (pools) to speed up1700allocation. Number of elements that can be allocated is not bounded because1701allocator can create multiple blocks.1702T should be POD because constructor and destructor is not called in Alloc or1703Free.1704*/1705template<typename T>1706class PoolAllocator1707{1708D3D12MA_CLASS_NO_COPY(PoolAllocator)1709public:1710// allocationCallbacks externally owned, must outlive this object.1711PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity);1712~PoolAllocator() { Clear(); }17131714void Clear();1715template<typename... Types>1716T* Alloc(Types... args);1717void Free(T* ptr);17181719private:1720union Item1721{1722UINT NextFreeIndex; // UINT32_MAX means end of list.1723alignas(T) char Value[sizeof(T)];1724};17251726struct ItemBlock1727{1728Item* pItems;1729UINT Capacity;1730UINT FirstFreeIndex;1731};17321733const ALLOCATION_CALLBACKS& m_AllocationCallbacks;1734const UINT m_FirstBlockCapacity;1735Vector<ItemBlock> m_ItemBlocks;17361737ItemBlock& CreateNewBlock();1738};17391740#ifndef _D3D12MA_POOL_ALLOCATOR_FUNCTIONS1741template<typename T>1742PoolAllocator<T>::PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity)1743: m_AllocationCallbacks(allocationCallbacks),1744m_FirstBlockCapacity(firstBlockCapacity),1745m_ItemBlocks(allocationCallbacks)1746{1747D3D12MA_ASSERT(m_FirstBlockCapacity > 1);1748}17491750template<typename T>1751void PoolAllocator<T>::Clear()1752{1753for(size_t i = m_ItemBlocks.size(); i--; )1754{1755D3D12MA_DELETE_ARRAY(m_AllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);1756}1757m_ItemBlocks.clear(true);1758}17591760template<typename T> template<typename... Types>1761T* PoolAllocator<T>::Alloc(Types... args)1762{1763for(size_t i = m_ItemBlocks.size(); i--; )1764{1765ItemBlock& block = m_ItemBlocks[i];1766// This block has some free items: Use first one.1767if(block.FirstFreeIndex != UINT32_MAX)1768{1769Item* const pItem = &block.pItems[block.FirstFreeIndex];1770block.FirstFreeIndex = pItem->NextFreeIndex;1771T* result = (T*)&pItem->Value;1772new(result)T(std::forward<Types>(args)...); // Explicit constructor call.1773return result;1774}1775}17761777// No block has free item: Create new one and use it.1778ItemBlock& newBlock = CreateNewBlock();1779Item* const pItem = &newBlock.pItems[0];1780newBlock.FirstFreeIndex = pItem->NextFreeIndex;1781T* result = (T*)pItem->Value;1782new(result)T(std::forward<Types>(args)...); // Explicit constructor call.1783return result;1784}17851786template<typename T>1787void PoolAllocator<T>::Free(T* ptr)1788{1789// Search all memory blocks to find ptr.1790for(size_t i = m_ItemBlocks.size(); i--; )1791{1792ItemBlock& block = m_ItemBlocks[i];17931794Item* pItemPtr;1795memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));17961797// Check if pItemPtr is in address range of this block.1798if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))1799{1800ptr->~T(); // Explicit destructor call.1801const UINT index = static_cast<UINT>(pItemPtr - block.pItems);1802pItemPtr->NextFreeIndex = block.FirstFreeIndex;1803block.FirstFreeIndex = index;1804return;1805}1806}1807D3D12MA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");1808}18091810template<typename T>1811typename PoolAllocator<T>::ItemBlock& PoolAllocator<T>::CreateNewBlock()1812{1813const UINT newBlockCapacity = m_ItemBlocks.empty() ?1814m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;18151816const ItemBlock newBlock = {1817D3D12MA_NEW_ARRAY(m_AllocationCallbacks, Item, newBlockCapacity),1818newBlockCapacity,18190 };18201821m_ItemBlocks.push_back(newBlock);18221823// Setup singly-linked list of all free items in this block.1824for(UINT i = 0; i < newBlockCapacity - 1; ++i)1825{1826newBlock.pItems[i].NextFreeIndex = i + 1;1827}1828newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;1829return m_ItemBlocks.back();1830}1831#endif // _D3D12MA_POOL_ALLOCATOR_FUNCTIONS1832#endif // _D3D12MA_POOL_ALLOCATOR18331834#ifndef _D3D12MA_LIST1835/*1836Doubly linked list, with elements allocated out of PoolAllocator.1837Has custom interface, as well as STL-style interface, including iterator and1838const_iterator.1839*/1840template<typename T>1841class List1842{1843D3D12MA_CLASS_NO_COPY(List)1844public:1845struct Item1846{1847Item* pPrev;1848Item* pNext;1849T Value;1850};18511852class reverse_iterator;1853class const_reverse_iterator;1854class iterator1855{1856friend class List<T>;1857friend class const_iterator;18581859public:1860iterator() = default;1861iterator(const reverse_iterator& src)1862: m_pList(src.m_pList), m_pItem(src.m_pItem) {}18631864T& operator*() const;1865T* operator->() const;18661867iterator& operator++();1868iterator& operator--();1869iterator operator++(int);1870iterator operator--(int);18711872bool operator==(const iterator& rhs) const;1873bool operator!=(const iterator& rhs) const;18741875private:1876List<T>* m_pList = NULL;1877Item* m_pItem = NULL;18781879iterator(List<T>* pList, Item* pItem) : m_pList(pList), m_pItem(pItem) {}1880};18811882class reverse_iterator1883{1884friend class List<T>;1885friend class const_reverse_iterator;18861887public:1888reverse_iterator() = default;1889reverse_iterator(const iterator& src)1890: m_pList(src.m_pList), m_pItem(src.m_pItem) {}18911892T& operator*() const;1893T* operator->() const;18941895reverse_iterator& operator++();1896reverse_iterator& operator--();1897reverse_iterator operator++(int);1898reverse_iterator operator--(int);18991900bool operator==(const reverse_iterator& rhs) const;1901bool operator!=(const reverse_iterator& rhs) const;19021903private:1904List<T>* m_pList = NULL;1905Item* m_pItem = NULL;19061907reverse_iterator(List<T>* pList, Item* pItem)1908: m_pList(pList), m_pItem(pItem) {}1909};19101911class const_iterator1912{1913friend class List<T>;19141915public:1916const_iterator() = default;1917const_iterator(const iterator& src)1918: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1919const_iterator(const reverse_iterator& src)1920: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1921const_iterator(const const_reverse_iterator& src)1922: m_pList(src.m_pList), m_pItem(src.m_pItem) {}19231924iterator dropConst() const;1925const T& operator*() const;1926const T* operator->() const;19271928const_iterator& operator++();1929const_iterator& operator--();1930const_iterator operator++(int);1931const_iterator operator--(int);19321933bool operator==(const const_iterator& rhs) const;1934bool operator!=(const const_iterator& rhs) const;19351936private:1937const List<T>* m_pList = NULL;1938const Item* m_pItem = NULL;19391940const_iterator(const List<T>* pList, const Item* pItem)1941: m_pList(pList), m_pItem(pItem) {}1942};19431944class const_reverse_iterator1945{1946friend class List<T>;19471948public:1949const_reverse_iterator() = default;1950const_reverse_iterator(const iterator& src)1951: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1952const_reverse_iterator(const reverse_iterator& src)1953: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1954const_reverse_iterator(const const_iterator& src)1955: m_pList(src.m_pList), m_pItem(src.m_pItem) {}19561957reverse_iterator dropConst() const;1958const T& operator*() const;1959const T* operator->() const;19601961const_reverse_iterator& operator++();1962const_reverse_iterator& operator--();1963const_reverse_iterator operator++(int);1964const_reverse_iterator operator--(int);19651966bool operator==(const const_reverse_iterator& rhs) const;1967bool operator!=(const const_reverse_iterator& rhs) const;19681969private:1970const List<T>* m_pList = NULL;1971const Item* m_pItem = NULL;19721973const_reverse_iterator(const List<T>* pList, const Item* pItem)1974: m_pList(pList), m_pItem(pItem) {}1975};19761977// allocationCallbacks externally owned, must outlive this object.1978List(const ALLOCATION_CALLBACKS& allocationCallbacks);1979// Intentionally not calling Clear, because that would be unnecessary1980// computations to return all items to m_ItemAllocator as free.1981~List() = default;19821983size_t GetCount() const { return m_Count; }1984bool IsEmpty() const { return m_Count == 0; }19851986Item* Front() { return m_pFront; }1987const Item* Front() const { return m_pFront; }1988Item* Back() { return m_pBack; }1989const Item* Back() const { return m_pBack; }19901991bool empty() const { return IsEmpty(); }1992size_t size() const { return GetCount(); }1993void push_back(const T& value) { PushBack(value); }1994iterator insert(iterator it, const T& value) { return iterator(this, InsertBefore(it.m_pItem, value)); }1995void clear() { Clear(); }1996void erase(iterator it) { Remove(it.m_pItem); }19971998iterator begin() { return iterator(this, Front()); }1999iterator end() { return iterator(this, NULL); }2000reverse_iterator rbegin() { return reverse_iterator(this, Back()); }2001reverse_iterator rend() { return reverse_iterator(this, NULL); }20022003const_iterator cbegin() const { return const_iterator(this, Front()); }2004const_iterator cend() const { return const_iterator(this, NULL); }2005const_iterator begin() const { return cbegin(); }2006const_iterator end() const { return cend(); }20072008const_reverse_iterator crbegin() const { return const_reverse_iterator(this, Back()); }2009const_reverse_iterator crend() const { return const_reverse_iterator(this, NULL); }2010const_reverse_iterator rbegin() const { return crbegin(); }2011const_reverse_iterator rend() const { return crend(); }20122013Item* PushBack();2014Item* PushFront();2015Item* PushBack(const T& value);2016Item* PushFront(const T& value);2017void PopBack();2018void PopFront();20192020// Item can be null - it means PushBack.2021Item* InsertBefore(Item* pItem);2022// Item can be null - it means PushFront.2023Item* InsertAfter(Item* pItem);2024Item* InsertBefore(Item* pItem, const T& value);2025Item* InsertAfter(Item* pItem, const T& value);20262027void Clear();2028void Remove(Item* pItem);20292030private:2031const ALLOCATION_CALLBACKS& m_AllocationCallbacks;2032PoolAllocator<Item> m_ItemAllocator;2033Item* m_pFront;2034Item* m_pBack;2035size_t m_Count;2036};20372038#ifndef _D3D12MA_LIST_ITERATOR_FUNCTIONS2039template<typename T>2040T& List<T>::iterator::operator*() const2041{2042D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2043return m_pItem->Value;2044}20452046template<typename T>2047T* List<T>::iterator::operator->() const2048{2049D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2050return &m_pItem->Value;2051}20522053template<typename T>2054typename List<T>::iterator& List<T>::iterator::operator++()2055{2056D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2057m_pItem = m_pItem->pNext;2058return *this;2059}20602061template<typename T>2062typename List<T>::iterator& List<T>::iterator::operator--()2063{2064if (m_pItem != NULL)2065{2066m_pItem = m_pItem->pPrev;2067}2068else2069{2070D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2071m_pItem = m_pList->Back();2072}2073return *this;2074}20752076template<typename T>2077typename List<T>::iterator List<T>::iterator::operator++(int)2078{2079iterator result = *this;2080++* this;2081return result;2082}20832084template<typename T>2085typename List<T>::iterator List<T>::iterator::operator--(int)2086{2087iterator result = *this;2088--* this;2089return result;2090}20912092template<typename T>2093bool List<T>::iterator::operator==(const iterator& rhs) const2094{2095D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2096return m_pItem == rhs.m_pItem;2097}20982099template<typename T>2100bool List<T>::iterator::operator!=(const iterator& rhs) const2101{2102D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2103return m_pItem != rhs.m_pItem;2104}2105#endif // _D3D12MA_LIST_ITERATOR_FUNCTIONS21062107#ifndef _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS2108template<typename T>2109T& List<T>::reverse_iterator::operator*() const2110{2111D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2112return m_pItem->Value;2113}21142115template<typename T>2116T* List<T>::reverse_iterator::operator->() const2117{2118D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2119return &m_pItem->Value;2120}21212122template<typename T>2123typename List<T>::reverse_iterator& List<T>::reverse_iterator::operator++()2124{2125D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2126m_pItem = m_pItem->pPrev;2127return *this;2128}21292130template<typename T>2131typename List<T>::reverse_iterator& List<T>::reverse_iterator::operator--()2132{2133if (m_pItem != NULL)2134{2135m_pItem = m_pItem->pNext;2136}2137else2138{2139D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2140m_pItem = m_pList->Front();2141}2142return *this;2143}21442145template<typename T>2146typename List<T>::reverse_iterator List<T>::reverse_iterator::operator++(int)2147{2148reverse_iterator result = *this;2149++* this;2150return result;2151}21522153template<typename T>2154typename List<T>::reverse_iterator List<T>::reverse_iterator::operator--(int)2155{2156reverse_iterator result = *this;2157--* this;2158return result;2159}21602161template<typename T>2162bool List<T>::reverse_iterator::operator==(const reverse_iterator& rhs) const2163{2164D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2165return m_pItem == rhs.m_pItem;2166}21672168template<typename T>2169bool List<T>::reverse_iterator::operator!=(const reverse_iterator& rhs) const2170{2171D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2172return m_pItem != rhs.m_pItem;2173}2174#endif // _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS21752176#ifndef _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS2177template<typename T>2178typename List<T>::iterator List<T>::const_iterator::dropConst() const2179{2180return iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));2181}21822183template<typename T>2184const T& List<T>::const_iterator::operator*() const2185{2186D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2187return m_pItem->Value;2188}21892190template<typename T>2191const T* List<T>::const_iterator::operator->() const2192{2193D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2194return &m_pItem->Value;2195}21962197template<typename T>2198typename List<T>::const_iterator& List<T>::const_iterator::operator++()2199{2200D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2201m_pItem = m_pItem->pNext;2202return *this;2203}22042205template<typename T>2206typename List<T>::const_iterator& List<T>::const_iterator::operator--()2207{2208if (m_pItem != NULL)2209{2210m_pItem = m_pItem->pPrev;2211}2212else2213{2214D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2215m_pItem = m_pList->Back();2216}2217return *this;2218}22192220template<typename T>2221typename List<T>::const_iterator List<T>::const_iterator::operator++(int)2222{2223const_iterator result = *this;2224++* this;2225return result;2226}22272228template<typename T>2229typename List<T>::const_iterator List<T>::const_iterator::operator--(int)2230{2231const_iterator result = *this;2232--* this;2233return result;2234}22352236template<typename T>2237bool List<T>::const_iterator::operator==(const const_iterator& rhs) const2238{2239D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2240return m_pItem == rhs.m_pItem;2241}22422243template<typename T>2244bool List<T>::const_iterator::operator!=(const const_iterator& rhs) const2245{2246D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2247return m_pItem != rhs.m_pItem;2248}2249#endif // _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS22502251#ifndef _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS2252template<typename T>2253typename List<T>::reverse_iterator List<T>::const_reverse_iterator::dropConst() const2254{2255return reverse_iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));2256}22572258template<typename T>2259const T& List<T>::const_reverse_iterator::operator*() const2260{2261D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2262return m_pItem->Value;2263}22642265template<typename T>2266const T* List<T>::const_reverse_iterator::operator->() const2267{2268D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2269return &m_pItem->Value;2270}22712272template<typename T>2273typename List<T>::const_reverse_iterator& List<T>::const_reverse_iterator::operator++()2274{2275D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2276m_pItem = m_pItem->pPrev;2277return *this;2278}22792280template<typename T>2281typename List<T>::const_reverse_iterator& List<T>::const_reverse_iterator::operator--()2282{2283if (m_pItem != NULL)2284{2285m_pItem = m_pItem->pNext;2286}2287else2288{2289D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2290m_pItem = m_pList->Front();2291}2292return *this;2293}22942295template<typename T>2296typename List<T>::const_reverse_iterator List<T>::const_reverse_iterator::operator++(int)2297{2298const_reverse_iterator result = *this;2299++* this;2300return result;2301}23022303template<typename T>2304typename List<T>::const_reverse_iterator List<T>::const_reverse_iterator::operator--(int)2305{2306const_reverse_iterator result = *this;2307--* this;2308return result;2309}23102311template<typename T>2312bool List<T>::const_reverse_iterator::operator==(const const_reverse_iterator& rhs) const2313{2314D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2315return m_pItem == rhs.m_pItem;2316}23172318template<typename T>2319bool List<T>::const_reverse_iterator::operator!=(const const_reverse_iterator& rhs) const2320{2321D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2322return m_pItem != rhs.m_pItem;2323}2324#endif // _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS23252326#ifndef _D3D12MA_LIST_FUNCTIONS2327template<typename T>2328List<T>::List(const ALLOCATION_CALLBACKS& allocationCallbacks)2329: m_AllocationCallbacks(allocationCallbacks),2330m_ItemAllocator(allocationCallbacks, 128),2331m_pFront(NULL),2332m_pBack(NULL),2333m_Count(0) {}23342335template<typename T>2336void List<T>::Clear()2337{2338if(!IsEmpty())2339{2340Item* pItem = m_pBack;2341while(pItem != NULL)2342{2343Item* const pPrevItem = pItem->pPrev;2344m_ItemAllocator.Free(pItem);2345pItem = pPrevItem;2346}2347m_pFront = NULL;2348m_pBack = NULL;2349m_Count = 0;2350}2351}23522353template<typename T>2354typename List<T>::Item* List<T>::PushBack()2355{2356Item* const pNewItem = m_ItemAllocator.Alloc();2357pNewItem->pNext = NULL;2358if(IsEmpty())2359{2360pNewItem->pPrev = NULL;2361m_pFront = pNewItem;2362m_pBack = pNewItem;2363m_Count = 1;2364}2365else2366{2367pNewItem->pPrev = m_pBack;2368m_pBack->pNext = pNewItem;2369m_pBack = pNewItem;2370++m_Count;2371}2372return pNewItem;2373}23742375template<typename T>2376typename List<T>::Item* List<T>::PushFront()2377{2378Item* const pNewItem = m_ItemAllocator.Alloc();2379pNewItem->pPrev = NULL;2380if(IsEmpty())2381{2382pNewItem->pNext = NULL;2383m_pFront = pNewItem;2384m_pBack = pNewItem;2385m_Count = 1;2386}2387else2388{2389pNewItem->pNext = m_pFront;2390m_pFront->pPrev = pNewItem;2391m_pFront = pNewItem;2392++m_Count;2393}2394return pNewItem;2395}23962397template<typename T>2398typename List<T>::Item* List<T>::PushBack(const T& value)2399{2400Item* const pNewItem = PushBack();2401pNewItem->Value = value;2402return pNewItem;2403}24042405template<typename T>2406typename List<T>::Item* List<T>::PushFront(const T& value)2407{2408Item* const pNewItem = PushFront();2409pNewItem->Value = value;2410return pNewItem;2411}24122413template<typename T>2414void List<T>::PopBack()2415{2416D3D12MA_HEAVY_ASSERT(m_Count > 0);2417Item* const pBackItem = m_pBack;2418Item* const pPrevItem = pBackItem->pPrev;2419if(pPrevItem != NULL)2420{2421pPrevItem->pNext = NULL;2422}2423m_pBack = pPrevItem;2424m_ItemAllocator.Free(pBackItem);2425--m_Count;2426}24272428template<typename T>2429void List<T>::PopFront()2430{2431D3D12MA_HEAVY_ASSERT(m_Count > 0);2432Item* const pFrontItem = m_pFront;2433Item* const pNextItem = pFrontItem->pNext;2434if(pNextItem != NULL)2435{2436pNextItem->pPrev = NULL;2437}2438m_pFront = pNextItem;2439m_ItemAllocator.Free(pFrontItem);2440--m_Count;2441}24422443template<typename T>2444void List<T>::Remove(Item* pItem)2445{2446D3D12MA_HEAVY_ASSERT(pItem != NULL);2447D3D12MA_HEAVY_ASSERT(m_Count > 0);24482449if(pItem->pPrev != NULL)2450{2451pItem->pPrev->pNext = pItem->pNext;2452}2453else2454{2455D3D12MA_HEAVY_ASSERT(m_pFront == pItem);2456m_pFront = pItem->pNext;2457}24582459if(pItem->pNext != NULL)2460{2461pItem->pNext->pPrev = pItem->pPrev;2462}2463else2464{2465D3D12MA_HEAVY_ASSERT(m_pBack == pItem);2466m_pBack = pItem->pPrev;2467}24682469m_ItemAllocator.Free(pItem);2470--m_Count;2471}24722473template<typename T>2474typename List<T>::Item* List<T>::InsertBefore(Item* pItem)2475{2476if(pItem != NULL)2477{2478Item* const prevItem = pItem->pPrev;2479Item* const newItem = m_ItemAllocator.Alloc();2480newItem->pPrev = prevItem;2481newItem->pNext = pItem;2482pItem->pPrev = newItem;2483if(prevItem != NULL)2484{2485prevItem->pNext = newItem;2486}2487else2488{2489D3D12MA_HEAVY_ASSERT(m_pFront == pItem);2490m_pFront = newItem;2491}2492++m_Count;2493return newItem;2494}2495else2496{2497return PushBack();2498}2499}25002501template<typename T>2502typename List<T>::Item* List<T>::InsertAfter(Item* pItem)2503{2504if(pItem != NULL)2505{2506Item* const nextItem = pItem->pNext;2507Item* const newItem = m_ItemAllocator.Alloc();2508newItem->pNext = nextItem;2509newItem->pPrev = pItem;2510pItem->pNext = newItem;2511if(nextItem != NULL)2512{2513nextItem->pPrev = newItem;2514}2515else2516{2517D3D12MA_HEAVY_ASSERT(m_pBack == pItem);2518m_pBack = newItem;2519}2520++m_Count;2521return newItem;2522}2523else2524return PushFront();2525}25262527template<typename T>2528typename List<T>::Item* List<T>::InsertBefore(Item* pItem, const T& value)2529{2530Item* const newItem = InsertBefore(pItem);2531newItem->Value = value;2532return newItem;2533}25342535template<typename T>2536typename List<T>::Item* List<T>::InsertAfter(Item* pItem, const T& value)2537{2538Item* const newItem = InsertAfter(pItem);2539newItem->Value = value;2540return newItem;2541}2542#endif // _D3D12MA_LIST_FUNCTIONS2543#endif // _D3D12MA_LIST25442545#ifndef _D3D12MA_INTRUSIVE_LINKED_LIST2546/*2547Expected interface of ItemTypeTraits:2548struct MyItemTypeTraits2549{2550using ItemType = MyItem;2551static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }2552static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }2553static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }2554static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }2555};2556*/2557template<typename ItemTypeTraits>2558class IntrusiveLinkedList2559{2560public:2561using ItemType = typename ItemTypeTraits::ItemType;2562static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }2563static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }25642565// Movable, not copyable.2566IntrusiveLinkedList() = default;2567IntrusiveLinkedList(const IntrusiveLinkedList&) = delete;2568IntrusiveLinkedList(IntrusiveLinkedList&& src);2569IntrusiveLinkedList& operator=(const IntrusiveLinkedList&) = delete;2570IntrusiveLinkedList& operator=(IntrusiveLinkedList&& src);2571~IntrusiveLinkedList() { D3D12MA_HEAVY_ASSERT(IsEmpty()); }25722573size_t GetCount() const { return m_Count; }2574bool IsEmpty() const { return m_Count == 0; }25752576ItemType* Front() { return m_Front; }2577ItemType* Back() { return m_Back; }2578const ItemType* Front() const { return m_Front; }2579const ItemType* Back() const { return m_Back; }25802581void PushBack(ItemType* item);2582void PushFront(ItemType* item);2583ItemType* PopBack();2584ItemType* PopFront();25852586// MyItem can be null - it means PushBack.2587void InsertBefore(ItemType* existingItem, ItemType* newItem);2588// MyItem can be null - it means PushFront.2589void InsertAfter(ItemType* existingItem, ItemType* newItem);25902591void Remove(ItemType* item);2592void RemoveAll();25932594private:2595ItemType* m_Front = NULL;2596ItemType* m_Back = NULL;2597size_t m_Count = 0;2598};25992600#ifndef _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS2601template<typename ItemTypeTraits>2602IntrusiveLinkedList<ItemTypeTraits>::IntrusiveLinkedList(IntrusiveLinkedList&& src)2603: m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)2604{2605src.m_Front = src.m_Back = NULL;2606src.m_Count = 0;2607}26082609template<typename ItemTypeTraits>2610IntrusiveLinkedList<ItemTypeTraits>& IntrusiveLinkedList<ItemTypeTraits>::operator=(IntrusiveLinkedList&& src)2611{2612if (&src != this)2613{2614D3D12MA_HEAVY_ASSERT(IsEmpty());2615m_Front = src.m_Front;2616m_Back = src.m_Back;2617m_Count = src.m_Count;2618src.m_Front = src.m_Back = NULL;2619src.m_Count = 0;2620}2621return *this;2622}26232624template<typename ItemTypeTraits>2625void IntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)2626{2627D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);2628if (IsEmpty())2629{2630m_Front = item;2631m_Back = item;2632m_Count = 1;2633}2634else2635{2636ItemTypeTraits::AccessPrev(item) = m_Back;2637ItemTypeTraits::AccessNext(m_Back) = item;2638m_Back = item;2639++m_Count;2640}2641}26422643template<typename ItemTypeTraits>2644void IntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)2645{2646D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);2647if (IsEmpty())2648{2649m_Front = item;2650m_Back = item;2651m_Count = 1;2652}2653else2654{2655ItemTypeTraits::AccessNext(item) = m_Front;2656ItemTypeTraits::AccessPrev(m_Front) = item;2657m_Front = item;2658++m_Count;2659}2660}26612662template<typename ItemTypeTraits>2663typename IntrusiveLinkedList<ItemTypeTraits>::ItemType* IntrusiveLinkedList<ItemTypeTraits>::PopBack()2664{2665D3D12MA_HEAVY_ASSERT(m_Count > 0);2666ItemType* const backItem = m_Back;2667ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);2668if (prevItem != NULL)2669{2670ItemTypeTraits::AccessNext(prevItem) = NULL;2671}2672m_Back = prevItem;2673--m_Count;2674ItemTypeTraits::AccessPrev(backItem) = NULL;2675ItemTypeTraits::AccessNext(backItem) = NULL;2676return backItem;2677}26782679template<typename ItemTypeTraits>2680typename IntrusiveLinkedList<ItemTypeTraits>::ItemType* IntrusiveLinkedList<ItemTypeTraits>::PopFront()2681{2682D3D12MA_HEAVY_ASSERT(m_Count > 0);2683ItemType* const frontItem = m_Front;2684ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);2685if (nextItem != NULL)2686{2687ItemTypeTraits::AccessPrev(nextItem) = NULL;2688}2689m_Front = nextItem;2690--m_Count;2691ItemTypeTraits::AccessPrev(frontItem) = NULL;2692ItemTypeTraits::AccessNext(frontItem) = NULL;2693return frontItem;2694}26952696template<typename ItemTypeTraits>2697void IntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)2698{2699D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);2700if (existingItem != NULL)2701{2702ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);2703ItemTypeTraits::AccessPrev(newItem) = prevItem;2704ItemTypeTraits::AccessNext(newItem) = existingItem;2705ItemTypeTraits::AccessPrev(existingItem) = newItem;2706if (prevItem != NULL)2707{2708ItemTypeTraits::AccessNext(prevItem) = newItem;2709}2710else2711{2712D3D12MA_HEAVY_ASSERT(m_Front == existingItem);2713m_Front = newItem;2714}2715++m_Count;2716}2717else2718PushBack(newItem);2719}27202721template<typename ItemTypeTraits>2722void IntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)2723{2724D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);2725if (existingItem != NULL)2726{2727ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);2728ItemTypeTraits::AccessNext(newItem) = nextItem;2729ItemTypeTraits::AccessPrev(newItem) = existingItem;2730ItemTypeTraits::AccessNext(existingItem) = newItem;2731if (nextItem != NULL)2732{2733ItemTypeTraits::AccessPrev(nextItem) = newItem;2734}2735else2736{2737D3D12MA_HEAVY_ASSERT(m_Back == existingItem);2738m_Back = newItem;2739}2740++m_Count;2741}2742else2743return PushFront(newItem);2744}27452746template<typename ItemTypeTraits>2747void IntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)2748{2749D3D12MA_HEAVY_ASSERT(item != NULL && m_Count > 0);2750if (ItemTypeTraits::GetPrev(item) != NULL)2751{2752ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);2753}2754else2755{2756D3D12MA_HEAVY_ASSERT(m_Front == item);2757m_Front = ItemTypeTraits::GetNext(item);2758}27592760if (ItemTypeTraits::GetNext(item) != NULL)2761{2762ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);2763}2764else2765{2766D3D12MA_HEAVY_ASSERT(m_Back == item);2767m_Back = ItemTypeTraits::GetPrev(item);2768}2769ItemTypeTraits::AccessPrev(item) = NULL;2770ItemTypeTraits::AccessNext(item) = NULL;2771--m_Count;2772}27732774template<typename ItemTypeTraits>2775void IntrusiveLinkedList<ItemTypeTraits>::RemoveAll()2776{2777if (!IsEmpty())2778{2779ItemType* item = m_Back;2780while (item != NULL)2781{2782ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);2783ItemTypeTraits::AccessPrev(item) = NULL;2784ItemTypeTraits::AccessNext(item) = NULL;2785item = prevItem;2786}2787m_Front = NULL;2788m_Back = NULL;2789m_Count = 0;2790}2791}2792#endif // _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS2793#endif // _D3D12MA_INTRUSIVE_LINKED_LIST27942795#ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR2796/*2797Thread-safe wrapper over PoolAllocator free list, for allocation of Allocation objects.2798*/2799class AllocationObjectAllocator2800{2801D3D12MA_CLASS_NO_COPY(AllocationObjectAllocator);2802public:2803AllocationObjectAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, bool useMutex)2804: m_UseMutex(useMutex), m_Allocator(allocationCallbacks, 1024) {}28052806template<typename... Types>2807Allocation* Allocate(Types... args);2808void Free(Allocation* alloc);28092810private:2811D3D12MA_MUTEX m_Mutex;2812bool m_UseMutex;2813PoolAllocator<Allocation> m_Allocator;2814};28152816#ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS2817template<typename... Types>2818Allocation* AllocationObjectAllocator::Allocate(Types... args)2819{2820MutexLock mutexLock(m_Mutex, m_UseMutex);2821return m_Allocator.Alloc(std::forward<Types>(args)...);2822}28232824void AllocationObjectAllocator::Free(Allocation* alloc)2825{2826MutexLock mutexLock(m_Mutex, m_UseMutex);2827m_Allocator.Free(alloc);2828}2829#endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS2830#endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR28312832#ifndef _D3D12MA_SUBALLOCATION2833/*2834Represents a region of NormalBlock that is either assigned and returned as2835allocated memory block or free.2836*/2837struct Suballocation2838{2839UINT64 offset;2840UINT64 size;2841void* privateData;2842SuballocationType type;2843};2844using SuballocationList = List<Suballocation>;28452846// Comparator for offsets.2847struct SuballocationOffsetLess2848{2849bool operator()(const Suballocation& lhs, const Suballocation& rhs) const2850{2851return lhs.offset < rhs.offset;2852}2853};28542855struct SuballocationOffsetGreater2856{2857bool operator()(const Suballocation& lhs, const Suballocation& rhs) const2858{2859return lhs.offset > rhs.offset;2860}2861};28622863struct SuballocationItemSizeLess2864{2865bool operator()(const SuballocationList::iterator lhs, const SuballocationList::iterator rhs) const2866{2867return lhs->size < rhs->size;2868}2869bool operator()(const SuballocationList::iterator lhs, UINT64 rhsSize) const2870{2871return lhs->size < rhsSize;2872}2873};2874#endif // _D3D12MA_SUBALLOCATION28752876#ifndef _D3D12MA_ALLOCATION_REQUEST2877/*2878Parameters of planned allocation inside a NormalBlock.2879*/2880struct AllocationRequest2881{2882AllocHandle allocHandle;2883UINT64 size;2884UINT64 algorithmData;2885UINT64 sumFreeSize; // Sum size of free items that overlap with proposed allocation.2886UINT64 sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.2887SuballocationList::iterator item;2888};2889#endif // _D3D12MA_ALLOCATION_REQUEST28902891#ifndef _D3D12MA_BLOCK_METADATA2892/*2893Data structure used for bookkeeping of allocations and unused ranges of memory2894in a single ID3D12Heap memory block.2895*/2896class BlockMetadata2897{2898public:2899BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);2900virtual ~BlockMetadata() = default;29012902virtual void Init(UINT64 size) { m_Size = size; }2903// Validates all data structures inside this object. If not valid, returns false.2904virtual bool Validate() const = 0;2905UINT64 GetSize() const { return m_Size; }2906bool IsVirtual() const { return m_IsVirtual; }2907virtual size_t GetAllocationCount() const = 0;2908virtual size_t GetFreeRegionsCount() const = 0;2909virtual UINT64 GetSumFreeSize() const = 0;2910virtual UINT64 GetAllocationOffset(AllocHandle allocHandle) const = 0;2911// Returns true if this block is empty - contains only single free suballocation.2912virtual bool IsEmpty() const = 0;29132914virtual void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const = 0;29152916// Tries to find a place for suballocation with given parameters inside this block.2917// If succeeded, fills pAllocationRequest and returns true.2918// If failed, returns false.2919virtual bool CreateAllocationRequest(2920UINT64 allocSize,2921UINT64 allocAlignment,2922bool upperAddress,2923UINT32 strategy,2924AllocationRequest* pAllocationRequest) = 0;29252926// Makes actual allocation based on request. Request must already be checked and valid.2927virtual void Alloc(2928const AllocationRequest& request,2929UINT64 allocSize,2930void* PrivateData) = 0;29312932virtual void Free(AllocHandle allocHandle) = 0;2933// Frees all allocations.2934// Careful! Don't call it if there are Allocation objects owned by pPrivateData of of cleared allocations!2935virtual void Clear() = 0;29362937virtual AllocHandle GetAllocationListBegin() const = 0;2938virtual AllocHandle GetNextAllocation(AllocHandle prevAlloc) const = 0;2939virtual UINT64 GetNextFreeRegionSize(AllocHandle alloc) const = 0;2940virtual void* GetAllocationPrivateData(AllocHandle allocHandle) const = 0;2941virtual void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) = 0;29422943virtual void AddStatistics(Statistics& inoutStats) const = 0;2944virtual void AddDetailedStatistics(DetailedStatistics& inoutStats) const = 0;2945virtual void WriteAllocationInfoToJson(JsonWriter& json) const = 0;2946virtual void DebugLogAllAllocations() const = 0;29472948protected:2949const ALLOCATION_CALLBACKS* GetAllocs() const { return m_pAllocationCallbacks; }2950UINT64 GetDebugMargin() const { return IsVirtual() ? 0 : D3D12MA_DEBUG_MARGIN; }29512952void DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const;2953void PrintDetailedMap_Begin(JsonWriter& json,2954UINT64 unusedBytes,2955size_t allocationCount,2956size_t unusedRangeCount) const;2957void PrintDetailedMap_Allocation(JsonWriter& json,2958UINT64 offset, UINT64 size, void* privateData) const;2959void PrintDetailedMap_UnusedRange(JsonWriter& json,2960UINT64 offset, UINT64 size) const;2961void PrintDetailedMap_End(JsonWriter& json) const;29622963private:2964UINT64 m_Size;2965bool m_IsVirtual;2966const ALLOCATION_CALLBACKS* m_pAllocationCallbacks;29672968D3D12MA_CLASS_NO_COPY(BlockMetadata);2969};29702971#ifndef _D3D12MA_BLOCK_METADATA_FUNCTIONS2972BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)2973: m_Size(0),2974m_IsVirtual(isVirtual),2975m_pAllocationCallbacks(allocationCallbacks)2976{2977D3D12MA_ASSERT(allocationCallbacks);2978}29792980void BlockMetadata::DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const2981{2982if (IsVirtual())2983{2984D3D12MA_DEBUG_LOG(L"UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p", offset, size, privateData);2985}2986else2987{2988D3D12MA_ASSERT(privateData != NULL);2989Allocation* allocation = reinterpret_cast<Allocation*>(privateData);29902991privateData = allocation->GetPrivateData();2992LPCWSTR name = allocation->GetName();29932994D3D12MA_DEBUG_LOG(L"UNFREED ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p; Name: %s",2995offset, size, privateData, name ? name : L"D3D12MA_Empty");2996}2997}29982999void BlockMetadata::PrintDetailedMap_Begin(JsonWriter& json,3000UINT64 unusedBytes, size_t allocationCount, size_t unusedRangeCount) const3001{3002json.WriteString(L"TotalBytes");3003json.WriteNumber(GetSize());30043005json.WriteString(L"UnusedBytes");3006json.WriteNumber(unusedBytes);30073008json.WriteString(L"Allocations");3009json.WriteNumber((UINT64)allocationCount);30103011json.WriteString(L"UnusedRanges");3012json.WriteNumber((UINT64)unusedRangeCount);30133014json.WriteString(L"Suballocations");3015json.BeginArray();3016}30173018void BlockMetadata::PrintDetailedMap_Allocation(JsonWriter& json,3019UINT64 offset, UINT64 size, void* privateData) const3020{3021json.BeginObject(true);30223023json.WriteString(L"Offset");3024json.WriteNumber(offset);30253026if (IsVirtual())3027{3028json.WriteString(L"Size");3029json.WriteNumber(size);3030if (privateData)3031{3032json.WriteString(L"CustomData");3033json.WriteNumber((uintptr_t)privateData);3034}3035}3036else3037{3038const Allocation* const alloc = (const Allocation*)privateData;3039D3D12MA_ASSERT(alloc);3040json.AddAllocationToObject(*alloc);3041}3042json.EndObject();3043}30443045void BlockMetadata::PrintDetailedMap_UnusedRange(JsonWriter& json,3046UINT64 offset, UINT64 size) const3047{3048json.BeginObject(true);30493050json.WriteString(L"Offset");3051json.WriteNumber(offset);30523053json.WriteString(L"Type");3054json.WriteString(L"FREE");30553056json.WriteString(L"Size");3057json.WriteNumber(size);30583059json.EndObject();3060}30613062void BlockMetadata::PrintDetailedMap_End(JsonWriter& json) const3063{3064json.EndArray();3065}3066#endif // _D3D12MA_BLOCK_METADATA_FUNCTIONS3067#endif // _D3D12MA_BLOCK_METADATA30683069#ifndef _D3D12MA_BLOCK_METADATA_LINEAR3070class BlockMetadata_Linear : public BlockMetadata3071{3072public:3073BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);3074virtual ~BlockMetadata_Linear() = default;30753076UINT64 GetSumFreeSize() const override { return m_SumFreeSize; }3077bool IsEmpty() const override { return GetAllocationCount() == 0; }3078UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; };30793080void Init(UINT64 size) override;3081bool Validate() const override;3082size_t GetAllocationCount() const override;3083size_t GetFreeRegionsCount() const override;3084void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;30853086bool CreateAllocationRequest(3087UINT64 allocSize,3088UINT64 allocAlignment,3089bool upperAddress,3090UINT32 strategy,3091AllocationRequest* pAllocationRequest) override;30923093void Alloc(3094const AllocationRequest& request,3095UINT64 allocSize,3096void* privateData) override;30973098void Free(AllocHandle allocHandle) override;3099void Clear() override;31003101AllocHandle GetAllocationListBegin() const override;3102AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override;3103UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override;3104void* GetAllocationPrivateData(AllocHandle allocHandle) const override;3105void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;31063107void AddStatistics(Statistics& inoutStats) const override;3108void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;3109void WriteAllocationInfoToJson(JsonWriter& json) const override;3110void DebugLogAllAllocations() const override;31113112private:3113/*3114There are two suballocation vectors, used in ping-pong way.3115The one with index m_1stVectorIndex is called 1st.3116The one with index (m_1stVectorIndex ^ 1) is called 2nd.31172nd can be non-empty only when 1st is not empty.3118When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.3119*/3120typedef Vector<Suballocation> SuballocationVectorType;31213122enum ALLOC_REQUEST_TYPE3123{3124ALLOC_REQUEST_UPPER_ADDRESS,3125ALLOC_REQUEST_END_OF_1ST,3126ALLOC_REQUEST_END_OF_2ND,3127};31283129enum SECOND_VECTOR_MODE3130{3131SECOND_VECTOR_EMPTY,3132/*3133Suballocations in 2nd vector are created later than the ones in 1st, but they3134all have smaller offset.3135*/3136SECOND_VECTOR_RING_BUFFER,3137/*3138Suballocations in 2nd vector are upper side of double stack.3139They all have offsets higher than those in 1st vector.3140Top of this stack means smaller offsets, but higher indices in this vector.3141*/3142SECOND_VECTOR_DOUBLE_STACK,3143};31443145UINT64 m_SumFreeSize;3146SuballocationVectorType m_Suballocations0, m_Suballocations1;3147UINT32 m_1stVectorIndex;3148SECOND_VECTOR_MODE m_2ndVectorMode;3149// Number of items in 1st vector with hAllocation = null at the beginning.3150size_t m_1stNullItemsBeginCount;3151// Number of other items in 1st vector with hAllocation = null somewhere in the middle.3152size_t m_1stNullItemsMiddleCount;3153// Number of items in 2nd vector with hAllocation = null.3154size_t m_2ndNullItemsCount;31553156SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }3157SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }3158const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }3159const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }31603161Suballocation& FindSuballocation(UINT64 offset) const;3162bool ShouldCompact1st() const;3163void CleanupAfterFree();31643165bool CreateAllocationRequest_LowerAddress(3166UINT64 allocSize,3167UINT64 allocAlignment,3168AllocationRequest* pAllocationRequest);3169bool CreateAllocationRequest_UpperAddress(3170UINT64 allocSize,3171UINT64 allocAlignment,3172AllocationRequest* pAllocationRequest);31733174D3D12MA_CLASS_NO_COPY(BlockMetadata_Linear)3175};31763177#ifndef _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS3178BlockMetadata_Linear::BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)3179: BlockMetadata(allocationCallbacks, isVirtual),3180m_SumFreeSize(0),3181m_Suballocations0(*allocationCallbacks),3182m_Suballocations1(*allocationCallbacks),3183m_1stVectorIndex(0),3184m_2ndVectorMode(SECOND_VECTOR_EMPTY),3185m_1stNullItemsBeginCount(0),3186m_1stNullItemsMiddleCount(0),3187m_2ndNullItemsCount(0)3188{3189D3D12MA_ASSERT(allocationCallbacks);3190}31913192void BlockMetadata_Linear::Init(UINT64 size)3193{3194BlockMetadata::Init(size);3195m_SumFreeSize = size;3196}31973198bool BlockMetadata_Linear::Validate() const3199{3200D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize());3201const SuballocationVectorType& suballocations1st = AccessSuballocations1st();3202const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();32033204D3D12MA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));3205D3D12MA_VALIDATE(!suballocations1st.empty() ||3206suballocations2nd.empty() ||3207m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);32083209if (!suballocations1st.empty())3210{3211// Null item at the beginning should be accounted into m_1stNullItemsBeginCount.3212D3D12MA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != SUBALLOCATION_TYPE_FREE);3213// Null item at the end should be just pop_back().3214D3D12MA_VALIDATE(suballocations1st.back().type != SUBALLOCATION_TYPE_FREE);3215}3216if (!suballocations2nd.empty())3217{3218// Null item at the end should be just pop_back().3219D3D12MA_VALIDATE(suballocations2nd.back().type != SUBALLOCATION_TYPE_FREE);3220}32213222D3D12MA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());3223D3D12MA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());32243225UINT64 sumUsedSize = 0;3226const size_t suballoc1stCount = suballocations1st.size();3227UINT64 offset = 0;32283229if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)3230{3231const size_t suballoc2ndCount = suballocations2nd.size();3232size_t nullItem2ndCount = 0;3233for (size_t i = 0; i < suballoc2ndCount; ++i)3234{3235const Suballocation& suballoc = suballocations2nd[i];3236const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);32373238const Allocation* alloc = (Allocation*)suballoc.privateData;3239if (!IsVirtual())3240{3241D3D12MA_VALIDATE(currFree == (alloc == NULL));3242}3243D3D12MA_VALIDATE(suballoc.offset >= offset);32443245if (!currFree)3246{3247if (!IsVirtual())3248{3249D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3250D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3251}3252sumUsedSize += suballoc.size;3253}3254else3255{3256++nullItem2ndCount;3257}32583259offset = suballoc.offset + suballoc.size + GetDebugMargin();3260}32613262D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);3263}32643265for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)3266{3267const Suballocation& suballoc = suballocations1st[i];3268D3D12MA_VALIDATE(suballoc.type == SUBALLOCATION_TYPE_FREE &&3269suballoc.privateData == NULL);3270}32713272size_t nullItem1stCount = m_1stNullItemsBeginCount;32733274for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)3275{3276const Suballocation& suballoc = suballocations1st[i];3277const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);32783279const Allocation* alloc = (Allocation*)suballoc.privateData;3280if (!IsVirtual())3281{3282D3D12MA_VALIDATE(currFree == (alloc == NULL));3283}3284D3D12MA_VALIDATE(suballoc.offset >= offset);3285D3D12MA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);32863287if (!currFree)3288{3289if (!IsVirtual())3290{3291D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3292D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3293}3294sumUsedSize += suballoc.size;3295}3296else3297{3298++nullItem1stCount;3299}33003301offset = suballoc.offset + suballoc.size + GetDebugMargin();3302}3303D3D12MA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);33043305if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)3306{3307const size_t suballoc2ndCount = suballocations2nd.size();3308size_t nullItem2ndCount = 0;3309for (size_t i = suballoc2ndCount; i--; )3310{3311const Suballocation& suballoc = suballocations2nd[i];3312const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);33133314const Allocation* alloc = (Allocation*)suballoc.privateData;3315if (!IsVirtual())3316{3317D3D12MA_VALIDATE(currFree == (alloc == NULL));3318}3319D3D12MA_VALIDATE(suballoc.offset >= offset);33203321if (!currFree)3322{3323if (!IsVirtual())3324{3325D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3326D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3327}3328sumUsedSize += suballoc.size;3329}3330else3331{3332++nullItem2ndCount;3333}33343335offset = suballoc.offset + suballoc.size + GetDebugMargin();3336}33373338D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);3339}33403341D3D12MA_VALIDATE(offset <= GetSize());3342D3D12MA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);33433344return true;3345}33463347size_t BlockMetadata_Linear::GetAllocationCount() const3348{3349return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +3350AccessSuballocations2nd().size() - m_2ndNullItemsCount;3351}33523353size_t BlockMetadata_Linear::GetFreeRegionsCount() const3354{3355// Function only used for defragmentation, which is disabled for this algorithm3356D3D12MA_ASSERT(0);3357return SIZE_MAX;3358}33593360void BlockMetadata_Linear::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const3361{3362const Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1);3363outInfo.Offset = suballoc.offset;3364outInfo.Size = suballoc.size;3365outInfo.pPrivateData = suballoc.privateData;3366}33673368bool BlockMetadata_Linear::CreateAllocationRequest(3369UINT64 allocSize,3370UINT64 allocAlignment,3371bool upperAddress,3372UINT32 strategy,3373AllocationRequest* pAllocationRequest)3374{3375D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");3376D3D12MA_ASSERT(pAllocationRequest != NULL);3377D3D12MA_HEAVY_ASSERT(Validate());33783379if(allocSize > GetSize())3380return false;33813382pAllocationRequest->size = allocSize;3383return upperAddress ?3384CreateAllocationRequest_UpperAddress(3385allocSize, allocAlignment, pAllocationRequest) :3386CreateAllocationRequest_LowerAddress(3387allocSize, allocAlignment, pAllocationRequest);3388}33893390void BlockMetadata_Linear::Alloc(3391const AllocationRequest& request,3392UINT64 allocSize,3393void* privateData)3394{3395UINT64 offset = (UINT64)request.allocHandle - 1;3396const Suballocation newSuballoc = { offset, request.size, privateData, SUBALLOCATION_TYPE_ALLOCATION };33973398switch (request.algorithmData)3399{3400case ALLOC_REQUEST_UPPER_ADDRESS:3401{3402D3D12MA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&3403"CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");3404SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();3405suballocations2nd.push_back(newSuballoc);3406m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;3407break;3408}3409case ALLOC_REQUEST_END_OF_1ST:3410{3411SuballocationVectorType& suballocations1st = AccessSuballocations1st();34123413D3D12MA_ASSERT(suballocations1st.empty() ||3414offset >= suballocations1st.back().offset + suballocations1st.back().size);3415// Check if it fits before the end of the block.3416D3D12MA_ASSERT(offset + request.size <= GetSize());34173418suballocations1st.push_back(newSuballoc);3419break;3420}3421case ALLOC_REQUEST_END_OF_2ND:3422{3423SuballocationVectorType& suballocations1st = AccessSuballocations1st();3424// New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.3425D3D12MA_ASSERT(!suballocations1st.empty() &&3426offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);3427SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();34283429switch (m_2ndVectorMode)3430{3431case SECOND_VECTOR_EMPTY:3432// First allocation from second part ring buffer.3433D3D12MA_ASSERT(suballocations2nd.empty());3434m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;3435break;3436case SECOND_VECTOR_RING_BUFFER:3437// 2-part ring buffer is already started.3438D3D12MA_ASSERT(!suballocations2nd.empty());3439break;3440case SECOND_VECTOR_DOUBLE_STACK:3441D3D12MA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");3442break;3443default:3444D3D12MA_ASSERT(0);3445}34463447suballocations2nd.push_back(newSuballoc);3448break;3449}3450default:3451D3D12MA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");3452}3453m_SumFreeSize -= newSuballoc.size;3454}34553456void BlockMetadata_Linear::Free(AllocHandle allocHandle)3457{3458SuballocationVectorType& suballocations1st = AccessSuballocations1st();3459SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();3460UINT64 offset = (UINT64)allocHandle - 1;34613462if (!suballocations1st.empty())3463{3464// First allocation: Mark it as next empty at the beginning.3465Suballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];3466if (firstSuballoc.offset == offset)3467{3468firstSuballoc.type = SUBALLOCATION_TYPE_FREE;3469firstSuballoc.privateData = NULL;3470m_SumFreeSize += firstSuballoc.size;3471++m_1stNullItemsBeginCount;3472CleanupAfterFree();3473return;3474}3475}34763477// Last allocation in 2-part ring buffer or top of upper stack (same logic).3478if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||3479m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)3480{3481Suballocation& lastSuballoc = suballocations2nd.back();3482if (lastSuballoc.offset == offset)3483{3484m_SumFreeSize += lastSuballoc.size;3485suballocations2nd.pop_back();3486CleanupAfterFree();3487return;3488}3489}3490// Last allocation in 1st vector.3491else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)3492{3493Suballocation& lastSuballoc = suballocations1st.back();3494if (lastSuballoc.offset == offset)3495{3496m_SumFreeSize += lastSuballoc.size;3497suballocations1st.pop_back();3498CleanupAfterFree();3499return;3500}3501}35023503Suballocation refSuballoc;3504refSuballoc.offset = offset;3505// Rest of members stays uninitialized intentionally for better performance.35063507// Item from the middle of 1st vector.3508{3509const SuballocationVectorType::iterator it = BinaryFindSorted(3510suballocations1st.begin() + m_1stNullItemsBeginCount,3511suballocations1st.end(),3512refSuballoc,3513SuballocationOffsetLess());3514if (it != suballocations1st.end())3515{3516it->type = SUBALLOCATION_TYPE_FREE;3517it->privateData = NULL;3518++m_1stNullItemsMiddleCount;3519m_SumFreeSize += it->size;3520CleanupAfterFree();3521return;3522}3523}35243525if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)3526{3527// Item from the middle of 2nd vector.3528const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?3529BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) :3530BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater());3531if (it != suballocations2nd.end())3532{3533it->type = SUBALLOCATION_TYPE_FREE;3534it->privateData = NULL;3535++m_2ndNullItemsCount;3536m_SumFreeSize += it->size;3537CleanupAfterFree();3538return;3539}3540}35413542D3D12MA_ASSERT(0 && "Allocation to free not found in linear allocator!");3543}35443545void BlockMetadata_Linear::Clear()3546{3547m_SumFreeSize = GetSize();3548m_Suballocations0.clear();3549m_Suballocations1.clear();3550// Leaving m_1stVectorIndex unchanged - it doesn't matter.3551m_2ndVectorMode = SECOND_VECTOR_EMPTY;3552m_1stNullItemsBeginCount = 0;3553m_1stNullItemsMiddleCount = 0;3554m_2ndNullItemsCount = 0;3555}35563557AllocHandle BlockMetadata_Linear::GetAllocationListBegin() const3558{3559// Function only used for defragmentation, which is disabled for this algorithm3560D3D12MA_ASSERT(0);3561return (AllocHandle)0;3562}35633564AllocHandle BlockMetadata_Linear::GetNextAllocation(AllocHandle prevAlloc) const3565{3566// Function only used for defragmentation, which is disabled for this algorithm3567D3D12MA_ASSERT(0);3568return (AllocHandle)0;3569}35703571UINT64 BlockMetadata_Linear::GetNextFreeRegionSize(AllocHandle alloc) const3572{3573// Function only used for defragmentation, which is disabled for this algorithm3574D3D12MA_ASSERT(0);3575return 0;3576}35773578void* BlockMetadata_Linear::GetAllocationPrivateData(AllocHandle allocHandle) const3579{3580return FindSuballocation((UINT64)allocHandle - 1).privateData;3581}35823583void BlockMetadata_Linear::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)3584{3585Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1);3586suballoc.privateData = privateData;3587}35883589void BlockMetadata_Linear::AddStatistics(Statistics& inoutStats) const3590{3591inoutStats.BlockCount++;3592inoutStats.AllocationCount += (UINT)GetAllocationCount();3593inoutStats.BlockBytes += GetSize();3594inoutStats.AllocationBytes += GetSize() - m_SumFreeSize;3595}35963597void BlockMetadata_Linear::AddDetailedStatistics(DetailedStatistics& inoutStats) const3598{3599inoutStats.Stats.BlockCount++;3600inoutStats.Stats.BlockBytes += GetSize();36013602const UINT64 size = GetSize();3603const SuballocationVectorType& suballocations1st = AccessSuballocations1st();3604const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();3605const size_t suballoc1stCount = suballocations1st.size();3606const size_t suballoc2ndCount = suballocations2nd.size();36073608UINT64 lastOffset = 0;3609if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)3610{3611const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;3612size_t nextAlloc2ndIndex = 0;3613while (lastOffset < freeSpace2ndTo1stEnd)3614{3615// Find next non-null allocation or move nextAllocIndex to the end.3616while (nextAlloc2ndIndex < suballoc2ndCount &&3617suballocations2nd[nextAlloc2ndIndex].privateData == NULL)3618{3619++nextAlloc2ndIndex;3620}36213622// Found non-null allocation.3623if (nextAlloc2ndIndex < suballoc2ndCount)3624{3625const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];36263627// 1. Process free space before this allocation.3628if (lastOffset < suballoc.offset)3629{3630// There is free space from lastOffset to suballoc.offset.3631const UINT64 unusedRangeSize = suballoc.offset - lastOffset;3632AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);3633}36343635// 2. Process this allocation.3636// There is allocation with suballoc.offset, suballoc.size.3637AddDetailedStatisticsAllocation(inoutStats, suballoc.size);36383639// 3. Prepare for next iteration.3640lastOffset = suballoc.offset + suballoc.size;3641++nextAlloc2ndIndex;3642}3643// We are at the end.3644else3645{3646// There is free space from lastOffset to freeSpace2ndTo1stEnd.3647if (lastOffset < freeSpace2ndTo1stEnd)3648{3649const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;3650AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);3651}36523653// End of loop.3654lastOffset = freeSpace2ndTo1stEnd;3655}3656}3657}36583659size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;3660const UINT64 freeSpace1stTo2ndEnd =3661m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;3662while (lastOffset < freeSpace1stTo2ndEnd)3663{3664// Find next non-null allocation or move nextAllocIndex to the end.3665while (nextAlloc1stIndex < suballoc1stCount &&3666suballocations1st[nextAlloc1stIndex].privateData == NULL)3667{3668++nextAlloc1stIndex;3669}36703671// Found non-null allocation.3672if (nextAlloc1stIndex < suballoc1stCount)3673{3674const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];36753676// 1. Process free space before this allocation.3677if (lastOffset < suballoc.offset)3678{3679// There is free space from lastOffset to suballoc.offset.3680const UINT64 unusedRangeSize = suballoc.offset - lastOffset;3681AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);3682}36833684// 2. Process this allocation.3685// There is allocation with suballoc.offset, suballoc.size.3686AddDetailedStatisticsAllocation(inoutStats, suballoc.size);36873688// 3. Prepare for next iteration.3689lastOffset = suballoc.offset + suballoc.size;3690++nextAlloc1stIndex;3691}3692// We are at the end.3693else3694{3695// There is free space from lastOffset to freeSpace1stTo2ndEnd.3696if (lastOffset < freeSpace1stTo2ndEnd)3697{3698const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;3699AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);3700}37013702// End of loop.3703lastOffset = freeSpace1stTo2ndEnd;3704}3705}37063707if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)3708{3709size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;3710while (lastOffset < size)3711{3712// Find next non-null allocation or move nextAllocIndex to the end.3713while (nextAlloc2ndIndex != SIZE_MAX &&3714suballocations2nd[nextAlloc2ndIndex].privateData == NULL)3715{3716--nextAlloc2ndIndex;3717}37183719// Found non-null allocation.3720if (nextAlloc2ndIndex != SIZE_MAX)3721{3722const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];37233724// 1. Process free space before this allocation.3725if (lastOffset < suballoc.offset)3726{3727// There is free space from lastOffset to suballoc.offset.3728const UINT64 unusedRangeSize = suballoc.offset - lastOffset;3729AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);3730}37313732// 2. Process this allocation.3733// There is allocation with suballoc.offset, suballoc.size.3734AddDetailedStatisticsAllocation(inoutStats, suballoc.size);37353736// 3. Prepare for next iteration.3737lastOffset = suballoc.offset + suballoc.size;3738--nextAlloc2ndIndex;3739}3740// We are at the end.3741else3742{3743// There is free space from lastOffset to size.3744if (lastOffset < size)3745{3746const UINT64 unusedRangeSize = size - lastOffset;3747AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);3748}37493750// End of loop.3751lastOffset = size;3752}3753}3754}3755}37563757void BlockMetadata_Linear::WriteAllocationInfoToJson(JsonWriter& json) const3758{3759const UINT64 size = GetSize();3760const SuballocationVectorType& suballocations1st = AccessSuballocations1st();3761const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();3762const size_t suballoc1stCount = suballocations1st.size();3763const size_t suballoc2ndCount = suballocations2nd.size();37643765// FIRST PASS37663767size_t unusedRangeCount = 0;3768UINT64 usedBytes = 0;37693770UINT64 lastOffset = 0;37713772size_t alloc2ndCount = 0;3773if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)3774{3775const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;3776size_t nextAlloc2ndIndex = 0;3777while (lastOffset < freeSpace2ndTo1stEnd)3778{3779// Find next non-null allocation or move nextAlloc2ndIndex to the end.3780while (nextAlloc2ndIndex < suballoc2ndCount &&3781suballocations2nd[nextAlloc2ndIndex].privateData == NULL)3782{3783++nextAlloc2ndIndex;3784}37853786// Found non-null allocation.3787if (nextAlloc2ndIndex < suballoc2ndCount)3788{3789const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];37903791// 1. Process free space before this allocation.3792if (lastOffset < suballoc.offset)3793{3794// There is free space from lastOffset to suballoc.offset.3795++unusedRangeCount;3796}37973798// 2. Process this allocation.3799// There is allocation with suballoc.offset, suballoc.size.3800++alloc2ndCount;3801usedBytes += suballoc.size;38023803// 3. Prepare for next iteration.3804lastOffset = suballoc.offset + suballoc.size;3805++nextAlloc2ndIndex;3806}3807// We are at the end.3808else3809{3810if (lastOffset < freeSpace2ndTo1stEnd)3811{3812// There is free space from lastOffset to freeSpace2ndTo1stEnd.3813++unusedRangeCount;3814}38153816// End of loop.3817lastOffset = freeSpace2ndTo1stEnd;3818}3819}3820}38213822size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;3823size_t alloc1stCount = 0;3824const UINT64 freeSpace1stTo2ndEnd =3825m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;3826while (lastOffset < freeSpace1stTo2ndEnd)3827{3828// Find next non-null allocation or move nextAllocIndex to the end.3829while (nextAlloc1stIndex < suballoc1stCount &&3830suballocations1st[nextAlloc1stIndex].privateData == NULL)3831{3832++nextAlloc1stIndex;3833}38343835// Found non-null allocation.3836if (nextAlloc1stIndex < suballoc1stCount)3837{3838const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];38393840// 1. Process free space before this allocation.3841if (lastOffset < suballoc.offset)3842{3843// There is free space from lastOffset to suballoc.offset.3844++unusedRangeCount;3845}38463847// 2. Process this allocation.3848// There is allocation with suballoc.offset, suballoc.size.3849++alloc1stCount;3850usedBytes += suballoc.size;38513852// 3. Prepare for next iteration.3853lastOffset = suballoc.offset + suballoc.size;3854++nextAlloc1stIndex;3855}3856// We are at the end.3857else3858{3859if (lastOffset < size)3860{3861// There is free space from lastOffset to freeSpace1stTo2ndEnd.3862++unusedRangeCount;3863}38643865// End of loop.3866lastOffset = freeSpace1stTo2ndEnd;3867}3868}38693870if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)3871{3872size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;3873while (lastOffset < size)3874{3875// Find next non-null allocation or move nextAlloc2ndIndex to the end.3876while (nextAlloc2ndIndex != SIZE_MAX &&3877suballocations2nd[nextAlloc2ndIndex].privateData == NULL)3878{3879--nextAlloc2ndIndex;3880}38813882// Found non-null allocation.3883if (nextAlloc2ndIndex != SIZE_MAX)3884{3885const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];38863887// 1. Process free space before this allocation.3888if (lastOffset < suballoc.offset)3889{3890// There is free space from lastOffset to suballoc.offset.3891++unusedRangeCount;3892}38933894// 2. Process this allocation.3895// There is allocation with suballoc.offset, suballoc.size.3896++alloc2ndCount;3897usedBytes += suballoc.size;38983899// 3. Prepare for next iteration.3900lastOffset = suballoc.offset + suballoc.size;3901--nextAlloc2ndIndex;3902}3903// We are at the end.3904else3905{3906if (lastOffset < size)3907{3908// There is free space from lastOffset to size.3909++unusedRangeCount;3910}39113912// End of loop.3913lastOffset = size;3914}3915}3916}39173918const UINT64 unusedBytes = size - usedBytes;3919PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);39203921// SECOND PASS3922lastOffset = 0;3923if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)3924{3925const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;3926size_t nextAlloc2ndIndex = 0;3927while (lastOffset < freeSpace2ndTo1stEnd)3928{3929// Find next non-null allocation or move nextAlloc2ndIndex to the end.3930while (nextAlloc2ndIndex < suballoc2ndCount &&3931suballocations2nd[nextAlloc2ndIndex].privateData == NULL)3932{3933++nextAlloc2ndIndex;3934}39353936// Found non-null allocation.3937if (nextAlloc2ndIndex < suballoc2ndCount)3938{3939const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];39403941// 1. Process free space before this allocation.3942if (lastOffset < suballoc.offset)3943{3944// There is free space from lastOffset to suballoc.offset.3945const UINT64 unusedRangeSize = suballoc.offset - lastOffset;3946PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);3947}39483949// 2. Process this allocation.3950// There is allocation with suballoc.offset, suballoc.size.3951PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);39523953// 3. Prepare for next iteration.3954lastOffset = suballoc.offset + suballoc.size;3955++nextAlloc2ndIndex;3956}3957// We are at the end.3958else3959{3960if (lastOffset < freeSpace2ndTo1stEnd)3961{3962// There is free space from lastOffset to freeSpace2ndTo1stEnd.3963const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;3964PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);3965}39663967// End of loop.3968lastOffset = freeSpace2ndTo1stEnd;3969}3970}3971}39723973nextAlloc1stIndex = m_1stNullItemsBeginCount;3974while (lastOffset < freeSpace1stTo2ndEnd)3975{3976// Find next non-null allocation or move nextAllocIndex to the end.3977while (nextAlloc1stIndex < suballoc1stCount &&3978suballocations1st[nextAlloc1stIndex].privateData == NULL)3979{3980++nextAlloc1stIndex;3981}39823983// Found non-null allocation.3984if (nextAlloc1stIndex < suballoc1stCount)3985{3986const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];39873988// 1. Process free space before this allocation.3989if (lastOffset < suballoc.offset)3990{3991// There is free space from lastOffset to suballoc.offset.3992const UINT64 unusedRangeSize = suballoc.offset - lastOffset;3993PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);3994}39953996// 2. Process this allocation.3997// There is allocation with suballoc.offset, suballoc.size.3998PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);39994000// 3. Prepare for next iteration.4001lastOffset = suballoc.offset + suballoc.size;4002++nextAlloc1stIndex;4003}4004// We are at the end.4005else4006{4007if (lastOffset < freeSpace1stTo2ndEnd)4008{4009// There is free space from lastOffset to freeSpace1stTo2ndEnd.4010const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;4011PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4012}40134014// End of loop.4015lastOffset = freeSpace1stTo2ndEnd;4016}4017}40184019if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4020{4021size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;4022while (lastOffset < size)4023{4024// Find next non-null allocation or move nextAlloc2ndIndex to the end.4025while (nextAlloc2ndIndex != SIZE_MAX &&4026suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4027{4028--nextAlloc2ndIndex;4029}40304031// Found non-null allocation.4032if (nextAlloc2ndIndex != SIZE_MAX)4033{4034const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];40354036// 1. Process free space before this allocation.4037if (lastOffset < suballoc.offset)4038{4039// There is free space from lastOffset to suballoc.offset.4040const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4041PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4042}40434044// 2. Process this allocation.4045// There is allocation with suballoc.offset, suballoc.size.4046PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);40474048// 3. Prepare for next iteration.4049lastOffset = suballoc.offset + suballoc.size;4050--nextAlloc2ndIndex;4051}4052// We are at the end.4053else4054{4055if (lastOffset < size)4056{4057// There is free space from lastOffset to size.4058const UINT64 unusedRangeSize = size - lastOffset;4059PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4060}40614062// End of loop.4063lastOffset = size;4064}4065}4066}40674068PrintDetailedMap_End(json);4069}40704071void BlockMetadata_Linear::DebugLogAllAllocations() const4072{4073const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4074for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)4075if (it->type != SUBALLOCATION_TYPE_FREE)4076DebugLogAllocation(it->offset, it->size, it->privateData);40774078const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4079for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)4080if (it->type != SUBALLOCATION_TYPE_FREE)4081DebugLogAllocation(it->offset, it->size, it->privateData);4082}40834084Suballocation& BlockMetadata_Linear::FindSuballocation(UINT64 offset) const4085{4086const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4087const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();40884089Suballocation refSuballoc;4090refSuballoc.offset = offset;4091// Rest of members stays uninitialized intentionally for better performance.40924093// Item from the 1st vector.4094{4095const SuballocationVectorType::const_iterator it = BinaryFindSorted(4096suballocations1st.begin() + m_1stNullItemsBeginCount,4097suballocations1st.end(),4098refSuballoc,4099SuballocationOffsetLess());4100if (it != suballocations1st.end())4101{4102return const_cast<Suballocation&>(*it);4103}4104}41054106if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)4107{4108// Rest of members stays uninitialized intentionally for better performance.4109const SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?4110BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) :4111BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater());4112if (it != suballocations2nd.end())4113{4114return const_cast<Suballocation&>(*it);4115}4116}41174118D3D12MA_ASSERT(0 && "Allocation not found in linear allocator!");4119return const_cast<Suballocation&>(suballocations1st.back()); // Should never occur.4120}41214122bool BlockMetadata_Linear::ShouldCompact1st() const4123{4124const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;4125const size_t suballocCount = AccessSuballocations1st().size();4126return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;4127}41284129void BlockMetadata_Linear::CleanupAfterFree()4130{4131SuballocationVectorType& suballocations1st = AccessSuballocations1st();4132SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();41334134if (IsEmpty())4135{4136suballocations1st.clear();4137suballocations2nd.clear();4138m_1stNullItemsBeginCount = 0;4139m_1stNullItemsMiddleCount = 0;4140m_2ndNullItemsCount = 0;4141m_2ndVectorMode = SECOND_VECTOR_EMPTY;4142}4143else4144{4145const size_t suballoc1stCount = suballocations1st.size();4146const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;4147D3D12MA_ASSERT(nullItem1stCount <= suballoc1stCount);41484149// Find more null items at the beginning of 1st vector.4150while (m_1stNullItemsBeginCount < suballoc1stCount &&4151suballocations1st[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE)4152{4153++m_1stNullItemsBeginCount;4154--m_1stNullItemsMiddleCount;4155}41564157// Find more null items at the end of 1st vector.4158while (m_1stNullItemsMiddleCount > 0 &&4159suballocations1st.back().type == SUBALLOCATION_TYPE_FREE)4160{4161--m_1stNullItemsMiddleCount;4162suballocations1st.pop_back();4163}41644165// Find more null items at the end of 2nd vector.4166while (m_2ndNullItemsCount > 0 &&4167suballocations2nd.back().type == SUBALLOCATION_TYPE_FREE)4168{4169--m_2ndNullItemsCount;4170suballocations2nd.pop_back();4171}41724173// Find more null items at the beginning of 2nd vector.4174while (m_2ndNullItemsCount > 0 &&4175suballocations2nd[0].type == SUBALLOCATION_TYPE_FREE)4176{4177--m_2ndNullItemsCount;4178suballocations2nd.remove(0);4179}41804181if (ShouldCompact1st())4182{4183const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;4184size_t srcIndex = m_1stNullItemsBeginCount;4185for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)4186{4187while (suballocations1st[srcIndex].type == SUBALLOCATION_TYPE_FREE)4188{4189++srcIndex;4190}4191if (dstIndex != srcIndex)4192{4193suballocations1st[dstIndex] = suballocations1st[srcIndex];4194}4195++srcIndex;4196}4197suballocations1st.resize(nonNullItemCount);4198m_1stNullItemsBeginCount = 0;4199m_1stNullItemsMiddleCount = 0;4200}42014202// 2nd vector became empty.4203if (suballocations2nd.empty())4204{4205m_2ndVectorMode = SECOND_VECTOR_EMPTY;4206}42074208// 1st vector became empty.4209if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)4210{4211suballocations1st.clear();4212m_1stNullItemsBeginCount = 0;42134214if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4215{4216// Swap 1st with 2nd. Now 2nd is empty.4217m_2ndVectorMode = SECOND_VECTOR_EMPTY;4218m_1stNullItemsMiddleCount = m_2ndNullItemsCount;4219while (m_1stNullItemsBeginCount < suballocations2nd.size() &&4220suballocations2nd[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE)4221{4222++m_1stNullItemsBeginCount;4223--m_1stNullItemsMiddleCount;4224}4225m_2ndNullItemsCount = 0;4226m_1stVectorIndex ^= 1;4227}4228}4229}42304231D3D12MA_HEAVY_ASSERT(Validate());4232}42334234bool BlockMetadata_Linear::CreateAllocationRequest_LowerAddress(4235UINT64 allocSize,4236UINT64 allocAlignment,4237AllocationRequest* pAllocationRequest)4238{4239const UINT64 blockSize = GetSize();4240SuballocationVectorType& suballocations1st = AccessSuballocations1st();4241SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();42424243if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4244{4245// Try to allocate at the end of 1st vector.42464247UINT64 resultBaseOffset = 0;4248if (!suballocations1st.empty())4249{4250const Suballocation& lastSuballoc = suballocations1st.back();4251resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin();4252}42534254// Start from offset equal to beginning of free space.4255UINT64 resultOffset = resultBaseOffset;4256// Apply alignment.4257resultOffset = AlignUp(resultOffset, allocAlignment);42584259const UINT64 freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?4260suballocations2nd.back().offset : blockSize;42614262// There is enough free space at the end after alignment.4263if (resultOffset + allocSize + GetDebugMargin() <= freeSpaceEnd)4264{4265// All tests passed: Success.4266pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);4267// pAllocationRequest->item, customData unused.4268pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_1ST;4269return true;4270}4271}42724273// Wrap-around to end of 2nd vector. Try to allocate there, watching for the4274// beginning of 1st vector as the end of free space.4275if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4276{4277D3D12MA_ASSERT(!suballocations1st.empty());42784279UINT64 resultBaseOffset = 0;4280if (!suballocations2nd.empty())4281{4282const Suballocation& lastSuballoc = suballocations2nd.back();4283resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin();4284}42854286// Start from offset equal to beginning of free space.4287UINT64 resultOffset = resultBaseOffset;42884289// Apply alignment.4290resultOffset = AlignUp(resultOffset, allocAlignment);42914292size_t index1st = m_1stNullItemsBeginCount;4293// There is enough free space at the end after alignment.4294if ((index1st == suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= blockSize) ||4295(index1st < suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= suballocations1st[index1st].offset))4296{4297// All tests passed: Success.4298pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);4299pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_2ND;4300// pAllocationRequest->item, customData unused.4301return true;4302}4303}4304return false;4305}43064307bool BlockMetadata_Linear::CreateAllocationRequest_UpperAddress(4308UINT64 allocSize,4309UINT64 allocAlignment,4310AllocationRequest* pAllocationRequest)4311{4312const UINT64 blockSize = GetSize();4313SuballocationVectorType& suballocations1st = AccessSuballocations1st();4314SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();43154316if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4317{4318D3D12MA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");4319return false;4320}43214322// Try to allocate before 2nd.back(), or end of block if 2nd.empty().4323if (allocSize > blockSize)4324{4325return false;4326}4327UINT64 resultBaseOffset = blockSize - allocSize;4328if (!suballocations2nd.empty())4329{4330const Suballocation& lastSuballoc = suballocations2nd.back();4331resultBaseOffset = lastSuballoc.offset - allocSize;4332if (allocSize > lastSuballoc.offset)4333{4334return false;4335}4336}43374338// Start from offset equal to end of free space.4339UINT64 resultOffset = resultBaseOffset;4340// Apply debugMargin at the end.4341if (GetDebugMargin() > 0)4342{4343if (resultOffset < GetDebugMargin())4344{4345return false;4346}4347resultOffset -= GetDebugMargin();4348}43494350// Apply alignment.4351resultOffset = AlignDown(resultOffset, allocAlignment);4352// There is enough free space.4353const UINT64 endOf1st = !suballocations1st.empty() ?4354suballocations1st.back().offset + suballocations1st.back().size : 0;43554356if (endOf1st + GetDebugMargin() <= resultOffset)4357{4358// All tests passed: Success.4359pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);4360// pAllocationRequest->item unused.4361pAllocationRequest->algorithmData = ALLOC_REQUEST_UPPER_ADDRESS;4362return true;4363}4364return false;4365}4366#endif // _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS4367#endif // _D3D12MA_BLOCK_METADATA_LINEAR43684369#ifndef _D3D12MA_BLOCK_METADATA_TLSF4370class BlockMetadata_TLSF : public BlockMetadata4371{4372public:4373BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);4374virtual ~BlockMetadata_TLSF();43754376size_t GetAllocationCount() const override { return m_AllocCount; }4377size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }4378UINT64 GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }4379bool IsEmpty() const override { return m_NullBlock->offset == 0; }4380UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; };43814382void Init(UINT64 size) override;4383bool Validate() const override;4384void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;43854386bool CreateAllocationRequest(4387UINT64 allocSize,4388UINT64 allocAlignment,4389bool upperAddress,4390UINT32 strategy,4391AllocationRequest* pAllocationRequest) override;43924393void Alloc(4394const AllocationRequest& request,4395UINT64 allocSize,4396void* privateData) override;43974398void Free(AllocHandle allocHandle) override;4399void Clear() override;44004401AllocHandle GetAllocationListBegin() const override;4402AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override;4403UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override;4404void* GetAllocationPrivateData(AllocHandle allocHandle) const override;4405void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;44064407void AddStatistics(Statistics& inoutStats) const override;4408void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;4409void WriteAllocationInfoToJson(JsonWriter& json) const override;4410void DebugLogAllAllocations() const override;44114412private:4413// According to original paper it should be preferable 4 or 5:4414// M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"4415// http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf4416static const UINT8 SECOND_LEVEL_INDEX = 5;4417static const UINT16 SMALL_BUFFER_SIZE = 256;4418static const UINT INITIAL_BLOCK_ALLOC_COUNT = 16;4419static const UINT8 MEMORY_CLASS_SHIFT = 7;4420static const UINT8 MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;44214422class Block4423{4424public:4425UINT64 offset;4426UINT64 size;4427Block* prevPhysical;4428Block* nextPhysical;44294430void MarkFree() { prevFree = NULL; }4431void MarkTaken() { prevFree = this; }4432bool IsFree() const { return prevFree != this; }4433void*& PrivateData() { D3D12MA_HEAVY_ASSERT(!IsFree()); return privateData; }4434Block*& PrevFree() { return prevFree; }4435Block*& NextFree() { D3D12MA_HEAVY_ASSERT(IsFree()); return nextFree; }44364437private:4438Block* prevFree; // Address of the same block here indicates that block is taken4439union4440{4441Block* nextFree;4442void* privateData;4443};4444};44454446size_t m_AllocCount = 0;4447// Total number of free blocks besides null block4448size_t m_BlocksFreeCount = 0;4449// Total size of free blocks excluding null block4450UINT64 m_BlocksFreeSize = 0;4451UINT32 m_IsFreeBitmap = 0;4452UINT8 m_MemoryClasses = 0;4453UINT32 m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];4454UINT32 m_ListsCount = 0;4455/*4456* 0: 0-3 lists for small buffers4457* 1+: 0-(2^SLI-1) lists for normal buffers4458*/4459Block** m_FreeList = NULL;4460PoolAllocator<Block> m_BlockAllocator;4461Block* m_NullBlock = NULL;44624463UINT8 SizeToMemoryClass(UINT64 size) const;4464UINT16 SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const;4465UINT32 GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const;4466UINT32 GetListIndex(UINT64 size) const;44674468void RemoveFreeBlock(Block* block);4469void InsertFreeBlock(Block* block);4470void MergeBlock(Block* block, Block* prev);44714472Block* FindFreeBlock(UINT64 size, UINT32& listIndex) const;4473bool CheckBlock(4474Block& block,4475UINT32 listIndex,4476UINT64 allocSize,4477UINT64 allocAlignment,4478AllocationRequest* pAllocationRequest);44794480D3D12MA_CLASS_NO_COPY(BlockMetadata_TLSF)4481};44824483#ifndef _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS4484BlockMetadata_TLSF::BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)4485: BlockMetadata(allocationCallbacks, isVirtual),4486m_BlockAllocator(*allocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT)4487{4488D3D12MA_ASSERT(allocationCallbacks);4489}44904491BlockMetadata_TLSF::~BlockMetadata_TLSF()4492{4493D3D12MA_DELETE_ARRAY(*GetAllocs(), m_FreeList, m_ListsCount);4494}44954496void BlockMetadata_TLSF::Init(UINT64 size)4497{4498BlockMetadata::Init(size);44994500m_NullBlock = m_BlockAllocator.Alloc();4501m_NullBlock->size = size;4502m_NullBlock->offset = 0;4503m_NullBlock->prevPhysical = NULL;4504m_NullBlock->nextPhysical = NULL;4505m_NullBlock->MarkFree();4506m_NullBlock->NextFree() = NULL;4507m_NullBlock->PrevFree() = NULL;4508UINT8 memoryClass = SizeToMemoryClass(size);4509UINT16 sli = SizeToSecondIndex(size, memoryClass);4510m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;4511if (IsVirtual())4512m_ListsCount += 1UL << SECOND_LEVEL_INDEX;4513else4514m_ListsCount += 4;45154516m_MemoryClasses = memoryClass + 2;4517memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(UINT32));45184519m_FreeList = D3D12MA_NEW_ARRAY(*GetAllocs(), Block*, m_ListsCount);4520memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));4521}45224523bool BlockMetadata_TLSF::Validate() const4524{4525D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize());45264527UINT64 calculatedSize = m_NullBlock->size;4528UINT64 calculatedFreeSize = m_NullBlock->size;4529size_t allocCount = 0;4530size_t freeCount = 0;45314532// Check integrity of free lists4533for (UINT32 list = 0; list < m_ListsCount; ++list)4534{4535Block* block = m_FreeList[list];4536if (block != NULL)4537{4538D3D12MA_VALIDATE(block->IsFree());4539D3D12MA_VALIDATE(block->PrevFree() == NULL);4540while (block->NextFree())4541{4542D3D12MA_VALIDATE(block->NextFree()->IsFree());4543D3D12MA_VALIDATE(block->NextFree()->PrevFree() == block);4544block = block->NextFree();4545}4546}4547}45484549D3D12MA_VALIDATE(m_NullBlock->nextPhysical == NULL);4550if (m_NullBlock->prevPhysical)4551{4552D3D12MA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);4553}45544555// Check all blocks4556UINT64 nextOffset = m_NullBlock->offset;4557for (Block* prev = m_NullBlock->prevPhysical; prev != NULL; prev = prev->prevPhysical)4558{4559D3D12MA_VALIDATE(prev->offset + prev->size == nextOffset);4560nextOffset = prev->offset;4561calculatedSize += prev->size;45624563UINT32 listIndex = GetListIndex(prev->size);4564if (prev->IsFree())4565{4566++freeCount;4567// Check if free block belongs to free list4568Block* freeBlock = m_FreeList[listIndex];4569D3D12MA_VALIDATE(freeBlock != NULL);45704571bool found = false;4572do4573{4574if (freeBlock == prev)4575found = true;45764577freeBlock = freeBlock->NextFree();4578} while (!found && freeBlock != NULL);45794580D3D12MA_VALIDATE(found);4581calculatedFreeSize += prev->size;4582}4583else4584{4585++allocCount;4586// Check if taken block is not on a free list4587Block* freeBlock = m_FreeList[listIndex];4588while (freeBlock)4589{4590D3D12MA_VALIDATE(freeBlock != prev);4591freeBlock = freeBlock->NextFree();4592}4593}45944595if (prev->prevPhysical)4596{4597D3D12MA_VALIDATE(prev->prevPhysical->nextPhysical == prev);4598}4599}46004601D3D12MA_VALIDATE(nextOffset == 0);4602D3D12MA_VALIDATE(calculatedSize == GetSize());4603D3D12MA_VALIDATE(calculatedFreeSize == GetSumFreeSize());4604D3D12MA_VALIDATE(allocCount == m_AllocCount);4605D3D12MA_VALIDATE(freeCount == m_BlocksFreeCount);46064607return true;4608}46094610void BlockMetadata_TLSF::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const4611{4612Block* block = (Block*)allocHandle;4613D3D12MA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");4614outInfo.Offset = block->offset;4615outInfo.Size = block->size;4616outInfo.pPrivateData = block->PrivateData();4617}46184619bool BlockMetadata_TLSF::CreateAllocationRequest(4620UINT64 allocSize,4621UINT64 allocAlignment,4622bool upperAddress,4623UINT32 strategy,4624AllocationRequest* pAllocationRequest)4625{4626D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");4627D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm.");4628D3D12MA_ASSERT(pAllocationRequest != NULL);4629D3D12MA_HEAVY_ASSERT(Validate());46304631allocSize += GetDebugMargin();4632// Quick check for too small pool4633if (allocSize > GetSumFreeSize())4634return false;46354636// If no free blocks in pool then check only null block4637if (m_BlocksFreeCount == 0)4638return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest);46394640// Round up to the next block4641UINT64 sizeForNextList = allocSize;4642UINT16 smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4);4643if (allocSize > SMALL_BUFFER_SIZE)4644{4645sizeForNextList += (1ULL << (BitScanMSB(allocSize) - SECOND_LEVEL_INDEX));4646}4647else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)4648sizeForNextList = SMALL_BUFFER_SIZE + 1;4649else4650sizeForNextList += smallSizeStep;46514652UINT32 nextListIndex = 0;4653UINT32 prevListIndex = 0;4654Block* nextListBlock = NULL;4655Block* prevListBlock = NULL;46564657// Check blocks according to strategies4658if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_TIME)4659{4660// Quick check for larger block first4661nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);4662if (nextListBlock != NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))4663return true;46644665// If not fitted then null block4666if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))4667return true;46684669// Null block failed, search larger bucket4670while (nextListBlock)4671{4672if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))4673return true;4674nextListBlock = nextListBlock->NextFree();4675}46764677// Failed again, check best fit bucket4678prevListBlock = FindFreeBlock(allocSize, prevListIndex);4679while (prevListBlock)4680{4681if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))4682return true;4683prevListBlock = prevListBlock->NextFree();4684}4685}4686else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_MEMORY)4687{4688// Check best fit bucket4689prevListBlock = FindFreeBlock(allocSize, prevListIndex);4690while (prevListBlock)4691{4692if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))4693return true;4694prevListBlock = prevListBlock->NextFree();4695}46964697// If failed check null block4698if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))4699return true;47004701// Check larger bucket4702nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);4703while (nextListBlock)4704{4705if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))4706return true;4707nextListBlock = nextListBlock->NextFree();4708}4709}4710else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_OFFSET)4711{4712// Perform search from the start4713Vector<Block*> blockList(m_BlocksFreeCount, *GetAllocs());47144715size_t i = m_BlocksFreeCount;4716for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)4717{4718if (block->IsFree() && block->size >= allocSize)4719blockList[--i] = block;4720}47214722for (; i < m_BlocksFreeCount; ++i)4723{4724Block& block = *blockList[i];4725if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, pAllocationRequest))4726return true;4727}47284729// If failed check null block4730if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))4731return true;47324733// Whole range searched, no more memory4734return false;4735}4736else4737{4738// Check larger bucket4739nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);4740while (nextListBlock)4741{4742if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))4743return true;4744nextListBlock = nextListBlock->NextFree();4745}47464747// If failed check null block4748if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))4749return true;47504751// Check best fit bucket4752prevListBlock = FindFreeBlock(allocSize, prevListIndex);4753while (prevListBlock)4754{4755if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))4756return true;4757prevListBlock = prevListBlock->NextFree();4758}4759}47604761// Worst case, full search has to be done4762while (++nextListIndex < m_ListsCount)4763{4764nextListBlock = m_FreeList[nextListIndex];4765while (nextListBlock)4766{4767if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))4768return true;4769nextListBlock = nextListBlock->NextFree();4770}4771}47724773// No more memory sadly4774return false;4775}47764777void BlockMetadata_TLSF::Alloc(4778const AllocationRequest& request,4779UINT64 allocSize,4780void* privateData)4781{4782// Get block and pop it from the free list4783Block* currentBlock = (Block*)request.allocHandle;4784UINT64 offset = request.algorithmData;4785D3D12MA_ASSERT(currentBlock != NULL);4786D3D12MA_ASSERT(currentBlock->offset <= offset);47874788if (currentBlock != m_NullBlock)4789RemoveFreeBlock(currentBlock);47904791// Append missing alignment to prev block or create new one4792UINT64 misssingAlignment = offset - currentBlock->offset;4793if (misssingAlignment)4794{4795Block* prevBlock = currentBlock->prevPhysical;4796D3D12MA_ASSERT(prevBlock != NULL && "There should be no missing alignment at offset 0!");47974798if (prevBlock->IsFree() && prevBlock->size != GetDebugMargin())4799{4800UINT32 oldList = GetListIndex(prevBlock->size);4801prevBlock->size += misssingAlignment;4802// Check if new size crosses list bucket4803if (oldList != GetListIndex(prevBlock->size))4804{4805prevBlock->size -= misssingAlignment;4806RemoveFreeBlock(prevBlock);4807prevBlock->size += misssingAlignment;4808InsertFreeBlock(prevBlock);4809}4810else4811m_BlocksFreeSize += misssingAlignment;4812}4813else4814{4815Block* newBlock = m_BlockAllocator.Alloc();4816currentBlock->prevPhysical = newBlock;4817prevBlock->nextPhysical = newBlock;4818newBlock->prevPhysical = prevBlock;4819newBlock->nextPhysical = currentBlock;4820newBlock->size = misssingAlignment;4821newBlock->offset = currentBlock->offset;4822newBlock->MarkTaken();48234824InsertFreeBlock(newBlock);4825}48264827currentBlock->size -= misssingAlignment;4828currentBlock->offset += misssingAlignment;4829}48304831UINT64 size = request.size + GetDebugMargin();4832if (currentBlock->size == size)4833{4834if (currentBlock == m_NullBlock)4835{4836// Setup new null block4837m_NullBlock = m_BlockAllocator.Alloc();4838m_NullBlock->size = 0;4839m_NullBlock->offset = currentBlock->offset + size;4840m_NullBlock->prevPhysical = currentBlock;4841m_NullBlock->nextPhysical = NULL;4842m_NullBlock->MarkFree();4843m_NullBlock->PrevFree() = NULL;4844m_NullBlock->NextFree() = NULL;4845currentBlock->nextPhysical = m_NullBlock;4846currentBlock->MarkTaken();4847}4848}4849else4850{4851D3D12MA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");48524853// Create new free block4854Block* newBlock = m_BlockAllocator.Alloc();4855newBlock->size = currentBlock->size - size;4856newBlock->offset = currentBlock->offset + size;4857newBlock->prevPhysical = currentBlock;4858newBlock->nextPhysical = currentBlock->nextPhysical;4859currentBlock->nextPhysical = newBlock;4860currentBlock->size = size;48614862if (currentBlock == m_NullBlock)4863{4864m_NullBlock = newBlock;4865m_NullBlock->MarkFree();4866m_NullBlock->NextFree() = NULL;4867m_NullBlock->PrevFree() = NULL;4868currentBlock->MarkTaken();4869}4870else4871{4872newBlock->nextPhysical->prevPhysical = newBlock;4873newBlock->MarkTaken();4874InsertFreeBlock(newBlock);4875}4876}4877currentBlock->PrivateData() = privateData;48784879if (GetDebugMargin() > 0)4880{4881currentBlock->size -= GetDebugMargin();4882Block* newBlock = m_BlockAllocator.Alloc();4883newBlock->size = GetDebugMargin();4884newBlock->offset = currentBlock->offset + currentBlock->size;4885newBlock->prevPhysical = currentBlock;4886newBlock->nextPhysical = currentBlock->nextPhysical;4887newBlock->MarkTaken();4888currentBlock->nextPhysical->prevPhysical = newBlock;4889currentBlock->nextPhysical = newBlock;4890InsertFreeBlock(newBlock);4891}4892++m_AllocCount;4893}48944895void BlockMetadata_TLSF::Free(AllocHandle allocHandle)4896{4897Block* block = (Block*)allocHandle;4898Block* next = block->nextPhysical;4899D3D12MA_ASSERT(!block->IsFree() && "Block is already free!");49004901--m_AllocCount;4902if (GetDebugMargin() > 0)4903{4904RemoveFreeBlock(next);4905MergeBlock(next, block);4906block = next;4907next = next->nextPhysical;4908}49094910// Try merging4911Block* prev = block->prevPhysical;4912if (prev != NULL && prev->IsFree() && prev->size != GetDebugMargin())4913{4914RemoveFreeBlock(prev);4915MergeBlock(block, prev);4916}49174918if (!next->IsFree())4919InsertFreeBlock(block);4920else if (next == m_NullBlock)4921MergeBlock(m_NullBlock, block);4922else4923{4924RemoveFreeBlock(next);4925MergeBlock(next, block);4926InsertFreeBlock(next);4927}4928}49294930void BlockMetadata_TLSF::Clear()4931{4932m_AllocCount = 0;4933m_BlocksFreeCount = 0;4934m_BlocksFreeSize = 0;4935m_IsFreeBitmap = 0;4936m_NullBlock->offset = 0;4937m_NullBlock->size = GetSize();4938Block* block = m_NullBlock->prevPhysical;4939m_NullBlock->prevPhysical = NULL;4940while (block)4941{4942Block* prev = block->prevPhysical;4943m_BlockAllocator.Free(block);4944block = prev;4945}4946memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));4947memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(UINT32));4948}49494950AllocHandle BlockMetadata_TLSF::GetAllocationListBegin() const4951{4952if (m_AllocCount == 0)4953return (AllocHandle)0;49544955for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)4956{4957if (!block->IsFree())4958return (AllocHandle)block;4959}4960D3D12MA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");4961return (AllocHandle)0;4962}49634964AllocHandle BlockMetadata_TLSF::GetNextAllocation(AllocHandle prevAlloc) const4965{4966Block* startBlock = (Block*)prevAlloc;4967D3D12MA_ASSERT(!startBlock->IsFree() && "Incorrect block!");49684969for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)4970{4971if (!block->IsFree())4972return (AllocHandle)block;4973}4974return (AllocHandle)0;4975}49764977UINT64 BlockMetadata_TLSF::GetNextFreeRegionSize(AllocHandle alloc) const4978{4979Block* block = (Block*)alloc;4980D3D12MA_ASSERT(!block->IsFree() && "Incorrect block!");49814982if (block->prevPhysical)4983return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;4984return 0;4985}49864987void* BlockMetadata_TLSF::GetAllocationPrivateData(AllocHandle allocHandle) const4988{4989Block* block = (Block*)allocHandle;4990D3D12MA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");4991return block->PrivateData();4992}49934994void BlockMetadata_TLSF::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)4995{4996Block* block = (Block*)allocHandle;4997D3D12MA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");4998block->PrivateData() = privateData;4999}50005001void BlockMetadata_TLSF::AddStatistics(Statistics& inoutStats) const5002{5003inoutStats.BlockCount++;5004inoutStats.AllocationCount += static_cast<UINT>(m_AllocCount);5005inoutStats.BlockBytes += GetSize();5006inoutStats.AllocationBytes += GetSize() - GetSumFreeSize();5007}50085009void BlockMetadata_TLSF::AddDetailedStatistics(DetailedStatistics& inoutStats) const5010{5011inoutStats.Stats.BlockCount++;5012inoutStats.Stats.BlockBytes += GetSize();50135014for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5015{5016if (block->IsFree())5017AddDetailedStatisticsUnusedRange(inoutStats, block->size);5018else5019AddDetailedStatisticsAllocation(inoutStats, block->size);5020}50215022if (m_NullBlock->size > 0)5023AddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);5024}50255026void BlockMetadata_TLSF::WriteAllocationInfoToJson(JsonWriter& json) const5027{5028size_t blockCount = m_AllocCount + m_BlocksFreeCount;5029Vector<Block*> blockList(blockCount, *GetAllocs());50305031size_t i = blockCount;5032if (m_NullBlock->size > 0)5033{5034++blockCount;5035blockList.push_back(m_NullBlock);5036}5037for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5038{5039blockList[--i] = block;5040}5041D3D12MA_ASSERT(i == 0);50425043PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_BlocksFreeCount +5044(m_NullBlock->size > 0 ? 1 : 0));5045for (; i < blockCount; ++i)5046{5047Block* block = blockList[i];5048if (block->IsFree())5049PrintDetailedMap_UnusedRange(json, block->offset, block->size);5050else5051PrintDetailedMap_Allocation(json, block->offset, block->size, block->PrivateData());5052}5053PrintDetailedMap_End(json);5054}50555056void BlockMetadata_TLSF::DebugLogAllAllocations() const5057{5058for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5059{5060if (!block->IsFree())5061{5062DebugLogAllocation(block->offset, block->size, block->PrivateData());5063}5064}5065}50665067UINT8 BlockMetadata_TLSF::SizeToMemoryClass(UINT64 size) const5068{5069if (size > SMALL_BUFFER_SIZE)5070return BitScanMSB(size) - MEMORY_CLASS_SHIFT;5071return 0;5072}50735074UINT16 BlockMetadata_TLSF::SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const5075{5076if (memoryClass == 0)5077{5078if (IsVirtual())5079return static_cast<UINT16>((size - 1) / 8);5080else5081return static_cast<UINT16>((size - 1) / 64);5082}5083return static_cast<UINT16>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));5084}50855086UINT32 BlockMetadata_TLSF::GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const5087{5088if (memoryClass == 0)5089return secondIndex;50905091const UINT32 index = static_cast<UINT32>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;5092if (IsVirtual())5093return index + (1 << SECOND_LEVEL_INDEX);5094else5095return index + 4;5096}50975098UINT32 BlockMetadata_TLSF::GetListIndex(UINT64 size) const5099{5100UINT8 memoryClass = SizeToMemoryClass(size);5101return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));5102}51035104void BlockMetadata_TLSF::RemoveFreeBlock(Block* block)5105{5106D3D12MA_ASSERT(block != m_NullBlock);5107D3D12MA_ASSERT(block->IsFree());51085109if (block->NextFree() != NULL)5110block->NextFree()->PrevFree() = block->PrevFree();5111if (block->PrevFree() != NULL)5112block->PrevFree()->NextFree() = block->NextFree();5113else5114{5115UINT8 memClass = SizeToMemoryClass(block->size);5116UINT16 secondIndex = SizeToSecondIndex(block->size, memClass);5117UINT32 index = GetListIndex(memClass, secondIndex);5118m_FreeList[index] = block->NextFree();5119if (block->NextFree() == NULL)5120{5121m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);5122if (m_InnerIsFreeBitmap[memClass] == 0)5123m_IsFreeBitmap &= ~(1UL << memClass);5124}5125}5126block->MarkTaken();5127block->PrivateData() = NULL;5128--m_BlocksFreeCount;5129m_BlocksFreeSize -= block->size;5130}51315132void BlockMetadata_TLSF::InsertFreeBlock(Block* block)5133{5134D3D12MA_ASSERT(block != m_NullBlock);5135D3D12MA_ASSERT(!block->IsFree() && "Cannot insert block twice!");51365137UINT8 memClass = SizeToMemoryClass(block->size);5138UINT16 secondIndex = SizeToSecondIndex(block->size, memClass);5139UINT32 index = GetListIndex(memClass, secondIndex);5140block->PrevFree() = NULL;5141block->NextFree() = m_FreeList[index];5142m_FreeList[index] = block;5143if (block->NextFree() != NULL)5144block->NextFree()->PrevFree() = block;5145else5146{5147m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;5148m_IsFreeBitmap |= 1UL << memClass;5149}5150++m_BlocksFreeCount;5151m_BlocksFreeSize += block->size;5152}51535154void BlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)5155{5156D3D12MA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!");5157D3D12MA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");51585159block->offset = prev->offset;5160block->size += prev->size;5161block->prevPhysical = prev->prevPhysical;5162if (block->prevPhysical)5163block->prevPhysical->nextPhysical = block;5164m_BlockAllocator.Free(prev);5165}51665167BlockMetadata_TLSF::Block* BlockMetadata_TLSF::FindFreeBlock(UINT64 size, UINT32& listIndex) const5168{5169UINT8 memoryClass = SizeToMemoryClass(size);5170UINT32 innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));5171if (!innerFreeMap)5172{5173// Check higher levels for avaiable blocks5174UINT32 freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));5175if (!freeMap)5176return NULL; // No more memory avaible51775178// Find lowest free region5179memoryClass = BitScanLSB(freeMap);5180innerFreeMap = m_InnerIsFreeBitmap[memoryClass];5181D3D12MA_ASSERT(innerFreeMap != 0);5182}5183// Find lowest free subregion5184listIndex = GetListIndex(memoryClass, BitScanLSB(innerFreeMap));5185return m_FreeList[listIndex];5186}51875188bool BlockMetadata_TLSF::CheckBlock(5189Block& block,5190UINT32 listIndex,5191UINT64 allocSize,5192UINT64 allocAlignment,5193AllocationRequest* pAllocationRequest)5194{5195D3D12MA_ASSERT(block.IsFree() && "Block is already taken!");51965197UINT64 alignedOffset = AlignUp(block.offset, allocAlignment);5198if (block.size < allocSize + alignedOffset - block.offset)5199return false;52005201// Alloc successful5202pAllocationRequest->allocHandle = (AllocHandle)█5203pAllocationRequest->size = allocSize - GetDebugMargin();5204pAllocationRequest->algorithmData = alignedOffset;52055206// Place block at the start of list if it's normal block5207if (listIndex != m_ListsCount && block.PrevFree())5208{5209block.PrevFree()->NextFree() = block.NextFree();5210if (block.NextFree())5211block.NextFree()->PrevFree() = block.PrevFree();5212block.PrevFree() = NULL;5213block.NextFree() = m_FreeList[listIndex];5214m_FreeList[listIndex] = █5215if (block.NextFree())5216block.NextFree()->PrevFree() = █5217}52185219return true;5220}5221#endif // _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS5222#endif // _D3D12MA_BLOCK_METADATA_TLSF52235224#ifndef _D3D12MA_MEMORY_BLOCK5225/*5226Represents a single block of device memory (heap).5227Base class for inheritance.5228Thread-safety: This class must be externally synchronized.5229*/5230class MemoryBlock5231{5232public:5233// Creates the ID3D12Heap.5234MemoryBlock(5235AllocatorPimpl* allocator,5236const D3D12_HEAP_PROPERTIES& heapProps,5237D3D12_HEAP_FLAGS heapFlags,5238UINT64 size,5239UINT id);5240virtual ~MemoryBlock();52415242const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }5243D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }5244UINT64 GetSize() const { return m_Size; }5245UINT GetId() const { return m_Id; }5246ID3D12Heap* GetHeap() const { return m_Heap; }52475248protected:5249AllocatorPimpl* const m_Allocator;5250const D3D12_HEAP_PROPERTIES m_HeapProps;5251const D3D12_HEAP_FLAGS m_HeapFlags;5252const UINT64 m_Size;5253const UINT m_Id;52545255HRESULT Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures);52565257private:5258ID3D12Heap* m_Heap = NULL;52595260D3D12MA_CLASS_NO_COPY(MemoryBlock)5261};5262#endif // _D3D12MA_MEMORY_BLOCK52635264#ifndef _D3D12MA_NORMAL_BLOCK5265/*5266Represents a single block of device memory (heap) with all the data about its5267regions (aka suballocations, Allocation), assigned and free.5268Thread-safety: This class must be externally synchronized.5269*/5270class NormalBlock : public MemoryBlock5271{5272public:5273BlockMetadata* m_pMetadata;52745275NormalBlock(5276AllocatorPimpl* allocator,5277BlockVector* blockVector,5278const D3D12_HEAP_PROPERTIES& heapProps,5279D3D12_HEAP_FLAGS heapFlags,5280UINT64 size,5281UINT id);5282virtual ~NormalBlock();52835284BlockVector* GetBlockVector() const { return m_BlockVector; }52855286// 'algorithm' should be one of the *_ALGORITHM_* flags in enums POOL_FLAGS or VIRTUAL_BLOCK_FLAGS5287HRESULT Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures);52885289// Validates all data structures inside this object. If not valid, returns false.5290bool Validate() const;52915292private:5293BlockVector* m_BlockVector;52945295D3D12MA_CLASS_NO_COPY(NormalBlock)5296};5297#endif // _D3D12MA_NORMAL_BLOCK52985299#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS5300struct CommittedAllocationListItemTraits5301{5302using ItemType = Allocation;53035304static ItemType* GetPrev(const ItemType* item)5305{5306D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5307return item->m_Committed.prev;5308}5309static ItemType* GetNext(const ItemType* item)5310{5311D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5312return item->m_Committed.next;5313}5314static ItemType*& AccessPrev(ItemType* item)5315{5316D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5317return item->m_Committed.prev;5318}5319static ItemType*& AccessNext(ItemType* item)5320{5321D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5322return item->m_Committed.next;5323}5324};5325#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS53265327#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST5328/*5329Stores linked list of Allocation objects that are of TYPE_COMMITTED or TYPE_HEAP.5330Thread-safe, synchronized internally.5331*/5332class CommittedAllocationList5333{5334public:5335CommittedAllocationList() = default;5336void Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool);5337~CommittedAllocationList();53385339D3D12_HEAP_TYPE GetHeapType() const { return m_HeapType; }5340PoolPimpl* GetPool() const { return m_Pool; }5341UINT GetMemorySegmentGroup(AllocatorPimpl* allocator) const;53425343void AddStatistics(Statistics& inoutStats);5344void AddDetailedStatistics(DetailedStatistics& inoutStats);5345// Writes JSON array with the list of allocations.5346void BuildStatsString(JsonWriter& json);53475348void Register(Allocation* alloc);5349void Unregister(Allocation* alloc);53505351private:5352using CommittedAllocationLinkedList = IntrusiveLinkedList<CommittedAllocationListItemTraits>;53535354bool m_UseMutex = true;5355D3D12_HEAP_TYPE m_HeapType = D3D12_HEAP_TYPE_CUSTOM;5356PoolPimpl* m_Pool = NULL;53575358D3D12MA_RW_MUTEX m_Mutex;5359CommittedAllocationLinkedList m_AllocationList;5360};5361#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST53625363#ifndef _D3D12M_COMMITTED_ALLOCATION_PARAMETERS5364struct CommittedAllocationParameters5365{5366CommittedAllocationList* m_List = NULL;5367D3D12_HEAP_PROPERTIES m_HeapProperties = {};5368D3D12_HEAP_FLAGS m_HeapFlags = D3D12_HEAP_FLAG_NONE;5369ID3D12ProtectedResourceSession* m_ProtectedSession = NULL;5370bool m_CanAlias = false;5371D3D12_RESIDENCY_PRIORITY m_ResidencyPriority = D3D12_RESIDENCY_PRIORITY_NONE;53725373bool IsValid() const { return m_List != NULL; }5374};5375#endif // _D3D12M_COMMITTED_ALLOCATION_PARAMETERS53765377// Simple variant data structure to hold all possible variations of ID3D12Device*::CreateCommittedResource* and ID3D12Device*::CreatePlacedResource* arguments5378struct CREATE_RESOURCE_PARAMS5379{5380CREATE_RESOURCE_PARAMS() = delete;5381CREATE_RESOURCE_PARAMS(5382const D3D12_RESOURCE_DESC* pResourceDesc,5383D3D12_RESOURCE_STATES InitialResourceState,5384const D3D12_CLEAR_VALUE* pOptimizedClearValue)5385: Variant(VARIANT_WITH_STATE)5386, pResourceDesc(pResourceDesc)5387, InitialResourceState(InitialResourceState)5388, pOptimizedClearValue(pOptimizedClearValue)5389{5390}5391#ifdef __ID3D12Device8_INTERFACE_DEFINED__5392CREATE_RESOURCE_PARAMS(5393const D3D12_RESOURCE_DESC1* pResourceDesc,5394D3D12_RESOURCE_STATES InitialResourceState,5395const D3D12_CLEAR_VALUE* pOptimizedClearValue)5396: Variant(VARIANT_WITH_STATE_AND_DESC1)5397, pResourceDesc1(pResourceDesc)5398, InitialResourceState(InitialResourceState)5399, pOptimizedClearValue(pOptimizedClearValue)5400{5401}5402#endif5403#ifdef __ID3D12Device10_INTERFACE_DEFINED__5404CREATE_RESOURCE_PARAMS(5405const D3D12_RESOURCE_DESC1* pResourceDesc,5406D3D12_BARRIER_LAYOUT InitialLayout,5407const D3D12_CLEAR_VALUE* pOptimizedClearValue,5408UINT32 NumCastableFormats,5409DXGI_FORMAT* pCastableFormats)5410: Variant(VARIANT_WITH_LAYOUT)5411, pResourceDesc1(pResourceDesc)5412, InitialLayout(InitialLayout)5413, pOptimizedClearValue(pOptimizedClearValue)5414, NumCastableFormats(NumCastableFormats)5415, pCastableFormats(pCastableFormats)5416{5417}5418#endif54195420enum VARIANT5421{5422VARIANT_INVALID = 0,5423VARIANT_WITH_STATE,5424VARIANT_WITH_STATE_AND_DESC1,5425VARIANT_WITH_LAYOUT5426};54275428VARIANT Variant = VARIANT_INVALID;54295430const D3D12_RESOURCE_DESC* GetResourceDesc() const5431{5432D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE);5433return pResourceDesc;5434}5435const D3D12_RESOURCE_DESC*& AccessResourceDesc()5436{5437D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE);5438return pResourceDesc;5439}5440const D3D12_RESOURCE_DESC* GetBaseResourceDesc() const5441{5442// D3D12_RESOURCE_DESC1 can be cast to D3D12_RESOURCE_DESC by discarding the new members at the end.5443return pResourceDesc;5444}5445D3D12_RESOURCE_STATES GetInitialResourceState() const5446{5447D3D12MA_ASSERT(Variant < VARIANT_WITH_LAYOUT);5448return InitialResourceState;5449}5450const D3D12_CLEAR_VALUE* GetOptimizedClearValue() const5451{5452return pOptimizedClearValue;5453}54545455#ifdef __ID3D12Device8_INTERFACE_DEFINED__5456const D3D12_RESOURCE_DESC1* GetResourceDesc1() const5457{5458D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1);5459return pResourceDesc1;5460}5461const D3D12_RESOURCE_DESC1*& AccessResourceDesc1()5462{5463D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1);5464return pResourceDesc1;5465}5466#endif54675468#ifdef __ID3D12Device10_INTERFACE_DEFINED__5469D3D12_BARRIER_LAYOUT GetInitialLayout() const5470{5471D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);5472return InitialLayout;5473}5474UINT32 GetNumCastableFormats() const5475{5476D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);5477return NumCastableFormats;5478}5479DXGI_FORMAT* GetCastableFormats() const5480{5481D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);5482return pCastableFormats;5483}5484#endif54855486private:5487union5488{5489const D3D12_RESOURCE_DESC* pResourceDesc;5490#ifdef __ID3D12Device8_INTERFACE_DEFINED__5491const D3D12_RESOURCE_DESC1* pResourceDesc1;5492#endif5493};5494union5495{5496D3D12_RESOURCE_STATES InitialResourceState;5497#ifdef __ID3D12Device10_INTERFACE_DEFINED__5498D3D12_BARRIER_LAYOUT InitialLayout;5499#endif5500};5501const D3D12_CLEAR_VALUE* pOptimizedClearValue;5502#ifdef __ID3D12Device10_INTERFACE_DEFINED__5503UINT32 NumCastableFormats;5504DXGI_FORMAT* pCastableFormats;5505#endif5506};55075508#ifndef _D3D12MA_BLOCK_VECTOR5509/*5510Sequence of NormalBlock. Represents memory blocks allocated for a specific5511heap type and possibly resource type (if only Tier 1 is supported).55125513Synchronized internally with a mutex.5514*/5515class BlockVector5516{5517friend class DefragmentationContextPimpl;5518D3D12MA_CLASS_NO_COPY(BlockVector)5519public:5520BlockVector(5521AllocatorPimpl* hAllocator,5522const D3D12_HEAP_PROPERTIES& heapProps,5523D3D12_HEAP_FLAGS heapFlags,5524UINT64 preferredBlockSize,5525size_t minBlockCount,5526size_t maxBlockCount,5527bool explicitBlockSize,5528UINT64 minAllocationAlignment,5529UINT32 algorithm,5530bool denyMsaaTextures,5531ID3D12ProtectedResourceSession* pProtectedSession,5532D3D12_RESIDENCY_PRIORITY residencyPriority);5533~BlockVector();5534D3D12_RESIDENCY_PRIORITY GetResidencyPriority() const { return m_ResidencyPriority; }55355536const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }5537D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }5538UINT64 GetPreferredBlockSize() const { return m_PreferredBlockSize; }5539UINT32 GetAlgorithm() const { return m_Algorithm; }5540bool DeniesMsaaTextures() const { return m_DenyMsaaTextures; }5541// To be used only while the m_Mutex is locked. Used during defragmentation.5542size_t GetBlockCount() const { return m_Blocks.size(); }5543// To be used only while the m_Mutex is locked. Used during defragmentation.5544NormalBlock* GetBlock(size_t index) const { return m_Blocks[index]; }5545D3D12MA_RW_MUTEX& GetMutex() { return m_Mutex; }55465547HRESULT CreateMinBlocks();5548bool IsEmpty();55495550HRESULT Allocate(5551UINT64 size,5552UINT64 alignment,5553const ALLOCATION_DESC& allocDesc,5554size_t allocationCount,5555Allocation** pAllocations);55565557void Free(Allocation* hAllocation);55585559HRESULT CreateResource(5560UINT64 size,5561UINT64 alignment,5562const ALLOCATION_DESC& allocDesc,5563const CREATE_RESOURCE_PARAMS& createParams,5564Allocation** ppAllocation,5565REFIID riidResource,5566void** ppvResource);55675568void AddStatistics(Statistics& inoutStats);5569void AddDetailedStatistics(DetailedStatistics& inoutStats);55705571void WriteBlockInfoToJson(JsonWriter& json);55725573private:5574AllocatorPimpl* const m_hAllocator;5575const D3D12_HEAP_PROPERTIES m_HeapProps;5576const D3D12_HEAP_FLAGS m_HeapFlags;5577const UINT64 m_PreferredBlockSize;5578const size_t m_MinBlockCount;5579const size_t m_MaxBlockCount;5580const bool m_ExplicitBlockSize;5581const UINT64 m_MinAllocationAlignment;5582const UINT32 m_Algorithm;5583const bool m_DenyMsaaTextures;5584ID3D12ProtectedResourceSession* const m_ProtectedSession;5585const D3D12_RESIDENCY_PRIORITY m_ResidencyPriority;5586/* There can be at most one allocation that is completely empty - a5587hysteresis to avoid pessimistic case of alternating creation and destruction5588of a ID3D12Heap. */5589bool m_HasEmptyBlock;5590D3D12MA_RW_MUTEX m_Mutex;5591// Incrementally sorted by sumFreeSize, ascending.5592Vector<NormalBlock*> m_Blocks;5593UINT m_NextBlockId;5594bool m_IncrementalSort = true;55955596// Disable incremental sorting when freeing allocations5597void SetIncrementalSort(bool val) { m_IncrementalSort = val; }55985599UINT64 CalcSumBlockSize() const;5600UINT64 CalcMaxBlockSize() const;56015602// Finds and removes given block from vector.5603void Remove(NormalBlock* pBlock);56045605// Performs single step in sorting m_Blocks. They may not be fully sorted5606// after this call.5607void IncrementallySortBlocks();5608void SortByFreeSize();56095610HRESULT AllocatePage(5611UINT64 size,5612UINT64 alignment,5613const ALLOCATION_DESC& allocDesc,5614Allocation** pAllocation);56155616HRESULT AllocateFromBlock(5617NormalBlock* pBlock,5618UINT64 size,5619UINT64 alignment,5620ALLOCATION_FLAGS allocFlags,5621void* pPrivateData,5622UINT32 strategy,5623Allocation** pAllocation);56245625HRESULT CommitAllocationRequest(5626AllocationRequest& allocRequest,5627NormalBlock* pBlock,5628UINT64 size,5629UINT64 alignment,5630void* pPrivateData,5631Allocation** pAllocation);56325633HRESULT CreateBlock(5634UINT64 blockSize,5635size_t* pNewBlockIndex);5636};5637#endif // _D3D12MA_BLOCK_VECTOR56385639#ifndef _D3D12MA_CURRENT_BUDGET_DATA5640class CurrentBudgetData5641{5642public:5643bool ShouldUpdateBudget() const { return m_OperationsSinceBudgetFetch >= 30; }56445645void GetStatistics(Statistics& outStats, UINT group) const;5646void GetBudget(bool useMutex,5647UINT64* outLocalUsage, UINT64* outLocalBudget,5648UINT64* outNonLocalUsage, UINT64* outNonLocalBudget);56495650#if D3D12MA_DXGI_1_45651HRESULT UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex);5652#endif56535654void AddAllocation(UINT group, UINT64 allocationBytes);5655void RemoveAllocation(UINT group, UINT64 allocationBytes);56565657void AddBlock(UINT group, UINT64 blockBytes);5658void RemoveBlock(UINT group, UINT64 blockBytes);56595660private:5661D3D12MA_ATOMIC_UINT32 m_BlockCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};5662D3D12MA_ATOMIC_UINT32 m_AllocationCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};5663D3D12MA_ATOMIC_UINT64 m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};5664D3D12MA_ATOMIC_UINT64 m_AllocationBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};56655666D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch = {0};5667D3D12MA_RW_MUTEX m_BudgetMutex;5668UINT64 m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};5669UINT64 m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};5670UINT64 m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};5671};56725673#ifndef _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS5674void CurrentBudgetData::GetStatistics(Statistics& outStats, UINT group) const5675{5676outStats.BlockCount = m_BlockCount[group];5677outStats.AllocationCount = m_AllocationCount[group];5678outStats.BlockBytes = m_BlockBytes[group];5679outStats.AllocationBytes = m_AllocationBytes[group];5680}56815682void CurrentBudgetData::GetBudget(bool useMutex,5683UINT64* outLocalUsage, UINT64* outLocalBudget,5684UINT64* outNonLocalUsage, UINT64* outNonLocalBudget)5685{5686MutexLockRead lockRead(m_BudgetMutex, useMutex);56875688if (outLocalUsage)5689{5690const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];5691const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];5692const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];5693*outLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ?5694D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0;5695}5696if (outLocalBudget)5697*outLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];56985699if (outNonLocalUsage)5700{5701const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];5702const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];5703const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];5704*outNonLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ?5705D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0;5706}5707if (outNonLocalBudget)5708*outNonLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];5709}57105711#if D3D12MA_DXGI_1_45712HRESULT CurrentBudgetData::UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex)5713{5714D3D12MA_ASSERT(adapter3);57155716DXGI_QUERY_VIDEO_MEMORY_INFO infoLocal = {};5717DXGI_QUERY_VIDEO_MEMORY_INFO infoNonLocal = {};5718const HRESULT hrLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &infoLocal);5719const HRESULT hrNonLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &infoNonLocal);57205721if (SUCCEEDED(hrLocal) || SUCCEEDED(hrNonLocal))5722{5723MutexLockWrite lockWrite(m_BudgetMutex, useMutex);57245725if (SUCCEEDED(hrLocal))5726{5727m_D3D12Usage[0] = infoLocal.CurrentUsage;5728m_D3D12Budget[0] = infoLocal.Budget;5729}5730if (SUCCEEDED(hrNonLocal))5731{5732m_D3D12Usage[1] = infoNonLocal.CurrentUsage;5733m_D3D12Budget[1] = infoNonLocal.Budget;5734}57355736m_BlockBytesAtD3D12Fetch[0] = m_BlockBytes[0];5737m_BlockBytesAtD3D12Fetch[1] = m_BlockBytes[1];5738m_OperationsSinceBudgetFetch = 0;5739}57405741return FAILED(hrLocal) ? hrLocal : hrNonLocal;5742}5743#endif // #if D3D12MA_DXGI_1_457445745void CurrentBudgetData::AddAllocation(UINT group, UINT64 allocationBytes)5746{5747++m_AllocationCount[group];5748m_AllocationBytes[group] += allocationBytes;5749++m_OperationsSinceBudgetFetch;5750}57515752void CurrentBudgetData::RemoveAllocation(UINT group, UINT64 allocationBytes)5753{5754D3D12MA_ASSERT(m_AllocationBytes[group] >= allocationBytes);5755D3D12MA_ASSERT(m_AllocationCount[group] > 0);5756m_AllocationBytes[group] -= allocationBytes;5757--m_AllocationCount[group];5758++m_OperationsSinceBudgetFetch;5759}57605761void CurrentBudgetData::AddBlock(UINT group, UINT64 blockBytes)5762{5763++m_BlockCount[group];5764m_BlockBytes[group] += blockBytes;5765++m_OperationsSinceBudgetFetch;5766}57675768void CurrentBudgetData::RemoveBlock(UINT group, UINT64 blockBytes)5769{5770D3D12MA_ASSERT(m_BlockBytes[group] >= blockBytes);5771D3D12MA_ASSERT(m_BlockCount[group] > 0);5772m_BlockBytes[group] -= blockBytes;5773--m_BlockCount[group];5774++m_OperationsSinceBudgetFetch;5775}5776#endif // _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS5777#endif // _D3D12MA_CURRENT_BUDGET_DATA57785779#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL5780class DefragmentationContextPimpl5781{5782D3D12MA_CLASS_NO_COPY(DefragmentationContextPimpl)5783public:5784DefragmentationContextPimpl(5785AllocatorPimpl* hAllocator,5786const DEFRAGMENTATION_DESC& desc,5787BlockVector* poolVector);5788~DefragmentationContextPimpl();57895790void GetStats(DEFRAGMENTATION_STATS& outStats) { outStats = m_GlobalStats; }5791const ALLOCATION_CALLBACKS& GetAllocs() const { return m_Moves.GetAllocs(); }57925793HRESULT DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo);5794HRESULT DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo);57955796private:5797// Max number of allocations to ignore due to size constraints before ending single pass5798static const UINT8 MAX_ALLOCS_TO_IGNORE = 16;5799enum class CounterStatus { Pass, Ignore, End };58005801struct FragmentedBlock5802{5803UINT32 data;5804NormalBlock* block;5805};5806struct StateBalanced5807{5808UINT64 avgFreeSize = 0;5809UINT64 avgAllocSize = UINT64_MAX;5810};5811struct MoveAllocationData5812{5813UINT64 size;5814UINT64 alignment;5815ALLOCATION_FLAGS flags;5816DEFRAGMENTATION_MOVE move = {};5817};58185819const UINT64 m_MaxPassBytes;5820const UINT32 m_MaxPassAllocations;58215822Vector<DEFRAGMENTATION_MOVE> m_Moves;58235824UINT8 m_IgnoredAllocs = 0;5825UINT32 m_Algorithm;5826UINT32 m_BlockVectorCount;5827BlockVector* m_PoolBlockVector;5828BlockVector** m_pBlockVectors;5829size_t m_ImmovableBlockCount = 0;5830DEFRAGMENTATION_STATS m_GlobalStats = { 0 };5831DEFRAGMENTATION_STATS m_PassStats = { 0 };5832void* m_AlgorithmState = NULL;58335834static MoveAllocationData GetMoveData(AllocHandle handle, BlockMetadata* metadata);5835CounterStatus CheckCounters(UINT64 bytes);5836bool IncrementCounters(UINT64 bytes);5837bool ReallocWithinBlock(BlockVector& vector, NormalBlock* block);5838bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector);58395840bool ComputeDefragmentation(BlockVector& vector, size_t index);5841bool ComputeDefragmentation_Fast(BlockVector& vector);5842bool ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update);5843bool ComputeDefragmentation_Full(BlockVector& vector);58445845void UpdateVectorStatistics(BlockVector& vector, StateBalanced& state);5846};5847#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL58485849#ifndef _D3D12MA_POOL_PIMPL5850class PoolPimpl5851{5852friend class Allocator;5853friend struct PoolListItemTraits;5854public:5855PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc);5856~PoolPimpl();58575858AllocatorPimpl* GetAllocator() const { return m_Allocator; }5859const POOL_DESC& GetDesc() const { return m_Desc; }5860bool AlwaysCommitted() const { return (m_Desc.Flags & POOL_FLAG_ALWAYS_COMMITTED) != 0; }5861bool SupportsCommittedAllocations() const { return m_Desc.BlockSize == 0; }5862LPCWSTR GetName() const { return m_Name; }58635864BlockVector* GetBlockVector() { return m_BlockVector; }5865CommittedAllocationList* GetCommittedAllocationList() { return SupportsCommittedAllocations() ? &m_CommittedAllocations : NULL; }58665867HRESULT Init();5868void GetStatistics(Statistics& outStats);5869void CalculateStatistics(DetailedStatistics& outStats);5870void AddDetailedStatistics(DetailedStatistics& inoutStats);5871void SetName(LPCWSTR Name);58725873private:5874AllocatorPimpl* m_Allocator; // Externally owned object.5875POOL_DESC m_Desc;5876BlockVector* m_BlockVector; // Owned object.5877CommittedAllocationList m_CommittedAllocations;5878wchar_t* m_Name;5879PoolPimpl* m_PrevPool = NULL;5880PoolPimpl* m_NextPool = NULL;58815882void FreeName();5883};58845885struct PoolListItemTraits5886{5887using ItemType = PoolPimpl;5888static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }5889static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }5890static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }5891static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }5892};5893#endif // _D3D12MA_POOL_PIMPL589458955896#ifndef _D3D12MA_ALLOCATOR_PIMPL5897class AllocatorPimpl5898{5899friend class Allocator;5900friend class Pool;5901public:5902std::atomic_uint32_t m_RefCount = {1};5903CurrentBudgetData m_Budget;59045905AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc);5906~AllocatorPimpl();59075908ID3D12Device* GetDevice() const { return m_Device; }5909#ifdef __ID3D12Device1_INTERFACE_DEFINED__5910ID3D12Device1* GetDevice1() const { return m_Device1; }5911#endif5912#ifdef __ID3D12Device4_INTERFACE_DEFINED__5913ID3D12Device4* GetDevice4() const { return m_Device4; }5914#endif5915#ifdef __ID3D12Device8_INTERFACE_DEFINED__5916ID3D12Device8* GetDevice8() const { return m_Device8; }5917#endif5918// Shortcut for "Allocation Callbacks", because this function is called so often.5919const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }5920const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const { return m_D3D12Options; }5921BOOL IsUMA() const { return m_D3D12Architecture.UMA; }5922BOOL IsCacheCoherentUMA() const { return m_D3D12Architecture.CacheCoherentUMA; }5923bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; }5924bool IsGPUUploadHeapSupported() const { return m_GPUUploadHeapSupported != FALSE; }5925bool UseMutex() const { return m_UseMutex; }5926AllocationObjectAllocator& GetAllocationObjectAllocator() { return m_AllocationObjectAllocator; }5927UINT GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }5928/*5929If SupportsResourceHeapTier2():59300: D3D12_HEAP_TYPE_DEFAULT59311: D3D12_HEAP_TYPE_UPLOAD59322: D3D12_HEAP_TYPE_READBACK59333: D3D12_HEAP_TYPE_GPU_UPLOAD5934else:59350: D3D12_HEAP_TYPE_DEFAULT + buffer59361: D3D12_HEAP_TYPE_DEFAULT + texture59372: D3D12_HEAP_TYPE_DEFAULT + texture RT or DS59383: D3D12_HEAP_TYPE_UPLOAD + buffer59394: D3D12_HEAP_TYPE_UPLOAD + texture59405: D3D12_HEAP_TYPE_UPLOAD + texture RT or DS59416: D3D12_HEAP_TYPE_READBACK + buffer59427: D3D12_HEAP_TYPE_READBACK + texture59438: D3D12_HEAP_TYPE_READBACK + texture RT or DS59449: D3D12_HEAP_TYPE_GPU_UPLOAD + buffer594510: D3D12_HEAP_TYPE_GPU_UPLOAD + texture594611: D3D12_HEAP_TYPE_GPU_UPLOAD + texture RT or DS5947*/5948UINT GetDefaultPoolCount() const { return SupportsResourceHeapTier2() ? 4 : 12; }5949BlockVector** GetDefaultPools() { return m_BlockVectors; }59505951HRESULT Init(const ALLOCATOR_DESC& desc);5952bool HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const;5953UINT StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const;5954UINT HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const;5955UINT64 GetMemoryCapacity(UINT memorySegmentGroup) const;59565957HRESULT CreatePlacedResourceWrap(5958ID3D12Heap *pHeap,5959UINT64 HeapOffset,5960const CREATE_RESOURCE_PARAMS& createParams,5961REFIID riidResource,5962void** ppvResource);59635964HRESULT CreateResource(5965const ALLOCATION_DESC* pAllocDesc,5966const CREATE_RESOURCE_PARAMS& createParams,5967Allocation** ppAllocation,5968REFIID riidResource,5969void** ppvResource);59705971HRESULT CreateAliasingResource(5972Allocation* pAllocation,5973UINT64 AllocationLocalOffset,5974const CREATE_RESOURCE_PARAMS& createParams,5975REFIID riidResource,5976void** ppvResource);59775978HRESULT AllocateMemory(5979const ALLOCATION_DESC* pAllocDesc,5980const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,5981Allocation** ppAllocation);59825983// Unregisters allocation from the collection of dedicated allocations.5984// Allocation object must be deleted externally afterwards.5985void FreeCommittedMemory(Allocation* allocation);5986// Unregisters allocation from the collection of placed allocations.5987// Allocation object must be deleted externally afterwards.5988void FreePlacedMemory(Allocation* allocation);5989// Unregisters allocation from the collection of dedicated allocations and destroys associated heap.5990// Allocation object must be deleted externally afterwards.5991void FreeHeapMemory(Allocation* allocation);59925993void SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const;59945995void SetCurrentFrameIndex(UINT frameIndex);5996// For more deailed stats use outCustomHeaps to access statistics divided into L0 and L1 group5997void CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCustomHeaps[2] = NULL);59985999void GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget);6000void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType);60016002void BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap);6003void FreeStatsString(WCHAR* pStatsString);60046005private:6006using PoolList = IntrusiveLinkedList<PoolListItemTraits>;60076008const bool m_UseMutex;6009const bool m_AlwaysCommitted;6010const bool m_MsaaAlwaysCommitted;6011const bool m_PreferSmallBuffersCommitted;6012bool m_DefaultPoolsNotZeroed = false;6013ID3D12Device* m_Device; // AddRef6014#ifdef __ID3D12Device1_INTERFACE_DEFINED__6015ID3D12Device1* m_Device1 = NULL; // AddRef, optional6016#endif6017#ifdef __ID3D12Device4_INTERFACE_DEFINED__6018ID3D12Device4* m_Device4 = NULL; // AddRef, optional6019#endif6020#ifdef __ID3D12Device8_INTERFACE_DEFINED__6021ID3D12Device8* m_Device8 = NULL; // AddRef, optional6022#endif6023#ifdef __ID3D12Device10_INTERFACE_DEFINED__6024ID3D12Device10* m_Device10 = NULL; // AddRef, optional6025#endif6026IDXGIAdapter* m_Adapter; // AddRef6027#if D3D12MA_DXGI_1_46028IDXGIAdapter3* m_Adapter3 = NULL; // AddRef, optional6029#endif6030UINT64 m_PreferredBlockSize;6031ALLOCATION_CALLBACKS m_AllocationCallbacks;6032D3D12MA_ATOMIC_UINT32 m_CurrentFrameIndex;6033DXGI_ADAPTER_DESC m_AdapterDesc;6034D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options;6035BOOL m_GPUUploadHeapSupported = FALSE;6036D3D12_FEATURE_DATA_ARCHITECTURE m_D3D12Architecture;6037AllocationObjectAllocator m_AllocationObjectAllocator;60386039D3D12MA_RW_MUTEX m_PoolsMutex[HEAP_TYPE_COUNT];6040PoolList m_Pools[HEAP_TYPE_COUNT];6041// Default pools.6042BlockVector* m_BlockVectors[DEFAULT_POOL_MAX_COUNT];6043CommittedAllocationList m_CommittedAllocations[STANDARD_HEAP_TYPE_COUNT];60446045/*6046Heuristics that decides whether a resource should better be placed in its own,6047dedicated allocation (committed resource rather than placed resource).6048*/6049template<typename D3D12_RESOURCE_DESC_T>6050bool PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc,6051ALLOCATION_FLAGS strategy);60526053// Allocates and registers new committed resource with implicit heap, as dedicated allocation.6054// Creates and returns Allocation object and optionally D3D12 resource.6055HRESULT AllocateCommittedResource(6056const CommittedAllocationParameters& committedAllocParams,6057UINT64 resourceSize, bool withinBudget, void* pPrivateData,6058const CREATE_RESOURCE_PARAMS& createParams,6059Allocation** ppAllocation, REFIID riidResource, void** ppvResource);60606061// Allocates and registers new heap without any resources placed in it, as dedicated allocation.6062// Creates and returns Allocation object.6063HRESULT AllocateHeap(6064const CommittedAllocationParameters& committedAllocParams,6065const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget,6066void* pPrivateData, Allocation** ppAllocation);60676068template<typename D3D12_RESOURCE_DESC_T>6069HRESULT CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,6070const D3D12_RESOURCE_DESC_T* resDesc, // Optional6071BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted);60726073// Returns UINT32_MAX if index cannot be calculcated.6074UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const;6075void CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const;60766077// Registers Pool object in m_Pools.6078void RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);6079// Unregisters Pool object from m_Pools.6080void UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);60816082HRESULT UpdateD3D12Budget();60836084D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const;6085#ifdef __ID3D12Device8_INTERFACE_DEFINED__6086D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const;6087#endif60886089template<typename D3D12_RESOURCE_DESC_T>6090D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const;60916092bool NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size);60936094// Writes object { } with data of given budget.6095static void WriteBudgetToJson(JsonWriter& json, const Budget& budget);6096};60976098#ifndef _D3D12MA_ALLOCATOR_PIMPL_FUNCTINOS6099AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc)6100: m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0),6101m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0),6102m_MsaaAlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0),6103m_PreferSmallBuffersCommitted((desc.Flags & ALLOCATOR_FLAG_DONT_PREFER_SMALL_BUFFERS_COMMITTED) == 0),6104m_Device(desc.pDevice),6105m_Adapter(desc.pAdapter),6106m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE),6107m_AllocationCallbacks(allocationCallbacks),6108m_CurrentFrameIndex(0),6109// Below this line don't use allocationCallbacks but m_AllocationCallbacks!!!6110m_AllocationObjectAllocator(m_AllocationCallbacks, m_UseMutex)6111{6112// desc.pAllocationCallbacks intentionally ignored here, preprocessed by CreateAllocator.6113ZeroMemory(&m_D3D12Options, sizeof(m_D3D12Options));6114ZeroMemory(&m_D3D12Architecture, sizeof(m_D3D12Architecture));61156116ZeroMemory(m_BlockVectors, sizeof(m_BlockVectors));61176118for (UINT i = 0; i < STANDARD_HEAP_TYPE_COUNT; ++i)6119{6120m_CommittedAllocations[i].Init(6121m_UseMutex,6122IndexToStandardHeapType(i),6123NULL); // pool6124}61256126m_Device->AddRef();6127m_Adapter->AddRef();6128}61296130HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc)6131{6132#if D3D12MA_DXGI_1_46133desc.pAdapter->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Adapter3));6134#endif61356136#ifdef __ID3D12Device1_INTERFACE_DEFINED__6137m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device1));6138#endif61396140#ifdef __ID3D12Device4_INTERFACE_DEFINED__6141m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device4));6142#endif61436144#ifdef __ID3D12Device8_INTERFACE_DEFINED__6145m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device8));61466147if((desc.Flags & ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED) != 0)6148{6149D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = {};6150if(SUCCEEDED(m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, sizeof(options7))))6151{6152// DEFAULT_POOLS_NOT_ZEROED both supported and enabled by the user.6153m_DefaultPoolsNotZeroed = true;6154}6155}6156#endif61576158#ifdef __ID3D12Device10_INTERFACE_DEFINED__6159m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device10));6160#endif61616162HRESULT hr = m_Adapter->GetDesc(&m_AdapterDesc);6163if (FAILED(hr))6164{6165return hr;6166}61676168hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_D3D12Options, sizeof(m_D3D12Options));6169if (FAILED(hr))6170{6171return hr;6172}6173#ifdef D3D12MA_FORCE_RESOURCE_HEAP_TIER6174m_D3D12Options.ResourceHeapTier = (D3D12MA_FORCE_RESOURCE_HEAP_TIER);6175#endif61766177#if D3D12MA_OPTIONS16_SUPPORTED6178{6179D3D12_FEATURE_DATA_D3D12_OPTIONS16 options16 = {};6180hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS16, &options16, sizeof(options16));6181if (SUCCEEDED(hr))6182{6183m_GPUUploadHeapSupported = options16.GPUUploadHeapSupported;6184}6185}6186#endif // #if D3D12MA_OPTIONS16_SUPPORTED61876188hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &m_D3D12Architecture, sizeof(m_D3D12Architecture));6189if (FAILED(hr))6190{6191m_D3D12Architecture.UMA = FALSE;6192m_D3D12Architecture.CacheCoherentUMA = FALSE;6193}61946195D3D12_HEAP_PROPERTIES heapProps = {};6196const UINT defaultPoolCount = GetDefaultPoolCount();6197for (UINT i = 0; i < defaultPoolCount; ++i)6198{6199D3D12_HEAP_FLAGS heapFlags;6200CalcDefaultPoolParams(heapProps.Type, heapFlags, i);62016202#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE6203if(m_DefaultPoolsNotZeroed)6204{6205heapFlags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;6206}6207#endif62086209m_BlockVectors[i] = D3D12MA_NEW(GetAllocs(), BlockVector)(6210this, // hAllocator6211heapProps, // heapType6212heapFlags, // heapFlags6213m_PreferredBlockSize,62140, // minBlockCount6215SIZE_MAX, // maxBlockCount6216false, // explicitBlockSize6217D3D12MA_DEBUG_ALIGNMENT, // minAllocationAlignment62180, // Default algorithm,6219m_MsaaAlwaysCommitted,6220NULL, // pProtectedSession6221D3D12_RESIDENCY_PRIORITY_NONE); // residencyPriority6222// No need to call m_pBlockVectors[i]->CreateMinBlocks here, becase minBlockCount is 0.6223}62246225#if D3D12MA_DXGI_1_46226UpdateD3D12Budget();6227#endif62286229return S_OK;6230}62316232AllocatorPimpl::~AllocatorPimpl()6233{6234#ifdef __ID3D12Device10_INTERFACE_DEFINED__6235SAFE_RELEASE(m_Device10);6236#endif6237#ifdef __ID3D12Device8_INTERFACE_DEFINED__6238SAFE_RELEASE(m_Device8);6239#endif6240#ifdef __ID3D12Device4_INTERFACE_DEFINED__6241SAFE_RELEASE(m_Device4);6242#endif6243#ifdef __ID3D12Device1_INTERFACE_DEFINED__6244SAFE_RELEASE(m_Device1);6245#endif6246#if D3D12MA_DXGI_1_46247SAFE_RELEASE(m_Adapter3);6248#endif6249SAFE_RELEASE(m_Adapter);6250SAFE_RELEASE(m_Device);62516252for (UINT i = DEFAULT_POOL_MAX_COUNT; i--; )6253{6254D3D12MA_DELETE(GetAllocs(), m_BlockVectors[i]);6255}62566257for (UINT i = HEAP_TYPE_COUNT; i--; )6258{6259if (!m_Pools[i].IsEmpty())6260{6261D3D12MA_ASSERT(0 && "Unfreed pools found!");6262}6263}6264}62656266bool AllocatorPimpl::HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const6267{6268if (SupportsResourceHeapTier2())6269{6270return true;6271}6272else6273{6274const bool allowBuffers = (flags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;6275const bool allowRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;6276const bool allowNonRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;6277const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);6278return allowedGroupCount == 1;6279}6280}62816282UINT AllocatorPimpl::StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const6283{6284D3D12MA_ASSERT(IsHeapTypeStandard(heapType));6285if (IsUMA())6286return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY;6287return (heapType == D3D12_HEAP_TYPE_DEFAULT || heapType == D3D12_HEAP_TYPE_GPU_UPLOAD_COPY) ?6288DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY;6289}62906291UINT AllocatorPimpl::HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const6292{6293if (IsUMA())6294return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY;6295if (heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_UNKNOWN)6296return StandardHeapTypeToMemorySegmentGroup(heapProps.Type);6297return heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_L1 ?6298DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY;6299}63006301UINT64 AllocatorPimpl::GetMemoryCapacity(UINT memorySegmentGroup) const6302{6303switch (memorySegmentGroup)6304{6305case DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY:6306return IsUMA() ?6307m_AdapterDesc.DedicatedVideoMemory + m_AdapterDesc.SharedSystemMemory : m_AdapterDesc.DedicatedVideoMemory;6308case DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY:6309return IsUMA() ? 0 : m_AdapterDesc.SharedSystemMemory;6310default:6311D3D12MA_ASSERT(0);6312return UINT64_MAX;6313}6314}63156316HRESULT AllocatorPimpl::CreatePlacedResourceWrap(6317ID3D12Heap *pHeap,6318UINT64 HeapOffset,6319const CREATE_RESOURCE_PARAMS& createParams,6320REFIID riidResource,6321void** ppvResource)6322{6323#ifdef __ID3D12Device10_INTERFACE_DEFINED__6324if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)6325{6326if (!m_Device10)6327{6328return E_NOINTERFACE;6329}6330return m_Device10->CreatePlacedResource2(pHeap, HeapOffset,6331createParams.GetResourceDesc1(), createParams.GetInitialLayout(),6332createParams.GetOptimizedClearValue(), createParams.GetNumCastableFormats(),6333createParams.GetCastableFormats(), riidResource, ppvResource);6334} else6335#endif6336#ifdef __ID3D12Device8_INTERFACE_DEFINED__6337if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)6338{6339if (!m_Device8)6340{6341return E_NOINTERFACE;6342}6343return m_Device8->CreatePlacedResource1(pHeap, HeapOffset,6344createParams.GetResourceDesc1(), createParams.GetInitialResourceState(),6345createParams.GetOptimizedClearValue(), riidResource, ppvResource);6346} else6347#endif6348if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)6349{6350return m_Device->CreatePlacedResource(pHeap, HeapOffset,6351createParams.GetResourceDesc(), createParams.GetInitialResourceState(),6352createParams.GetOptimizedClearValue(), riidResource, ppvResource);6353}6354else6355{6356D3D12MA_ASSERT(0);6357return E_INVALIDARG;6358}6359}636063616362HRESULT AllocatorPimpl::CreateResource(6363const ALLOCATION_DESC* pAllocDesc,6364const CREATE_RESOURCE_PARAMS& createParams,6365Allocation** ppAllocation,6366REFIID riidResource,6367void** ppvResource)6368{6369D3D12MA_ASSERT(pAllocDesc && createParams.GetBaseResourceDesc() && ppAllocation);63706371*ppAllocation = NULL;6372if (ppvResource)6373{6374*ppvResource = NULL;6375}63766377CREATE_RESOURCE_PARAMS finalCreateParams = createParams;6378D3D12_RESOURCE_DESC finalResourceDesc;6379#ifdef __ID3D12Device8_INTERFACE_DEFINED__6380D3D12_RESOURCE_DESC1 finalResourceDesc1;6381#endif6382D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo;6383if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)6384{6385finalResourceDesc = *createParams.GetResourceDesc();6386finalCreateParams.AccessResourceDesc() = &finalResourceDesc;6387resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);6388}6389#ifdef __ID3D12Device8_INTERFACE_DEFINED__6390else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)6391{6392if (!m_Device8)6393{6394return E_NOINTERFACE;6395}6396finalResourceDesc1 = *createParams.GetResourceDesc1();6397finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;6398resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);6399}6400#endif6401#ifdef __ID3D12Device10_INTERFACE_DEFINED__6402else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)6403{6404if (!m_Device10)6405{6406return E_NOINTERFACE;6407}6408finalResourceDesc1 = *createParams.GetResourceDesc1();6409finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;6410resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);6411}6412#endif6413else6414{6415D3D12MA_ASSERT(0);6416return E_INVALIDARG;6417}6418D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));6419D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);64206421BlockVector* blockVector = NULL;6422CommittedAllocationParameters committedAllocationParams = {};6423bool preferCommitted = false;64246425HRESULT hr;6426#ifdef __ID3D12Device8_INTERFACE_DEFINED__6427if (createParams.Variant >= CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)6428{6429hr = CalcAllocationParams<D3D12_RESOURCE_DESC1>(*pAllocDesc, resAllocInfo.SizeInBytes,6430createParams.GetResourceDesc1(),6431blockVector, committedAllocationParams, preferCommitted);6432}6433else6434#endif6435{6436hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, resAllocInfo.SizeInBytes,6437createParams.GetResourceDesc(),6438blockVector, committedAllocationParams, preferCommitted);6439}6440if (FAILED(hr))6441return hr;64426443const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0;6444hr = E_INVALIDARG;6445if (committedAllocationParams.IsValid() && preferCommitted)6446{6447hr = AllocateCommittedResource(committedAllocationParams,6448resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData,6449finalCreateParams, ppAllocation, riidResource, ppvResource);6450if (SUCCEEDED(hr))6451return hr;6452}6453if (blockVector != NULL)6454{6455hr = blockVector->CreateResource(resAllocInfo.SizeInBytes, resAllocInfo.Alignment,6456*pAllocDesc, finalCreateParams,6457ppAllocation, riidResource, ppvResource);6458if (SUCCEEDED(hr))6459return hr;6460}6461if (committedAllocationParams.IsValid() && !preferCommitted)6462{6463hr = AllocateCommittedResource(committedAllocationParams,6464resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData,6465finalCreateParams, ppAllocation, riidResource, ppvResource);6466if (SUCCEEDED(hr))6467return hr;6468}6469return hr;6470}64716472HRESULT AllocatorPimpl::AllocateMemory(6473const ALLOCATION_DESC* pAllocDesc,6474const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,6475Allocation** ppAllocation)6476{6477*ppAllocation = NULL;64786479BlockVector* blockVector = NULL;6480CommittedAllocationParameters committedAllocationParams = {};6481bool preferCommitted = false;6482HRESULT hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, pAllocInfo->SizeInBytes,6483NULL, // pResDesc6484blockVector, committedAllocationParams, preferCommitted);6485if (FAILED(hr))6486return hr;64876488const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0;6489hr = E_INVALIDARG;6490if (committedAllocationParams.IsValid() && preferCommitted)6491{6492hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation);6493if (SUCCEEDED(hr))6494return hr;6495}6496if (blockVector != NULL)6497{6498hr = blockVector->Allocate(pAllocInfo->SizeInBytes, pAllocInfo->Alignment,6499*pAllocDesc, 1, (Allocation**)ppAllocation);6500if (SUCCEEDED(hr))6501return hr;6502}6503if (committedAllocationParams.IsValid() && !preferCommitted)6504{6505hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation);6506if (SUCCEEDED(hr))6507return hr;6508}6509return hr;6510}65116512HRESULT AllocatorPimpl::CreateAliasingResource(6513Allocation* pAllocation,6514UINT64 AllocationLocalOffset,6515const CREATE_RESOURCE_PARAMS& createParams,6516REFIID riidResource,6517void** ppvResource)6518{6519*ppvResource = NULL;65206521CREATE_RESOURCE_PARAMS finalCreateParams = createParams;6522D3D12_RESOURCE_DESC finalResourceDesc;6523#ifdef __ID3D12Device8_INTERFACE_DEFINED__6524D3D12_RESOURCE_DESC1 finalResourceDesc1;6525#endif6526D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo;6527if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)6528{6529finalResourceDesc = *createParams.GetResourceDesc();6530finalCreateParams.AccessResourceDesc() = &finalResourceDesc;6531resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);6532}6533#ifdef __ID3D12Device8_INTERFACE_DEFINED__6534else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)6535{6536if (!m_Device8)6537{6538return E_NOINTERFACE;6539}6540finalResourceDesc1 = *createParams.GetResourceDesc1();6541finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;6542resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);6543}6544#endif6545#ifdef __ID3D12Device10_INTERFACE_DEFINED__6546else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)6547{6548if (!m_Device10)6549{6550return E_NOINTERFACE;6551}6552finalResourceDesc1 = *createParams.GetResourceDesc1();6553finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;6554resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);6555}6556#endif6557else6558{6559D3D12MA_ASSERT(0);6560return E_INVALIDARG;6561}6562D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));6563D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);65646565ID3D12Heap* const existingHeap = pAllocation->GetHeap();6566const UINT64 existingOffset = pAllocation->GetOffset();6567const UINT64 existingSize = pAllocation->GetSize();6568const UINT64 newOffset = existingOffset + AllocationLocalOffset;65696570if (existingHeap == NULL ||6571AllocationLocalOffset + resAllocInfo.SizeInBytes > existingSize ||6572newOffset % resAllocInfo.Alignment != 0)6573{6574return E_INVALIDARG;6575}65766577return CreatePlacedResourceWrap(existingHeap, newOffset, finalCreateParams, riidResource, ppvResource);6578}65796580void AllocatorPimpl::FreeCommittedMemory(Allocation* allocation)6581{6582D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_COMMITTED);65836584CommittedAllocationList* const allocList = allocation->m_Committed.list;6585allocList->Unregister(allocation);65866587const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this);6588const UINT64 allocSize = allocation->GetSize();6589m_Budget.RemoveAllocation(memSegmentGroup, allocSize);6590m_Budget.RemoveBlock(memSegmentGroup, allocSize);6591}65926593void AllocatorPimpl::FreePlacedMemory(Allocation* allocation)6594{6595D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_PLACED);65966597NormalBlock* const block = allocation->m_Placed.block;6598D3D12MA_ASSERT(block);6599BlockVector* const blockVector = block->GetBlockVector();6600D3D12MA_ASSERT(blockVector);6601m_Budget.RemoveAllocation(HeapPropertiesToMemorySegmentGroup(block->GetHeapProperties()), allocation->GetSize());6602blockVector->Free(allocation);6603}66046605void AllocatorPimpl::FreeHeapMemory(Allocation* allocation)6606{6607D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_HEAP);66086609CommittedAllocationList* const allocList = allocation->m_Committed.list;6610allocList->Unregister(allocation);6611SAFE_RELEASE(allocation->m_Heap.heap);66126613const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this);6614const UINT64 allocSize = allocation->GetSize();6615m_Budget.RemoveAllocation(memSegmentGroup, allocSize);6616m_Budget.RemoveBlock(memSegmentGroup, allocSize);6617}66186619void AllocatorPimpl::SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const6620{6621#ifdef __ID3D12Device1_INTERFACE_DEFINED__6622if (priority != D3D12_RESIDENCY_PRIORITY_NONE && m_Device1)6623{6624// Intentionally ignoring the result.6625m_Device1->SetResidencyPriority(1, &obj, &priority);6626}6627#endif6628}66296630void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex)6631{6632m_CurrentFrameIndex.store(frameIndex);66336634#if D3D12MA_DXGI_1_46635UpdateD3D12Budget();6636#endif6637}66386639void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCustomHeaps[2])6640{6641// Init stats6642for (size_t i = 0; i < HEAP_TYPE_COUNT; i++)6643ClearDetailedStatistics(outStats.HeapType[i]);6644for (size_t i = 0; i < DXGI_MEMORY_SEGMENT_GROUP_COUNT; i++)6645ClearDetailedStatistics(outStats.MemorySegmentGroup[i]);6646ClearDetailedStatistics(outStats.Total);6647if (outCustomHeaps)6648{6649ClearDetailedStatistics(outCustomHeaps[0]);6650ClearDetailedStatistics(outCustomHeaps[1]);6651}66526653// Process default pools. 4 standard heap types only. Add them to outStats.HeapType[i].6654if (SupportsResourceHeapTier2())6655{6656// DEFAULT, UPLOAD, READBACK, GPU_UPLOAD.6657for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)6658{6659BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex];6660D3D12MA_ASSERT(pBlockVector);6661const size_t outputIndex = heapTypeIndex < 3 ? heapTypeIndex : 4; // GPU_UPLOAD 3 -> 46662pBlockVector->AddDetailedStatistics(outStats.HeapType[outputIndex]);6663}6664}6665else6666{6667// DEFAULT, UPLOAD, READBACK.6668for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)6669{6670for (size_t heapSubType = 0; heapSubType < 3; ++heapSubType)6671{6672BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex * 3 + heapSubType];6673D3D12MA_ASSERT(pBlockVector);66746675const size_t outputIndex = heapTypeIndex < 3 ? heapTypeIndex : 4; // GPU_UPLOAD 3 -> 46676pBlockVector->AddDetailedStatistics(outStats.HeapType[outputIndex]);6677}6678}6679}66806681// Sum them up to memory segment groups.6682AddDetailedStatistics(6683outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_DEFAULT)],6684outStats.HeapType[0]);6685AddDetailedStatistics(6686outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_UPLOAD)],6687outStats.HeapType[1]);6688AddDetailedStatistics(6689outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_READBACK)],6690outStats.HeapType[2]);6691AddDetailedStatistics(6692outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_GPU_UPLOAD_COPY)],6693outStats.HeapType[4]);66946695// Process custom pools.6696DetailedStatistics tmpStats;6697for (size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)6698{6699MutexLockRead lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);6700PoolList& poolList = m_Pools[heapTypeIndex];6701for (PoolPimpl* pool = poolList.Front(); pool != NULL; pool = poolList.GetNext(pool))6702{6703const D3D12_HEAP_PROPERTIES& poolHeapProps = pool->GetDesc().HeapProperties;6704ClearDetailedStatistics(tmpStats);6705pool->AddDetailedStatistics(tmpStats);6706AddDetailedStatistics(6707outStats.HeapType[heapTypeIndex], tmpStats);67086709UINT memorySegment = HeapPropertiesToMemorySegmentGroup(poolHeapProps);6710AddDetailedStatistics(6711outStats.MemorySegmentGroup[memorySegment], tmpStats);67126713if (outCustomHeaps)6714AddDetailedStatistics(outCustomHeaps[memorySegment], tmpStats);6715}6716}67176718// Process committed allocations. standard heap types only.6719for (UINT heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)6720{6721ClearDetailedStatistics(tmpStats);6722m_CommittedAllocations[heapTypeIndex].AddDetailedStatistics(tmpStats);6723const size_t outputIndex = heapTypeIndex < 3 ? heapTypeIndex : 4; // GPU_UPLOAD 3 -> 46724AddDetailedStatistics(6725outStats.HeapType[outputIndex], tmpStats);6726AddDetailedStatistics(6727outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(IndexToStandardHeapType(heapTypeIndex))], tmpStats);6728}67296730// Sum up memory segment groups to totals.6731AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[0]);6732AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[1]);67336734D3D12MA_ASSERT(outStats.Total.Stats.BlockCount ==6735outStats.MemorySegmentGroup[0].Stats.BlockCount + outStats.MemorySegmentGroup[1].Stats.BlockCount);6736D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount ==6737outStats.MemorySegmentGroup[0].Stats.AllocationCount + outStats.MemorySegmentGroup[1].Stats.AllocationCount);6738D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes ==6739outStats.MemorySegmentGroup[0].Stats.BlockBytes + outStats.MemorySegmentGroup[1].Stats.BlockBytes);6740D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes ==6741outStats.MemorySegmentGroup[0].Stats.AllocationBytes + outStats.MemorySegmentGroup[1].Stats.AllocationBytes);6742D3D12MA_ASSERT(outStats.Total.UnusedRangeCount ==6743outStats.MemorySegmentGroup[0].UnusedRangeCount + outStats.MemorySegmentGroup[1].UnusedRangeCount);67446745D3D12MA_ASSERT(outStats.Total.Stats.BlockCount ==6746outStats.HeapType[0].Stats.BlockCount + outStats.HeapType[1].Stats.BlockCount +6747outStats.HeapType[2].Stats.BlockCount + outStats.HeapType[3].Stats.BlockCount +6748outStats.HeapType[4].Stats.BlockCount);6749D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount ==6750outStats.HeapType[0].Stats.AllocationCount + outStats.HeapType[1].Stats.AllocationCount +6751outStats.HeapType[2].Stats.AllocationCount + outStats.HeapType[3].Stats.AllocationCount +6752outStats.HeapType[4].Stats.AllocationCount);6753D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes ==6754outStats.HeapType[0].Stats.BlockBytes + outStats.HeapType[1].Stats.BlockBytes +6755outStats.HeapType[2].Stats.BlockBytes + outStats.HeapType[3].Stats.BlockBytes +6756outStats.HeapType[4].Stats.BlockBytes);6757D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes ==6758outStats.HeapType[0].Stats.AllocationBytes + outStats.HeapType[1].Stats.AllocationBytes +6759outStats.HeapType[2].Stats.AllocationBytes + outStats.HeapType[3].Stats.AllocationBytes +6760outStats.HeapType[4].Stats.AllocationBytes);6761D3D12MA_ASSERT(outStats.Total.UnusedRangeCount ==6762outStats.HeapType[0].UnusedRangeCount + outStats.HeapType[1].UnusedRangeCount +6763outStats.HeapType[2].UnusedRangeCount + outStats.HeapType[3].UnusedRangeCount +6764outStats.HeapType[4].UnusedRangeCount);6765}67666767void AllocatorPimpl::GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget)6768{6769if (outLocalBudget)6770m_Budget.GetStatistics(outLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY);6771if (outNonLocalBudget)6772m_Budget.GetStatistics(outNonLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY);67736774#if D3D12MA_DXGI_1_46775if (m_Adapter3)6776{6777if (!m_Budget.ShouldUpdateBudget())6778{6779m_Budget.GetBudget(m_UseMutex,6780outLocalBudget ? &outLocalBudget->UsageBytes : NULL,6781outLocalBudget ? &outLocalBudget->BudgetBytes : NULL,6782outNonLocalBudget ? &outNonLocalBudget->UsageBytes : NULL,6783outNonLocalBudget ? &outNonLocalBudget->BudgetBytes : NULL);6784}6785else6786{6787UpdateD3D12Budget();6788GetBudget(outLocalBudget, outNonLocalBudget); // Recursion6789}6790}6791else6792#endif6793{6794if (outLocalBudget)6795{6796outLocalBudget->UsageBytes = outLocalBudget->Stats.BlockBytes;6797outLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY) * 8 / 10; // 80% heuristics.6798}6799if (outNonLocalBudget)6800{6801outNonLocalBudget->UsageBytes = outNonLocalBudget->Stats.BlockBytes;6802outNonLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY) * 8 / 10; // 80% heuristics.6803}6804}6805}68066807void AllocatorPimpl::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType)6808{6809switch (heapType)6810{6811case D3D12_HEAP_TYPE_DEFAULT:6812case D3D12_HEAP_TYPE_GPU_UPLOAD_COPY:6813GetBudget(&outBudget, NULL);6814break;6815case D3D12_HEAP_TYPE_UPLOAD:6816case D3D12_HEAP_TYPE_READBACK:6817GetBudget(NULL, &outBudget);6818break;6819default: D3D12MA_ASSERT(0);6820}6821}68226823void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)6824{6825StringBuilder sb(GetAllocs());6826{6827Budget localBudget = {}, nonLocalBudget = {};6828GetBudget(&localBudget, &nonLocalBudget);68296830TotalStatistics stats;6831DetailedStatistics customHeaps[2];6832CalculateStatistics(stats, customHeaps);68336834JsonWriter json(GetAllocs(), sb);6835json.BeginObject();6836{6837json.WriteString(L"General");6838json.BeginObject();6839{6840json.WriteString(L"API");6841json.WriteString(L"Direct3D 12");68426843json.WriteString(L"GPU");6844json.WriteString(m_AdapterDesc.Description);68456846json.WriteString(L"DedicatedVideoMemory");6847json.WriteNumber((UINT64)m_AdapterDesc.DedicatedVideoMemory);6848json.WriteString(L"DedicatedSystemMemory");6849json.WriteNumber((UINT64)m_AdapterDesc.DedicatedSystemMemory);6850json.WriteString(L"SharedSystemMemory");6851json.WriteNumber((UINT64)m_AdapterDesc.SharedSystemMemory);68526853json.WriteString(L"ResourceHeapTier");6854json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceHeapTier));68556856json.WriteString(L"ResourceBindingTier");6857json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceBindingTier));68586859json.WriteString(L"TiledResourcesTier");6860json.WriteNumber(static_cast<UINT>(m_D3D12Options.TiledResourcesTier));68616862json.WriteString(L"TileBasedRenderer");6863json.WriteBool(m_D3D12Architecture.TileBasedRenderer);68646865json.WriteString(L"UMA");6866json.WriteBool(m_D3D12Architecture.UMA);6867json.WriteString(L"CacheCoherentUMA");6868json.WriteBool(m_D3D12Architecture.CacheCoherentUMA);68696870json.WriteString(L"GPUUploadHeapSupported");6871json.WriteBool(m_GPUUploadHeapSupported != FALSE);6872}6873json.EndObject();6874}6875{6876json.WriteString(L"Total");6877json.AddDetailedStatisticsInfoObject(stats.Total);6878}6879{6880json.WriteString(L"MemoryInfo");6881json.BeginObject();6882{6883json.WriteString(L"L0");6884json.BeginObject();6885{6886json.WriteString(L"Budget");6887WriteBudgetToJson(json, IsUMA() ? localBudget : nonLocalBudget); // When UMA device only L0 present as local68886889json.WriteString(L"Stats");6890json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[!IsUMA()]);68916892json.WriteString(L"MemoryPools");6893json.BeginObject();6894{6895if (IsUMA())6896{6897json.WriteString(L"DEFAULT");6898json.BeginObject();6899{6900json.WriteString(L"Stats");6901json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);6902}6903json.EndObject();69046905if(IsGPUUploadHeapSupported())6906{6907json.WriteString(L"GPU_UPLOAD");6908json.BeginObject();6909{6910json.WriteString(L"Stats");6911json.AddDetailedStatisticsInfoObject(stats.HeapType[4]);6912}6913json.EndObject();6914}6915}6916json.WriteString(L"UPLOAD");6917json.BeginObject();6918{6919json.WriteString(L"Stats");6920json.AddDetailedStatisticsInfoObject(stats.HeapType[1]);6921}6922json.EndObject();69236924json.WriteString(L"READBACK");6925json.BeginObject();6926{6927json.WriteString(L"Stats");6928json.AddDetailedStatisticsInfoObject(stats.HeapType[2]);6929}6930json.EndObject();69316932json.WriteString(L"CUSTOM");6933json.BeginObject();6934{6935json.WriteString(L"Stats");6936json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]);6937}6938json.EndObject();6939}6940json.EndObject();6941}6942json.EndObject();6943if (!IsUMA())6944{6945json.WriteString(L"L1");6946json.BeginObject();6947{6948json.WriteString(L"Budget");6949WriteBudgetToJson(json, localBudget);69506951json.WriteString(L"Stats");6952json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[0]);69536954json.WriteString(L"MemoryPools");6955json.BeginObject();6956{6957json.WriteString(L"DEFAULT");6958json.BeginObject();6959{6960json.WriteString(L"Stats");6961json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);6962}6963json.EndObject();69646965if(IsGPUUploadHeapSupported())6966{6967json.WriteString(L"GPU_UPLOAD");6968json.BeginObject();6969{6970json.WriteString(L"Stats");6971json.AddDetailedStatisticsInfoObject(stats.HeapType[4]);6972}6973json.EndObject();6974}69756976json.WriteString(L"CUSTOM");6977json.BeginObject();6978{6979json.WriteString(L"Stats");6980json.AddDetailedStatisticsInfoObject(customHeaps[0]);6981}6982json.EndObject();6983}6984json.EndObject();6985}6986json.EndObject();6987}6988}6989json.EndObject();6990}69916992if (detailedMap)6993{6994const auto writeHeapInfo = [&](BlockVector* blockVector, CommittedAllocationList* committedAllocs, bool customHeap)6995{6996D3D12MA_ASSERT(blockVector);69976998D3D12_HEAP_FLAGS flags = blockVector->GetHeapFlags();6999json.WriteString(L"Flags");7000json.BeginArray(true);7001{7002if (flags & D3D12_HEAP_FLAG_SHARED)7003json.WriteString(L"HEAP_FLAG_SHARED");7004if (flags & D3D12_HEAP_FLAG_ALLOW_DISPLAY)7005json.WriteString(L"HEAP_FLAG_ALLOW_DISPLAY");7006if (flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER)7007json.WriteString(L"HEAP_FLAG_CROSS_ADAPTER");7008if (flags & D3D12_HEAP_FLAG_HARDWARE_PROTECTED)7009json.WriteString(L"HEAP_FLAG_HARDWARE_PROTECTED");7010if (flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH)7011json.WriteString(L"HEAP_FLAG_ALLOW_WRITE_WATCH");7012if (flags & D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS)7013json.WriteString(L"HEAP_FLAG_ALLOW_SHADER_ATOMICS");7014#ifdef __ID3D12Device8_INTERFACE_DEFINED__7015if (flags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT)7016json.WriteString(L"HEAP_FLAG_CREATE_NOT_RESIDENT");7017if (flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED)7018json.WriteString(L"HEAP_FLAG_CREATE_NOT_ZEROED");7019#endif70207021if (flags & D3D12_HEAP_FLAG_DENY_BUFFERS)7022json.WriteString(L"HEAP_FLAG_DENY_BUFFERS");7023if (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES)7024json.WriteString(L"HEAP_FLAG_DENY_RT_DS_TEXTURES");7025if (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES)7026json.WriteString(L"HEAP_FLAG_DENY_NON_RT_DS_TEXTURES");70277028flags &= ~(D3D12_HEAP_FLAG_SHARED7029| D3D12_HEAP_FLAG_DENY_BUFFERS7030| D3D12_HEAP_FLAG_ALLOW_DISPLAY7031| D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER7032| D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES7033| D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES7034| D3D12_HEAP_FLAG_HARDWARE_PROTECTED7035| D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH7036| D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS);7037#ifdef __ID3D12Device8_INTERFACE_DEFINED__7038flags &= ~(D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT7039| D3D12_HEAP_FLAG_CREATE_NOT_ZEROED);7040#endif7041if (flags != 0)7042json.WriteNumber((UINT)flags);70437044if (customHeap)7045{7046const D3D12_HEAP_PROPERTIES& properties = blockVector->GetHeapProperties();7047switch (properties.MemoryPoolPreference)7048{7049default:7050D3D12MA_ASSERT(0);7051case D3D12_MEMORY_POOL_UNKNOWN:7052json.WriteString(L"MEMORY_POOL_UNKNOWN");7053break;7054case D3D12_MEMORY_POOL_L0:7055json.WriteString(L"MEMORY_POOL_L0");7056break;7057case D3D12_MEMORY_POOL_L1:7058json.WriteString(L"MEMORY_POOL_L1");7059break;7060}7061switch (properties.CPUPageProperty)7062{7063default:7064D3D12MA_ASSERT(0);7065case D3D12_CPU_PAGE_PROPERTY_UNKNOWN:7066json.WriteString(L"CPU_PAGE_PROPERTY_UNKNOWN");7067break;7068case D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE:7069json.WriteString(L"CPU_PAGE_PROPERTY_NOT_AVAILABLE");7070break;7071case D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE:7072json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_COMBINE");7073break;7074case D3D12_CPU_PAGE_PROPERTY_WRITE_BACK:7075json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_BACK");7076break;7077}7078}7079}7080json.EndArray();70817082json.WriteString(L"PreferredBlockSize");7083json.WriteNumber(blockVector->GetPreferredBlockSize());70847085json.WriteString(L"Blocks");7086blockVector->WriteBlockInfoToJson(json);70877088json.WriteString(L"DedicatedAllocations");7089json.BeginArray();7090if (committedAllocs)7091committedAllocs->BuildStatsString(json);7092json.EndArray();7093};70947095json.WriteString(L"DefaultPools");7096json.BeginObject();7097{7098if (SupportsResourceHeapTier2())7099{7100for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)7101{7102json.WriteString(StandardHeapTypeNames[heapType]);7103json.BeginObject();7104writeHeapInfo(m_BlockVectors[heapType], m_CommittedAllocations + heapType, false);7105json.EndObject();7106}7107}7108else7109{7110for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)7111{7112for (uint8_t heapSubType = 0; heapSubType < 3; ++heapSubType)7113{7114static const WCHAR* const heapSubTypeName[] = {7115L" - Buffers",7116L" - Textures",7117L" - Textures RT/DS",7118};7119json.BeginString(StandardHeapTypeNames[heapType]);7120json.EndString(heapSubTypeName[heapSubType]);71217122json.BeginObject();7123writeHeapInfo(m_BlockVectors[heapType * 3 + heapSubType], m_CommittedAllocations + heapType, false);7124json.EndObject();7125}7126}7127}7128}7129json.EndObject();71307131json.WriteString(L"CustomPools");7132json.BeginObject();7133for (uint8_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)7134{7135MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex);7136auto* item = m_Pools[heapTypeIndex].Front();7137if (item != NULL)7138{7139size_t index = 0;7140json.WriteString(HeapTypeNames[heapTypeIndex]);7141json.BeginArray();7142do7143{7144json.BeginObject();7145json.WriteString(L"Name");7146json.BeginString();7147json.ContinueString(index++);7148if (item->GetName())7149{7150json.ContinueString(L" - ");7151json.ContinueString(item->GetName());7152}7153json.EndString();71547155writeHeapInfo(item->GetBlockVector(), item->GetCommittedAllocationList(), heapTypeIndex == 3);7156json.EndObject();7157} while ((item = PoolList::GetNext(item)) != NULL);7158json.EndArray();7159}7160}7161json.EndObject();7162}7163json.EndObject();7164}71657166const size_t length = sb.GetLength();7167WCHAR* result = AllocateArray<WCHAR>(GetAllocs(), length + 2);7168result[0] = 0xFEFF;7169memcpy(result + 1, sb.GetData(), length * sizeof(WCHAR));7170result[length + 1] = L'\0';7171*ppStatsString = result;7172}71737174void AllocatorPimpl::FreeStatsString(WCHAR* pStatsString)7175{7176D3D12MA_ASSERT(pStatsString);7177Free(GetAllocs(), pStatsString);7178}71797180template<typename D3D12_RESOURCE_DESC_T>7181bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc,7182ALLOCATION_FLAGS strategy)7183{7184// Prefer creating small buffers <= 32 KB as committed, because drivers pack them better,7185// while placed buffers require 64 KB alignment.7186if(resourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER &&7187resourceDesc.Width <= D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT / 2 &&7188strategy != ALLOCATION_FLAG_STRATEGY_MIN_TIME && // Creating as committed would be slower.7189m_PreferSmallBuffersCommitted)7190{7191return true;7192}71937194// Intentional. It may change in the future.7195return false;7196}71977198HRESULT AllocatorPimpl::AllocateCommittedResource(7199const CommittedAllocationParameters& committedAllocParams,7200UINT64 resourceSize, bool withinBudget, void* pPrivateData,7201const CREATE_RESOURCE_PARAMS& createParams,7202Allocation** ppAllocation, REFIID riidResource, void** ppvResource)7203{7204D3D12MA_ASSERT(committedAllocParams.IsValid());72057206HRESULT hr;7207ID3D12Resource* res = NULL;7208// Allocate aliasing memory with explicit heap7209if (committedAllocParams.m_CanAlias)7210{7211D3D12_RESOURCE_ALLOCATION_INFO heapAllocInfo = {};7212heapAllocInfo.SizeInBytes = resourceSize;7213heapAllocInfo.Alignment = HeapFlagsToAlignment(committedAllocParams.m_HeapFlags, m_MsaaAlwaysCommitted);7214hr = AllocateHeap(committedAllocParams, heapAllocInfo, withinBudget, pPrivateData, ppAllocation);7215if (SUCCEEDED(hr))7216{7217hr = CreatePlacedResourceWrap((*ppAllocation)->GetHeap(), 0,7218createParams, D3D12MA_IID_PPV_ARGS(&res));7219if (SUCCEEDED(hr))7220{7221if (ppvResource != NULL)7222hr = res->QueryInterface(riidResource, ppvResource);7223if (SUCCEEDED(hr))7224{7225(*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc());7226return hr;7227}7228res->Release();7229}7230FreeHeapMemory(*ppAllocation);7231}7232return hr;7233}72347235if (withinBudget &&7236!NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, resourceSize))7237{7238return E_OUTOFMEMORY;7239}72407241/* D3D12 ERROR:7242* ID3D12Device::CreateCommittedResource:7243* When creating a committed resource, D3D12_HEAP_FLAGS must not have either7244* D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES,7245* D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES,7246* nor D3D12_HEAP_FLAG_DENY_BUFFERS set.7247* These flags will be set automatically to correspond with the committed resource type.7248*7249* [ STATE_CREATION ERROR #640: CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS]7250*/72517252#ifdef __ID3D12Device10_INTERFACE_DEFINED__7253if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)7254{7255if (!m_Device10)7256{7257return E_NOINTERFACE;7258}7259hr = m_Device10->CreateCommittedResource3(7260&committedAllocParams.m_HeapProperties,7261committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7262createParams.GetResourceDesc1(), createParams.GetInitialLayout(),7263createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7264createParams.GetNumCastableFormats(), createParams.GetCastableFormats(),7265D3D12MA_IID_PPV_ARGS(&res));7266} else7267#endif7268#ifdef __ID3D12Device8_INTERFACE_DEFINED__7269if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7270{7271if (!m_Device8)7272{7273return E_NOINTERFACE;7274}7275hr = m_Device8->CreateCommittedResource2(7276&committedAllocParams.m_HeapProperties,7277committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7278createParams.GetResourceDesc1(), createParams.GetInitialResourceState(),7279createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7280D3D12MA_IID_PPV_ARGS(&res));7281} else7282#endif7283if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)7284{7285#ifdef __ID3D12Device4_INTERFACE_DEFINED__7286if (m_Device4)7287{7288hr = m_Device4->CreateCommittedResource1(7289&committedAllocParams.m_HeapProperties,7290committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7291createParams.GetResourceDesc(), createParams.GetInitialResourceState(),7292createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7293D3D12MA_IID_PPV_ARGS(&res));7294}7295else7296#endif7297{7298if (committedAllocParams.m_ProtectedSession == NULL)7299{7300hr = m_Device->CreateCommittedResource(7301&committedAllocParams.m_HeapProperties,7302committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7303createParams.GetResourceDesc(), createParams.GetInitialResourceState(),7304createParams.GetOptimizedClearValue(), D3D12MA_IID_PPV_ARGS(&res));7305}7306else7307hr = E_NOINTERFACE;7308}7309}7310else7311{7312D3D12MA_ASSERT(0);7313return E_INVALIDARG;7314}73157316if (SUCCEEDED(hr))7317{7318SetResidencyPriority(res, committedAllocParams.m_ResidencyPriority);73197320if (ppvResource != NULL)7321{7322hr = res->QueryInterface(riidResource, ppvResource);7323}7324if (SUCCEEDED(hr))7325{7326Allocation* alloc = m_AllocationObjectAllocator.Allocate(7327this, resourceSize, createParams.GetBaseResourceDesc()->Alignment);7328alloc->InitCommitted(committedAllocParams.m_List);7329alloc->SetResourcePointer(res, createParams.GetBaseResourceDesc());7330alloc->SetPrivateData(pPrivateData);73317332*ppAllocation = alloc;73337334committedAllocParams.m_List->Register(alloc);73357336const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties);7337m_Budget.AddBlock(memSegmentGroup, resourceSize);7338m_Budget.AddAllocation(memSegmentGroup, resourceSize);7339}7340else7341{7342res->Release();7343}7344}7345return hr;7346}73477348HRESULT AllocatorPimpl::AllocateHeap(7349const CommittedAllocationParameters& committedAllocParams,7350const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget,7351void* pPrivateData, Allocation** ppAllocation)7352{7353D3D12MA_ASSERT(committedAllocParams.IsValid());73547355*ppAllocation = nullptr;73567357if (withinBudget &&7358!NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, allocInfo.SizeInBytes))7359{7360return E_OUTOFMEMORY;7361}73627363D3D12_HEAP_DESC heapDesc = {};7364heapDesc.SizeInBytes = allocInfo.SizeInBytes;7365heapDesc.Properties = committedAllocParams.m_HeapProperties;7366heapDesc.Alignment = allocInfo.Alignment;7367heapDesc.Flags = committedAllocParams.m_HeapFlags;73687369HRESULT hr;7370ID3D12Heap* heap = nullptr;7371#ifdef __ID3D12Device4_INTERFACE_DEFINED__7372if (m_Device4)7373hr = m_Device4->CreateHeap1(&heapDesc, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&heap));7374else7375#endif7376{7377if (committedAllocParams.m_ProtectedSession == NULL)7378hr = m_Device->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&heap));7379else7380hr = E_NOINTERFACE;7381}73827383if (SUCCEEDED(hr))7384{7385SetResidencyPriority(heap, committedAllocParams.m_ResidencyPriority);7386(*ppAllocation) = m_AllocationObjectAllocator.Allocate(this, allocInfo.SizeInBytes, allocInfo.Alignment);7387(*ppAllocation)->InitHeap(committedAllocParams.m_List, heap);7388(*ppAllocation)->SetPrivateData(pPrivateData);7389committedAllocParams.m_List->Register(*ppAllocation);73907391const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties);7392m_Budget.AddBlock(memSegmentGroup, allocInfo.SizeInBytes);7393m_Budget.AddAllocation(memSegmentGroup, allocInfo.SizeInBytes);7394}7395return hr;7396}73977398template<typename D3D12_RESOURCE_DESC_T>7399HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,7400const D3D12_RESOURCE_DESC_T* resDesc,7401BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted)7402{7403outBlockVector = NULL;7404outCommittedAllocationParams = CommittedAllocationParameters();7405outPreferCommitted = false;74067407if (allocDesc.HeapType == D3D12_HEAP_TYPE_GPU_UPLOAD_COPY && !IsGPUUploadHeapSupported())7408return E_NOTIMPL;74097410bool msaaAlwaysCommitted;7411if (allocDesc.CustomPool != NULL)7412{7413PoolPimpl* const pool = allocDesc.CustomPool->m_Pimpl;74147415msaaAlwaysCommitted = pool->GetBlockVector()->DeniesMsaaTextures();7416if(!pool->AlwaysCommitted())7417outBlockVector = pool->GetBlockVector();74187419const auto& desc = pool->GetDesc();7420outCommittedAllocationParams.m_ProtectedSession = desc.pProtectedSession;7421outCommittedAllocationParams.m_HeapProperties = desc.HeapProperties;7422outCommittedAllocationParams.m_HeapFlags = desc.HeapFlags;7423outCommittedAllocationParams.m_List = pool->GetCommittedAllocationList();7424outCommittedAllocationParams.m_ResidencyPriority = pool->GetDesc().ResidencyPriority;7425}7426else7427{7428if (!IsHeapTypeStandard(allocDesc.HeapType))7429{7430return E_INVALIDARG;7431}7432msaaAlwaysCommitted = m_MsaaAlwaysCommitted;74337434outCommittedAllocationParams.m_HeapProperties = StandardHeapTypeToHeapProperties(allocDesc.HeapType);7435outCommittedAllocationParams.m_HeapFlags = allocDesc.ExtraHeapFlags;7436outCommittedAllocationParams.m_List = &m_CommittedAllocations[StandardHeapTypeToIndex(allocDesc.HeapType)];7437// outCommittedAllocationParams.m_ResidencyPriority intentionally left with default value.74387439const ResourceClass resourceClass = (resDesc != NULL) ?7440ResourceDescToResourceClass(*resDesc) : HeapFlagsToResourceClass(allocDesc.ExtraHeapFlags);7441const UINT defaultPoolIndex = CalcDefaultPoolIndex(allocDesc, resourceClass);7442if (defaultPoolIndex != UINT32_MAX)7443{7444outBlockVector = m_BlockVectors[defaultPoolIndex];7445const UINT64 preferredBlockSize = outBlockVector->GetPreferredBlockSize();7446if (allocSize > preferredBlockSize)7447{7448outBlockVector = NULL;7449}7450else if (allocSize > preferredBlockSize / 2)7451{7452// Heuristics: Allocate committed memory if requested size if greater than half of preferred block size.7453outPreferCommitted = true;7454}7455}7456}74577458if ((allocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0 ||7459m_AlwaysCommitted)7460{7461outBlockVector = NULL;7462}7463if ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) != 0)7464{7465outCommittedAllocationParams.m_List = NULL;7466}7467outCommittedAllocationParams.m_CanAlias = allocDesc.Flags & ALLOCATION_FLAG_CAN_ALIAS;74687469if (resDesc != NULL)7470{7471if (resDesc->SampleDesc.Count > 1 && msaaAlwaysCommitted)7472outBlockVector = NULL;7473if (!outPreferCommitted && PrefersCommittedAllocation(*resDesc, allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK))7474outPreferCommitted = true;7475}74767477return (outBlockVector != NULL || outCommittedAllocationParams.m_List != NULL) ? S_OK : E_INVALIDARG;7478}74797480UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const7481{7482D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS;74837484#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE7485extraHeapFlags &= ~D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;7486#endif74877488if (extraHeapFlags != 0)7489{7490return UINT32_MAX;7491}74927493UINT poolIndex = UINT_MAX;7494switch (allocDesc.HeapType)7495{7496case D3D12_HEAP_TYPE_DEFAULT: poolIndex = 0; break;7497case D3D12_HEAP_TYPE_UPLOAD: poolIndex = 1; break;7498case D3D12_HEAP_TYPE_READBACK: poolIndex = 2; break;7499case D3D12_HEAP_TYPE_GPU_UPLOAD_COPY: poolIndex = 3; break;7500default: D3D12MA_ASSERT(0);7501}75027503if (SupportsResourceHeapTier2())7504return poolIndex;7505else7506{7507switch (resourceClass)7508{7509case ResourceClass::Buffer:7510return poolIndex * 3;7511case ResourceClass::Non_RT_DS_Texture:7512return poolIndex * 3 + 1;7513case ResourceClass::RT_DS_Texture:7514return poolIndex * 3 + 2;7515default:7516return UINT32_MAX;7517}7518}7519}75207521void AllocatorPimpl::CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const7522{7523outHeapType = D3D12_HEAP_TYPE_DEFAULT;7524outHeapFlags = D3D12_HEAP_FLAG_NONE;75257526if (!SupportsResourceHeapTier2())7527{7528switch (index % 3)7529{7530case 0:7531outHeapFlags = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;7532break;7533case 1:7534outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;7535break;7536case 2:7537outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;7538break;7539}75407541index /= 3;7542}75437544switch (index)7545{7546case 0:7547outHeapType = D3D12_HEAP_TYPE_DEFAULT;7548break;7549case 1:7550outHeapType = D3D12_HEAP_TYPE_UPLOAD;7551break;7552case 2:7553outHeapType = D3D12_HEAP_TYPE_READBACK;7554break;7555case 3:7556outHeapType = D3D12_HEAP_TYPE_GPU_UPLOAD_COPY;7557break;7558default:7559D3D12MA_ASSERT(0);7560}7561}75627563void AllocatorPimpl::RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)7564{7565const UINT heapTypeIndex = (UINT)heapType - 1;75667567MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);7568m_Pools[heapTypeIndex].PushBack(pool->m_Pimpl);7569}75707571void AllocatorPimpl::UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)7572{7573const UINT heapTypeIndex = (UINT)heapType - 1;75747575MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);7576m_Pools[heapTypeIndex].Remove(pool->m_Pimpl);7577}75787579HRESULT AllocatorPimpl::UpdateD3D12Budget()7580{7581#if D3D12MA_DXGI_1_47582if (m_Adapter3)7583return m_Budget.UpdateBudget(m_Adapter3, m_UseMutex);7584else7585return E_NOINTERFACE;7586#else7587return S_OK;7588#endif7589}75907591D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const7592{7593// This is how new D3D12 headers define GetResourceAllocationInfo function -7594// different signature depending on these macros.7595#if defined(_MSC_VER) || !defined(_WIN32)7596return m_Device->GetResourceAllocationInfo(0, 1, &resourceDesc);7597#else7598D3D12_RESOURCE_ALLOCATION_INFO retVal;7599return *m_Device->GetResourceAllocationInfo(&retVal, 0, 1, &resourceDesc);7600#endif7601}76027603#ifdef __ID3D12Device8_INTERFACE_DEFINED__7604D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const7605{7606D3D12MA_ASSERT(m_Device8 != NULL);7607D3D12_RESOURCE_ALLOCATION_INFO1 info1Unused;76087609// This is how new D3D12 headers define GetResourceAllocationInfo function -7610// different signature depending on these macros.7611#if defined(_MSC_VER) || !defined(_WIN32)7612return m_Device8->GetResourceAllocationInfo2(0, 1, &resourceDesc, &info1Unused);7613#else7614D3D12_RESOURCE_ALLOCATION_INFO retVal;7615return *m_Device8->GetResourceAllocationInfo2(&retVal, 0, 1, &resourceDesc, &info1Unused);7616#endif7617}7618#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__76197620template<typename D3D12_RESOURCE_DESC_T>7621D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const7622{7623#ifdef __ID3D12Device1_INTERFACE_DEFINED__7624/* Optional optimization: Microsoft documentation says:7625https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getresourceallocationinfo76267627Your application can forgo using GetResourceAllocationInfo for buffer resources7628(D3D12_RESOURCE_DIMENSION_BUFFER). Buffers have the same size on all adapters,7629which is merely the smallest multiple of 64KB that's greater or equal to7630D3D12_RESOURCE_DESC::Width.7631*/7632if (inOutResourceDesc.Alignment == 0 &&7633inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)7634{7635return {7636AlignUp<UINT64>(inOutResourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), // SizeInBytes7637D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT }; // Alignment7638}7639#endif // #ifdef __ID3D12Device1_INTERFACE_DEFINED__76407641#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT7642if (inOutResourceDesc.Alignment == 0 &&7643inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&7644(inOutResourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 07645#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT == 17646&& CanUseSmallAlignment(inOutResourceDesc)7647#endif7648)7649{7650/*7651The algorithm here is based on Microsoft sample: "Small Resources Sample"7652https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12SmallResources7653*/7654const UINT64 smallAlignmentToTry = inOutResourceDesc.SampleDesc.Count > 1 ?7655D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT :7656D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;7657inOutResourceDesc.Alignment = smallAlignmentToTry;7658const D3D12_RESOURCE_ALLOCATION_INFO smallAllocInfo = GetResourceAllocationInfoNative(inOutResourceDesc);7659// Check if alignment requested has been granted.7660if (smallAllocInfo.Alignment == smallAlignmentToTry)7661{7662return smallAllocInfo;7663}7664inOutResourceDesc.Alignment = 0; // Restore original7665}7666#endif // #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT76677668return GetResourceAllocationInfoNative(inOutResourceDesc);7669}76707671bool AllocatorPimpl::NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size)7672{7673Budget budget = {};7674GetBudgetForHeapType(budget, heapType);7675return budget.UsageBytes + size <= budget.BudgetBytes;7676}76777678void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget)7679{7680json.BeginObject();7681{7682json.WriteString(L"BudgetBytes");7683json.WriteNumber(budget.BudgetBytes);7684json.WriteString(L"UsageBytes");7685json.WriteNumber(budget.UsageBytes);7686}7687json.EndObject();7688}76897690#endif // _D3D12MA_ALLOCATOR_PIMPL7691#endif // _D3D12MA_ALLOCATOR_PIMPL76927693#ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL7694class VirtualBlockPimpl7695{7696public:7697const ALLOCATION_CALLBACKS m_AllocationCallbacks;7698const UINT64 m_Size;7699BlockMetadata* m_Metadata;77007701VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc);7702~VirtualBlockPimpl();7703};77047705#ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS7706VirtualBlockPimpl::VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc)7707: m_AllocationCallbacks(allocationCallbacks), m_Size(desc.Size)7708{7709switch (desc.Flags & VIRTUAL_BLOCK_FLAG_ALGORITHM_MASK)7710{7711case VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR:7712m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_Linear)(&m_AllocationCallbacks, true);7713break;7714default:7715D3D12MA_ASSERT(0);7716case 0:7717m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_TLSF)(&m_AllocationCallbacks, true);7718break;7719}7720m_Metadata->Init(m_Size);7721}77227723VirtualBlockPimpl::~VirtualBlockPimpl()7724{7725D3D12MA_DELETE(m_AllocationCallbacks, m_Metadata);7726}7727#endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS7728#endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL772977307731#ifndef _D3D12MA_MEMORY_BLOCK_FUNCTIONS7732MemoryBlock::MemoryBlock(7733AllocatorPimpl* allocator,7734const D3D12_HEAP_PROPERTIES& heapProps,7735D3D12_HEAP_FLAGS heapFlags,7736UINT64 size,7737UINT id)7738: m_Allocator(allocator),7739m_HeapProps(heapProps),7740m_HeapFlags(heapFlags),7741m_Size(size),7742m_Id(id) {}77437744MemoryBlock::~MemoryBlock()7745{7746if (m_Heap)7747{7748m_Heap->Release();7749m_Allocator->m_Budget.RemoveBlock(7750m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size);7751}7752}77537754HRESULT MemoryBlock::Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures)7755{7756D3D12MA_ASSERT(m_Heap == NULL && m_Size > 0);77577758D3D12_HEAP_DESC heapDesc = {};7759heapDesc.SizeInBytes = m_Size;7760heapDesc.Properties = m_HeapProps;7761heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags, denyMsaaTextures);7762heapDesc.Flags = m_HeapFlags;77637764HRESULT hr;7765#ifdef __ID3D12Device4_INTERFACE_DEFINED__7766ID3D12Device4* const device4 = m_Allocator->GetDevice4();7767if (device4)7768hr = m_Allocator->GetDevice4()->CreateHeap1(&heapDesc, pProtectedSession, D3D12MA_IID_PPV_ARGS(&m_Heap));7769else7770#endif7771{7772if (pProtectedSession == NULL)7773hr = m_Allocator->GetDevice()->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&m_Heap));7774else7775hr = E_NOINTERFACE;7776}77777778if (SUCCEEDED(hr))7779{7780m_Allocator->m_Budget.AddBlock(7781m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size);7782}7783return hr;7784}7785#endif // _D3D12MA_MEMORY_BLOCK_FUNCTIONS77867787#ifndef _D3D12MA_NORMAL_BLOCK_FUNCTIONS7788NormalBlock::NormalBlock(7789AllocatorPimpl* allocator,7790BlockVector* blockVector,7791const D3D12_HEAP_PROPERTIES& heapProps,7792D3D12_HEAP_FLAGS heapFlags,7793UINT64 size,7794UINT id)7795: MemoryBlock(allocator, heapProps, heapFlags, size, id),7796m_pMetadata(NULL),7797m_BlockVector(blockVector) {}77987799NormalBlock::~NormalBlock()7800{7801if (m_pMetadata != NULL)7802{7803// Define macro D3D12MA_DEBUG_LOG to receive the list of the unfreed allocations.7804if (!m_pMetadata->IsEmpty())7805m_pMetadata->DebugLogAllAllocations();78067807// THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY!7808// Hitting it means you have some memory leak - unreleased Allocation objects.7809D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");78107811D3D12MA_DELETE(m_Allocator->GetAllocs(), m_pMetadata);7812}7813}78147815HRESULT NormalBlock::Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures)7816{7817HRESULT hr = MemoryBlock::Init(pProtectedSession, denyMsaaTextures);7818if (FAILED(hr))7819{7820return hr;7821}78227823switch (algorithm)7824{7825case POOL_FLAG_ALGORITHM_LINEAR:7826m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_Linear)(&m_Allocator->GetAllocs(), false);7827break;7828default:7829D3D12MA_ASSERT(0);7830case 0:7831m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_TLSF)(&m_Allocator->GetAllocs(), false);7832break;7833}7834m_pMetadata->Init(m_Size);78357836return hr;7837}78387839bool NormalBlock::Validate() const7840{7841D3D12MA_VALIDATE(GetHeap() &&7842m_pMetadata &&7843m_pMetadata->GetSize() != 0 &&7844m_pMetadata->GetSize() == GetSize());7845return m_pMetadata->Validate();7846}7847#endif // _D3D12MA_NORMAL_BLOCK_FUNCTIONS78487849#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS7850void CommittedAllocationList::Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool)7851{7852m_UseMutex = useMutex;7853m_HeapType = heapType;7854m_Pool = pool;7855}78567857CommittedAllocationList::~CommittedAllocationList()7858{7859if (!m_AllocationList.IsEmpty())7860{7861D3D12MA_ASSERT(0 && "Unfreed committed allocations found!");7862}7863}78647865UINT CommittedAllocationList::GetMemorySegmentGroup(AllocatorPimpl* allocator) const7866{7867if (m_Pool)7868return allocator->HeapPropertiesToMemorySegmentGroup(m_Pool->GetDesc().HeapProperties);7869else7870return allocator->StandardHeapTypeToMemorySegmentGroup(m_HeapType);7871}78727873void CommittedAllocationList::AddStatistics(Statistics& inoutStats)7874{7875MutexLockRead lock(m_Mutex, m_UseMutex);78767877for (Allocation* alloc = m_AllocationList.Front();7878alloc != NULL; alloc = m_AllocationList.GetNext(alloc))7879{7880const UINT64 size = alloc->GetSize();7881inoutStats.BlockCount++;7882inoutStats.AllocationCount++;7883inoutStats.BlockBytes += size;7884inoutStats.AllocationBytes += size;7885}7886}78877888void CommittedAllocationList::AddDetailedStatistics(DetailedStatistics& inoutStats)7889{7890MutexLockRead lock(m_Mutex, m_UseMutex);78917892for (Allocation* alloc = m_AllocationList.Front();7893alloc != NULL; alloc = m_AllocationList.GetNext(alloc))7894{7895const UINT64 size = alloc->GetSize();7896inoutStats.Stats.BlockCount++;7897inoutStats.Stats.BlockBytes += size;7898AddDetailedStatisticsAllocation(inoutStats, size);7899}7900}79017902void CommittedAllocationList::BuildStatsString(JsonWriter& json)7903{7904MutexLockRead lock(m_Mutex, m_UseMutex);79057906for (Allocation* alloc = m_AllocationList.Front();7907alloc != NULL; alloc = m_AllocationList.GetNext(alloc))7908{7909json.BeginObject(true);7910json.AddAllocationToObject(*alloc);7911json.EndObject();7912}7913}79147915void CommittedAllocationList::Register(Allocation* alloc)7916{7917MutexLockWrite lock(m_Mutex, m_UseMutex);7918m_AllocationList.PushBack(alloc);7919}79207921void CommittedAllocationList::Unregister(Allocation* alloc)7922{7923MutexLockWrite lock(m_Mutex, m_UseMutex);7924m_AllocationList.Remove(alloc);7925}7926#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS79277928#ifndef _D3D12MA_BLOCK_VECTOR_FUNCTIONS7929BlockVector::BlockVector(7930AllocatorPimpl* hAllocator,7931const D3D12_HEAP_PROPERTIES& heapProps,7932D3D12_HEAP_FLAGS heapFlags,7933UINT64 preferredBlockSize,7934size_t minBlockCount,7935size_t maxBlockCount,7936bool explicitBlockSize,7937UINT64 minAllocationAlignment,7938UINT32 algorithm,7939bool denyMsaaTextures,7940ID3D12ProtectedResourceSession* pProtectedSession,7941D3D12_RESIDENCY_PRIORITY residencyPriority)7942: m_hAllocator(hAllocator),7943m_HeapProps(heapProps),7944m_HeapFlags(heapFlags),7945m_PreferredBlockSize(preferredBlockSize),7946m_MinBlockCount(minBlockCount),7947m_MaxBlockCount(maxBlockCount),7948m_ExplicitBlockSize(explicitBlockSize),7949m_MinAllocationAlignment(minAllocationAlignment),7950m_Algorithm(algorithm),7951m_DenyMsaaTextures(denyMsaaTextures),7952m_ProtectedSession(pProtectedSession),7953m_ResidencyPriority(residencyPriority),7954m_HasEmptyBlock(false),7955m_Blocks(hAllocator->GetAllocs()),7956m_NextBlockId(0) {}79577958BlockVector::~BlockVector()7959{7960for (size_t i = m_Blocks.size(); i--; )7961{7962D3D12MA_DELETE(m_hAllocator->GetAllocs(), m_Blocks[i]);7963}7964}79657966HRESULT BlockVector::CreateMinBlocks()7967{7968for (size_t i = 0; i < m_MinBlockCount; ++i)7969{7970HRESULT hr = CreateBlock(m_PreferredBlockSize, NULL);7971if (FAILED(hr))7972{7973return hr;7974}7975}7976return S_OK;7977}79787979bool BlockVector::IsEmpty()7980{7981MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());7982return m_Blocks.empty();7983}79847985HRESULT BlockVector::Allocate(7986UINT64 size,7987UINT64 alignment,7988const ALLOCATION_DESC& allocDesc,7989size_t allocationCount,7990Allocation** pAllocations)7991{7992size_t allocIndex;7993HRESULT hr = S_OK;79947995{7996MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());7997for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)7998{7999hr = AllocatePage(8000size,8001alignment,8002allocDesc,8003pAllocations + allocIndex);8004if (FAILED(hr))8005{8006break;8007}8008}8009}80108011if (FAILED(hr))8012{8013// Free all already created allocations.8014while (allocIndex--)8015{8016Free(pAllocations[allocIndex]);8017}8018ZeroMemory(pAllocations, sizeof(Allocation*) * allocationCount);8019}80208021return hr;8022}80238024void BlockVector::Free(Allocation* hAllocation)8025{8026NormalBlock* pBlockToDelete = NULL;80278028bool budgetExceeded = false;8029if (IsHeapTypeStandard(m_HeapProps.Type))8030{8031Budget budget = {};8032m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type);8033budgetExceeded = budget.UsageBytes >= budget.BudgetBytes;8034}80358036// Scope for lock.8037{8038MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());80398040NormalBlock* pBlock = hAllocation->m_Placed.block;80418042pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());8043D3D12MA_HEAVY_ASSERT(pBlock->Validate());80448045const size_t blockCount = m_Blocks.size();8046// pBlock became empty after this deallocation.8047if (pBlock->m_pMetadata->IsEmpty())8048{8049// Already has empty Allocation. We don't want to have two, so delete this one.8050if ((m_HasEmptyBlock || budgetExceeded) &&8051blockCount > m_MinBlockCount)8052{8053pBlockToDelete = pBlock;8054Remove(pBlock);8055}8056// We now have first empty block.8057else8058{8059m_HasEmptyBlock = true;8060}8061}8062// pBlock didn't become empty, but we have another empty block - find and free that one.8063// (This is optional, heuristics.)8064else if (m_HasEmptyBlock && blockCount > m_MinBlockCount)8065{8066NormalBlock* pLastBlock = m_Blocks.back();8067if (pLastBlock->m_pMetadata->IsEmpty())8068{8069pBlockToDelete = pLastBlock;8070m_Blocks.pop_back();8071m_HasEmptyBlock = false;8072}8073}80748075IncrementallySortBlocks();8076}80778078// Destruction of a free Allocation. Deferred until this point, outside of mutex8079// lock, for performance reason.8080if (pBlockToDelete != NULL)8081{8082D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlockToDelete);8083}8084}80858086HRESULT BlockVector::CreateResource(8087UINT64 size,8088UINT64 alignment,8089const ALLOCATION_DESC& allocDesc,8090const CREATE_RESOURCE_PARAMS& createParams,8091Allocation** ppAllocation,8092REFIID riidResource,8093void** ppvResource)8094{8095HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation);8096if (SUCCEEDED(hr))8097{8098ID3D12Resource* res = NULL;8099hr = m_hAllocator->CreatePlacedResourceWrap(8100(*ppAllocation)->m_Placed.block->GetHeap(),8101(*ppAllocation)->GetOffset(),8102createParams,8103D3D12MA_IID_PPV_ARGS(&res));8104if (SUCCEEDED(hr))8105{8106if (ppvResource != NULL)8107{8108hr = res->QueryInterface(riidResource, ppvResource);8109}8110if (SUCCEEDED(hr))8111{8112(*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc());8113}8114else8115{8116res->Release();8117SAFE_RELEASE(*ppAllocation);8118}8119}8120else8121{8122SAFE_RELEASE(*ppAllocation);8123}8124}8125return hr;8126}81278128void BlockVector::AddStatistics(Statistics& inoutStats)8129{8130MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());81318132for (size_t i = 0; i < m_Blocks.size(); ++i)8133{8134const NormalBlock* const pBlock = m_Blocks[i];8135D3D12MA_ASSERT(pBlock);8136D3D12MA_HEAVY_ASSERT(pBlock->Validate());8137pBlock->m_pMetadata->AddStatistics(inoutStats);8138}8139}81408141void BlockVector::AddDetailedStatistics(DetailedStatistics& inoutStats)8142{8143MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());81448145for (size_t i = 0; i < m_Blocks.size(); ++i)8146{8147const NormalBlock* const pBlock = m_Blocks[i];8148D3D12MA_ASSERT(pBlock);8149D3D12MA_HEAVY_ASSERT(pBlock->Validate());8150pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);8151}8152}81538154void BlockVector::WriteBlockInfoToJson(JsonWriter& json)8155{8156MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());81578158json.BeginObject();81598160for (size_t i = 0, count = m_Blocks.size(); i < count; ++i)8161{8162const NormalBlock* const pBlock = m_Blocks[i];8163D3D12MA_ASSERT(pBlock);8164D3D12MA_HEAVY_ASSERT(pBlock->Validate());8165json.BeginString();8166json.ContinueString(pBlock->GetId());8167json.EndString();81688169json.BeginObject();8170pBlock->m_pMetadata->WriteAllocationInfoToJson(json);8171json.EndObject();8172}81738174json.EndObject();8175}81768177UINT64 BlockVector::CalcSumBlockSize() const8178{8179UINT64 result = 0;8180for (size_t i = m_Blocks.size(); i--; )8181{8182result += m_Blocks[i]->m_pMetadata->GetSize();8183}8184return result;8185}81868187UINT64 BlockVector::CalcMaxBlockSize() const8188{8189UINT64 result = 0;8190for (size_t i = m_Blocks.size(); i--; )8191{8192result = D3D12MA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());8193if (result >= m_PreferredBlockSize)8194{8195break;8196}8197}8198return result;8199}82008201void BlockVector::Remove(NormalBlock* pBlock)8202{8203for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)8204{8205if (m_Blocks[blockIndex] == pBlock)8206{8207m_Blocks.remove(blockIndex);8208return;8209}8210}8211D3D12MA_ASSERT(0);8212}82138214void BlockVector::IncrementallySortBlocks()8215{8216if (!m_IncrementalSort)8217return;8218// Bubble sort only until first swap.8219for (size_t i = 1; i < m_Blocks.size(); ++i)8220{8221if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())8222{8223D3D12MA_SWAP(m_Blocks[i - 1], m_Blocks[i]);8224return;8225}8226}8227}82288229void BlockVector::SortByFreeSize()8230{8231D3D12MA_SORT(m_Blocks.begin(), m_Blocks.end(),8232[](auto* b1, auto* b2)8233{8234return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();8235});8236}82378238HRESULT BlockVector::AllocatePage(8239UINT64 size,8240UINT64 alignment,8241const ALLOCATION_DESC& allocDesc,8242Allocation** pAllocation)8243{8244// Early reject: requested allocation size is larger that maximum block size for this block vector.8245if (size + D3D12MA_DEBUG_MARGIN > m_PreferredBlockSize)8246{8247return E_OUTOFMEMORY;8248}82498250UINT64 freeMemory = UINT64_MAX;8251if (IsHeapTypeStandard(m_HeapProps.Type))8252{8253Budget budget = {};8254m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type);8255freeMemory = (budget.UsageBytes < budget.BudgetBytes) ? (budget.BudgetBytes - budget.UsageBytes) : 0;8256}82578258const bool canCreateNewBlock =8259((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0) &&8260(m_Blocks.size() < m_MaxBlockCount) &&8261// Even if we don't have to stay within budget with this allocation, when the8262// budget would be exceeded, we don't want to allocate new blocks, but always8263// create resources as committed.8264freeMemory >= size;82658266// 1. Search existing allocations8267{8268// Forward order in m_Blocks - prefer blocks with smallest amount of free space.8269for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)8270{8271NormalBlock* const pCurrBlock = m_Blocks[blockIndex];8272D3D12MA_ASSERT(pCurrBlock);8273HRESULT hr = AllocateFromBlock(8274pCurrBlock,8275size,8276alignment,8277allocDesc.Flags,8278allocDesc.pPrivateData,8279allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK,8280pAllocation);8281if (SUCCEEDED(hr))8282{8283return hr;8284}8285}8286}82878288// 2. Try to create new block.8289if (canCreateNewBlock)8290{8291// Calculate optimal size for new block.8292UINT64 newBlockSize = m_PreferredBlockSize;8293UINT newBlockSizeShift = 0;82948295if (!m_ExplicitBlockSize)8296{8297// Allocate 1/8, 1/4, 1/2 as first blocks.8298const UINT64 maxExistingBlockSize = CalcMaxBlockSize();8299for (UINT i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)8300{8301const UINT64 smallerNewBlockSize = newBlockSize / 2;8302if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)8303{8304newBlockSize = smallerNewBlockSize;8305++newBlockSizeShift;8306}8307else8308{8309break;8310}8311}8312}83138314size_t newBlockIndex = 0;8315HRESULT hr = newBlockSize <= freeMemory ?8316CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;8317// Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.8318if (!m_ExplicitBlockSize)8319{8320while (FAILED(hr) && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)8321{8322const UINT64 smallerNewBlockSize = newBlockSize / 2;8323if (smallerNewBlockSize >= size)8324{8325newBlockSize = smallerNewBlockSize;8326++newBlockSizeShift;8327hr = newBlockSize <= freeMemory ?8328CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;8329}8330else8331{8332break;8333}8334}8335}83368337if (SUCCEEDED(hr))8338{8339NormalBlock* const pBlock = m_Blocks[newBlockIndex];8340D3D12MA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);83418342hr = AllocateFromBlock(8343pBlock,8344size,8345alignment,8346allocDesc.Flags,8347allocDesc.pPrivateData,8348allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK,8349pAllocation);8350if (SUCCEEDED(hr))8351{8352return hr;8353}8354else8355{8356// Allocation from new block failed, possibly due to D3D12MA_DEBUG_MARGIN or alignment.8357return E_OUTOFMEMORY;8358}8359}8360}83618362return E_OUTOFMEMORY;8363}83648365HRESULT BlockVector::AllocateFromBlock(8366NormalBlock* pBlock,8367UINT64 size,8368UINT64 alignment,8369ALLOCATION_FLAGS allocFlags,8370void* pPrivateData,8371UINT32 strategy,8372Allocation** pAllocation)8373{8374alignment = D3D12MA_MAX(alignment, m_MinAllocationAlignment);83758376AllocationRequest currRequest = {};8377if (pBlock->m_pMetadata->CreateAllocationRequest(8378size,8379alignment,8380allocFlags & ALLOCATION_FLAG_UPPER_ADDRESS,8381strategy,8382&currRequest))8383{8384return CommitAllocationRequest(currRequest, pBlock, size, alignment, pPrivateData, pAllocation);8385}8386return E_OUTOFMEMORY;8387}83888389HRESULT BlockVector::CommitAllocationRequest(8390AllocationRequest& allocRequest,8391NormalBlock* pBlock,8392UINT64 size,8393UINT64 alignment,8394void* pPrivateData,8395Allocation** pAllocation)8396{8397// We no longer have an empty Allocation.8398if (pBlock->m_pMetadata->IsEmpty())8399m_HasEmptyBlock = false;84008401*pAllocation = m_hAllocator->GetAllocationObjectAllocator().Allocate(m_hAllocator, size, alignment);8402pBlock->m_pMetadata->Alloc(allocRequest, size, *pAllocation);84038404(*pAllocation)->InitPlaced(allocRequest.allocHandle, pBlock);8405(*pAllocation)->SetPrivateData(pPrivateData);84068407D3D12MA_HEAVY_ASSERT(pBlock->Validate());8408m_hAllocator->m_Budget.AddAllocation(m_hAllocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), size);84098410return S_OK;8411}84128413HRESULT BlockVector::CreateBlock(8414UINT64 blockSize,8415size_t* pNewBlockIndex)8416{8417NormalBlock* const pBlock = D3D12MA_NEW(m_hAllocator->GetAllocs(), NormalBlock)(8418m_hAllocator,8419this,8420m_HeapProps,8421m_HeapFlags,8422blockSize,8423m_NextBlockId++);8424HRESULT hr = pBlock->Init(m_Algorithm, m_ProtectedSession, m_DenyMsaaTextures);8425if (FAILED(hr))8426{8427D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlock);8428return hr;8429}84308431m_hAllocator->SetResidencyPriority(pBlock->GetHeap(), m_ResidencyPriority);84328433m_Blocks.push_back(pBlock);8434if (pNewBlockIndex != NULL)8435{8436*pNewBlockIndex = m_Blocks.size() - 1;8437}84388439return hr;8440}8441#endif // _D3D12MA_BLOCK_VECTOR_FUNCTIONS84428443#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS8444DefragmentationContextPimpl::DefragmentationContextPimpl(8445AllocatorPimpl* hAllocator,8446const DEFRAGMENTATION_DESC& desc,8447BlockVector* poolVector)8448: m_MaxPassBytes(desc.MaxBytesPerPass == 0 ? UINT64_MAX : desc.MaxBytesPerPass),8449m_MaxPassAllocations(desc.MaxAllocationsPerPass == 0 ? UINT32_MAX : desc.MaxAllocationsPerPass),8450m_Moves(hAllocator->GetAllocs())8451{8452m_Algorithm = desc.Flags & DEFRAGMENTATION_FLAG_ALGORITHM_MASK;84538454if (poolVector != NULL)8455{8456m_BlockVectorCount = 1;8457m_PoolBlockVector = poolVector;8458m_pBlockVectors = &m_PoolBlockVector;8459m_PoolBlockVector->SetIncrementalSort(false);8460m_PoolBlockVector->SortByFreeSize();8461}8462else8463{8464m_BlockVectorCount = hAllocator->GetDefaultPoolCount();8465m_PoolBlockVector = NULL;8466m_pBlockVectors = hAllocator->GetDefaultPools();8467for (UINT32 i = 0; i < m_BlockVectorCount; ++i)8468{8469BlockVector* vector = m_pBlockVectors[i];8470if (vector != NULL)8471{8472vector->SetIncrementalSort(false);8473vector->SortByFreeSize();8474}8475}8476}84778478switch (m_Algorithm)8479{8480case 0: // Default algorithm8481m_Algorithm = DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED;8482case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:8483{8484m_AlgorithmState = D3D12MA_NEW_ARRAY(hAllocator->GetAllocs(), StateBalanced, m_BlockVectorCount);8485break;8486}8487}8488}84898490DefragmentationContextPimpl::~DefragmentationContextPimpl()8491{8492if (m_PoolBlockVector != NULL)8493m_PoolBlockVector->SetIncrementalSort(true);8494else8495{8496for (UINT32 i = 0; i < m_BlockVectorCount; ++i)8497{8498BlockVector* vector = m_pBlockVectors[i];8499if (vector != NULL)8500vector->SetIncrementalSort(true);8501}8502}85038504if (m_AlgorithmState)8505{8506switch (m_Algorithm)8507{8508case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:8509D3D12MA_DELETE_ARRAY(m_Moves.GetAllocs(), reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);8510break;8511default:8512D3D12MA_ASSERT(0);8513}8514}8515}85168517HRESULT DefragmentationContextPimpl::DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo)8518{8519if (m_PoolBlockVector != NULL)8520{8521MutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->m_hAllocator->UseMutex());85228523if (m_PoolBlockVector->GetBlockCount() > 1)8524ComputeDefragmentation(*m_PoolBlockVector, 0);8525else if (m_PoolBlockVector->GetBlockCount() == 1)8526ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));85278528// Setup index into block vector8529for (size_t i = 0; i < m_Moves.size(); ++i)8530m_Moves[i].pDstTmpAllocation->SetPrivateData(0);8531}8532else8533{8534for (UINT32 i = 0; i < m_BlockVectorCount; ++i)8535{8536if (m_pBlockVectors[i] != NULL)8537{8538MutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->m_hAllocator->UseMutex());85398540bool end = false;8541size_t movesOffset = m_Moves.size();8542if (m_pBlockVectors[i]->GetBlockCount() > 1)8543{8544end = ComputeDefragmentation(*m_pBlockVectors[i], i);8545}8546else if (m_pBlockVectors[i]->GetBlockCount() == 1)8547{8548end = ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0));8549}85508551// Setup index into block vector8552for (; movesOffset < m_Moves.size(); ++movesOffset)8553m_Moves[movesOffset].pDstTmpAllocation->SetPrivateData(reinterpret_cast<void*>(static_cast<uintptr_t>(i)));85548555if (end)8556break;8557}8558}8559}85608561moveInfo.MoveCount = static_cast<UINT32>(m_Moves.size());8562if (moveInfo.MoveCount > 0)8563{8564moveInfo.pMoves = m_Moves.data();8565return S_FALSE;8566}85678568moveInfo.pMoves = NULL;8569return S_OK;8570}85718572HRESULT DefragmentationContextPimpl::DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo)8573{8574D3D12MA_ASSERT(moveInfo.MoveCount > 0 ? moveInfo.pMoves != NULL : true);85758576HRESULT result = S_OK;8577Vector<FragmentedBlock> immovableBlocks(m_Moves.GetAllocs());85788579for (uint32_t i = 0; i < moveInfo.MoveCount; ++i)8580{8581DEFRAGMENTATION_MOVE& move = moveInfo.pMoves[i];8582size_t prevCount = 0, currentCount = 0;8583UINT64 freedBlockSize = 0;85848585UINT32 vectorIndex;8586BlockVector* vector;8587if (m_PoolBlockVector != NULL)8588{8589vectorIndex = 0;8590vector = m_PoolBlockVector;8591}8592else8593{8594vectorIndex = static_cast<UINT32>(reinterpret_cast<uintptr_t>(move.pDstTmpAllocation->GetPrivateData()));8595vector = m_pBlockVectors[vectorIndex];8596D3D12MA_ASSERT(vector != NULL);8597}85988599switch (move.Operation)8600{8601case DEFRAGMENTATION_MOVE_OPERATION_COPY:8602{8603move.pSrcAllocation->SwapBlockAllocation(move.pDstTmpAllocation);86048605// Scope for locks, Free have it's own lock8606{8607MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());8608prevCount = vector->GetBlockCount();8609freedBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize();8610}8611move.pDstTmpAllocation->Release();8612{8613MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());8614currentCount = vector->GetBlockCount();8615}86168617result = S_FALSE;8618break;8619}8620case DEFRAGMENTATION_MOVE_OPERATION_IGNORE:8621{8622m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize();8623--m_PassStats.AllocationsMoved;8624move.pDstTmpAllocation->Release();86258626NormalBlock* newBlock = move.pSrcAllocation->GetBlock();8627bool notPresent = true;8628for (const FragmentedBlock& block : immovableBlocks)8629{8630if (block.block == newBlock)8631{8632notPresent = false;8633break;8634}8635}8636if (notPresent)8637immovableBlocks.push_back({ vectorIndex, newBlock });8638break;8639}8640case DEFRAGMENTATION_MOVE_OPERATION_DESTROY:8641{8642m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize();8643--m_PassStats.AllocationsMoved;8644// Scope for locks, Free have it's own lock8645{8646MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());8647prevCount = vector->GetBlockCount();8648freedBlockSize = move.pSrcAllocation->GetBlock()->m_pMetadata->GetSize();8649}8650move.pSrcAllocation->Release();8651{8652MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());8653currentCount = vector->GetBlockCount();8654}8655freedBlockSize *= prevCount - currentCount;86568657UINT64 dstBlockSize;8658{8659MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());8660dstBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize();8661}8662move.pDstTmpAllocation->Release();8663{8664MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());8665freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());8666currentCount = vector->GetBlockCount();8667}86688669result = S_FALSE;8670break;8671}8672default:8673D3D12MA_ASSERT(0);8674}86758676if (prevCount > currentCount)8677{8678size_t freedBlocks = prevCount - currentCount;8679m_PassStats.HeapsFreed += static_cast<UINT32>(freedBlocks);8680m_PassStats.BytesFreed += freedBlockSize;8681}8682}8683moveInfo.MoveCount = 0;8684moveInfo.pMoves = NULL;8685m_Moves.clear();86868687// Update stats8688m_GlobalStats.AllocationsMoved += m_PassStats.AllocationsMoved;8689m_GlobalStats.BytesFreed += m_PassStats.BytesFreed;8690m_GlobalStats.BytesMoved += m_PassStats.BytesMoved;8691m_GlobalStats.HeapsFreed += m_PassStats.HeapsFreed;8692m_PassStats = { 0 };86938694// Move blocks with immovable allocations according to algorithm8695if (immovableBlocks.size() > 0)8696{8697// Move to the begining8698for (const FragmentedBlock& block : immovableBlocks)8699{8700BlockVector* vector = m_pBlockVectors[block.data];8701MutexLockWrite lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());87028703for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)8704{8705if (vector->GetBlock(i) == block.block)8706{8707D3D12MA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);8708break;8709}8710}8711}8712}8713return result;8714}87158716bool DefragmentationContextPimpl::ComputeDefragmentation(BlockVector& vector, size_t index)8717{8718switch (m_Algorithm)8719{8720case DEFRAGMENTATION_FLAG_ALGORITHM_FAST:8721return ComputeDefragmentation_Fast(vector);8722default:8723D3D12MA_ASSERT(0);8724case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:8725return ComputeDefragmentation_Balanced(vector, index, true);8726case DEFRAGMENTATION_FLAG_ALGORITHM_FULL:8727return ComputeDefragmentation_Full(vector);8728}8729}87308731DefragmentationContextPimpl::MoveAllocationData DefragmentationContextPimpl::GetMoveData(8732AllocHandle handle, BlockMetadata* metadata)8733{8734MoveAllocationData moveData;8735moveData.move.pSrcAllocation = (Allocation*)metadata->GetAllocationPrivateData(handle);8736moveData.size = moveData.move.pSrcAllocation->GetSize();8737moveData.alignment = moveData.move.pSrcAllocation->GetAlignment();8738moveData.flags = ALLOCATION_FLAG_NONE;87398740return moveData;8741}87428743DefragmentationContextPimpl::CounterStatus DefragmentationContextPimpl::CheckCounters(UINT64 bytes)8744{8745// Ignore allocation if will exceed max size for copy8746if (m_PassStats.BytesMoved + bytes > m_MaxPassBytes)8747{8748if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)8749return CounterStatus::Ignore;8750else8751return CounterStatus::End;8752}8753return CounterStatus::Pass;8754}87558756bool DefragmentationContextPimpl::IncrementCounters(UINT64 bytes)8757{8758m_PassStats.BytesMoved += bytes;8759// Early return when max found8760if (++m_PassStats.AllocationsMoved >= m_MaxPassAllocations || m_PassStats.BytesMoved >= m_MaxPassBytes)8761{8762D3D12MA_ASSERT((m_PassStats.AllocationsMoved == m_MaxPassAllocations ||8763m_PassStats.BytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!");8764return true;8765}8766return false;8767}87688769bool DefragmentationContextPimpl::ReallocWithinBlock(BlockVector& vector, NormalBlock* block)8770{8771BlockMetadata* metadata = block->m_pMetadata;87728773for (AllocHandle handle = metadata->GetAllocationListBegin();8774handle != (AllocHandle)0;8775handle = metadata->GetNextAllocation(handle))8776{8777MoveAllocationData moveData = GetMoveData(handle, metadata);8778// Ignore newly created allocations by defragmentation algorithm8779if (moveData.move.pSrcAllocation->GetPrivateData() == this)8780continue;8781switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))8782{8783case CounterStatus::Ignore:8784continue;8785case CounterStatus::End:8786return true;8787default:8788D3D12MA_ASSERT(0);8789case CounterStatus::Pass:8790break;8791}87928793UINT64 offset = moveData.move.pSrcAllocation->GetOffset();8794if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)8795{8796AllocationRequest request = {};8797if (metadata->CreateAllocationRequest(8798moveData.size,8799moveData.alignment,8800false,8801ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,8802&request))8803{8804if (metadata->GetAllocationOffset(request.allocHandle) < offset)8805{8806if (SUCCEEDED(vector.CommitAllocationRequest(8807request,8808block,8809moveData.size,8810moveData.alignment,8811this,8812&moveData.move.pDstTmpAllocation)))8813{8814m_Moves.push_back(moveData.move);8815if (IncrementCounters(moveData.size))8816return true;8817}8818}8819}8820}8821}8822return false;8823}88248825bool DefragmentationContextPimpl::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector)8826{8827for (; start < end; ++start)8828{8829NormalBlock* dstBlock = vector.GetBlock(start);8830if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)8831{8832if (SUCCEEDED(vector.AllocateFromBlock(dstBlock,8833data.size,8834data.alignment,8835data.flags,8836this,88370,8838&data.move.pDstTmpAllocation)))8839{8840m_Moves.push_back(data.move);8841if (IncrementCounters(data.size))8842return true;8843break;8844}8845}8846}8847return false;8848}88498850bool DefragmentationContextPimpl::ComputeDefragmentation_Fast(BlockVector& vector)8851{8852// Move only between blocks88538854// Go through allocations in last blocks and try to fit them inside first ones8855for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)8856{8857BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;88588859for (AllocHandle handle = metadata->GetAllocationListBegin();8860handle != (AllocHandle)0;8861handle = metadata->GetNextAllocation(handle))8862{8863MoveAllocationData moveData = GetMoveData(handle, metadata);8864// Ignore newly created allocations by defragmentation algorithm8865if (moveData.move.pSrcAllocation->GetPrivateData() == this)8866continue;8867switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))8868{8869case CounterStatus::Ignore:8870continue;8871case CounterStatus::End:8872return true;8873default:8874D3D12MA_ASSERT(0);8875case CounterStatus::Pass:8876break;8877}88788879// Check all previous blocks for free space8880if (AllocInOtherBlock(0, i, moveData, vector))8881return true;8882}8883}8884return false;8885}88868887bool DefragmentationContextPimpl::ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update)8888{8889// Go over every allocation and try to fit it in previous blocks at lowest offsets,8890// if not possible: realloc within single block to minimize offset (exclude offset == 0),8891// but only if there are noticable gaps between them (some heuristic, ex. average size of allocation in block)8892D3D12MA_ASSERT(m_AlgorithmState != NULL);88938894StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];8895if (update && vectorState.avgAllocSize == UINT64_MAX)8896UpdateVectorStatistics(vector, vectorState);88978898const size_t startMoveCount = m_Moves.size();8899UINT64 minimalFreeRegion = vectorState.avgFreeSize / 2;8900for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)8901{8902NormalBlock* block = vector.GetBlock(i);8903BlockMetadata* metadata = block->m_pMetadata;8904UINT64 prevFreeRegionSize = 0;89058906for (AllocHandle handle = metadata->GetAllocationListBegin();8907handle != (AllocHandle)0;8908handle = metadata->GetNextAllocation(handle))8909{8910MoveAllocationData moveData = GetMoveData(handle, metadata);8911// Ignore newly created allocations by defragmentation algorithm8912if (moveData.move.pSrcAllocation->GetPrivateData() == this)8913continue;8914switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))8915{8916case CounterStatus::Ignore:8917continue;8918case CounterStatus::End:8919return true;8920default:8921D3D12MA_ASSERT(0);8922case CounterStatus::Pass:8923break;8924}89258926// Check all previous blocks for free space8927const size_t prevMoveCount = m_Moves.size();8928if (AllocInOtherBlock(0, i, moveData, vector))8929return true;89308931UINT64 nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);8932// If no room found then realloc within block for lower offset8933UINT64 offset = moveData.move.pSrcAllocation->GetOffset();8934if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)8935{8936// Check if realloc will make sense8937if (prevFreeRegionSize >= minimalFreeRegion ||8938nextFreeRegionSize >= minimalFreeRegion ||8939moveData.size <= vectorState.avgFreeSize ||8940moveData.size <= vectorState.avgAllocSize)8941{8942AllocationRequest request = {};8943if (metadata->CreateAllocationRequest(8944moveData.size,8945moveData.alignment,8946false,8947ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,8948&request))8949{8950if (metadata->GetAllocationOffset(request.allocHandle) < offset)8951{8952if (SUCCEEDED(vector.CommitAllocationRequest(8953request,8954block,8955moveData.size,8956moveData.alignment,8957this,8958&moveData.move.pDstTmpAllocation)))8959{8960m_Moves.push_back(moveData.move);8961if (IncrementCounters(moveData.size))8962return true;8963}8964}8965}8966}8967}8968prevFreeRegionSize = nextFreeRegionSize;8969}8970}89718972// No moves perfomed, update statistics to current vector state8973if (startMoveCount == m_Moves.size() && !update)8974{8975vectorState.avgAllocSize = UINT64_MAX;8976return ComputeDefragmentation_Balanced(vector, index, false);8977}8978return false;8979}89808981bool DefragmentationContextPimpl::ComputeDefragmentation_Full(BlockVector& vector)8982{8983// Go over every allocation and try to fit it in previous blocks at lowest offsets,8984// if not possible: realloc within single block to minimize offset (exclude offset == 0)89858986for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)8987{8988NormalBlock* block = vector.GetBlock(i);8989BlockMetadata* metadata = block->m_pMetadata;89908991for (AllocHandle handle = metadata->GetAllocationListBegin();8992handle != (AllocHandle)0;8993handle = metadata->GetNextAllocation(handle))8994{8995MoveAllocationData moveData = GetMoveData(handle, metadata);8996// Ignore newly created allocations by defragmentation algorithm8997if (moveData.move.pSrcAllocation->GetPrivateData() == this)8998continue;8999switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9000{9001case CounterStatus::Ignore:9002continue;9003case CounterStatus::End:9004return true;9005default:9006D3D12MA_ASSERT(0);9007case CounterStatus::Pass:9008break;9009}90109011// Check all previous blocks for free space9012const size_t prevMoveCount = m_Moves.size();9013if (AllocInOtherBlock(0, i, moveData, vector))9014return true;90159016// If no room found then realloc within block for lower offset9017UINT64 offset = moveData.move.pSrcAllocation->GetOffset();9018if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)9019{9020AllocationRequest request = {};9021if (metadata->CreateAllocationRequest(9022moveData.size,9023moveData.alignment,9024false,9025ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,9026&request))9027{9028if (metadata->GetAllocationOffset(request.allocHandle) < offset)9029{9030if (SUCCEEDED(vector.CommitAllocationRequest(9031request,9032block,9033moveData.size,9034moveData.alignment,9035this,9036&moveData.move.pDstTmpAllocation)))9037{9038m_Moves.push_back(moveData.move);9039if (IncrementCounters(moveData.size))9040return true;9041}9042}9043}9044}9045}9046}9047return false;9048}90499050void DefragmentationContextPimpl::UpdateVectorStatistics(BlockVector& vector, StateBalanced& state)9051{9052size_t allocCount = 0;9053size_t freeCount = 0;9054state.avgFreeSize = 0;9055state.avgAllocSize = 0;90569057for (size_t i = 0; i < vector.GetBlockCount(); ++i)9058{9059BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;90609061allocCount += metadata->GetAllocationCount();9062freeCount += metadata->GetFreeRegionsCount();9063state.avgFreeSize += metadata->GetSumFreeSize();9064state.avgAllocSize += metadata->GetSize();9065}90669067state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;9068state.avgFreeSize /= freeCount;9069}9070#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS90719072#ifndef _D3D12MA_POOL_PIMPL_FUNCTIONS9073PoolPimpl::PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc)9074: m_Allocator(allocator),9075m_Desc(desc),9076m_BlockVector(NULL),9077m_Name(NULL)9078{9079const bool explicitBlockSize = desc.BlockSize != 0;9080const UINT64 preferredBlockSize = explicitBlockSize ? desc.BlockSize : D3D12MA_DEFAULT_BLOCK_SIZE;9081UINT maxBlockCount = desc.MaxBlockCount != 0 ? desc.MaxBlockCount : UINT_MAX;90829083#ifndef __ID3D12Device4_INTERFACE_DEFINED__9084D3D12MA_ASSERT(m_Desc.pProtectedSession == NULL);9085#endif90869087m_BlockVector = D3D12MA_NEW(allocator->GetAllocs(), BlockVector)(9088allocator, desc.HeapProperties, desc.HeapFlags,9089preferredBlockSize,9090desc.MinBlockCount, maxBlockCount,9091explicitBlockSize,9092D3D12MA_MAX(desc.MinAllocationAlignment, (UINT64)D3D12MA_DEBUG_ALIGNMENT),9093(desc.Flags & POOL_FLAG_ALGORITHM_MASK) != 0,9094(desc.Flags & POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0,9095desc.pProtectedSession,9096desc.ResidencyPriority);9097}90989099PoolPimpl::~PoolPimpl()9100{9101D3D12MA_ASSERT(m_PrevPool == NULL && m_NextPool == NULL);9102FreeName();9103D3D12MA_DELETE(m_Allocator->GetAllocs(), m_BlockVector);9104}91059106HRESULT PoolPimpl::Init()9107{9108m_CommittedAllocations.Init(m_Allocator->UseMutex(), m_Desc.HeapProperties.Type, this);9109return m_BlockVector->CreateMinBlocks();9110}91119112void PoolPimpl::GetStatistics(Statistics& outStats)9113{9114ClearStatistics(outStats);9115m_BlockVector->AddStatistics(outStats);9116m_CommittedAllocations.AddStatistics(outStats);9117}91189119void PoolPimpl::CalculateStatistics(DetailedStatistics& outStats)9120{9121ClearDetailedStatistics(outStats);9122AddDetailedStatistics(outStats);9123}91249125void PoolPimpl::AddDetailedStatistics(DetailedStatistics& inoutStats)9126{9127m_BlockVector->AddDetailedStatistics(inoutStats);9128m_CommittedAllocations.AddDetailedStatistics(inoutStats);9129}91309131void PoolPimpl::SetName(LPCWSTR Name)9132{9133FreeName();91349135if (Name)9136{9137const size_t nameCharCount = wcslen(Name) + 1;9138m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);9139memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));9140}9141}91429143void PoolPimpl::FreeName()9144{9145if (m_Name)9146{9147const size_t nameCharCount = wcslen(m_Name) + 1;9148D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);9149m_Name = NULL;9150}9151}9152#endif // _D3D12MA_POOL_PIMPL_FUNCTIONS915391549155#ifndef _D3D12MA_PUBLIC_INTERFACE9156HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)9157{9158if (!pDesc || !ppAllocator || !pDesc->pDevice || !pDesc->pAdapter ||9159!(pDesc->PreferredBlockSize == 0 || (pDesc->PreferredBlockSize >= 16 && pDesc->PreferredBlockSize < 0x10000000000ull)))9160{9161D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateAllocator.");9162return E_INVALIDARG;9163}91649165D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK91669167ALLOCATION_CALLBACKS allocationCallbacks;9168SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);91699170*ppAllocator = D3D12MA_NEW(allocationCallbacks, Allocator)(allocationCallbacks, *pDesc);9171HRESULT hr = (*ppAllocator)->m_Pimpl->Init(*pDesc);9172if (FAILED(hr))9173{9174D3D12MA_DELETE(allocationCallbacks, *ppAllocator);9175*ppAllocator = NULL;9176}9177return hr;9178}91799180HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock)9181{9182if (!pDesc || !ppVirtualBlock)9183{9184D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateVirtualBlock.");9185return E_INVALIDARG;9186}91879188D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK91899190ALLOCATION_CALLBACKS allocationCallbacks;9191SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);91929193*ppVirtualBlock = D3D12MA_NEW(allocationCallbacks, VirtualBlock)(allocationCallbacks, *pDesc);9194return S_OK;9195}91969197#ifndef _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS9198HRESULT STDMETHODCALLTYPE IUnknownImpl::QueryInterface(REFIID riid, void** ppvObject)9199{9200if (ppvObject == NULL)9201return E_POINTER;9202if (riid == IID_IUnknown)9203{9204++m_RefCount;9205*ppvObject = this;9206return S_OK;9207}9208*ppvObject = NULL;9209return E_NOINTERFACE;9210}92119212ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef()9213{9214return ++m_RefCount;9215}92169217ULONG STDMETHODCALLTYPE IUnknownImpl::Release()9218{9219D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK92209221const uint32_t newRefCount = --m_RefCount;9222if (newRefCount == 0)9223ReleaseThis();9224return newRefCount;9225}9226#endif // _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS92279228#ifndef _D3D12MA_ALLOCATION_FUNCTIONS9229void Allocation::PackedData::SetType(Type type)9230{9231const UINT u = (UINT)type;9232D3D12MA_ASSERT(u < (1u << 2));9233m_Type = u;9234}92359236void Allocation::PackedData::SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension)9237{9238const UINT u = (UINT)resourceDimension;9239D3D12MA_ASSERT(u < (1u << 3));9240m_ResourceDimension = u;9241}92429243void Allocation::PackedData::SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags)9244{9245const UINT u = (UINT)resourceFlags;9246D3D12MA_ASSERT(u < (1u << 24));9247m_ResourceFlags = u;9248}92499250void Allocation::PackedData::SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout)9251{9252const UINT u = (UINT)textureLayout;9253D3D12MA_ASSERT(u < (1u << 9));9254m_TextureLayout = u;9255}92569257UINT64 Allocation::GetOffset() const9258{9259switch (m_PackedData.GetType())9260{9261case TYPE_COMMITTED:9262case TYPE_HEAP:9263return 0;9264case TYPE_PLACED:9265return m_Placed.block->m_pMetadata->GetAllocationOffset(m_Placed.allocHandle);9266default:9267D3D12MA_ASSERT(0);9268return 0;9269}9270}92719272void Allocation::SetResource(ID3D12Resource* pResource)9273{9274if (pResource != m_Resource)9275{9276if (m_Resource)9277m_Resource->Release();9278m_Resource = pResource;9279if (m_Resource)9280m_Resource->AddRef();9281}9282}92839284ID3D12Heap* Allocation::GetHeap() const9285{9286switch (m_PackedData.GetType())9287{9288case TYPE_COMMITTED:9289return NULL;9290case TYPE_PLACED:9291return m_Placed.block->GetHeap();9292case TYPE_HEAP:9293return m_Heap.heap;9294default:9295D3D12MA_ASSERT(0);9296return 0;9297}9298}92999300void Allocation::SetName(LPCWSTR Name)9301{9302FreeName();93039304if (Name)9305{9306const size_t nameCharCount = wcslen(Name) + 1;9307m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);9308memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));9309}9310}93119312void Allocation::ReleaseThis()9313{9314SAFE_RELEASE(m_Resource);93159316switch (m_PackedData.GetType())9317{9318case TYPE_COMMITTED:9319m_Allocator->FreeCommittedMemory(this);9320break;9321case TYPE_PLACED:9322m_Allocator->FreePlacedMemory(this);9323break;9324case TYPE_HEAP:9325m_Allocator->FreeHeapMemory(this);9326break;9327}93289329FreeName();93309331m_Allocator->GetAllocationObjectAllocator().Free(this);9332}93339334Allocation::Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment)9335: m_Allocator{ allocator },9336m_Size{ size },9337m_Alignment{ alignment },9338m_Resource{ NULL },9339m_pPrivateData{ NULL },9340m_Name{ NULL }9341{9342D3D12MA_ASSERT(allocator);93439344m_PackedData.SetType(TYPE_COUNT);9345m_PackedData.SetResourceDimension(D3D12_RESOURCE_DIMENSION_UNKNOWN);9346m_PackedData.SetResourceFlags(D3D12_RESOURCE_FLAG_NONE);9347m_PackedData.SetTextureLayout(D3D12_TEXTURE_LAYOUT_UNKNOWN);9348}93499350void Allocation::InitCommitted(CommittedAllocationList* list)9351{9352m_PackedData.SetType(TYPE_COMMITTED);9353m_Committed.list = list;9354m_Committed.prev = NULL;9355m_Committed.next = NULL;9356}93579358void Allocation::InitPlaced(AllocHandle allocHandle, NormalBlock* block)9359{9360m_PackedData.SetType(TYPE_PLACED);9361m_Placed.allocHandle = allocHandle;9362m_Placed.block = block;9363}93649365void Allocation::InitHeap(CommittedAllocationList* list, ID3D12Heap* heap)9366{9367m_PackedData.SetType(TYPE_HEAP);9368m_Heap.list = list;9369m_Committed.prev = NULL;9370m_Committed.next = NULL;9371m_Heap.heap = heap;9372}93739374void Allocation::SwapBlockAllocation(Allocation* allocation)9375{9376D3D12MA_ASSERT(allocation != NULL);9377D3D12MA_ASSERT(m_PackedData.GetType() == TYPE_PLACED);9378D3D12MA_ASSERT(allocation->m_PackedData.GetType() == TYPE_PLACED);93799380D3D12MA_SWAP(m_Resource, allocation->m_Resource);9381m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, allocation);9382D3D12MA_SWAP(m_Placed, allocation->m_Placed);9383m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, this);9384}93859386AllocHandle Allocation::GetAllocHandle() const9387{9388switch (m_PackedData.GetType())9389{9390case TYPE_COMMITTED:9391case TYPE_HEAP:9392return (AllocHandle)0;9393case TYPE_PLACED:9394return m_Placed.allocHandle;9395default:9396D3D12MA_ASSERT(0);9397return (AllocHandle)0;9398}9399}94009401NormalBlock* Allocation::GetBlock()9402{9403switch (m_PackedData.GetType())9404{9405case TYPE_COMMITTED:9406case TYPE_HEAP:9407return NULL;9408case TYPE_PLACED:9409return m_Placed.block;9410default:9411D3D12MA_ASSERT(0);9412return NULL;9413}9414}94159416template<typename D3D12_RESOURCE_DESC_T>9417void Allocation::SetResourcePointer(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc)9418{9419D3D12MA_ASSERT(m_Resource == NULL && pResourceDesc);9420m_Resource = resource;9421m_PackedData.SetResourceDimension(pResourceDesc->Dimension);9422m_PackedData.SetResourceFlags(pResourceDesc->Flags);9423m_PackedData.SetTextureLayout(pResourceDesc->Layout);9424}94259426void Allocation::FreeName()9427{9428if (m_Name)9429{9430const size_t nameCharCount = wcslen(m_Name) + 1;9431D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);9432m_Name = NULL;9433}9434}9435#endif // _D3D12MA_ALLOCATION_FUNCTIONS94369437#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS9438HRESULT DefragmentationContext::BeginPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo)9439{9440D3D12MA_ASSERT(pPassInfo);9441return m_Pimpl->DefragmentPassBegin(*pPassInfo);9442}94439444HRESULT DefragmentationContext::EndPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo)9445{9446D3D12MA_ASSERT(pPassInfo);9447return m_Pimpl->DefragmentPassEnd(*pPassInfo);9448}94499450void DefragmentationContext::GetStats(DEFRAGMENTATION_STATS* pStats)9451{9452D3D12MA_ASSERT(pStats);9453m_Pimpl->GetStats(*pStats);9454}94559456void DefragmentationContext::ReleaseThis()9457{9458D3D12MA_DELETE(m_Pimpl->GetAllocs(), this);9459}94609461DefragmentationContext::DefragmentationContext(AllocatorPimpl* allocator,9462const DEFRAGMENTATION_DESC& desc,9463BlockVector* poolVector)9464: m_Pimpl(D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContextPimpl)(allocator, desc, poolVector)) {}94659466DefragmentationContext::~DefragmentationContext()9467{9468D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);9469}9470#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS94719472#ifndef _D3D12MA_POOL_FUNCTIONS9473POOL_DESC Pool::GetDesc() const9474{9475return m_Pimpl->GetDesc();9476}94779478void Pool::GetStatistics(Statistics* pStats)9479{9480D3D12MA_ASSERT(pStats);9481D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9482m_Pimpl->GetStatistics(*pStats);9483}94849485void Pool::CalculateStatistics(DetailedStatistics* pStats)9486{9487D3D12MA_ASSERT(pStats);9488D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9489m_Pimpl->CalculateStatistics(*pStats);9490}94919492void Pool::SetName(LPCWSTR Name)9493{9494D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9495m_Pimpl->SetName(Name);9496}94979498LPCWSTR Pool::GetName() const9499{9500return m_Pimpl->GetName();9501}95029503HRESULT Pool::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext)9504{9505D3D12MA_ASSERT(pDesc && ppContext);95069507// Check for support9508if (m_Pimpl->GetBlockVector()->GetAlgorithm() & POOL_FLAG_ALGORITHM_LINEAR)9509return E_NOINTERFACE;9510if(m_Pimpl->AlwaysCommitted())9511return E_NOINTERFACE;95129513AllocatorPimpl* allocator = m_Pimpl->GetAllocator();9514*ppContext = D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContext)(allocator, *pDesc, m_Pimpl->GetBlockVector());9515return S_OK;9516}95179518void Pool::ReleaseThis()9519{9520D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), this);9521}95229523Pool::Pool(Allocator* allocator, const POOL_DESC& desc)9524: m_Pimpl(D3D12MA_NEW(allocator->m_Pimpl->GetAllocs(), PoolPimpl)(allocator->m_Pimpl, desc)) {}95259526Pool::~Pool()9527{9528m_Pimpl->GetAllocator()->UnregisterPool(this, m_Pimpl->GetDesc().HeapProperties.Type);95299530D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), m_Pimpl);9531}9532#endif // _D3D12MA_POOL_FUNCTIONS95339534#ifndef _D3D12MA_ALLOCATOR_FUNCTIONS9535const D3D12_FEATURE_DATA_D3D12_OPTIONS& Allocator::GetD3D12Options() const9536{9537return m_Pimpl->GetD3D12Options();9538}95399540BOOL Allocator::IsUMA() const9541{9542return m_Pimpl->IsUMA();9543}95449545BOOL Allocator::IsCacheCoherentUMA() const9546{9547return m_Pimpl->IsCacheCoherentUMA();9548}95499550BOOL Allocator::IsGPUUploadHeapSupported() const9551{9552return m_Pimpl->IsGPUUploadHeapSupported();9553}95549555UINT64 Allocator::GetMemoryCapacity(UINT memorySegmentGroup) const9556{9557return m_Pimpl->GetMemoryCapacity(memorySegmentGroup);9558}95599560HRESULT Allocator::CreateResource(9561const ALLOCATION_DESC* pAllocDesc,9562const D3D12_RESOURCE_DESC* pResourceDesc,9563D3D12_RESOURCE_STATES InitialResourceState,9564const D3D12_CLEAR_VALUE* pOptimizedClearValue,9565Allocation** ppAllocation,9566REFIID riidResource,9567void** ppvResource)9568{9569if (!pAllocDesc || !pResourceDesc || !ppAllocation)9570{9571D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource.");9572return E_INVALIDARG;9573}9574D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9575return m_Pimpl->CreateResource(9576pAllocDesc,9577CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),9578ppAllocation,9579riidResource,9580ppvResource);9581}95829583#ifdef __ID3D12Device8_INTERFACE_DEFINED__9584HRESULT Allocator::CreateResource2(9585const ALLOCATION_DESC* pAllocDesc,9586const D3D12_RESOURCE_DESC1* pResourceDesc,9587D3D12_RESOURCE_STATES InitialResourceState,9588const D3D12_CLEAR_VALUE* pOptimizedClearValue,9589Allocation** ppAllocation,9590REFIID riidResource,9591void** ppvResource)9592{9593if (!pAllocDesc || !pResourceDesc || !ppAllocation)9594{9595D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource2.");9596return E_INVALIDARG;9597}9598D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9599return m_Pimpl->CreateResource(9600pAllocDesc,9601CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),9602ppAllocation,9603riidResource,9604ppvResource);9605}9606#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__96079608#ifdef __ID3D12Device10_INTERFACE_DEFINED__9609HRESULT Allocator::CreateResource3(9610const ALLOCATION_DESC* pAllocDesc,9611const D3D12_RESOURCE_DESC1* pResourceDesc,9612D3D12_BARRIER_LAYOUT InitialLayout,9613const D3D12_CLEAR_VALUE* pOptimizedClearValue,9614UINT32 NumCastableFormats,9615DXGI_FORMAT* pCastableFormats,9616Allocation** ppAllocation,9617REFIID riidResource,9618void** ppvResource)9619{9620if (!pAllocDesc || !pResourceDesc || !ppAllocation)9621{9622D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource3.");9623return E_INVALIDARG;9624}9625D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9626return m_Pimpl->CreateResource(9627pAllocDesc,9628CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats),9629ppAllocation,9630riidResource,9631ppvResource);9632}9633#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__96349635HRESULT Allocator::AllocateMemory(9636const ALLOCATION_DESC* pAllocDesc,9637const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,9638Allocation** ppAllocation)9639{9640if (!ValidateAllocateMemoryParameters(pAllocDesc, pAllocInfo, ppAllocation))9641{9642D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::AllocateMemory.");9643return E_INVALIDARG;9644}9645D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9646return m_Pimpl->AllocateMemory(pAllocDesc, pAllocInfo, ppAllocation);9647}96489649HRESULT Allocator::CreateAliasingResource(9650Allocation* pAllocation,9651UINT64 AllocationLocalOffset,9652const D3D12_RESOURCE_DESC* pResourceDesc,9653D3D12_RESOURCE_STATES InitialResourceState,9654const D3D12_CLEAR_VALUE* pOptimizedClearValue,9655REFIID riidResource,9656void** ppvResource)9657{9658if (!pAllocation || !pResourceDesc || !ppvResource)9659{9660D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");9661return E_INVALIDARG;9662}9663D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9664return m_Pimpl->CreateAliasingResource(9665pAllocation,9666AllocationLocalOffset,9667CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),9668riidResource,9669ppvResource);9670}96719672#ifdef __ID3D12Device8_INTERFACE_DEFINED__9673HRESULT Allocator::CreateAliasingResource1(9674Allocation* pAllocation,9675UINT64 AllocationLocalOffset,9676const D3D12_RESOURCE_DESC1* pResourceDesc,9677D3D12_RESOURCE_STATES InitialResourceState,9678const D3D12_CLEAR_VALUE* pOptimizedClearValue,9679REFIID riidResource,9680void** ppvResource)9681{9682if (!pAllocation || !pResourceDesc || !ppvResource)9683{9684D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");9685return E_INVALIDARG;9686}9687D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9688return m_Pimpl->CreateAliasingResource(9689pAllocation,9690AllocationLocalOffset,9691CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),9692riidResource,9693ppvResource);9694}9695#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__96969697#ifdef __ID3D12Device10_INTERFACE_DEFINED__9698HRESULT Allocator::CreateAliasingResource2(9699Allocation* pAllocation,9700UINT64 AllocationLocalOffset,9701const D3D12_RESOURCE_DESC1* pResourceDesc,9702D3D12_BARRIER_LAYOUT InitialLayout,9703const D3D12_CLEAR_VALUE* pOptimizedClearValue,9704UINT32 NumCastableFormats,9705DXGI_FORMAT* pCastableFormats,9706REFIID riidResource,9707void** ppvResource)9708{9709if (!pAllocation || !pResourceDesc || !ppvResource)9710{9711D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");9712return E_INVALIDARG;9713}9714D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9715return m_Pimpl->CreateAliasingResource(9716pAllocation,9717AllocationLocalOffset,9718CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats),9719riidResource,9720ppvResource);9721}9722#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__97239724HRESULT Allocator::CreatePool(9725const POOL_DESC* pPoolDesc,9726Pool** ppPool)9727{9728if (!pPoolDesc || !ppPool ||9729(pPoolDesc->MaxBlockCount > 0 && pPoolDesc->MaxBlockCount < pPoolDesc->MinBlockCount) ||9730(pPoolDesc->MinAllocationAlignment > 0 && !IsPow2(pPoolDesc->MinAllocationAlignment)))9731{9732D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreatePool.");9733return E_INVALIDARG;9734}9735if ((pPoolDesc->Flags & POOL_FLAG_ALWAYS_COMMITTED) != 0 &&9736(pPoolDesc->BlockSize != 0 || pPoolDesc->MinBlockCount > 0))9737{9738D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreatePool while POOL_FLAG_ALWAYS_COMMITTED is specified.");9739return E_INVALIDARG;9740}9741if (!m_Pimpl->HeapFlagsFulfillResourceHeapTier(pPoolDesc->HeapFlags))9742{9743D3D12MA_ASSERT(0 && "Invalid pPoolDesc->HeapFlags passed to Allocator::CreatePool. Did you forget to handle ResourceHeapTier=1?");9744return E_INVALIDARG;9745}9746D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9747* ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc);9748HRESULT hr = (*ppPool)->m_Pimpl->Init();9749if (SUCCEEDED(hr))9750{9751m_Pimpl->RegisterPool(*ppPool, pPoolDesc->HeapProperties.Type);9752}9753else9754{9755D3D12MA_DELETE(m_Pimpl->GetAllocs(), *ppPool);9756*ppPool = NULL;9757}9758return hr;9759}97609761void Allocator::SetCurrentFrameIndex(UINT frameIndex)9762{9763D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9764m_Pimpl->SetCurrentFrameIndex(frameIndex);9765}97669767void Allocator::GetBudget(Budget* pLocalBudget, Budget* pNonLocalBudget)9768{9769if (pLocalBudget == NULL && pNonLocalBudget == NULL)9770{9771return;9772}9773D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9774m_Pimpl->GetBudget(pLocalBudget, pNonLocalBudget);9775}97769777void Allocator::CalculateStatistics(TotalStatistics* pStats)9778{9779D3D12MA_ASSERT(pStats);9780D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9781m_Pimpl->CalculateStatistics(*pStats);9782}97839784void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const9785{9786D3D12MA_ASSERT(ppStatsString);9787D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9788m_Pimpl->BuildStatsString(ppStatsString, DetailedMap);9789}97909791void Allocator::FreeStatsString(WCHAR* pStatsString) const9792{9793if (pStatsString != NULL)9794{9795D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9796m_Pimpl->FreeStatsString(pStatsString);9797}9798}97999800void Allocator::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext)9801{9802D3D12MA_ASSERT(pDesc && ppContext);98039804*ppContext = D3D12MA_NEW(m_Pimpl->GetAllocs(), DefragmentationContext)(m_Pimpl, *pDesc, NULL);9805}98069807void Allocator::ReleaseThis()9808{9809// Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.9810const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->GetAllocs();9811D3D12MA_DELETE(allocationCallbacksCopy, this);9812}98139814Allocator::Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc)9815: m_Pimpl(D3D12MA_NEW(allocationCallbacks, AllocatorPimpl)(allocationCallbacks, desc)) {}98169817Allocator::~Allocator()9818{9819D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);9820}9821#endif // _D3D12MA_ALLOCATOR_FUNCTIONS98229823#ifndef _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS9824BOOL VirtualBlock::IsEmpty() const9825{9826D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9827return m_Pimpl->m_Metadata->IsEmpty() ? TRUE : FALSE;9828}98299830void VirtualBlock::GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOCATION_INFO* pInfo) const9831{9832D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0 && pInfo);98339834D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9835m_Pimpl->m_Metadata->GetAllocationInfo(allocation.AllocHandle, *pInfo);9836}98379838HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, VirtualAllocation* pAllocation, UINT64* pOffset)9839{9840if (!pDesc || !pAllocation || pDesc->Size == 0 || !IsPow2(pDesc->Alignment))9841{9842D3D12MA_ASSERT(0 && "Invalid arguments passed to VirtualBlock::Allocate.");9843return E_INVALIDARG;9844}98459846D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK98479848const UINT64 alignment = pDesc->Alignment != 0 ? pDesc->Alignment : 1;9849AllocationRequest allocRequest = {};9850if (m_Pimpl->m_Metadata->CreateAllocationRequest(9851pDesc->Size,9852alignment,9853pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_UPPER_ADDRESS,9854pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_STRATEGY_MASK,9855&allocRequest))9856{9857m_Pimpl->m_Metadata->Alloc(allocRequest, pDesc->Size, pDesc->pPrivateData);9858D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());9859pAllocation->AllocHandle = allocRequest.allocHandle;98609861if (pOffset)9862*pOffset = m_Pimpl->m_Metadata->GetAllocationOffset(allocRequest.allocHandle);9863return S_OK;9864}98659866pAllocation->AllocHandle = (AllocHandle)0;9867if (pOffset)9868*pOffset = UINT64_MAX;98699870return E_OUTOFMEMORY;9871}98729873void VirtualBlock::FreeAllocation(VirtualAllocation allocation)9874{9875if (allocation.AllocHandle == (AllocHandle)0)9876return;98779878D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK98799880m_Pimpl->m_Metadata->Free(allocation.AllocHandle);9881D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());9882}98839884void VirtualBlock::Clear()9885{9886D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK98879888m_Pimpl->m_Metadata->Clear();9889D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());9890}98919892void VirtualBlock::SetAllocationPrivateData(VirtualAllocation allocation, void* pPrivateData)9893{9894D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0);98959896D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9897m_Pimpl->m_Metadata->SetAllocationPrivateData(allocation.AllocHandle, pPrivateData);9898}98999900void VirtualBlock::GetStatistics(Statistics* pStats) const9901{9902D3D12MA_ASSERT(pStats);9903D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9904D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());9905ClearStatistics(*pStats);9906m_Pimpl->m_Metadata->AddStatistics(*pStats);9907}99089909void VirtualBlock::CalculateStatistics(DetailedStatistics* pStats) const9910{9911D3D12MA_ASSERT(pStats);9912D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9913D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());9914ClearDetailedStatistics(*pStats);9915m_Pimpl->m_Metadata->AddDetailedStatistics(*pStats);9916}99179918void VirtualBlock::BuildStatsString(WCHAR** ppStatsString) const9919{9920D3D12MA_ASSERT(ppStatsString);99219922D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK99239924StringBuilder sb(m_Pimpl->m_AllocationCallbacks);9925{9926JsonWriter json(m_Pimpl->m_AllocationCallbacks, sb);9927D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());9928json.BeginObject();9929m_Pimpl->m_Metadata->WriteAllocationInfoToJson(json);9930json.EndObject();9931} // Scope for JsonWriter99329933const size_t length = sb.GetLength();9934WCHAR* result = AllocateArray<WCHAR>(m_Pimpl->m_AllocationCallbacks, length + 1);9935memcpy(result, sb.GetData(), length * sizeof(WCHAR));9936result[length] = L'\0';9937*ppStatsString = result;9938}99399940void VirtualBlock::FreeStatsString(WCHAR* pStatsString) const9941{9942if (pStatsString != NULL)9943{9944D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK9945D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString);9946}9947}99489949void VirtualBlock::ReleaseThis()9950{9951// Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.9952const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->m_AllocationCallbacks;9953D3D12MA_DELETE(allocationCallbacksCopy, this);9954}99559956VirtualBlock::VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc)9957: m_Pimpl(D3D12MA_NEW(allocationCallbacks, VirtualBlockPimpl)(allocationCallbacks, desc)) {}99589959VirtualBlock::~VirtualBlock()9960{9961// THIS IS AN IMPORTANT ASSERT!9962// Hitting it means you have some memory leak - unreleased allocations in this virtual block.9963D3D12MA_ASSERT(m_Pimpl->m_Metadata->IsEmpty() && "Some allocations were not freed before destruction of this virtual block!");99649965D3D12MA_DELETE(m_Pimpl->m_AllocationCallbacks, m_Pimpl);9966}9967#endif // _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS9968#endif // _D3D12MA_PUBLIC_INTERFACE9969} // namespace D3D12MA997099719972