Path: blob/master/thirdparty/d3d12ma/D3D12MemAlloc.cpp
9896 views
//1// Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved.2//3// Permission is hereby granted, free of charge, to any person obtaining a copy4// of this software and associated documentation files (the "Software"), to deal5// in the Software without restriction, including without limitation the rights6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell7// copies of the Software, and to permit persons to whom the Software is8// furnished to do so, subject to the following conditions:9//10// The above copyright notice and this permission notice shall be included in11// all copies or substantial portions of the Software.12//13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN19// THE SOFTWARE.20//2122#include "D3D12MemAlloc.h"2324#include <combaseapi.h>25#include <mutex>26#include <algorithm>27#include <utility>28#include <cstdlib>29#include <cstdint>30#include <malloc.h> // for _aligned_malloc, _aligned_free31#ifndef _WIN3232#include <shared_mutex>33#endif3435#if !defined(_MSC_VER)36#include <guiddef.h>3738#include <dxguids.h>39#endif4041////////////////////////////////////////////////////////////////////////////////42////////////////////////////////////////////////////////////////////////////////43//44// Configuration Begin45//46////////////////////////////////////////////////////////////////////////////////47////////////////////////////////////////////////////////////////////////////////48#ifndef _D3D12MA_CONFIGURATION4950#ifdef _WIN3251#if !defined(WINVER) || WINVER < 0x060052#error Required at least WinAPI version supporting: client = Windows Vista, server = Windows Server 2008.53#endif54#endif5556#ifndef D3D12MA_SORT57#define D3D12MA_SORT(beg, end, cmp) std::sort(beg, end, cmp)58#endif5960#ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED61#include <dxgi.h>62#if D3D12MA_DXGI_1_463#include <dxgi1_4.h>64#endif65#endif6667#ifndef D3D12MA_ASSERT68#include <cassert>69#define D3D12MA_ASSERT(cond) assert(cond)70#endif7172// Assert that will be called very often, like inside data structures e.g. operator[].73// Making it non-empty can make program slow.74#ifndef D3D12MA_HEAVY_ASSERT75#ifdef _DEBUG76#define D3D12MA_HEAVY_ASSERT(expr) //D3D12MA_ASSERT(expr)77#else78#define D3D12MA_HEAVY_ASSERT(expr)79#endif80#endif8182#ifndef D3D12MA_DEBUG_ALIGNMENT83/*84Minimum alignment of all allocations, in bytes.85Set to more than 1 for debugging purposes only. Must be power of two.86*/87#define D3D12MA_DEBUG_ALIGNMENT (1)88#endif8990#ifndef D3D12MA_DEBUG_MARGIN91// Minimum margin before and after every allocation, in bytes.92// Set nonzero for debugging purposes only.93#define D3D12MA_DEBUG_MARGIN (0)94#endif9596#ifndef D3D12MA_DEBUG_GLOBAL_MUTEX97/*98Set this to 1 for debugging purposes only, to enable single mutex protecting all99entry calls to the library. Can be useful for debugging multithreading issues.100*/101#define D3D12MA_DEBUG_GLOBAL_MUTEX (0)102#endif103104/*105Define this macro for debugging purposes only to force specific D3D12_RESOURCE_HEAP_TIER,106especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs.107*/108//#define D3D12MA_FORCE_RESOURCE_HEAP_TIER D3D12_RESOURCE_HEAP_TIER_1109110#ifndef D3D12MA_DEFAULT_BLOCK_SIZE111/// Default size of a block allocated as single ID3D12Heap.112#define D3D12MA_DEFAULT_BLOCK_SIZE (64ull * 1024 * 1024)113#endif114115#ifndef D3D12MA_DEBUG_LOG116#define D3D12MA_DEBUG_LOG(format, ...)117/*118#define D3D12MA_DEBUG_LOG(format, ...) do { \119wprintf(format, __VA_ARGS__); \120wprintf(L"\n"); \121} while(false)122*/123#endif124125#endif // _D3D12MA_CONFIGURATION126////////////////////////////////////////////////////////////////////////////////127////////////////////////////////////////////////////////////////////////////////128//129// Configuration End130//131////////////////////////////////////////////////////////////////////////////////132////////////////////////////////////////////////////////////////////////////////133134#define D3D12MA_IID_PPV_ARGS(ppType) __uuidof(**(ppType)), reinterpret_cast<void**>(ppType)135136#ifdef __ID3D12Device8_INTERFACE_DEFINED__137#define D3D12MA_CREATE_NOT_ZEROED_AVAILABLE 1138#endif139140namespace D3D12MA141{142static constexpr UINT HEAP_TYPE_COUNT = 4;143static constexpr UINT STANDARD_HEAP_TYPE_COUNT = 3; // Only DEFAULT, UPLOAD, READBACK.144static constexpr UINT DEFAULT_POOL_MAX_COUNT = 9;145static const UINT NEW_BLOCK_SIZE_SHIFT_MAX = 3;146// Minimum size of a free suballocation to register it in the free suballocation collection.147static const UINT64 MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;148149static const WCHAR* const HeapTypeNames[] =150{151L"DEFAULT",152L"UPLOAD",153L"READBACK",154L"CUSTOM",155};156157static const D3D12_HEAP_FLAGS RESOURCE_CLASS_HEAP_FLAGS =158D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;159160static const D3D12_RESIDENCY_PRIORITY D3D12_RESIDENCY_PRIORITY_NONE = D3D12_RESIDENCY_PRIORITY(0);161162#ifndef _D3D12MA_ENUM_DECLARATIONS163164// Local copy of this enum, as it is provided only by <dxgi1_4.h>, so it may not be available.165enum DXGI_MEMORY_SEGMENT_GROUP_COPY166{167DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY = 0,168DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY = 1,169DXGI_MEMORY_SEGMENT_GROUP_COUNT170};171172enum class ResourceClass173{174Unknown, Buffer, Non_RT_DS_Texture, RT_DS_Texture175};176177enum SuballocationType178{179SUBALLOCATION_TYPE_FREE = 0,180SUBALLOCATION_TYPE_ALLOCATION = 1,181};182183#endif // _D3D12MA_ENUM_DECLARATIONS184185186#ifndef _D3D12MA_FUNCTIONS187188static void* DefaultAllocate(size_t Size, size_t Alignment, void* /*pPrivateData*/)189{190#ifdef _WIN32191return _aligned_malloc(Size, Alignment);192#else193return aligned_alloc(Alignment, Size);194#endif195}196static void DefaultFree(void* pMemory, void* /*pPrivateData*/)197{198#ifdef _WIN32199return _aligned_free(pMemory);200#else201return free(pMemory);202#endif203}204205static void* Malloc(const ALLOCATION_CALLBACKS& allocs, size_t size, size_t alignment)206{207void* const result = (*allocs.pAllocate)(size, alignment, allocs.pPrivateData);208D3D12MA_ASSERT(result);209return result;210}211static void Free(const ALLOCATION_CALLBACKS& allocs, void* memory)212{213(*allocs.pFree)(memory, allocs.pPrivateData);214}215216template<typename T>217static T* Allocate(const ALLOCATION_CALLBACKS& allocs)218{219return (T*)Malloc(allocs, sizeof(T), __alignof(T));220}221template<typename T>222static T* AllocateArray(const ALLOCATION_CALLBACKS& allocs, size_t count)223{224return (T*)Malloc(allocs, sizeof(T) * count, __alignof(T));225}226227#define D3D12MA_NEW(allocs, type) new(D3D12MA::Allocate<type>(allocs))(type)228#define D3D12MA_NEW_ARRAY(allocs, type, count) new(D3D12MA::AllocateArray<type>((allocs), (count)))(type)229230template<typename T>231void D3D12MA_DELETE(const ALLOCATION_CALLBACKS& allocs, T* memory)232{233if (memory)234{235memory->~T();236Free(allocs, memory);237}238}239template<typename T>240void D3D12MA_DELETE_ARRAY(const ALLOCATION_CALLBACKS& allocs, T* memory, size_t count)241{242if (memory)243{244for (size_t i = count; i--; )245{246memory[i].~T();247}248Free(allocs, memory);249}250}251252static void SetupAllocationCallbacks(ALLOCATION_CALLBACKS& outAllocs, const ALLOCATION_CALLBACKS* allocationCallbacks)253{254if (allocationCallbacks)255{256outAllocs = *allocationCallbacks;257D3D12MA_ASSERT(outAllocs.pAllocate != NULL && outAllocs.pFree != NULL);258}259else260{261outAllocs.pAllocate = &DefaultAllocate;262outAllocs.pFree = &DefaultFree;263outAllocs.pPrivateData = NULL;264}265}266267#define SAFE_RELEASE(ptr) do { if(ptr) { (ptr)->Release(); (ptr) = NULL; } } while(false)268269#define D3D12MA_VALIDATE(cond) do { if(!(cond)) { \270D3D12MA_ASSERT(0 && "Validation failed: " #cond); \271return false; \272} } while(false)273274template<typename T>275static T D3D12MA_MIN(const T& a, const T& b) { return a <= b ? a : b; }276template<typename T>277static T D3D12MA_MAX(const T& a, const T& b) { return a <= b ? b : a; }278279template<typename T>280static void D3D12MA_SWAP(T& a, T& b) { T tmp = a; a = b; b = tmp; }281282// Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX283static UINT8 BitScanLSB(UINT64 mask)284{285#if defined(_MSC_VER) && defined(_WIN64)286unsigned long pos;287if (_BitScanForward64(&pos, mask))288return static_cast<UINT8>(pos);289return UINT8_MAX;290#elif defined __GNUC__ || defined __clang__291return static_cast<UINT8>(__builtin_ffsll(mask)) - 1U;292#else293UINT8 pos = 0;294UINT64 bit = 1;295do296{297if (mask & bit)298return pos;299bit <<= 1;300} while (pos++ < 63);301return UINT8_MAX;302#endif303}304// Scans integer for index of first nonzero bit from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX305static UINT8 BitScanLSB(UINT32 mask)306{307#ifdef _MSC_VER308unsigned long pos;309if (_BitScanForward(&pos, mask))310return static_cast<UINT8>(pos);311return UINT8_MAX;312#elif defined __GNUC__ || defined __clang__313return static_cast<UINT8>(__builtin_ffs(mask)) - 1U;314#else315UINT8 pos = 0;316UINT32 bit = 1;317do318{319if (mask & bit)320return pos;321bit <<= 1;322} while (pos++ < 31);323return UINT8_MAX;324#endif325}326327// Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX328static UINT8 BitScanMSB(UINT64 mask)329{330#if defined(_MSC_VER) && defined(_WIN64)331unsigned long pos;332if (_BitScanReverse64(&pos, mask))333return static_cast<UINT8>(pos);334#elif defined __GNUC__ || defined __clang__335if (mask)336return 63 - static_cast<UINT8>(__builtin_clzll(mask));337#else338UINT8 pos = 63;339UINT64 bit = 1ULL << 63;340do341{342if (mask & bit)343return pos;344bit >>= 1;345} while (pos-- > 0);346#endif347return UINT8_MAX;348}349// Scans integer for index of first nonzero bit from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX350static UINT8 BitScanMSB(UINT32 mask)351{352#ifdef _MSC_VER353unsigned long pos;354if (_BitScanReverse(&pos, mask))355return static_cast<UINT8>(pos);356#elif defined __GNUC__ || defined __clang__357if (mask)358return 31 - static_cast<UINT8>(__builtin_clz(mask));359#else360UINT8 pos = 31;361UINT32 bit = 1UL << 31;362do363{364if (mask & bit)365return pos;366bit >>= 1;367} while (pos-- > 0);368#endif369return UINT8_MAX;370}371372/*373Returns true if given number is a power of two.374T must be unsigned integer number or signed integer but always nonnegative.375For 0 returns true.376*/377template <typename T>378static bool IsPow2(T x) { return (x & (x - 1)) == 0; }379380// Aligns given value up to nearest multiply of align value. For example: AlignUp(11, 8) = 16.381// Use types like UINT, uint64_t as T.382template <typename T>383static T AlignUp(T val, T alignment)384{385D3D12MA_HEAVY_ASSERT(IsPow2(alignment));386return (val + alignment - 1) & ~(alignment - 1);387}388// Aligns given value down to nearest multiply of align value. For example: AlignUp(11, 8) = 8.389// Use types like UINT, uint64_t as T.390template <typename T>391static T AlignDown(T val, T alignment)392{393D3D12MA_HEAVY_ASSERT(IsPow2(alignment));394return val & ~(alignment - 1);395}396397// Division with mathematical rounding to nearest number.398template <typename T>399static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; }400template <typename T>401static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; }402403static WCHAR HexDigitToChar(UINT8 digit)404{405if(digit < 10)406return L'0' + digit;407else408return L'A' + (digit - 10);409}410411/*412Performs binary search and returns iterator to first element that is greater or413equal to `key`, according to comparison `cmp`.414415Cmp should return true if first argument is less than second argument.416417Returned value is the found element, if present in the collection or place where418new element with value (key) should be inserted.419*/420template <typename CmpLess, typename IterT, typename KeyT>421static IterT BinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)422{423size_t down = 0, up = (end - beg);424while (down < up)425{426const size_t mid = (down + up) / 2;427if (cmp(*(beg + mid), key))428{429down = mid + 1;430}431else432{433up = mid;434}435}436return beg + down;437}438439/*440Performs binary search and returns iterator to an element that is equal to `key`,441according to comparison `cmp`.442443Cmp should return true if first argument is less than second argument.444445Returned value is the found element, if present in the collection or end if not446found.447*/448template<typename CmpLess, typename IterT, typename KeyT>449static IterT BinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)450{451IterT it = BinaryFindFirstNotLess<CmpLess, IterT, KeyT>(beg, end, value, cmp);452if (it == end ||453(!cmp(*it, value) && !cmp(value, *it)))454{455return it;456}457return end;458}459460static UINT HeapTypeToIndex(D3D12_HEAP_TYPE type)461{462switch (type)463{464case D3D12_HEAP_TYPE_DEFAULT: return 0;465case D3D12_HEAP_TYPE_UPLOAD: return 1;466case D3D12_HEAP_TYPE_READBACK: return 2;467case D3D12_HEAP_TYPE_CUSTOM: return 3;468default: D3D12MA_ASSERT(0); return UINT_MAX;469}470}471472static D3D12_HEAP_TYPE IndexToHeapType(UINT heapTypeIndex)473{474D3D12MA_ASSERT(heapTypeIndex < 4);475// D3D12_HEAP_TYPE_DEFAULT starts at 1.476return (D3D12_HEAP_TYPE)(heapTypeIndex + 1);477}478479static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags, bool denyMsaaTextures)480{481/*482Documentation of D3D12_HEAP_DESC structure says:483484- D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT defined as 64KB.485- D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT defined as 4MB. An486application must decide whether the heap will contain multi-sample487anti-aliasing (MSAA), in which case, the application must choose [this flag].488489https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_heap_desc490*/491492if (denyMsaaTextures)493return D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;494495const D3D12_HEAP_FLAGS denyAllTexturesFlags =496D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;497const bool canContainAnyTextures =498(flags & denyAllTexturesFlags) != denyAllTexturesFlags;499return canContainAnyTextures ?500D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT : D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;501}502503static ResourceClass HeapFlagsToResourceClass(D3D12_HEAP_FLAGS heapFlags)504{505const bool allowBuffers = (heapFlags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;506const bool allowRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;507const bool allowNonRtDsTextures = (heapFlags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;508509const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);510if (allowedGroupCount != 1)511return ResourceClass::Unknown;512513if (allowRtDsTextures)514return ResourceClass::RT_DS_Texture;515if (allowNonRtDsTextures)516return ResourceClass::Non_RT_DS_Texture;517return ResourceClass::Buffer;518}519520static bool IsHeapTypeStandard(D3D12_HEAP_TYPE type)521{522return type == D3D12_HEAP_TYPE_DEFAULT ||523type == D3D12_HEAP_TYPE_UPLOAD ||524type == D3D12_HEAP_TYPE_READBACK;525}526527static D3D12_HEAP_PROPERTIES StandardHeapTypeToHeapProperties(D3D12_HEAP_TYPE type)528{529D3D12MA_ASSERT(IsHeapTypeStandard(type));530D3D12_HEAP_PROPERTIES result = {};531result.Type = type;532return result;533}534535static bool IsFormatCompressed(DXGI_FORMAT format)536{537switch (format)538{539case DXGI_FORMAT_BC1_TYPELESS:540case DXGI_FORMAT_BC1_UNORM:541case DXGI_FORMAT_BC1_UNORM_SRGB:542case DXGI_FORMAT_BC2_TYPELESS:543case DXGI_FORMAT_BC2_UNORM:544case DXGI_FORMAT_BC2_UNORM_SRGB:545case DXGI_FORMAT_BC3_TYPELESS:546case DXGI_FORMAT_BC3_UNORM:547case DXGI_FORMAT_BC3_UNORM_SRGB:548case DXGI_FORMAT_BC4_TYPELESS:549case DXGI_FORMAT_BC4_UNORM:550case DXGI_FORMAT_BC4_SNORM:551case DXGI_FORMAT_BC5_TYPELESS:552case DXGI_FORMAT_BC5_UNORM:553case DXGI_FORMAT_BC5_SNORM:554case DXGI_FORMAT_BC6H_TYPELESS:555case DXGI_FORMAT_BC6H_UF16:556case DXGI_FORMAT_BC6H_SF16:557case DXGI_FORMAT_BC7_TYPELESS:558case DXGI_FORMAT_BC7_UNORM:559case DXGI_FORMAT_BC7_UNORM_SRGB:560return true;561default:562return false;563}564}565566// Only some formats are supported. For others it returns 0.567static UINT GetBitsPerPixel(DXGI_FORMAT format)568{569switch (format)570{571case DXGI_FORMAT_R32G32B32A32_TYPELESS:572case DXGI_FORMAT_R32G32B32A32_FLOAT:573case DXGI_FORMAT_R32G32B32A32_UINT:574case DXGI_FORMAT_R32G32B32A32_SINT:575return 128;576case DXGI_FORMAT_R32G32B32_TYPELESS:577case DXGI_FORMAT_R32G32B32_FLOAT:578case DXGI_FORMAT_R32G32B32_UINT:579case DXGI_FORMAT_R32G32B32_SINT:580return 96;581case DXGI_FORMAT_R16G16B16A16_TYPELESS:582case DXGI_FORMAT_R16G16B16A16_FLOAT:583case DXGI_FORMAT_R16G16B16A16_UNORM:584case DXGI_FORMAT_R16G16B16A16_UINT:585case DXGI_FORMAT_R16G16B16A16_SNORM:586case DXGI_FORMAT_R16G16B16A16_SINT:587return 64;588case DXGI_FORMAT_R32G32_TYPELESS:589case DXGI_FORMAT_R32G32_FLOAT:590case DXGI_FORMAT_R32G32_UINT:591case DXGI_FORMAT_R32G32_SINT:592return 64;593case DXGI_FORMAT_R32G8X24_TYPELESS:594case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:595case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:596case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:597return 64;598case DXGI_FORMAT_R10G10B10A2_TYPELESS:599case DXGI_FORMAT_R10G10B10A2_UNORM:600case DXGI_FORMAT_R10G10B10A2_UINT:601case DXGI_FORMAT_R11G11B10_FLOAT:602return 32;603case DXGI_FORMAT_R8G8B8A8_TYPELESS:604case DXGI_FORMAT_R8G8B8A8_UNORM:605case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:606case DXGI_FORMAT_R8G8B8A8_UINT:607case DXGI_FORMAT_R8G8B8A8_SNORM:608case DXGI_FORMAT_R8G8B8A8_SINT:609return 32;610case DXGI_FORMAT_R16G16_TYPELESS:611case DXGI_FORMAT_R16G16_FLOAT:612case DXGI_FORMAT_R16G16_UNORM:613case DXGI_FORMAT_R16G16_UINT:614case DXGI_FORMAT_R16G16_SNORM:615case DXGI_FORMAT_R16G16_SINT:616return 32;617case DXGI_FORMAT_R32_TYPELESS:618case DXGI_FORMAT_D32_FLOAT:619case DXGI_FORMAT_R32_FLOAT:620case DXGI_FORMAT_R32_UINT:621case DXGI_FORMAT_R32_SINT:622return 32;623case DXGI_FORMAT_R24G8_TYPELESS:624case DXGI_FORMAT_D24_UNORM_S8_UINT:625case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:626case DXGI_FORMAT_X24_TYPELESS_G8_UINT:627return 32;628case DXGI_FORMAT_R8G8_TYPELESS:629case DXGI_FORMAT_R8G8_UNORM:630case DXGI_FORMAT_R8G8_UINT:631case DXGI_FORMAT_R8G8_SNORM:632case DXGI_FORMAT_R8G8_SINT:633return 16;634case DXGI_FORMAT_R16_TYPELESS:635case DXGI_FORMAT_R16_FLOAT:636case DXGI_FORMAT_D16_UNORM:637case DXGI_FORMAT_R16_UNORM:638case DXGI_FORMAT_R16_UINT:639case DXGI_FORMAT_R16_SNORM:640case DXGI_FORMAT_R16_SINT:641return 16;642case DXGI_FORMAT_R8_TYPELESS:643case DXGI_FORMAT_R8_UNORM:644case DXGI_FORMAT_R8_UINT:645case DXGI_FORMAT_R8_SNORM:646case DXGI_FORMAT_R8_SINT:647case DXGI_FORMAT_A8_UNORM:648return 8;649case DXGI_FORMAT_BC1_TYPELESS:650case DXGI_FORMAT_BC1_UNORM:651case DXGI_FORMAT_BC1_UNORM_SRGB:652return 4;653case DXGI_FORMAT_BC2_TYPELESS:654case DXGI_FORMAT_BC2_UNORM:655case DXGI_FORMAT_BC2_UNORM_SRGB:656return 8;657case DXGI_FORMAT_BC3_TYPELESS:658case DXGI_FORMAT_BC3_UNORM:659case DXGI_FORMAT_BC3_UNORM_SRGB:660return 8;661case DXGI_FORMAT_BC4_TYPELESS:662case DXGI_FORMAT_BC4_UNORM:663case DXGI_FORMAT_BC4_SNORM:664return 4;665case DXGI_FORMAT_BC5_TYPELESS:666case DXGI_FORMAT_BC5_UNORM:667case DXGI_FORMAT_BC5_SNORM:668return 8;669case DXGI_FORMAT_BC6H_TYPELESS:670case DXGI_FORMAT_BC6H_UF16:671case DXGI_FORMAT_BC6H_SF16:672return 8;673case DXGI_FORMAT_BC7_TYPELESS:674case DXGI_FORMAT_BC7_UNORM:675case DXGI_FORMAT_BC7_UNORM_SRGB:676return 8;677default:678return 0;679}680}681682template<typename D3D12_RESOURCE_DESC_T>683static ResourceClass ResourceDescToResourceClass(const D3D12_RESOURCE_DESC_T& resDesc)684{685if (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)686return ResourceClass::Buffer;687// Else: it's surely a texture.688const bool isRenderTargetOrDepthStencil =689(resDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0;690return isRenderTargetOrDepthStencil ? ResourceClass::RT_DS_Texture : ResourceClass::Non_RT_DS_Texture;691}692693// This algorithm is overly conservative.694template<typename D3D12_RESOURCE_DESC_T>695static bool CanUseSmallAlignment(const D3D12_RESOURCE_DESC_T& resourceDesc)696{697if (resourceDesc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D)698return false;699if ((resourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0)700return false;701if (resourceDesc.SampleDesc.Count > 1)702return false;703if (resourceDesc.DepthOrArraySize != 1)704return false;705706UINT sizeX = (UINT)resourceDesc.Width;707UINT sizeY = resourceDesc.Height;708UINT bitsPerPixel = GetBitsPerPixel(resourceDesc.Format);709if (bitsPerPixel == 0)710return false;711712if (IsFormatCompressed(resourceDesc.Format))713{714sizeX = DivideRoundingUp(sizeX, 4u);715sizeY = DivideRoundingUp(sizeY, 4u);716bitsPerPixel *= 16;717}718719UINT tileSizeX = 0, tileSizeY = 0;720switch (bitsPerPixel)721{722case 8: tileSizeX = 64; tileSizeY = 64; break;723case 16: tileSizeX = 64; tileSizeY = 32; break;724case 32: tileSizeX = 32; tileSizeY = 32; break;725case 64: tileSizeX = 32; tileSizeY = 16; break;726case 128: tileSizeX = 16; tileSizeY = 16; break;727default: return false;728}729730const UINT tileCount = DivideRoundingUp(sizeX, tileSizeX) * DivideRoundingUp(sizeY, tileSizeY);731return tileCount <= 16;732}733734static bool ValidateAllocateMemoryParameters(735const ALLOCATION_DESC* pAllocDesc,736const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,737Allocation** ppAllocation)738{739return pAllocDesc &&740pAllocInfo &&741ppAllocation &&742(pAllocInfo->Alignment == 0 ||743pAllocInfo->Alignment == D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT ||744pAllocInfo->Alignment == D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT) &&745pAllocInfo->SizeInBytes != 0 &&746pAllocInfo->SizeInBytes % (64ull * 1024) == 0;747}748749#endif // _D3D12MA_FUNCTIONS750751#ifndef _D3D12MA_STATISTICS_FUNCTIONS752753static void ClearStatistics(Statistics& outStats)754{755outStats.BlockCount = 0;756outStats.AllocationCount = 0;757outStats.BlockBytes = 0;758outStats.AllocationBytes = 0;759}760761static void ClearDetailedStatistics(DetailedStatistics& outStats)762{763ClearStatistics(outStats.Stats);764outStats.UnusedRangeCount = 0;765outStats.AllocationSizeMin = UINT64_MAX;766outStats.AllocationSizeMax = 0;767outStats.UnusedRangeSizeMin = UINT64_MAX;768outStats.UnusedRangeSizeMax = 0;769}770771static void AddStatistics(Statistics& inoutStats, const Statistics& src)772{773inoutStats.BlockCount += src.BlockCount;774inoutStats.AllocationCount += src.AllocationCount;775inoutStats.BlockBytes += src.BlockBytes;776inoutStats.AllocationBytes += src.AllocationBytes;777}778779static void AddDetailedStatistics(DetailedStatistics& inoutStats, const DetailedStatistics& src)780{781AddStatistics(inoutStats.Stats, src.Stats);782inoutStats.UnusedRangeCount += src.UnusedRangeCount;783inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, src.AllocationSizeMin);784inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, src.AllocationSizeMax);785inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, src.UnusedRangeSizeMin);786inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, src.UnusedRangeSizeMax);787}788789static void AddDetailedStatisticsAllocation(DetailedStatistics& inoutStats, UINT64 size)790{791inoutStats.Stats.AllocationCount++;792inoutStats.Stats.AllocationBytes += size;793inoutStats.AllocationSizeMin = D3D12MA_MIN(inoutStats.AllocationSizeMin, size);794inoutStats.AllocationSizeMax = D3D12MA_MAX(inoutStats.AllocationSizeMax, size);795}796797static void AddDetailedStatisticsUnusedRange(DetailedStatistics& inoutStats, UINT64 size)798{799inoutStats.UnusedRangeCount++;800inoutStats.UnusedRangeSizeMin = D3D12MA_MIN(inoutStats.UnusedRangeSizeMin, size);801inoutStats.UnusedRangeSizeMax = D3D12MA_MAX(inoutStats.UnusedRangeSizeMax, size);802}803804#endif // _D3D12MA_STATISTICS_FUNCTIONS805806807#ifndef _D3D12MA_MUTEX808809#ifndef D3D12MA_MUTEX810class Mutex811{812public:813void Lock() { m_Mutex.lock(); }814void Unlock() { m_Mutex.unlock(); }815816private:817std::mutex m_Mutex;818};819#define D3D12MA_MUTEX Mutex820#endif821822#ifndef D3D12MA_RW_MUTEX823#ifdef _WIN32824class RWMutex825{826public:827RWMutex() { InitializeSRWLock(&m_Lock); }828void LockRead() { AcquireSRWLockShared(&m_Lock); }829void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }830void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }831void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }832833private:834SRWLOCK m_Lock;835};836#else // #ifdef _WIN32837class RWMutex838{839public:840RWMutex() {}841void LockRead() { m_Mutex.lock_shared(); }842void UnlockRead() { m_Mutex.unlock_shared(); }843void LockWrite() { m_Mutex.lock(); }844void UnlockWrite() { m_Mutex.unlock(); }845846private:847std::shared_timed_mutex m_Mutex;848};849#endif // #ifdef _WIN32850#define D3D12MA_RW_MUTEX RWMutex851#endif // #ifndef D3D12MA_RW_MUTEX852853// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).854struct MutexLock855{856D3D12MA_CLASS_NO_COPY(MutexLock);857public:858MutexLock(D3D12MA_MUTEX& mutex, bool useMutex = true) :859m_pMutex(useMutex ? &mutex : NULL)860{861if (m_pMutex) m_pMutex->Lock();862}863~MutexLock() { if (m_pMutex) m_pMutex->Unlock(); }864865private:866D3D12MA_MUTEX* m_pMutex;867};868869// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.870struct MutexLockRead871{872D3D12MA_CLASS_NO_COPY(MutexLockRead);873public:874MutexLockRead(D3D12MA_RW_MUTEX& mutex, bool useMutex)875: m_pMutex(useMutex ? &mutex : NULL)876{877if(m_pMutex)878{879m_pMutex->LockRead();880}881}882~MutexLockRead() { if (m_pMutex) m_pMutex->UnlockRead(); }883884private:885D3D12MA_RW_MUTEX* m_pMutex;886};887888// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.889struct MutexLockWrite890{891D3D12MA_CLASS_NO_COPY(MutexLockWrite);892public:893MutexLockWrite(D3D12MA_RW_MUTEX& mutex, bool useMutex)894: m_pMutex(useMutex ? &mutex : NULL)895{896if (m_pMutex) m_pMutex->LockWrite();897}898~MutexLockWrite() { if (m_pMutex) m_pMutex->UnlockWrite(); }899900private:901D3D12MA_RW_MUTEX* m_pMutex;902};903904#if D3D12MA_DEBUG_GLOBAL_MUTEX905static D3D12MA_MUTEX g_DebugGlobalMutex;906#define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK MutexLock debugGlobalMutexLock(g_DebugGlobalMutex, true);907#else908#define D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK909#endif910#endif // _D3D12MA_MUTEX911912#ifndef _D3D12MA_VECTOR913/*914Dynamically resizing continuous array. Class with interface similar to std::vector.915T must be POD because constructors and destructors are not called and memcpy is916used for these objects.917*/918template<typename T>919class Vector920{921public:922using value_type = T;923using iterator = T*;924using const_iterator = const T*;925926// allocationCallbacks externally owned, must outlive this object.927Vector(const ALLOCATION_CALLBACKS& allocationCallbacks);928Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks);929Vector(const Vector<T>& src);930~Vector();931932const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }933bool empty() const { return m_Count == 0; }934size_t size() const { return m_Count; }935T* data() { return m_pArray; }936const T* data() const { return m_pArray; }937void clear(bool freeMemory = false) { resize(0, freeMemory); }938939iterator begin() { return m_pArray; }940iterator end() { return m_pArray + m_Count; }941const_iterator cbegin() const { return m_pArray; }942const_iterator cend() const { return m_pArray + m_Count; }943const_iterator begin() const { return cbegin(); }944const_iterator end() const { return cend(); }945946void push_front(const T& src) { insert(0, src); }947void push_back(const T& src);948void pop_front();949void pop_back();950951T& front();952T& back();953const T& front() const;954const T& back() const;955956void reserve(size_t newCapacity, bool freeMemory = false);957void resize(size_t newCount, bool freeMemory = false);958void insert(size_t index, const T& src);959void remove(size_t index);960961template<typename CmpLess>962size_t InsertSorted(const T& value, const CmpLess& cmp);963template<typename CmpLess>964bool RemoveSorted(const T& value, const CmpLess& cmp);965966Vector& operator=(const Vector<T>& rhs);967T& operator[](size_t index);968const T& operator[](size_t index) const;969970private:971const ALLOCATION_CALLBACKS& m_AllocationCallbacks;972T* m_pArray;973size_t m_Count;974size_t m_Capacity;975};976977#ifndef _D3D12MA_VECTOR_FUNCTIONS978template<typename T>979Vector<T>::Vector(const ALLOCATION_CALLBACKS& allocationCallbacks)980: m_AllocationCallbacks(allocationCallbacks),981m_pArray(NULL),982m_Count(0),983m_Capacity(0) {}984985template<typename T>986Vector<T>::Vector(size_t count, const ALLOCATION_CALLBACKS& allocationCallbacks)987: m_AllocationCallbacks(allocationCallbacks),988m_pArray(count ? AllocateArray<T>(allocationCallbacks, count) : NULL),989m_Count(count),990m_Capacity(count) {}991992template<typename T>993Vector<T>::Vector(const Vector<T>& src)994: m_AllocationCallbacks(src.m_AllocationCallbacks),995m_pArray(src.m_Count ? AllocateArray<T>(src.m_AllocationCallbacks, src.m_Count) : NULL),996m_Count(src.m_Count),997m_Capacity(src.m_Count)998{999if (m_Count > 0)1000{1001memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));1002}1003}10041005template<typename T>1006Vector<T>::~Vector()1007{1008Free(m_AllocationCallbacks, m_pArray);1009}10101011template<typename T>1012void Vector<T>::push_back(const T& src)1013{1014const size_t newIndex = size();1015resize(newIndex + 1);1016m_pArray[newIndex] = src;1017}10181019template<typename T>1020void Vector<T>::pop_front()1021{1022D3D12MA_HEAVY_ASSERT(m_Count > 0);1023remove(0);1024}10251026template<typename T>1027void Vector<T>::pop_back()1028{1029D3D12MA_HEAVY_ASSERT(m_Count > 0);1030resize(size() - 1);1031}10321033template<typename T>1034T& Vector<T>::front()1035{1036D3D12MA_HEAVY_ASSERT(m_Count > 0);1037return m_pArray[0];1038}10391040template<typename T>1041T& Vector<T>::back()1042{1043D3D12MA_HEAVY_ASSERT(m_Count > 0);1044return m_pArray[m_Count - 1];1045}10461047template<typename T>1048const T& Vector<T>::front() const1049{1050D3D12MA_HEAVY_ASSERT(m_Count > 0);1051return m_pArray[0];1052}10531054template<typename T>1055const T& Vector<T>::back() const1056{1057D3D12MA_HEAVY_ASSERT(m_Count > 0);1058return m_pArray[m_Count - 1];1059}10601061template<typename T>1062void Vector<T>::reserve(size_t newCapacity, bool freeMemory)1063{1064newCapacity = D3D12MA_MAX(newCapacity, m_Count);10651066if ((newCapacity < m_Capacity) && !freeMemory)1067{1068newCapacity = m_Capacity;1069}10701071if (newCapacity != m_Capacity)1072{1073T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;1074if (m_Count != 0)1075{1076memcpy(newArray, m_pArray, m_Count * sizeof(T));1077}1078Free(m_AllocationCallbacks, m_pArray);1079m_Capacity = newCapacity;1080m_pArray = newArray;1081}1082}10831084template<typename T>1085void Vector<T>::resize(size_t newCount, bool freeMemory)1086{1087size_t newCapacity = m_Capacity;1088if (newCount > m_Capacity)1089{1090newCapacity = D3D12MA_MAX(newCount, D3D12MA_MAX(m_Capacity * 3 / 2, (size_t)8));1091}1092else if (freeMemory)1093{1094newCapacity = newCount;1095}10961097if (newCapacity != m_Capacity)1098{1099T* const newArray = newCapacity ? AllocateArray<T>(m_AllocationCallbacks, newCapacity) : NULL;1100const size_t elementsToCopy = D3D12MA_MIN(m_Count, newCount);1101if (elementsToCopy != 0)1102{1103memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));1104}1105Free(m_AllocationCallbacks, m_pArray);1106m_Capacity = newCapacity;1107m_pArray = newArray;1108}11091110m_Count = newCount;1111}11121113template<typename T>1114void Vector<T>::insert(size_t index, const T& src)1115{1116D3D12MA_HEAVY_ASSERT(index <= m_Count);1117const size_t oldCount = size();1118resize(oldCount + 1);1119if (index < oldCount)1120{1121memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));1122}1123m_pArray[index] = src;1124}11251126template<typename T>1127void Vector<T>::remove(size_t index)1128{1129D3D12MA_HEAVY_ASSERT(index < m_Count);1130const size_t oldCount = size();1131if (index < oldCount - 1)1132{1133memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));1134}1135resize(oldCount - 1);1136}11371138template<typename T> template<typename CmpLess>1139size_t Vector<T>::InsertSorted(const T& value, const CmpLess& cmp)1140{1141const size_t indexToInsert = BinaryFindFirstNotLess<CmpLess, iterator, T>(1142m_pArray,1143m_pArray + m_Count,1144value,1145cmp) - m_pArray;1146insert(indexToInsert, value);1147return indexToInsert;1148}11491150template<typename T> template<typename CmpLess>1151bool Vector<T>::RemoveSorted(const T& value, const CmpLess& cmp)1152{1153const iterator it = BinaryFindFirstNotLess(1154m_pArray,1155m_pArray + m_Count,1156value,1157cmp);1158if ((it != end()) && !cmp(*it, value) && !cmp(value, *it))1159{1160size_t indexToRemove = it - begin();1161remove(indexToRemove);1162return true;1163}1164return false;1165}11661167template<typename T>1168Vector<T>& Vector<T>::operator=(const Vector<T>& rhs)1169{1170if (&rhs != this)1171{1172resize(rhs.m_Count);1173if (m_Count != 0)1174{1175memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));1176}1177}1178return *this;1179}11801181template<typename T>1182T& Vector<T>::operator[](size_t index)1183{1184D3D12MA_HEAVY_ASSERT(index < m_Count);1185return m_pArray[index];1186}11871188template<typename T>1189const T& Vector<T>::operator[](size_t index) const1190{1191D3D12MA_HEAVY_ASSERT(index < m_Count);1192return m_pArray[index];1193}1194#endif // _D3D12MA_VECTOR_FUNCTIONS1195#endif // _D3D12MA_VECTOR11961197#ifndef _D3D12MA_STRING_BUILDER1198class StringBuilder1199{1200public:1201StringBuilder(const ALLOCATION_CALLBACKS& allocationCallbacks) : m_Data(allocationCallbacks) {}12021203size_t GetLength() const { return m_Data.size(); }1204LPCWSTR GetData() const { return m_Data.data(); }12051206void Add(WCHAR ch) { m_Data.push_back(ch); }1207void Add(LPCWSTR str);1208void AddNewLine() { Add(L'\n'); }1209void AddNumber(UINT num);1210void AddNumber(UINT64 num);1211void AddPointer(const void* ptr);12121213private:1214Vector<WCHAR> m_Data;1215};12161217#ifndef _D3D12MA_STRING_BUILDER_FUNCTIONS1218void StringBuilder::Add(LPCWSTR str)1219{1220const size_t len = wcslen(str);1221if (len > 0)1222{1223const size_t oldCount = m_Data.size();1224m_Data.resize(oldCount + len);1225memcpy(m_Data.data() + oldCount, str, len * sizeof(WCHAR));1226}1227}12281229void StringBuilder::AddNumber(UINT num)1230{1231WCHAR buf[11];1232buf[10] = L'\0';1233WCHAR *p = &buf[10];1234do1235{1236*--p = L'0' + (num % 10);1237num /= 10;1238}1239while (num);1240Add(p);1241}12421243void StringBuilder::AddNumber(UINT64 num)1244{1245WCHAR buf[21];1246buf[20] = L'\0';1247WCHAR *p = &buf[20];1248do1249{1250*--p = L'0' + (num % 10);1251num /= 10;1252}1253while (num);1254Add(p);1255}12561257void StringBuilder::AddPointer(const void* ptr)1258{1259WCHAR buf[21];1260uintptr_t num = (uintptr_t)ptr;1261buf[20] = L'\0';1262WCHAR *p = &buf[20];1263do1264{1265*--p = HexDigitToChar((UINT8)(num & 0xF));1266num >>= 4;1267}1268while (num);1269Add(p);1270}12711272#endif // _D3D12MA_STRING_BUILDER_FUNCTIONS1273#endif // _D3D12MA_STRING_BUILDER12741275#ifndef _D3D12MA_JSON_WRITER1276/*1277Allows to conveniently build a correct JSON document to be written to the1278StringBuilder passed to the constructor.1279*/1280class JsonWriter1281{1282public:1283// stringBuilder - string builder to write the document to. Must remain alive for the whole lifetime of this object.1284JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder);1285~JsonWriter();12861287// Begins object by writing "{".1288// Inside an object, you must call pairs of WriteString and a value, e.g.:1289// j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();1290// Will write: { "A": 1, "B": 2 }1291void BeginObject(bool singleLine = false);1292// Ends object by writing "}".1293void EndObject();12941295// Begins array by writing "[".1296// Inside an array, you can write a sequence of any values.1297void BeginArray(bool singleLine = false);1298// Ends array by writing "[".1299void EndArray();13001301// Writes a string value inside "".1302// pStr can contain any UTF-16 characters, including '"', new line etc. - they will be properly escaped.1303void WriteString(LPCWSTR pStr);13041305// Begins writing a string value.1306// Call BeginString, ContinueString, ContinueString, ..., EndString instead of1307// WriteString to conveniently build the string content incrementally, made of1308// parts including numbers.1309void BeginString(LPCWSTR pStr = NULL);1310// Posts next part of an open string.1311void ContinueString(LPCWSTR pStr);1312// Posts next part of an open string. The number is converted to decimal characters.1313void ContinueString(UINT num);1314void ContinueString(UINT64 num);1315void ContinueString_Pointer(const void* ptr);1316// Posts next part of an open string. Pointer value is converted to characters1317// using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad001318// void ContinueString_Pointer(const void* ptr);1319// Ends writing a string value by writing '"'.1320void EndString(LPCWSTR pStr = NULL);13211322// Writes a number value.1323void WriteNumber(UINT num);1324void WriteNumber(UINT64 num);1325// Writes a boolean value - false or true.1326void WriteBool(bool b);1327// Writes a null value.1328void WriteNull();13291330void AddAllocationToObject(const Allocation& alloc);1331void AddDetailedStatisticsInfoObject(const DetailedStatistics& stats);13321333private:1334static const WCHAR* const INDENT;13351336enum CollectionType1337{1338COLLECTION_TYPE_OBJECT,1339COLLECTION_TYPE_ARRAY,1340};1341struct StackItem1342{1343CollectionType type;1344UINT valueCount;1345bool singleLineMode;1346};13471348StringBuilder& m_SB;1349Vector<StackItem> m_Stack;1350bool m_InsideString;13511352void BeginValue(bool isString);1353void WriteIndent(bool oneLess = false);1354};13551356#ifndef _D3D12MA_JSON_WRITER_FUNCTIONS1357const WCHAR* const JsonWriter::INDENT = L" ";13581359JsonWriter::JsonWriter(const ALLOCATION_CALLBACKS& allocationCallbacks, StringBuilder& stringBuilder)1360: m_SB(stringBuilder),1361m_Stack(allocationCallbacks),1362m_InsideString(false) {}13631364JsonWriter::~JsonWriter()1365{1366D3D12MA_ASSERT(!m_InsideString);1367D3D12MA_ASSERT(m_Stack.empty());1368}13691370void JsonWriter::BeginObject(bool singleLine)1371{1372D3D12MA_ASSERT(!m_InsideString);13731374BeginValue(false);1375m_SB.Add(L'{');13761377StackItem stackItem;1378stackItem.type = COLLECTION_TYPE_OBJECT;1379stackItem.valueCount = 0;1380stackItem.singleLineMode = singleLine;1381m_Stack.push_back(stackItem);1382}13831384void JsonWriter::EndObject()1385{1386D3D12MA_ASSERT(!m_InsideString);1387D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);1388D3D12MA_ASSERT(m_Stack.back().valueCount % 2 == 0);13891390WriteIndent(true);1391m_SB.Add(L'}');13921393m_Stack.pop_back();1394}13951396void JsonWriter::BeginArray(bool singleLine)1397{1398D3D12MA_ASSERT(!m_InsideString);13991400BeginValue(false);1401m_SB.Add(L'[');14021403StackItem stackItem;1404stackItem.type = COLLECTION_TYPE_ARRAY;1405stackItem.valueCount = 0;1406stackItem.singleLineMode = singleLine;1407m_Stack.push_back(stackItem);1408}14091410void JsonWriter::EndArray()1411{1412D3D12MA_ASSERT(!m_InsideString);1413D3D12MA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);14141415WriteIndent(true);1416m_SB.Add(L']');14171418m_Stack.pop_back();1419}14201421void JsonWriter::WriteString(LPCWSTR pStr)1422{1423BeginString(pStr);1424EndString();1425}14261427void JsonWriter::BeginString(LPCWSTR pStr)1428{1429D3D12MA_ASSERT(!m_InsideString);14301431BeginValue(true);1432m_InsideString = true;1433m_SB.Add(L'"');1434if (pStr != NULL)1435{1436ContinueString(pStr);1437}1438}14391440void JsonWriter::ContinueString(LPCWSTR pStr)1441{1442D3D12MA_ASSERT(m_InsideString);1443D3D12MA_ASSERT(pStr);14441445for (const WCHAR *p = pStr; *p; ++p)1446{1447// the strings we encode are assumed to be in UTF-16LE format, the native1448// windows wide character Unicode format. In this encoding Unicode code1449// points U+0000 to U+D7FF and U+E000 to U+FFFF are encoded in two bytes,1450// and everything else takes more than two bytes. We will reject any1451// multi wchar character encodings for simplicity.1452UINT val = (UINT)*p;1453D3D12MA_ASSERT(((val <= 0xD7FF) || (0xE000 <= val && val <= 0xFFFF)) &&1454"Character not currently supported.");1455switch (*p)1456{1457case L'"': m_SB.Add(L'\\'); m_SB.Add(L'"'); break;1458case L'\\': m_SB.Add(L'\\'); m_SB.Add(L'\\'); break;1459case L'/': m_SB.Add(L'\\'); m_SB.Add(L'/'); break;1460case L'\b': m_SB.Add(L'\\'); m_SB.Add(L'b'); break;1461case L'\f': m_SB.Add(L'\\'); m_SB.Add(L'f'); break;1462case L'\n': m_SB.Add(L'\\'); m_SB.Add(L'n'); break;1463case L'\r': m_SB.Add(L'\\'); m_SB.Add(L'r'); break;1464case L'\t': m_SB.Add(L'\\'); m_SB.Add(L't'); break;1465default:1466// conservatively use encoding \uXXXX for any Unicode character1467// requiring more than one byte.1468if (32 <= val && val < 256)1469m_SB.Add(*p);1470else1471{1472m_SB.Add(L'\\');1473m_SB.Add(L'u');1474for (UINT i = 0; i < 4; ++i)1475{1476UINT hexDigit = (val & 0xF000) >> 12;1477val <<= 4;1478if (hexDigit < 10)1479m_SB.Add(L'0' + (WCHAR)hexDigit);1480else1481m_SB.Add(L'A' + (WCHAR)hexDigit);1482}1483}1484break;1485}1486}1487}14881489void JsonWriter::ContinueString(UINT num)1490{1491D3D12MA_ASSERT(m_InsideString);1492m_SB.AddNumber(num);1493}14941495void JsonWriter::ContinueString(UINT64 num)1496{1497D3D12MA_ASSERT(m_InsideString);1498m_SB.AddNumber(num);1499}15001501void JsonWriter::ContinueString_Pointer(const void* ptr)1502{1503D3D12MA_ASSERT(m_InsideString);1504m_SB.AddPointer(ptr);1505}15061507void JsonWriter::EndString(LPCWSTR pStr)1508{1509D3D12MA_ASSERT(m_InsideString);15101511if (pStr)1512ContinueString(pStr);1513m_SB.Add(L'"');1514m_InsideString = false;1515}15161517void JsonWriter::WriteNumber(UINT num)1518{1519D3D12MA_ASSERT(!m_InsideString);1520BeginValue(false);1521m_SB.AddNumber(num);1522}15231524void JsonWriter::WriteNumber(UINT64 num)1525{1526D3D12MA_ASSERT(!m_InsideString);1527BeginValue(false);1528m_SB.AddNumber(num);1529}15301531void JsonWriter::WriteBool(bool b)1532{1533D3D12MA_ASSERT(!m_InsideString);1534BeginValue(false);1535if (b)1536m_SB.Add(L"true");1537else1538m_SB.Add(L"false");1539}15401541void JsonWriter::WriteNull()1542{1543D3D12MA_ASSERT(!m_InsideString);1544BeginValue(false);1545m_SB.Add(L"null");1546}15471548void JsonWriter::AddAllocationToObject(const Allocation& alloc)1549{1550WriteString(L"Type");1551switch (alloc.m_PackedData.GetResourceDimension()) {1552case D3D12_RESOURCE_DIMENSION_UNKNOWN:1553WriteString(L"UNKNOWN");1554break;1555case D3D12_RESOURCE_DIMENSION_BUFFER:1556WriteString(L"BUFFER");1557break;1558case D3D12_RESOURCE_DIMENSION_TEXTURE1D:1559WriteString(L"TEXTURE1D");1560break;1561case D3D12_RESOURCE_DIMENSION_TEXTURE2D:1562WriteString(L"TEXTURE2D");1563break;1564case D3D12_RESOURCE_DIMENSION_TEXTURE3D:1565WriteString(L"TEXTURE3D");1566break;1567default: D3D12MA_ASSERT(0); break;1568}15691570WriteString(L"Size");1571WriteNumber(alloc.GetSize());1572WriteString(L"Usage");1573WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags());15741575void* privateData = alloc.GetPrivateData();1576if (privateData)1577{1578WriteString(L"CustomData");1579BeginString();1580ContinueString_Pointer(privateData);1581EndString();1582}15831584LPCWSTR name = alloc.GetName();1585if (name != NULL)1586{1587WriteString(L"Name");1588WriteString(name);1589}1590if (alloc.m_PackedData.GetTextureLayout())1591{1592WriteString(L"Layout");1593WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout());1594}1595}15961597void JsonWriter::AddDetailedStatisticsInfoObject(const DetailedStatistics& stats)1598{1599BeginObject();16001601WriteString(L"BlockCount");1602WriteNumber(stats.Stats.BlockCount);1603WriteString(L"BlockBytes");1604WriteNumber(stats.Stats.BlockBytes);1605WriteString(L"AllocationCount");1606WriteNumber(stats.Stats.AllocationCount);1607WriteString(L"AllocationBytes");1608WriteNumber(stats.Stats.AllocationBytes);1609WriteString(L"UnusedRangeCount");1610WriteNumber(stats.UnusedRangeCount);16111612if (stats.Stats.AllocationCount > 1)1613{1614WriteString(L"AllocationSizeMin");1615WriteNumber(stats.AllocationSizeMin);1616WriteString(L"AllocationSizeMax");1617WriteNumber(stats.AllocationSizeMax);1618}1619if (stats.UnusedRangeCount > 1)1620{1621WriteString(L"UnusedRangeSizeMin");1622WriteNumber(stats.UnusedRangeSizeMin);1623WriteString(L"UnusedRangeSizeMax");1624WriteNumber(stats.UnusedRangeSizeMax);1625}1626EndObject();1627}16281629void JsonWriter::BeginValue(bool isString)1630{1631if (!m_Stack.empty())1632{1633StackItem& currItem = m_Stack.back();1634if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 0)1635{1636D3D12MA_ASSERT(isString);1637}16381639if (currItem.type == COLLECTION_TYPE_OBJECT && currItem.valueCount % 2 == 1)1640{1641m_SB.Add(L':'); m_SB.Add(L' ');1642}1643else if (currItem.valueCount > 0)1644{1645m_SB.Add(L','); m_SB.Add(L' ');1646WriteIndent();1647}1648else1649{1650WriteIndent();1651}1652++currItem.valueCount;1653}1654}16551656void JsonWriter::WriteIndent(bool oneLess)1657{1658if (!m_Stack.empty() && !m_Stack.back().singleLineMode)1659{1660m_SB.AddNewLine();16611662size_t count = m_Stack.size();1663if (count > 0 && oneLess)1664{1665--count;1666}1667for (size_t i = 0; i < count; ++i)1668{1669m_SB.Add(INDENT);1670}1671}1672}1673#endif // _D3D12MA_JSON_WRITER_FUNCTIONS1674#endif // _D3D12MA_JSON_WRITER16751676#ifndef _D3D12MA_POOL_ALLOCATOR1677/*1678Allocator for objects of type T using a list of arrays (pools) to speed up1679allocation. Number of elements that can be allocated is not bounded because1680allocator can create multiple blocks.1681T should be POD because constructor and destructor is not called in Alloc or1682Free.1683*/1684template<typename T>1685class PoolAllocator1686{1687D3D12MA_CLASS_NO_COPY(PoolAllocator)1688public:1689// allocationCallbacks externally owned, must outlive this object.1690PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity);1691~PoolAllocator() { Clear(); }16921693void Clear();1694template<typename... Types>1695T* Alloc(Types... args);1696void Free(T* ptr);16971698private:1699union Item1700{1701UINT NextFreeIndex; // UINT32_MAX means end of list.1702alignas(T) char Value[sizeof(T)];1703};17041705struct ItemBlock1706{1707Item* pItems;1708UINT Capacity;1709UINT FirstFreeIndex;1710};17111712const ALLOCATION_CALLBACKS& m_AllocationCallbacks;1713const UINT m_FirstBlockCapacity;1714Vector<ItemBlock> m_ItemBlocks;17151716ItemBlock& CreateNewBlock();1717};17181719#ifndef _D3D12MA_POOL_ALLOCATOR_FUNCTIONS1720template<typename T>1721PoolAllocator<T>::PoolAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks, UINT firstBlockCapacity)1722: m_AllocationCallbacks(allocationCallbacks),1723m_FirstBlockCapacity(firstBlockCapacity),1724m_ItemBlocks(allocationCallbacks)1725{1726D3D12MA_ASSERT(m_FirstBlockCapacity > 1);1727}17281729template<typename T>1730void PoolAllocator<T>::Clear()1731{1732for(size_t i = m_ItemBlocks.size(); i--; )1733{1734D3D12MA_DELETE_ARRAY(m_AllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);1735}1736m_ItemBlocks.clear(true);1737}17381739template<typename T> template<typename... Types>1740T* PoolAllocator<T>::Alloc(Types... args)1741{1742for(size_t i = m_ItemBlocks.size(); i--; )1743{1744ItemBlock& block = m_ItemBlocks[i];1745// This block has some free items: Use first one.1746if(block.FirstFreeIndex != UINT32_MAX)1747{1748Item* const pItem = &block.pItems[block.FirstFreeIndex];1749block.FirstFreeIndex = pItem->NextFreeIndex;1750T* result = (T*)&pItem->Value;1751new(result)T(std::forward<Types>(args)...); // Explicit constructor call.1752return result;1753}1754}17551756// No block has free item: Create new one and use it.1757ItemBlock& newBlock = CreateNewBlock();1758Item* const pItem = &newBlock.pItems[0];1759newBlock.FirstFreeIndex = pItem->NextFreeIndex;1760T* result = (T*)pItem->Value;1761new(result)T(std::forward<Types>(args)...); // Explicit constructor call.1762return result;1763}17641765template<typename T>1766void PoolAllocator<T>::Free(T* ptr)1767{1768// Search all memory blocks to find ptr.1769for(size_t i = m_ItemBlocks.size(); i--; )1770{1771ItemBlock& block = m_ItemBlocks[i];17721773Item* pItemPtr;1774memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));17751776// Check if pItemPtr is in address range of this block.1777if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))1778{1779ptr->~T(); // Explicit destructor call.1780const UINT index = static_cast<UINT>(pItemPtr - block.pItems);1781pItemPtr->NextFreeIndex = block.FirstFreeIndex;1782block.FirstFreeIndex = index;1783return;1784}1785}1786D3D12MA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");1787}17881789template<typename T>1790typename PoolAllocator<T>::ItemBlock& PoolAllocator<T>::CreateNewBlock()1791{1792const UINT newBlockCapacity = m_ItemBlocks.empty() ?1793m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;17941795const ItemBlock newBlock = {1796D3D12MA_NEW_ARRAY(m_AllocationCallbacks, Item, newBlockCapacity),1797newBlockCapacity,17980 };17991800m_ItemBlocks.push_back(newBlock);18011802// Setup singly-linked list of all free items in this block.1803for(UINT i = 0; i < newBlockCapacity - 1; ++i)1804{1805newBlock.pItems[i].NextFreeIndex = i + 1;1806}1807newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;1808return m_ItemBlocks.back();1809}1810#endif // _D3D12MA_POOL_ALLOCATOR_FUNCTIONS1811#endif // _D3D12MA_POOL_ALLOCATOR18121813#ifndef _D3D12MA_LIST1814/*1815Doubly linked list, with elements allocated out of PoolAllocator.1816Has custom interface, as well as STL-style interface, including iterator and1817const_iterator.1818*/1819template<typename T>1820class List1821{1822D3D12MA_CLASS_NO_COPY(List)1823public:1824struct Item1825{1826Item* pPrev;1827Item* pNext;1828T Value;1829};18301831class reverse_iterator;1832class const_reverse_iterator;1833class iterator1834{1835friend class List<T>;1836friend class const_iterator;18371838public:1839iterator() = default;1840iterator(const reverse_iterator& src)1841: m_pList(src.m_pList), m_pItem(src.m_pItem) {}18421843T& operator*() const;1844T* operator->() const;18451846iterator& operator++();1847iterator& operator--();1848iterator operator++(int);1849iterator operator--(int);18501851bool operator==(const iterator& rhs) const;1852bool operator!=(const iterator& rhs) const;18531854private:1855List<T>* m_pList = NULL;1856Item* m_pItem = NULL;18571858iterator(List<T>* pList, Item* pItem) : m_pList(pList), m_pItem(pItem) {}1859};18601861class reverse_iterator1862{1863friend class List<T>;1864friend class const_reverse_iterator;18651866public:1867reverse_iterator() = default;1868reverse_iterator(const iterator& src)1869: m_pList(src.m_pList), m_pItem(src.m_pItem) {}18701871T& operator*() const;1872T* operator->() const;18731874reverse_iterator& operator++();1875reverse_iterator& operator--();1876reverse_iterator operator++(int);1877reverse_iterator operator--(int);18781879bool operator==(const reverse_iterator& rhs) const;1880bool operator!=(const reverse_iterator& rhs) const;18811882private:1883List<T>* m_pList = NULL;1884Item* m_pItem = NULL;18851886reverse_iterator(List<T>* pList, Item* pItem)1887: m_pList(pList), m_pItem(pItem) {}1888};18891890class const_iterator1891{1892friend class List<T>;18931894public:1895const_iterator() = default;1896const_iterator(const iterator& src)1897: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1898const_iterator(const reverse_iterator& src)1899: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1900const_iterator(const const_reverse_iterator& src)1901: m_pList(src.m_pList), m_pItem(src.m_pItem) {}19021903iterator dropConst() const;1904const T& operator*() const;1905const T* operator->() const;19061907const_iterator& operator++();1908const_iterator& operator--();1909const_iterator operator++(int);1910const_iterator operator--(int);19111912bool operator==(const const_iterator& rhs) const;1913bool operator!=(const const_iterator& rhs) const;19141915private:1916const List<T>* m_pList = NULL;1917const Item* m_pItem = NULL;19181919const_iterator(const List<T>* pList, const Item* pItem)1920: m_pList(pList), m_pItem(pItem) {}1921};19221923class const_reverse_iterator1924{1925friend class List<T>;19261927public:1928const_reverse_iterator() = default;1929const_reverse_iterator(const iterator& src)1930: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1931const_reverse_iterator(const reverse_iterator& src)1932: m_pList(src.m_pList), m_pItem(src.m_pItem) {}1933const_reverse_iterator(const const_iterator& src)1934: m_pList(src.m_pList), m_pItem(src.m_pItem) {}19351936reverse_iterator dropConst() const;1937const T& operator*() const;1938const T* operator->() const;19391940const_reverse_iterator& operator++();1941const_reverse_iterator& operator--();1942const_reverse_iterator operator++(int);1943const_reverse_iterator operator--(int);19441945bool operator==(const const_reverse_iterator& rhs) const;1946bool operator!=(const const_reverse_iterator& rhs) const;19471948private:1949const List<T>* m_pList = NULL;1950const Item* m_pItem = NULL;19511952const_reverse_iterator(const List<T>* pList, const Item* pItem)1953: m_pList(pList), m_pItem(pItem) {}1954};19551956// allocationCallbacks externally owned, must outlive this object.1957List(const ALLOCATION_CALLBACKS& allocationCallbacks);1958// Intentionally not calling Clear, because that would be unnecessary1959// computations to return all items to m_ItemAllocator as free.1960~List() = default;19611962size_t GetCount() const { return m_Count; }1963bool IsEmpty() const { return m_Count == 0; }19641965Item* Front() { return m_pFront; }1966const Item* Front() const { return m_pFront; }1967Item* Back() { return m_pBack; }1968const Item* Back() const { return m_pBack; }19691970bool empty() const { return IsEmpty(); }1971size_t size() const { return GetCount(); }1972void push_back(const T& value) { PushBack(value); }1973iterator insert(iterator it, const T& value) { return iterator(this, InsertBefore(it.m_pItem, value)); }1974void clear() { Clear(); }1975void erase(iterator it) { Remove(it.m_pItem); }19761977iterator begin() { return iterator(this, Front()); }1978iterator end() { return iterator(this, NULL); }1979reverse_iterator rbegin() { return reverse_iterator(this, Back()); }1980reverse_iterator rend() { return reverse_iterator(this, NULL); }19811982const_iterator cbegin() const { return const_iterator(this, Front()); }1983const_iterator cend() const { return const_iterator(this, NULL); }1984const_iterator begin() const { return cbegin(); }1985const_iterator end() const { return cend(); }19861987const_reverse_iterator crbegin() const { return const_reverse_iterator(this, Back()); }1988const_reverse_iterator crend() const { return const_reverse_iterator(this, NULL); }1989const_reverse_iterator rbegin() const { return crbegin(); }1990const_reverse_iterator rend() const { return crend(); }19911992Item* PushBack();1993Item* PushFront();1994Item* PushBack(const T& value);1995Item* PushFront(const T& value);1996void PopBack();1997void PopFront();19981999// Item can be null - it means PushBack.2000Item* InsertBefore(Item* pItem);2001// Item can be null - it means PushFront.2002Item* InsertAfter(Item* pItem);2003Item* InsertBefore(Item* pItem, const T& value);2004Item* InsertAfter(Item* pItem, const T& value);20052006void Clear();2007void Remove(Item* pItem);20082009private:2010const ALLOCATION_CALLBACKS& m_AllocationCallbacks;2011PoolAllocator<Item> m_ItemAllocator;2012Item* m_pFront;2013Item* m_pBack;2014size_t m_Count;2015};20162017#ifndef _D3D12MA_LIST_ITERATOR_FUNCTIONS2018template<typename T>2019T& List<T>::iterator::operator*() const2020{2021D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2022return m_pItem->Value;2023}20242025template<typename T>2026T* List<T>::iterator::operator->() const2027{2028D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2029return &m_pItem->Value;2030}20312032template<typename T>2033typename List<T>::iterator& List<T>::iterator::operator++()2034{2035D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2036m_pItem = m_pItem->pNext;2037return *this;2038}20392040template<typename T>2041typename List<T>::iterator& List<T>::iterator::operator--()2042{2043if (m_pItem != NULL)2044{2045m_pItem = m_pItem->pPrev;2046}2047else2048{2049D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2050m_pItem = m_pList->Back();2051}2052return *this;2053}20542055template<typename T>2056typename List<T>::iterator List<T>::iterator::operator++(int)2057{2058iterator result = *this;2059++* this;2060return result;2061}20622063template<typename T>2064typename List<T>::iterator List<T>::iterator::operator--(int)2065{2066iterator result = *this;2067--* this;2068return result;2069}20702071template<typename T>2072bool List<T>::iterator::operator==(const iterator& rhs) const2073{2074D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2075return m_pItem == rhs.m_pItem;2076}20772078template<typename T>2079bool List<T>::iterator::operator!=(const iterator& rhs) const2080{2081D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2082return m_pItem != rhs.m_pItem;2083}2084#endif // _D3D12MA_LIST_ITERATOR_FUNCTIONS20852086#ifndef _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS2087template<typename T>2088T& List<T>::reverse_iterator::operator*() const2089{2090D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2091return m_pItem->Value;2092}20932094template<typename T>2095T* List<T>::reverse_iterator::operator->() const2096{2097D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2098return &m_pItem->Value;2099}21002101template<typename T>2102typename List<T>::reverse_iterator& List<T>::reverse_iterator::operator++()2103{2104D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2105m_pItem = m_pItem->pPrev;2106return *this;2107}21082109template<typename T>2110typename List<T>::reverse_iterator& List<T>::reverse_iterator::operator--()2111{2112if (m_pItem != NULL)2113{2114m_pItem = m_pItem->pNext;2115}2116else2117{2118D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2119m_pItem = m_pList->Front();2120}2121return *this;2122}21232124template<typename T>2125typename List<T>::reverse_iterator List<T>::reverse_iterator::operator++(int)2126{2127reverse_iterator result = *this;2128++* this;2129return result;2130}21312132template<typename T>2133typename List<T>::reverse_iterator List<T>::reverse_iterator::operator--(int)2134{2135reverse_iterator result = *this;2136--* this;2137return result;2138}21392140template<typename T>2141bool List<T>::reverse_iterator::operator==(const reverse_iterator& rhs) const2142{2143D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2144return m_pItem == rhs.m_pItem;2145}21462147template<typename T>2148bool List<T>::reverse_iterator::operator!=(const reverse_iterator& rhs) const2149{2150D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2151return m_pItem != rhs.m_pItem;2152}2153#endif // _D3D12MA_LIST_REVERSE_ITERATOR_FUNCTIONS21542155#ifndef _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS2156template<typename T>2157typename List<T>::iterator List<T>::const_iterator::dropConst() const2158{2159return iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));2160}21612162template<typename T>2163const T& List<T>::const_iterator::operator*() const2164{2165D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2166return m_pItem->Value;2167}21682169template<typename T>2170const T* List<T>::const_iterator::operator->() const2171{2172D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2173return &m_pItem->Value;2174}21752176template<typename T>2177typename List<T>::const_iterator& List<T>::const_iterator::operator++()2178{2179D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2180m_pItem = m_pItem->pNext;2181return *this;2182}21832184template<typename T>2185typename List<T>::const_iterator& List<T>::const_iterator::operator--()2186{2187if (m_pItem != NULL)2188{2189m_pItem = m_pItem->pPrev;2190}2191else2192{2193D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2194m_pItem = m_pList->Back();2195}2196return *this;2197}21982199template<typename T>2200typename List<T>::const_iterator List<T>::const_iterator::operator++(int)2201{2202const_iterator result = *this;2203++* this;2204return result;2205}22062207template<typename T>2208typename List<T>::const_iterator List<T>::const_iterator::operator--(int)2209{2210const_iterator result = *this;2211--* this;2212return result;2213}22142215template<typename T>2216bool List<T>::const_iterator::operator==(const const_iterator& rhs) const2217{2218D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2219return m_pItem == rhs.m_pItem;2220}22212222template<typename T>2223bool List<T>::const_iterator::operator!=(const const_iterator& rhs) const2224{2225D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2226return m_pItem != rhs.m_pItem;2227}2228#endif // _D3D12MA_LIST_CONST_ITERATOR_FUNCTIONS22292230#ifndef _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS2231template<typename T>2232typename List<T>::reverse_iterator List<T>::const_reverse_iterator::dropConst() const2233{2234return reverse_iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));2235}22362237template<typename T>2238const T& List<T>::const_reverse_iterator::operator*() const2239{2240D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2241return m_pItem->Value;2242}22432244template<typename T>2245const T* List<T>::const_reverse_iterator::operator->() const2246{2247D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2248return &m_pItem->Value;2249}22502251template<typename T>2252typename List<T>::const_reverse_iterator& List<T>::const_reverse_iterator::operator++()2253{2254D3D12MA_HEAVY_ASSERT(m_pItem != NULL);2255m_pItem = m_pItem->pPrev;2256return *this;2257}22582259template<typename T>2260typename List<T>::const_reverse_iterator& List<T>::const_reverse_iterator::operator--()2261{2262if (m_pItem != NULL)2263{2264m_pItem = m_pItem->pNext;2265}2266else2267{2268D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());2269m_pItem = m_pList->Front();2270}2271return *this;2272}22732274template<typename T>2275typename List<T>::const_reverse_iterator List<T>::const_reverse_iterator::operator++(int)2276{2277const_reverse_iterator result = *this;2278++* this;2279return result;2280}22812282template<typename T>2283typename List<T>::const_reverse_iterator List<T>::const_reverse_iterator::operator--(int)2284{2285const_reverse_iterator result = *this;2286--* this;2287return result;2288}22892290template<typename T>2291bool List<T>::const_reverse_iterator::operator==(const const_reverse_iterator& rhs) const2292{2293D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2294return m_pItem == rhs.m_pItem;2295}22962297template<typename T>2298bool List<T>::const_reverse_iterator::operator!=(const const_reverse_iterator& rhs) const2299{2300D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);2301return m_pItem != rhs.m_pItem;2302}2303#endif // _D3D12MA_LIST_CONST_REVERSE_ITERATOR_FUNCTIONS23042305#ifndef _D3D12MA_LIST_FUNCTIONS2306template<typename T>2307List<T>::List(const ALLOCATION_CALLBACKS& allocationCallbacks)2308: m_AllocationCallbacks(allocationCallbacks),2309m_ItemAllocator(allocationCallbacks, 128),2310m_pFront(NULL),2311m_pBack(NULL),2312m_Count(0) {}23132314template<typename T>2315void List<T>::Clear()2316{2317if(!IsEmpty())2318{2319Item* pItem = m_pBack;2320while(pItem != NULL)2321{2322Item* const pPrevItem = pItem->pPrev;2323m_ItemAllocator.Free(pItem);2324pItem = pPrevItem;2325}2326m_pFront = NULL;2327m_pBack = NULL;2328m_Count = 0;2329}2330}23312332template<typename T>2333typename List<T>::Item* List<T>::PushBack()2334{2335Item* const pNewItem = m_ItemAllocator.Alloc();2336pNewItem->pNext = NULL;2337if(IsEmpty())2338{2339pNewItem->pPrev = NULL;2340m_pFront = pNewItem;2341m_pBack = pNewItem;2342m_Count = 1;2343}2344else2345{2346pNewItem->pPrev = m_pBack;2347m_pBack->pNext = pNewItem;2348m_pBack = pNewItem;2349++m_Count;2350}2351return pNewItem;2352}23532354template<typename T>2355typename List<T>::Item* List<T>::PushFront()2356{2357Item* const pNewItem = m_ItemAllocator.Alloc();2358pNewItem->pPrev = NULL;2359if(IsEmpty())2360{2361pNewItem->pNext = NULL;2362m_pFront = pNewItem;2363m_pBack = pNewItem;2364m_Count = 1;2365}2366else2367{2368pNewItem->pNext = m_pFront;2369m_pFront->pPrev = pNewItem;2370m_pFront = pNewItem;2371++m_Count;2372}2373return pNewItem;2374}23752376template<typename T>2377typename List<T>::Item* List<T>::PushBack(const T& value)2378{2379Item* const pNewItem = PushBack();2380pNewItem->Value = value;2381return pNewItem;2382}23832384template<typename T>2385typename List<T>::Item* List<T>::PushFront(const T& value)2386{2387Item* const pNewItem = PushFront();2388pNewItem->Value = value;2389return pNewItem;2390}23912392template<typename T>2393void List<T>::PopBack()2394{2395D3D12MA_HEAVY_ASSERT(m_Count > 0);2396Item* const pBackItem = m_pBack;2397Item* const pPrevItem = pBackItem->pPrev;2398if(pPrevItem != NULL)2399{2400pPrevItem->pNext = NULL;2401}2402m_pBack = pPrevItem;2403m_ItemAllocator.Free(pBackItem);2404--m_Count;2405}24062407template<typename T>2408void List<T>::PopFront()2409{2410D3D12MA_HEAVY_ASSERT(m_Count > 0);2411Item* const pFrontItem = m_pFront;2412Item* const pNextItem = pFrontItem->pNext;2413if(pNextItem != NULL)2414{2415pNextItem->pPrev = NULL;2416}2417m_pFront = pNextItem;2418m_ItemAllocator.Free(pFrontItem);2419--m_Count;2420}24212422template<typename T>2423void List<T>::Remove(Item* pItem)2424{2425D3D12MA_HEAVY_ASSERT(pItem != NULL);2426D3D12MA_HEAVY_ASSERT(m_Count > 0);24272428if(pItem->pPrev != NULL)2429{2430pItem->pPrev->pNext = pItem->pNext;2431}2432else2433{2434D3D12MA_HEAVY_ASSERT(m_pFront == pItem);2435m_pFront = pItem->pNext;2436}24372438if(pItem->pNext != NULL)2439{2440pItem->pNext->pPrev = pItem->pPrev;2441}2442else2443{2444D3D12MA_HEAVY_ASSERT(m_pBack == pItem);2445m_pBack = pItem->pPrev;2446}24472448m_ItemAllocator.Free(pItem);2449--m_Count;2450}24512452template<typename T>2453typename List<T>::Item* List<T>::InsertBefore(Item* pItem)2454{2455if(pItem != NULL)2456{2457Item* const prevItem = pItem->pPrev;2458Item* const newItem = m_ItemAllocator.Alloc();2459newItem->pPrev = prevItem;2460newItem->pNext = pItem;2461pItem->pPrev = newItem;2462if(prevItem != NULL)2463{2464prevItem->pNext = newItem;2465}2466else2467{2468D3D12MA_HEAVY_ASSERT(m_pFront == pItem);2469m_pFront = newItem;2470}2471++m_Count;2472return newItem;2473}2474else2475{2476return PushBack();2477}2478}24792480template<typename T>2481typename List<T>::Item* List<T>::InsertAfter(Item* pItem)2482{2483if(pItem != NULL)2484{2485Item* const nextItem = pItem->pNext;2486Item* const newItem = m_ItemAllocator.Alloc();2487newItem->pNext = nextItem;2488newItem->pPrev = pItem;2489pItem->pNext = newItem;2490if(nextItem != NULL)2491{2492nextItem->pPrev = newItem;2493}2494else2495{2496D3D12MA_HEAVY_ASSERT(m_pBack == pItem);2497m_pBack = newItem;2498}2499++m_Count;2500return newItem;2501}2502else2503return PushFront();2504}25052506template<typename T>2507typename List<T>::Item* List<T>::InsertBefore(Item* pItem, const T& value)2508{2509Item* const newItem = InsertBefore(pItem);2510newItem->Value = value;2511return newItem;2512}25132514template<typename T>2515typename List<T>::Item* List<T>::InsertAfter(Item* pItem, const T& value)2516{2517Item* const newItem = InsertAfter(pItem);2518newItem->Value = value;2519return newItem;2520}2521#endif // _D3D12MA_LIST_FUNCTIONS2522#endif // _D3D12MA_LIST25232524#ifndef _D3D12MA_INTRUSIVE_LINKED_LIST2525/*2526Expected interface of ItemTypeTraits:2527struct MyItemTypeTraits2528{2529using ItemType = MyItem;2530static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }2531static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }2532static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }2533static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }2534};2535*/2536template<typename ItemTypeTraits>2537class IntrusiveLinkedList2538{2539public:2540using ItemType = typename ItemTypeTraits::ItemType;2541static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }2542static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }25432544// Movable, not copyable.2545IntrusiveLinkedList() = default;2546IntrusiveLinkedList(const IntrusiveLinkedList&) = delete;2547IntrusiveLinkedList(IntrusiveLinkedList&& src);2548IntrusiveLinkedList& operator=(const IntrusiveLinkedList&) = delete;2549IntrusiveLinkedList& operator=(IntrusiveLinkedList&& src);2550~IntrusiveLinkedList() { D3D12MA_HEAVY_ASSERT(IsEmpty()); }25512552size_t GetCount() const { return m_Count; }2553bool IsEmpty() const { return m_Count == 0; }25542555ItemType* Front() { return m_Front; }2556ItemType* Back() { return m_Back; }2557const ItemType* Front() const { return m_Front; }2558const ItemType* Back() const { return m_Back; }25592560void PushBack(ItemType* item);2561void PushFront(ItemType* item);2562ItemType* PopBack();2563ItemType* PopFront();25642565// MyItem can be null - it means PushBack.2566void InsertBefore(ItemType* existingItem, ItemType* newItem);2567// MyItem can be null - it means PushFront.2568void InsertAfter(ItemType* existingItem, ItemType* newItem);25692570void Remove(ItemType* item);2571void RemoveAll();25722573private:2574ItemType* m_Front = NULL;2575ItemType* m_Back = NULL;2576size_t m_Count = 0;2577};25782579#ifndef _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS2580template<typename ItemTypeTraits>2581IntrusiveLinkedList<ItemTypeTraits>::IntrusiveLinkedList(IntrusiveLinkedList&& src)2582: m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)2583{2584src.m_Front = src.m_Back = NULL;2585src.m_Count = 0;2586}25872588template<typename ItemTypeTraits>2589IntrusiveLinkedList<ItemTypeTraits>& IntrusiveLinkedList<ItemTypeTraits>::operator=(IntrusiveLinkedList&& src)2590{2591if (&src != this)2592{2593D3D12MA_HEAVY_ASSERT(IsEmpty());2594m_Front = src.m_Front;2595m_Back = src.m_Back;2596m_Count = src.m_Count;2597src.m_Front = src.m_Back = NULL;2598src.m_Count = 0;2599}2600return *this;2601}26022603template<typename ItemTypeTraits>2604void IntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)2605{2606D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);2607if (IsEmpty())2608{2609m_Front = item;2610m_Back = item;2611m_Count = 1;2612}2613else2614{2615ItemTypeTraits::AccessPrev(item) = m_Back;2616ItemTypeTraits::AccessNext(m_Back) = item;2617m_Back = item;2618++m_Count;2619}2620}26212622template<typename ItemTypeTraits>2623void IntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)2624{2625D3D12MA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == NULL && ItemTypeTraits::GetNext(item) == NULL);2626if (IsEmpty())2627{2628m_Front = item;2629m_Back = item;2630m_Count = 1;2631}2632else2633{2634ItemTypeTraits::AccessNext(item) = m_Front;2635ItemTypeTraits::AccessPrev(m_Front) = item;2636m_Front = item;2637++m_Count;2638}2639}26402641template<typename ItemTypeTraits>2642typename IntrusiveLinkedList<ItemTypeTraits>::ItemType* IntrusiveLinkedList<ItemTypeTraits>::PopBack()2643{2644D3D12MA_HEAVY_ASSERT(m_Count > 0);2645ItemType* const backItem = m_Back;2646ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);2647if (prevItem != NULL)2648{2649ItemTypeTraits::AccessNext(prevItem) = NULL;2650}2651m_Back = prevItem;2652--m_Count;2653ItemTypeTraits::AccessPrev(backItem) = NULL;2654ItemTypeTraits::AccessNext(backItem) = NULL;2655return backItem;2656}26572658template<typename ItemTypeTraits>2659typename IntrusiveLinkedList<ItemTypeTraits>::ItemType* IntrusiveLinkedList<ItemTypeTraits>::PopFront()2660{2661D3D12MA_HEAVY_ASSERT(m_Count > 0);2662ItemType* const frontItem = m_Front;2663ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);2664if (nextItem != NULL)2665{2666ItemTypeTraits::AccessPrev(nextItem) = NULL;2667}2668m_Front = nextItem;2669--m_Count;2670ItemTypeTraits::AccessPrev(frontItem) = NULL;2671ItemTypeTraits::AccessNext(frontItem) = NULL;2672return frontItem;2673}26742675template<typename ItemTypeTraits>2676void IntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)2677{2678D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);2679if (existingItem != NULL)2680{2681ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);2682ItemTypeTraits::AccessPrev(newItem) = prevItem;2683ItemTypeTraits::AccessNext(newItem) = existingItem;2684ItemTypeTraits::AccessPrev(existingItem) = newItem;2685if (prevItem != NULL)2686{2687ItemTypeTraits::AccessNext(prevItem) = newItem;2688}2689else2690{2691D3D12MA_HEAVY_ASSERT(m_Front == existingItem);2692m_Front = newItem;2693}2694++m_Count;2695}2696else2697PushBack(newItem);2698}26992700template<typename ItemTypeTraits>2701void IntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)2702{2703D3D12MA_HEAVY_ASSERT(newItem != NULL && ItemTypeTraits::GetPrev(newItem) == NULL && ItemTypeTraits::GetNext(newItem) == NULL);2704if (existingItem != NULL)2705{2706ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);2707ItemTypeTraits::AccessNext(newItem) = nextItem;2708ItemTypeTraits::AccessPrev(newItem) = existingItem;2709ItemTypeTraits::AccessNext(existingItem) = newItem;2710if (nextItem != NULL)2711{2712ItemTypeTraits::AccessPrev(nextItem) = newItem;2713}2714else2715{2716D3D12MA_HEAVY_ASSERT(m_Back == existingItem);2717m_Back = newItem;2718}2719++m_Count;2720}2721else2722return PushFront(newItem);2723}27242725template<typename ItemTypeTraits>2726void IntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)2727{2728D3D12MA_HEAVY_ASSERT(item != NULL && m_Count > 0);2729if (ItemTypeTraits::GetPrev(item) != NULL)2730{2731ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);2732}2733else2734{2735D3D12MA_HEAVY_ASSERT(m_Front == item);2736m_Front = ItemTypeTraits::GetNext(item);2737}27382739if (ItemTypeTraits::GetNext(item) != NULL)2740{2741ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);2742}2743else2744{2745D3D12MA_HEAVY_ASSERT(m_Back == item);2746m_Back = ItemTypeTraits::GetPrev(item);2747}2748ItemTypeTraits::AccessPrev(item) = NULL;2749ItemTypeTraits::AccessNext(item) = NULL;2750--m_Count;2751}27522753template<typename ItemTypeTraits>2754void IntrusiveLinkedList<ItemTypeTraits>::RemoveAll()2755{2756if (!IsEmpty())2757{2758ItemType* item = m_Back;2759while (item != NULL)2760{2761ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);2762ItemTypeTraits::AccessPrev(item) = NULL;2763ItemTypeTraits::AccessNext(item) = NULL;2764item = prevItem;2765}2766m_Front = NULL;2767m_Back = NULL;2768m_Count = 0;2769}2770}2771#endif // _D3D12MA_INTRUSIVE_LINKED_LIST_FUNCTIONS2772#endif // _D3D12MA_INTRUSIVE_LINKED_LIST27732774#ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR2775/*2776Thread-safe wrapper over PoolAllocator free list, for allocation of Allocation objects.2777*/2778class AllocationObjectAllocator2779{2780D3D12MA_CLASS_NO_COPY(AllocationObjectAllocator);2781public:2782AllocationObjectAllocator(const ALLOCATION_CALLBACKS& allocationCallbacks)2783: m_Allocator(allocationCallbacks, 1024) {}27842785template<typename... Types>2786Allocation* Allocate(Types... args);2787void Free(Allocation* alloc);27882789private:2790D3D12MA_MUTEX m_Mutex;2791PoolAllocator<Allocation> m_Allocator;2792};27932794#ifndef _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS2795template<typename... Types>2796Allocation* AllocationObjectAllocator::Allocate(Types... args)2797{2798MutexLock mutexLock(m_Mutex);2799return m_Allocator.Alloc(std::forward<Types>(args)...);2800}28012802void AllocationObjectAllocator::Free(Allocation* alloc)2803{2804MutexLock mutexLock(m_Mutex);2805m_Allocator.Free(alloc);2806}2807#endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR_FUNCTIONS2808#endif // _D3D12MA_ALLOCATION_OBJECT_ALLOCATOR28092810#ifndef _D3D12MA_SUBALLOCATION2811/*2812Represents a region of NormalBlock that is either assigned and returned as2813allocated memory block or free.2814*/2815struct Suballocation2816{2817UINT64 offset;2818UINT64 size;2819void* privateData;2820SuballocationType type;2821};2822using SuballocationList = List<Suballocation>;28232824// Comparator for offsets.2825struct SuballocationOffsetLess2826{2827bool operator()(const Suballocation& lhs, const Suballocation& rhs) const2828{2829return lhs.offset < rhs.offset;2830}2831};28322833struct SuballocationOffsetGreater2834{2835bool operator()(const Suballocation& lhs, const Suballocation& rhs) const2836{2837return lhs.offset > rhs.offset;2838}2839};28402841struct SuballocationItemSizeLess2842{2843bool operator()(const SuballocationList::iterator lhs, const SuballocationList::iterator rhs) const2844{2845return lhs->size < rhs->size;2846}2847bool operator()(const SuballocationList::iterator lhs, UINT64 rhsSize) const2848{2849return lhs->size < rhsSize;2850}2851};2852#endif // _D3D12MA_SUBALLOCATION28532854#ifndef _D3D12MA_ALLOCATION_REQUEST2855/*2856Parameters of planned allocation inside a NormalBlock.2857*/2858struct AllocationRequest2859{2860AllocHandle allocHandle;2861UINT64 size;2862UINT64 algorithmData;2863UINT64 sumFreeSize; // Sum size of free items that overlap with proposed allocation.2864UINT64 sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.2865SuballocationList::iterator item;2866BOOL zeroInitialized = FALSE; // TODO Implement proper handling in TLSF and Linear, using ZeroInitializedRange class.2867};2868#endif // _D3D12MA_ALLOCATION_REQUEST28692870#ifndef _D3D12MA_ZERO_INITIALIZED_RANGE2871/*2872Keeps track of the range of bytes that are surely initialized with zeros.2873Everything outside of it is considered uninitialized memory that may contain2874garbage data.28752876The range is left-inclusive.2877*/2878class ZeroInitializedRange2879{2880public:2881void Reset(UINT64 size);2882BOOL IsRangeZeroInitialized(UINT64 beg, UINT64 end) const;2883void MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd);28842885private:2886UINT64 m_ZeroBeg = 0, m_ZeroEnd = 0;2887};28882889#ifndef _D3D12MA_ZERO_INITIALIZED_RANGE_FUNCTIONS2890void ZeroInitializedRange::Reset(UINT64 size)2891{2892D3D12MA_ASSERT(size > 0);2893m_ZeroBeg = 0;2894m_ZeroEnd = size;2895}28962897BOOL ZeroInitializedRange::IsRangeZeroInitialized(UINT64 beg, UINT64 end) const2898{2899D3D12MA_ASSERT(beg < end);2900return m_ZeroBeg <= beg && end <= m_ZeroEnd;2901}29022903void ZeroInitializedRange::MarkRangeAsUsed(UINT64 usedBeg, UINT64 usedEnd)2904{2905D3D12MA_ASSERT(usedBeg < usedEnd);2906// No new bytes marked.2907if (usedEnd <= m_ZeroBeg || m_ZeroEnd <= usedBeg)2908{2909return;2910}2911// All bytes marked.2912if (usedBeg <= m_ZeroBeg && m_ZeroEnd <= usedEnd)2913{2914m_ZeroBeg = m_ZeroEnd = 0;2915}2916// Some bytes marked.2917else2918{2919const UINT64 remainingZeroBefore = usedBeg > m_ZeroBeg ? usedBeg - m_ZeroBeg : 0;2920const UINT64 remainingZeroAfter = usedEnd < m_ZeroEnd ? m_ZeroEnd - usedEnd : 0;2921D3D12MA_ASSERT(remainingZeroBefore > 0 || remainingZeroAfter > 0);2922if (remainingZeroBefore > remainingZeroAfter)2923{2924m_ZeroEnd = usedBeg;2925}2926else2927{2928m_ZeroBeg = usedEnd;2929}2930}2931}2932#endif // _D3D12MA_ZERO_INITIALIZED_RANGE_FUNCTIONS2933#endif // _D3D12MA_ZERO_INITIALIZED_RANGE29342935#ifndef _D3D12MA_BLOCK_METADATA2936/*2937Data structure used for bookkeeping of allocations and unused ranges of memory2938in a single ID3D12Heap memory block.2939*/2940class BlockMetadata2941{2942public:2943BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);2944virtual ~BlockMetadata() = default;29452946virtual void Init(UINT64 size) { m_Size = size; }2947// Validates all data structures inside this object. If not valid, returns false.2948virtual bool Validate() const = 0;2949UINT64 GetSize() const { return m_Size; }2950bool IsVirtual() const { return m_IsVirtual; }2951virtual size_t GetAllocationCount() const = 0;2952virtual size_t GetFreeRegionsCount() const = 0;2953virtual UINT64 GetSumFreeSize() const = 0;2954virtual UINT64 GetAllocationOffset(AllocHandle allocHandle) const = 0;2955// Returns true if this block is empty - contains only single free suballocation.2956virtual bool IsEmpty() const = 0;29572958virtual void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const = 0;29592960// Tries to find a place for suballocation with given parameters inside this block.2961// If succeeded, fills pAllocationRequest and returns true.2962// If failed, returns false.2963virtual bool CreateAllocationRequest(2964UINT64 allocSize,2965UINT64 allocAlignment,2966bool upperAddress,2967UINT32 strategy,2968AllocationRequest* pAllocationRequest) = 0;29692970// Makes actual allocation based on request. Request must already be checked and valid.2971virtual void Alloc(2972const AllocationRequest& request,2973UINT64 allocSize,2974void* PrivateData) = 0;29752976virtual void Free(AllocHandle allocHandle) = 0;2977// Frees all allocations.2978// Careful! Don't call it if there are Allocation objects owned by pPrivateData of of cleared allocations!2979virtual void Clear() = 0;29802981virtual AllocHandle GetAllocationListBegin() const = 0;2982virtual AllocHandle GetNextAllocation(AllocHandle prevAlloc) const = 0;2983virtual UINT64 GetNextFreeRegionSize(AllocHandle alloc) const = 0;2984virtual void* GetAllocationPrivateData(AllocHandle allocHandle) const = 0;2985virtual void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) = 0;29862987virtual void AddStatistics(Statistics& inoutStats) const = 0;2988virtual void AddDetailedStatistics(DetailedStatistics& inoutStats) const = 0;2989virtual void WriteAllocationInfoToJson(JsonWriter& json) const = 0;2990virtual void DebugLogAllAllocations() const = 0;29912992protected:2993const ALLOCATION_CALLBACKS* GetAllocs() const { return m_pAllocationCallbacks; }2994UINT64 GetDebugMargin() const { return IsVirtual() ? 0 : D3D12MA_DEBUG_MARGIN; }29952996void DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const;2997void PrintDetailedMap_Begin(JsonWriter& json,2998UINT64 unusedBytes,2999size_t allocationCount,3000size_t unusedRangeCount) const;3001void PrintDetailedMap_Allocation(JsonWriter& json,3002UINT64 offset, UINT64 size, void* privateData) const;3003void PrintDetailedMap_UnusedRange(JsonWriter& json,3004UINT64 offset, UINT64 size) const;3005void PrintDetailedMap_End(JsonWriter& json) const;30063007private:3008UINT64 m_Size;3009bool m_IsVirtual;3010const ALLOCATION_CALLBACKS* m_pAllocationCallbacks;30113012D3D12MA_CLASS_NO_COPY(BlockMetadata);3013};30143015#ifndef _D3D12MA_BLOCK_METADATA_FUNCTIONS3016BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)3017: m_Size(0),3018m_IsVirtual(isVirtual),3019m_pAllocationCallbacks(allocationCallbacks)3020{3021D3D12MA_ASSERT(allocationCallbacks);3022}30233024void BlockMetadata::DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const3025{3026if (IsVirtual())3027{3028D3D12MA_DEBUG_LOG(L"UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p", offset, size, privateData);3029}3030else3031{3032D3D12MA_ASSERT(privateData != NULL);3033Allocation* allocation = reinterpret_cast<Allocation*>(privateData);30343035privateData = allocation->GetPrivateData();3036LPCWSTR name = allocation->GetName();30373038D3D12MA_DEBUG_LOG(L"UNFREED ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p; Name: %s",3039offset, size, privateData, name ? name : L"D3D12MA_Empty");3040}3041}30423043void BlockMetadata::PrintDetailedMap_Begin(JsonWriter& json,3044UINT64 unusedBytes, size_t allocationCount, size_t unusedRangeCount) const3045{3046json.WriteString(L"TotalBytes");3047json.WriteNumber(GetSize());30483049json.WriteString(L"UnusedBytes");3050json.WriteNumber(unusedBytes);30513052json.WriteString(L"Allocations");3053json.WriteNumber((UINT64)allocationCount);30543055json.WriteString(L"UnusedRanges");3056json.WriteNumber((UINT64)unusedRangeCount);30573058json.WriteString(L"Suballocations");3059json.BeginArray();3060}30613062void BlockMetadata::PrintDetailedMap_Allocation(JsonWriter& json,3063UINT64 offset, UINT64 size, void* privateData) const3064{3065json.BeginObject(true);30663067json.WriteString(L"Offset");3068json.WriteNumber(offset);30693070if (IsVirtual())3071{3072json.WriteString(L"Size");3073json.WriteNumber(size);3074if (privateData)3075{3076json.WriteString(L"CustomData");3077json.WriteNumber((uintptr_t)privateData);3078}3079}3080else3081{3082const Allocation* const alloc = (const Allocation*)privateData;3083D3D12MA_ASSERT(alloc);3084json.AddAllocationToObject(*alloc);3085}3086json.EndObject();3087}30883089void BlockMetadata::PrintDetailedMap_UnusedRange(JsonWriter& json,3090UINT64 offset, UINT64 size) const3091{3092json.BeginObject(true);30933094json.WriteString(L"Offset");3095json.WriteNumber(offset);30963097json.WriteString(L"Type");3098json.WriteString(L"FREE");30993100json.WriteString(L"Size");3101json.WriteNumber(size);31023103json.EndObject();3104}31053106void BlockMetadata::PrintDetailedMap_End(JsonWriter& json) const3107{3108json.EndArray();3109}3110#endif // _D3D12MA_BLOCK_METADATA_FUNCTIONS3111#endif // _D3D12MA_BLOCK_METADATA31123113#if 03114#ifndef _D3D12MA_BLOCK_METADATA_GENERIC3115class BlockMetadata_Generic : public BlockMetadata3116{3117public:3118BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);3119virtual ~BlockMetadata_Generic() = default;31203121size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; }3122UINT64 GetSumFreeSize() const override { return m_SumFreeSize; }3123UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; }31243125void Init(UINT64 size) override;3126bool Validate() const override;3127bool IsEmpty() const override;3128void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;31293130bool CreateAllocationRequest(3131UINT64 allocSize,3132UINT64 allocAlignment,3133bool upperAddress,3134AllocationRequest* pAllocationRequest) override;31353136void Alloc(3137const AllocationRequest& request,3138UINT64 allocSize,3139void* privateData) override;31403141void Free(AllocHandle allocHandle) override;3142void Clear() override;31433144void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;31453146void AddStatistics(Statistics& inoutStats) const override;3147void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;3148void WriteAllocationInfoToJson(JsonWriter& json) const override;31493150private:3151UINT m_FreeCount;3152UINT64 m_SumFreeSize;3153SuballocationList m_Suballocations;3154// Suballocations that are free and have size greater than certain threshold.3155// Sorted by size, ascending.3156Vector<SuballocationList::iterator> m_FreeSuballocationsBySize;3157ZeroInitializedRange m_ZeroInitializedRange;31583159SuballocationList::const_iterator FindAtOffset(UINT64 offset) const;3160bool ValidateFreeSuballocationList() const;31613162// Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.3163// If yes, fills pOffset and returns true. If no, returns false.3164bool CheckAllocation(3165UINT64 allocSize,3166UINT64 allocAlignment,3167SuballocationList::const_iterator suballocItem,3168AllocHandle* pAllocHandle,3169UINT64* pSumFreeSize,3170UINT64* pSumItemSize,3171BOOL *pZeroInitialized) const;3172// Given free suballocation, it merges it with following one, which must also be free.3173void MergeFreeWithNext(SuballocationList::iterator item);3174// Releases given suballocation, making it free.3175// Merges it with adjacent free suballocations if applicable.3176// Returns iterator to new free suballocation at this place.3177SuballocationList::iterator FreeSuballocation(SuballocationList::iterator suballocItem);3178// Given free suballocation, it inserts it into sorted list of3179// m_FreeSuballocationsBySize if it's suitable.3180void RegisterFreeSuballocation(SuballocationList::iterator item);3181// Given free suballocation, it removes it from sorted list of3182// m_FreeSuballocationsBySize if it's suitable.3183void UnregisterFreeSuballocation(SuballocationList::iterator item);31843185D3D12MA_CLASS_NO_COPY(BlockMetadata_Generic)3186};31873188#ifndef _D3D12MA_BLOCK_METADATA_GENERIC_FUNCTIONS3189BlockMetadata_Generic::BlockMetadata_Generic(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)3190: BlockMetadata(allocationCallbacks, isVirtual),3191m_FreeCount(0),3192m_SumFreeSize(0),3193m_Suballocations(*allocationCallbacks),3194m_FreeSuballocationsBySize(*allocationCallbacks)3195{3196D3D12MA_ASSERT(allocationCallbacks);3197}31983199void BlockMetadata_Generic::Init(UINT64 size)3200{3201BlockMetadata::Init(size);3202m_ZeroInitializedRange.Reset(size);32033204m_FreeCount = 1;3205m_SumFreeSize = size;32063207Suballocation suballoc = {};3208suballoc.offset = 0;3209suballoc.size = size;3210suballoc.type = SUBALLOCATION_TYPE_FREE;3211suballoc.privateData = NULL;32123213D3D12MA_ASSERT(size > MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);3214m_Suballocations.push_back(suballoc);3215SuballocationList::iterator suballocItem = m_Suballocations.end();3216--suballocItem;3217m_FreeSuballocationsBySize.push_back(suballocItem);3218}32193220bool BlockMetadata_Generic::Validate() const3221{3222D3D12MA_VALIDATE(!m_Suballocations.empty());32233224// Expected offset of new suballocation as calculated from previous ones.3225UINT64 calculatedOffset = 0;3226// Expected number of free suballocations as calculated from traversing their list.3227UINT calculatedFreeCount = 0;3228// Expected sum size of free suballocations as calculated from traversing their list.3229UINT64 calculatedSumFreeSize = 0;3230// Expected number of free suballocations that should be registered in3231// m_FreeSuballocationsBySize calculated from traversing their list.3232size_t freeSuballocationsToRegister = 0;3233// True if previous visited suballocation was free.3234bool prevFree = false;32353236for (const auto& subAlloc : m_Suballocations)3237{3238// Actual offset of this suballocation doesn't match expected one.3239D3D12MA_VALIDATE(subAlloc.offset == calculatedOffset);32403241const bool currFree = (subAlloc.type == SUBALLOCATION_TYPE_FREE);3242// Two adjacent free suballocations are invalid. They should be merged.3243D3D12MA_VALIDATE(!prevFree || !currFree);32443245const Allocation* const alloc = (Allocation*)subAlloc.privateData;3246if (!IsVirtual())3247{3248D3D12MA_VALIDATE(currFree == (alloc == NULL));3249}32503251if (currFree)3252{3253calculatedSumFreeSize += subAlloc.size;3254++calculatedFreeCount;3255if (subAlloc.size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)3256{3257++freeSuballocationsToRegister;3258}32593260// Margin required between allocations - every free space must be at least that large.3261D3D12MA_VALIDATE(subAlloc.size >= GetDebugMargin());3262}3263else3264{3265if (!IsVirtual())3266{3267D3D12MA_VALIDATE(alloc->GetOffset() == subAlloc.offset);3268D3D12MA_VALIDATE(alloc->GetSize() == subAlloc.size);3269}32703271// Margin required between allocations - previous allocation must be free.3272D3D12MA_VALIDATE(GetDebugMargin() == 0 || prevFree);3273}32743275calculatedOffset += subAlloc.size;3276prevFree = currFree;3277}32783279// Number of free suballocations registered in m_FreeSuballocationsBySize doesn't3280// match expected one.3281D3D12MA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);32823283UINT64 lastSize = 0;3284for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)3285{3286SuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];32873288// Only free suballocations can be registered in m_FreeSuballocationsBySize.3289D3D12MA_VALIDATE(suballocItem->type == SUBALLOCATION_TYPE_FREE);3290// They must be sorted by size ascending.3291D3D12MA_VALIDATE(suballocItem->size >= lastSize);32923293lastSize = suballocItem->size;3294}32953296// Check if totals match calculacted values.3297D3D12MA_VALIDATE(ValidateFreeSuballocationList());3298D3D12MA_VALIDATE(calculatedOffset == GetSize());3299D3D12MA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);3300D3D12MA_VALIDATE(calculatedFreeCount == m_FreeCount);33013302return true;3303}33043305bool BlockMetadata_Generic::IsEmpty() const3306{3307return (m_Suballocations.size() == 1) && (m_FreeCount == 1);3308}33093310void BlockMetadata_Generic::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const3311{3312Suballocation& suballoc = *FindAtOffset((UINT64)allocHandle - 1).dropConst();3313outInfo.Offset = suballoc.offset;3314outInfo.Size = suballoc.size;3315outInfo.pPrivateData = suballoc.privateData;3316}33173318bool BlockMetadata_Generic::CreateAllocationRequest(3319UINT64 allocSize,3320UINT64 allocAlignment,3321bool upperAddress,3322AllocationRequest* pAllocationRequest)3323{3324D3D12MA_ASSERT(allocSize > 0);3325D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm.");3326D3D12MA_ASSERT(pAllocationRequest != NULL);3327D3D12MA_HEAVY_ASSERT(Validate());33283329// There is not enough total free space in this block to fullfill the request: Early return.3330if (m_SumFreeSize < allocSize + GetDebugMargin())3331{3332return false;3333}33343335// New algorithm, efficiently searching freeSuballocationsBySize.3336const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();3337if (freeSuballocCount > 0)3338{3339// Find first free suballocation with size not less than allocSize + GetDebugMargin().3340SuballocationList::iterator* const it = BinaryFindFirstNotLess(3341m_FreeSuballocationsBySize.data(),3342m_FreeSuballocationsBySize.data() + freeSuballocCount,3343allocSize + GetDebugMargin(),3344SuballocationItemSizeLess());3345size_t index = it - m_FreeSuballocationsBySize.data();3346for (; index < freeSuballocCount; ++index)3347{3348if (CheckAllocation(3349allocSize,3350allocAlignment,3351m_FreeSuballocationsBySize[index],3352&pAllocationRequest->allocHandle,3353&pAllocationRequest->sumFreeSize,3354&pAllocationRequest->sumItemSize,3355&pAllocationRequest->zeroInitialized))3356{3357pAllocationRequest->item = m_FreeSuballocationsBySize[index];3358return true;3359}3360}3361}33623363return false;3364}33653366void BlockMetadata_Generic::Alloc(3367const AllocationRequest& request,3368UINT64 allocSize,3369void* privateData)3370{3371D3D12MA_ASSERT(request.item != m_Suballocations.end());3372Suballocation& suballoc = *request.item;3373// Given suballocation is a free block.3374D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE);3375// Given offset is inside this suballocation.3376UINT64 offset = (UINT64)request.allocHandle - 1;3377D3D12MA_ASSERT(offset >= suballoc.offset);3378const UINT64 paddingBegin = offset - suballoc.offset;3379D3D12MA_ASSERT(suballoc.size >= paddingBegin + allocSize);3380const UINT64 paddingEnd = suballoc.size - paddingBegin - allocSize;33813382// Unregister this free suballocation from m_FreeSuballocationsBySize and update3383// it to become used.3384UnregisterFreeSuballocation(request.item);33853386suballoc.offset = offset;3387suballoc.size = allocSize;3388suballoc.type = SUBALLOCATION_TYPE_ALLOCATION;3389suballoc.privateData = privateData;33903391// If there are any free bytes remaining at the end, insert new free suballocation after current one.3392if (paddingEnd)3393{3394Suballocation paddingSuballoc = {};3395paddingSuballoc.offset = offset + allocSize;3396paddingSuballoc.size = paddingEnd;3397paddingSuballoc.type = SUBALLOCATION_TYPE_FREE;3398SuballocationList::iterator next = request.item;3399++next;3400const SuballocationList::iterator paddingEndItem =3401m_Suballocations.insert(next, paddingSuballoc);3402RegisterFreeSuballocation(paddingEndItem);3403}34043405// If there are any free bytes remaining at the beginning, insert new free suballocation before current one.3406if (paddingBegin)3407{3408Suballocation paddingSuballoc = {};3409paddingSuballoc.offset = offset - paddingBegin;3410paddingSuballoc.size = paddingBegin;3411paddingSuballoc.type = SUBALLOCATION_TYPE_FREE;3412const SuballocationList::iterator paddingBeginItem =3413m_Suballocations.insert(request.item, paddingSuballoc);3414RegisterFreeSuballocation(paddingBeginItem);3415}34163417// Update totals.3418m_FreeCount = m_FreeCount - 1;3419if (paddingBegin > 0)3420{3421++m_FreeCount;3422}3423if (paddingEnd > 0)3424{3425++m_FreeCount;3426}3427m_SumFreeSize -= allocSize;34283429m_ZeroInitializedRange.MarkRangeAsUsed(offset, offset + allocSize);3430}34313432void BlockMetadata_Generic::Free(AllocHandle allocHandle)3433{3434FreeSuballocation(FindAtOffset((UINT64)allocHandle - 1).dropConst());3435}34363437void BlockMetadata_Generic::Clear()3438{3439m_FreeCount = 1;3440m_SumFreeSize = GetSize();34413442m_Suballocations.clear();3443Suballocation suballoc = {};3444suballoc.offset = 0;3445suballoc.size = GetSize();3446suballoc.type = SUBALLOCATION_TYPE_FREE;3447m_Suballocations.push_back(suballoc);34483449m_FreeSuballocationsBySize.clear();3450m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());3451}34523453SuballocationList::const_iterator BlockMetadata_Generic::FindAtOffset(UINT64 offset) const3454{3455const UINT64 last = m_Suballocations.crbegin()->offset;3456if (last == offset)3457return m_Suballocations.crbegin();3458const UINT64 first = m_Suballocations.cbegin()->offset;3459if (first == offset)3460return m_Suballocations.cbegin();34613462const size_t suballocCount = m_Suballocations.size();3463const UINT64 step = (last - first + m_Suballocations.cbegin()->size) / suballocCount;3464auto findSuballocation = [&](auto begin, auto end) -> SuballocationList::const_iterator3465{3466for (auto suballocItem = begin;3467suballocItem != end;3468++suballocItem)3469{3470const Suballocation& suballoc = *suballocItem;3471if (suballoc.offset == offset)3472return suballocItem;3473}3474D3D12MA_ASSERT(false && "Not found!");3475return m_Suballocations.end();3476};3477// If requested offset is closer to the end of range, search from the end3478if ((offset - first) > suballocCount * step / 2)3479{3480return findSuballocation(m_Suballocations.crbegin(), m_Suballocations.crend());3481}3482return findSuballocation(m_Suballocations.cbegin(), m_Suballocations.cend());3483}34843485bool BlockMetadata_Generic::ValidateFreeSuballocationList() const3486{3487UINT64 lastSize = 0;3488for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)3489{3490const SuballocationList::iterator it = m_FreeSuballocationsBySize[i];34913492D3D12MA_VALIDATE(it->type == SUBALLOCATION_TYPE_FREE);3493D3D12MA_VALIDATE(it->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);3494D3D12MA_VALIDATE(it->size >= lastSize);3495lastSize = it->size;3496}3497return true;3498}34993500bool BlockMetadata_Generic::CheckAllocation(3501UINT64 allocSize,3502UINT64 allocAlignment,3503SuballocationList::const_iterator suballocItem,3504AllocHandle* pAllocHandle,3505UINT64* pSumFreeSize,3506UINT64* pSumItemSize,3507BOOL* pZeroInitialized) const3508{3509D3D12MA_ASSERT(allocSize > 0);3510D3D12MA_ASSERT(suballocItem != m_Suballocations.cend());3511D3D12MA_ASSERT(pAllocHandle != NULL && pZeroInitialized != NULL);35123513*pSumFreeSize = 0;3514*pSumItemSize = 0;3515*pZeroInitialized = FALSE;35163517const Suballocation& suballoc = *suballocItem;3518D3D12MA_ASSERT(suballoc.type == SUBALLOCATION_TYPE_FREE);35193520*pSumFreeSize = suballoc.size;35213522// Size of this suballocation is too small for this request: Early return.3523if (suballoc.size < allocSize)3524{3525return false;3526}35273528// Start from offset equal to beginning of this suballocation and debug margin of previous allocation if present.3529UINT64 offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin());35303531// Apply alignment.3532offset = AlignUp(offset, allocAlignment);35333534// Calculate padding at the beginning based on current offset.3535const UINT64 paddingBegin = offset - suballoc.offset;35363537// Fail if requested size plus margin after is bigger than size of this suballocation.3538if (paddingBegin + allocSize + GetDebugMargin() > suballoc.size)3539{3540return false;3541}35423543// All tests passed: Success. Offset is already filled.3544*pZeroInitialized = m_ZeroInitializedRange.IsRangeZeroInitialized(offset, offset + allocSize);3545*pAllocHandle = (AllocHandle)(offset + 1);3546return true;3547}35483549void BlockMetadata_Generic::MergeFreeWithNext(SuballocationList::iterator item)3550{3551D3D12MA_ASSERT(item != m_Suballocations.end());3552D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);35533554SuballocationList::iterator nextItem = item;3555++nextItem;3556D3D12MA_ASSERT(nextItem != m_Suballocations.end());3557D3D12MA_ASSERT(nextItem->type == SUBALLOCATION_TYPE_FREE);35583559item->size += nextItem->size;3560--m_FreeCount;3561m_Suballocations.erase(nextItem);3562}35633564SuballocationList::iterator BlockMetadata_Generic::FreeSuballocation(SuballocationList::iterator suballocItem)3565{3566// Change this suballocation to be marked as free.3567Suballocation& suballoc = *suballocItem;3568suballoc.type = SUBALLOCATION_TYPE_FREE;3569suballoc.privateData = NULL;35703571// Update totals.3572++m_FreeCount;3573m_SumFreeSize += suballoc.size;35743575// Merge with previous and/or next suballocation if it's also free.3576bool mergeWithNext = false;3577bool mergeWithPrev = false;35783579SuballocationList::iterator nextItem = suballocItem;3580++nextItem;3581if ((nextItem != m_Suballocations.end()) && (nextItem->type == SUBALLOCATION_TYPE_FREE))3582{3583mergeWithNext = true;3584}35853586SuballocationList::iterator prevItem = suballocItem;3587if (suballocItem != m_Suballocations.begin())3588{3589--prevItem;3590if (prevItem->type == SUBALLOCATION_TYPE_FREE)3591{3592mergeWithPrev = true;3593}3594}35953596if (mergeWithNext)3597{3598UnregisterFreeSuballocation(nextItem);3599MergeFreeWithNext(suballocItem);3600}36013602if (mergeWithPrev)3603{3604UnregisterFreeSuballocation(prevItem);3605MergeFreeWithNext(prevItem);3606RegisterFreeSuballocation(prevItem);3607return prevItem;3608}3609else3610{3611RegisterFreeSuballocation(suballocItem);3612return suballocItem;3613}3614}36153616void BlockMetadata_Generic::RegisterFreeSuballocation(SuballocationList::iterator item)3617{3618D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);3619D3D12MA_ASSERT(item->size > 0);36203621// You may want to enable this validation at the beginning or at the end of3622// this function, depending on what do you want to check.3623D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());36243625if (item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)3626{3627if (m_FreeSuballocationsBySize.empty())3628{3629m_FreeSuballocationsBySize.push_back(item);3630}3631else3632{3633m_FreeSuballocationsBySize.InsertSorted(item, SuballocationItemSizeLess());3634}3635}36363637//D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());3638}36393640void BlockMetadata_Generic::UnregisterFreeSuballocation(SuballocationList::iterator item)3641{3642D3D12MA_ASSERT(item->type == SUBALLOCATION_TYPE_FREE);3643D3D12MA_ASSERT(item->size > 0);36443645// You may want to enable this validation at the beginning or at the end of3646// this function, depending on what do you want to check.3647D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());36483649if (item->size >= MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)3650{3651SuballocationList::iterator* const it = BinaryFindFirstNotLess(3652m_FreeSuballocationsBySize.data(),3653m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),3654item,3655SuballocationItemSizeLess());3656for (size_t index = it - m_FreeSuballocationsBySize.data();3657index < m_FreeSuballocationsBySize.size();3658++index)3659{3660if (m_FreeSuballocationsBySize[index] == item)3661{3662m_FreeSuballocationsBySize.remove(index);3663return;3664}3665D3D12MA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");3666}3667D3D12MA_ASSERT(0 && "Not found.");3668}36693670//D3D12MA_HEAVY_ASSERT(ValidateFreeSuballocationList());3671}36723673void BlockMetadata_Generic::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)3674{3675Suballocation& suballoc = *FindAtOffset((UINT64)allocHandle - 1).dropConst();3676suballoc.privateData = privateData;3677}36783679void BlockMetadata_Generic::AddStatistics(Statistics& inoutStats) const3680{3681inoutStats.BlockCount++;3682inoutStats.AllocationCount += (UINT)m_Suballocations.size() - m_FreeCount;3683inoutStats.BlockBytes += GetSize();3684inoutStats.AllocationBytes += GetSize() - m_SumFreeSize;3685}36863687void BlockMetadata_Generic::AddDetailedStatistics(DetailedStatistics& inoutStats) const3688{3689inoutStats.Stats.BlockCount++;3690inoutStats.Stats.BlockBytes += GetSize();36913692for (const auto& suballoc : m_Suballocations)3693{3694if (suballoc.type == SUBALLOCATION_TYPE_FREE)3695AddDetailedStatisticsUnusedRange(inoutStats, suballoc.size);3696else3697AddDetailedStatisticsAllocation(inoutStats, suballoc.size);3698}3699}37003701void BlockMetadata_Generic::WriteAllocationInfoToJson(JsonWriter& json) const3702{3703PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_FreeCount);3704for (const auto& suballoc : m_Suballocations)3705{3706if (suballoc.type == SUBALLOCATION_TYPE_FREE)3707PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);3708else3709PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);3710}3711PrintDetailedMap_End(json);3712}3713#endif // _D3D12MA_BLOCK_METADATA_GENERIC_FUNCTIONS3714#endif // _D3D12MA_BLOCK_METADATA_GENERIC3715#endif // #if 037163717#ifndef _D3D12MA_BLOCK_METADATA_LINEAR3718class BlockMetadata_Linear : public BlockMetadata3719{3720public:3721BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);3722virtual ~BlockMetadata_Linear() = default;37233724UINT64 GetSumFreeSize() const override { return m_SumFreeSize; }3725bool IsEmpty() const override { return GetAllocationCount() == 0; }3726UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return (UINT64)allocHandle - 1; };37273728void Init(UINT64 size) override;3729bool Validate() const override;3730size_t GetAllocationCount() const override;3731size_t GetFreeRegionsCount() const override;3732void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;37333734bool CreateAllocationRequest(3735UINT64 allocSize,3736UINT64 allocAlignment,3737bool upperAddress,3738UINT32 strategy,3739AllocationRequest* pAllocationRequest) override;37403741void Alloc(3742const AllocationRequest& request,3743UINT64 allocSize,3744void* privateData) override;37453746void Free(AllocHandle allocHandle) override;3747void Clear() override;37483749AllocHandle GetAllocationListBegin() const override;3750AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override;3751UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override;3752void* GetAllocationPrivateData(AllocHandle allocHandle) const override;3753void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;37543755void AddStatistics(Statistics& inoutStats) const override;3756void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;3757void WriteAllocationInfoToJson(JsonWriter& json) const override;3758void DebugLogAllAllocations() const override;37593760private:3761/*3762There are two suballocation vectors, used in ping-pong way.3763The one with index m_1stVectorIndex is called 1st.3764The one with index (m_1stVectorIndex ^ 1) is called 2nd.37652nd can be non-empty only when 1st is not empty.3766When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.3767*/3768typedef Vector<Suballocation> SuballocationVectorType;37693770enum ALLOC_REQUEST_TYPE3771{3772ALLOC_REQUEST_UPPER_ADDRESS,3773ALLOC_REQUEST_END_OF_1ST,3774ALLOC_REQUEST_END_OF_2ND,3775};37763777enum SECOND_VECTOR_MODE3778{3779SECOND_VECTOR_EMPTY,3780/*3781Suballocations in 2nd vector are created later than the ones in 1st, but they3782all have smaller offset.3783*/3784SECOND_VECTOR_RING_BUFFER,3785/*3786Suballocations in 2nd vector are upper side of double stack.3787They all have offsets higher than those in 1st vector.3788Top of this stack means smaller offsets, but higher indices in this vector.3789*/3790SECOND_VECTOR_DOUBLE_STACK,3791};37923793UINT64 m_SumFreeSize;3794SuballocationVectorType m_Suballocations0, m_Suballocations1;3795UINT32 m_1stVectorIndex;3796SECOND_VECTOR_MODE m_2ndVectorMode;3797// Number of items in 1st vector with hAllocation = null at the beginning.3798size_t m_1stNullItemsBeginCount;3799// Number of other items in 1st vector with hAllocation = null somewhere in the middle.3800size_t m_1stNullItemsMiddleCount;3801// Number of items in 2nd vector with hAllocation = null.3802size_t m_2ndNullItemsCount;38033804SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }3805SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }3806const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }3807const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }38083809Suballocation& FindSuballocation(UINT64 offset) const;3810bool ShouldCompact1st() const;3811void CleanupAfterFree();38123813bool CreateAllocationRequest_LowerAddress(3814UINT64 allocSize,3815UINT64 allocAlignment,3816AllocationRequest* pAllocationRequest);3817bool CreateAllocationRequest_UpperAddress(3818UINT64 allocSize,3819UINT64 allocAlignment,3820AllocationRequest* pAllocationRequest);38213822D3D12MA_CLASS_NO_COPY(BlockMetadata_Linear)3823};38243825#ifndef _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS3826BlockMetadata_Linear::BlockMetadata_Linear(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)3827: BlockMetadata(allocationCallbacks, isVirtual),3828m_SumFreeSize(0),3829m_Suballocations0(*allocationCallbacks),3830m_Suballocations1(*allocationCallbacks),3831m_1stVectorIndex(0),3832m_2ndVectorMode(SECOND_VECTOR_EMPTY),3833m_1stNullItemsBeginCount(0),3834m_1stNullItemsMiddleCount(0),3835m_2ndNullItemsCount(0)3836{3837D3D12MA_ASSERT(allocationCallbacks);3838}38393840void BlockMetadata_Linear::Init(UINT64 size)3841{3842BlockMetadata::Init(size);3843m_SumFreeSize = size;3844}38453846bool BlockMetadata_Linear::Validate() const3847{3848D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize());3849const SuballocationVectorType& suballocations1st = AccessSuballocations1st();3850const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();38513852D3D12MA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));3853D3D12MA_VALIDATE(!suballocations1st.empty() ||3854suballocations2nd.empty() ||3855m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);38563857if (!suballocations1st.empty())3858{3859// Null item at the beginning should be accounted into m_1stNullItemsBeginCount.3860D3D12MA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != SUBALLOCATION_TYPE_FREE);3861// Null item at the end should be just pop_back().3862D3D12MA_VALIDATE(suballocations1st.back().type != SUBALLOCATION_TYPE_FREE);3863}3864if (!suballocations2nd.empty())3865{3866// Null item at the end should be just pop_back().3867D3D12MA_VALIDATE(suballocations2nd.back().type != SUBALLOCATION_TYPE_FREE);3868}38693870D3D12MA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());3871D3D12MA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());38723873UINT64 sumUsedSize = 0;3874const size_t suballoc1stCount = suballocations1st.size();3875UINT64 offset = 0;38763877if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)3878{3879const size_t suballoc2ndCount = suballocations2nd.size();3880size_t nullItem2ndCount = 0;3881for (size_t i = 0; i < suballoc2ndCount; ++i)3882{3883const Suballocation& suballoc = suballocations2nd[i];3884const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);38853886const Allocation* alloc = (Allocation*)suballoc.privateData;3887if (!IsVirtual())3888{3889D3D12MA_VALIDATE(currFree == (alloc == NULL));3890}3891D3D12MA_VALIDATE(suballoc.offset >= offset);38923893if (!currFree)3894{3895if (!IsVirtual())3896{3897D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3898D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3899}3900sumUsedSize += suballoc.size;3901}3902else3903{3904++nullItem2ndCount;3905}39063907offset = suballoc.offset + suballoc.size + GetDebugMargin();3908}39093910D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);3911}39123913for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)3914{3915const Suballocation& suballoc = suballocations1st[i];3916D3D12MA_VALIDATE(suballoc.type == SUBALLOCATION_TYPE_FREE &&3917suballoc.privateData == NULL);3918}39193920size_t nullItem1stCount = m_1stNullItemsBeginCount;39213922for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)3923{3924const Suballocation& suballoc = suballocations1st[i];3925const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);39263927const Allocation* alloc = (Allocation*)suballoc.privateData;3928if (!IsVirtual())3929{3930D3D12MA_VALIDATE(currFree == (alloc == NULL));3931}3932D3D12MA_VALIDATE(suballoc.offset >= offset);3933D3D12MA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);39343935if (!currFree)3936{3937if (!IsVirtual())3938{3939D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3940D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3941}3942sumUsedSize += suballoc.size;3943}3944else3945{3946++nullItem1stCount;3947}39483949offset = suballoc.offset + suballoc.size + GetDebugMargin();3950}3951D3D12MA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);39523953if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)3954{3955const size_t suballoc2ndCount = suballocations2nd.size();3956size_t nullItem2ndCount = 0;3957for (size_t i = suballoc2ndCount; i--; )3958{3959const Suballocation& suballoc = suballocations2nd[i];3960const bool currFree = (suballoc.type == SUBALLOCATION_TYPE_FREE);39613962const Allocation* alloc = (Allocation*)suballoc.privateData;3963if (!IsVirtual())3964{3965D3D12MA_VALIDATE(currFree == (alloc == NULL));3966}3967D3D12MA_VALIDATE(suballoc.offset >= offset);39683969if (!currFree)3970{3971if (!IsVirtual())3972{3973D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset);3974D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size);3975}3976sumUsedSize += suballoc.size;3977}3978else3979{3980++nullItem2ndCount;3981}39823983offset = suballoc.offset + suballoc.size + GetDebugMargin();3984}39853986D3D12MA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);3987}39883989D3D12MA_VALIDATE(offset <= GetSize());3990D3D12MA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);39913992return true;3993}39943995size_t BlockMetadata_Linear::GetAllocationCount() const3996{3997return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +3998AccessSuballocations2nd().size() - m_2ndNullItemsCount;3999}40004001size_t BlockMetadata_Linear::GetFreeRegionsCount() const4002{4003// Function only used for defragmentation, which is disabled for this algorithm4004D3D12MA_ASSERT(0);4005return SIZE_MAX;4006}40074008void BlockMetadata_Linear::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const4009{4010const Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1);4011outInfo.Offset = suballoc.offset;4012outInfo.Size = suballoc.size;4013outInfo.pPrivateData = suballoc.privateData;4014}40154016bool BlockMetadata_Linear::CreateAllocationRequest(4017UINT64 allocSize,4018UINT64 allocAlignment,4019bool upperAddress,4020UINT32 strategy,4021AllocationRequest* pAllocationRequest)4022{4023D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");4024D3D12MA_ASSERT(pAllocationRequest != NULL);4025D3D12MA_HEAVY_ASSERT(Validate());4026pAllocationRequest->size = allocSize;4027return upperAddress ?4028CreateAllocationRequest_UpperAddress(4029allocSize, allocAlignment, pAllocationRequest) :4030CreateAllocationRequest_LowerAddress(4031allocSize, allocAlignment, pAllocationRequest);4032}40334034void BlockMetadata_Linear::Alloc(4035const AllocationRequest& request,4036UINT64 allocSize,4037void* privateData)4038{4039UINT64 offset = (UINT64)request.allocHandle - 1;4040const Suballocation newSuballoc = { offset, request.size, privateData, SUBALLOCATION_TYPE_ALLOCATION };40414042switch (request.algorithmData)4043{4044case ALLOC_REQUEST_UPPER_ADDRESS:4045{4046D3D12MA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&4047"CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");4048SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4049suballocations2nd.push_back(newSuballoc);4050m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;4051break;4052}4053case ALLOC_REQUEST_END_OF_1ST:4054{4055SuballocationVectorType& suballocations1st = AccessSuballocations1st();40564057D3D12MA_ASSERT(suballocations1st.empty() ||4058offset >= suballocations1st.back().offset + suballocations1st.back().size);4059// Check if it fits before the end of the block.4060D3D12MA_ASSERT(offset + request.size <= GetSize());40614062suballocations1st.push_back(newSuballoc);4063break;4064}4065case ALLOC_REQUEST_END_OF_2ND:4066{4067SuballocationVectorType& suballocations1st = AccessSuballocations1st();4068// New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.4069D3D12MA_ASSERT(!suballocations1st.empty() &&4070offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);4071SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();40724073switch (m_2ndVectorMode)4074{4075case SECOND_VECTOR_EMPTY:4076// First allocation from second part ring buffer.4077D3D12MA_ASSERT(suballocations2nd.empty());4078m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;4079break;4080case SECOND_VECTOR_RING_BUFFER:4081// 2-part ring buffer is already started.4082D3D12MA_ASSERT(!suballocations2nd.empty());4083break;4084case SECOND_VECTOR_DOUBLE_STACK:4085D3D12MA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");4086break;4087default:4088D3D12MA_ASSERT(0);4089}40904091suballocations2nd.push_back(newSuballoc);4092break;4093}4094default:4095D3D12MA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");4096}4097m_SumFreeSize -= newSuballoc.size;4098}40994100void BlockMetadata_Linear::Free(AllocHandle allocHandle)4101{4102SuballocationVectorType& suballocations1st = AccessSuballocations1st();4103SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4104UINT64 offset = (UINT64)allocHandle - 1;41054106if (!suballocations1st.empty())4107{4108// First allocation: Mark it as next empty at the beginning.4109Suballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];4110if (firstSuballoc.offset == offset)4111{4112firstSuballoc.type = SUBALLOCATION_TYPE_FREE;4113firstSuballoc.privateData = NULL;4114m_SumFreeSize += firstSuballoc.size;4115++m_1stNullItemsBeginCount;4116CleanupAfterFree();4117return;4118}4119}41204121// Last allocation in 2-part ring buffer or top of upper stack (same logic).4122if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||4123m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4124{4125Suballocation& lastSuballoc = suballocations2nd.back();4126if (lastSuballoc.offset == offset)4127{4128m_SumFreeSize += lastSuballoc.size;4129suballocations2nd.pop_back();4130CleanupAfterFree();4131return;4132}4133}4134// Last allocation in 1st vector.4135else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)4136{4137Suballocation& lastSuballoc = suballocations1st.back();4138if (lastSuballoc.offset == offset)4139{4140m_SumFreeSize += lastSuballoc.size;4141suballocations1st.pop_back();4142CleanupAfterFree();4143return;4144}4145}41464147Suballocation refSuballoc;4148refSuballoc.offset = offset;4149// Rest of members stays uninitialized intentionally for better performance.41504151// Item from the middle of 1st vector.4152{4153const SuballocationVectorType::iterator it = BinaryFindSorted(4154suballocations1st.begin() + m_1stNullItemsBeginCount,4155suballocations1st.end(),4156refSuballoc,4157SuballocationOffsetLess());4158if (it != suballocations1st.end())4159{4160it->type = SUBALLOCATION_TYPE_FREE;4161it->privateData = NULL;4162++m_1stNullItemsMiddleCount;4163m_SumFreeSize += it->size;4164CleanupAfterFree();4165return;4166}4167}41684169if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)4170{4171// Item from the middle of 2nd vector.4172const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?4173BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) :4174BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater());4175if (it != suballocations2nd.end())4176{4177it->type = SUBALLOCATION_TYPE_FREE;4178it->privateData = NULL;4179++m_2ndNullItemsCount;4180m_SumFreeSize += it->size;4181CleanupAfterFree();4182return;4183}4184}41854186D3D12MA_ASSERT(0 && "Allocation to free not found in linear allocator!");4187}41884189void BlockMetadata_Linear::Clear()4190{4191m_SumFreeSize = GetSize();4192m_Suballocations0.clear();4193m_Suballocations1.clear();4194// Leaving m_1stVectorIndex unchanged - it doesn't matter.4195m_2ndVectorMode = SECOND_VECTOR_EMPTY;4196m_1stNullItemsBeginCount = 0;4197m_1stNullItemsMiddleCount = 0;4198m_2ndNullItemsCount = 0;4199}42004201AllocHandle BlockMetadata_Linear::GetAllocationListBegin() const4202{4203// Function only used for defragmentation, which is disabled for this algorithm4204D3D12MA_ASSERT(0);4205return (AllocHandle)0;4206}42074208AllocHandle BlockMetadata_Linear::GetNextAllocation(AllocHandle prevAlloc) const4209{4210// Function only used for defragmentation, which is disabled for this algorithm4211D3D12MA_ASSERT(0);4212return (AllocHandle)0;4213}42144215UINT64 BlockMetadata_Linear::GetNextFreeRegionSize(AllocHandle alloc) const4216{4217// Function only used for defragmentation, which is disabled for this algorithm4218D3D12MA_ASSERT(0);4219return 0;4220}42214222void* BlockMetadata_Linear::GetAllocationPrivateData(AllocHandle allocHandle) const4223{4224return FindSuballocation((UINT64)allocHandle - 1).privateData;4225}42264227void BlockMetadata_Linear::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)4228{4229Suballocation& suballoc = FindSuballocation((UINT64)allocHandle - 1);4230suballoc.privateData = privateData;4231}42324233void BlockMetadata_Linear::AddStatistics(Statistics& inoutStats) const4234{4235inoutStats.BlockCount++;4236inoutStats.AllocationCount += (UINT)GetAllocationCount();4237inoutStats.BlockBytes += GetSize();4238inoutStats.AllocationBytes += GetSize() - m_SumFreeSize;4239}42404241void BlockMetadata_Linear::AddDetailedStatistics(DetailedStatistics& inoutStats) const4242{4243inoutStats.Stats.BlockCount++;4244inoutStats.Stats.BlockBytes += GetSize();42454246const UINT64 size = GetSize();4247const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4248const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4249const size_t suballoc1stCount = suballocations1st.size();4250const size_t suballoc2ndCount = suballocations2nd.size();42514252UINT64 lastOffset = 0;4253if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4254{4255const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;4256size_t nextAlloc2ndIndex = 0;4257while (lastOffset < freeSpace2ndTo1stEnd)4258{4259// Find next non-null allocation or move nextAllocIndex to the end.4260while (nextAlloc2ndIndex < suballoc2ndCount &&4261suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4262{4263++nextAlloc2ndIndex;4264}42654266// Found non-null allocation.4267if (nextAlloc2ndIndex < suballoc2ndCount)4268{4269const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];42704271// 1. Process free space before this allocation.4272if (lastOffset < suballoc.offset)4273{4274// There is free space from lastOffset to suballoc.offset.4275const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4276AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4277}42784279// 2. Process this allocation.4280// There is allocation with suballoc.offset, suballoc.size.4281AddDetailedStatisticsAllocation(inoutStats, suballoc.size);42824283// 3. Prepare for next iteration.4284lastOffset = suballoc.offset + suballoc.size;4285++nextAlloc2ndIndex;4286}4287// We are at the end.4288else4289{4290// There is free space from lastOffset to freeSpace2ndTo1stEnd.4291if (lastOffset < freeSpace2ndTo1stEnd)4292{4293const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;4294AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4295}42964297// End of loop.4298lastOffset = freeSpace2ndTo1stEnd;4299}4300}4301}43024303size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;4304const UINT64 freeSpace1stTo2ndEnd =4305m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;4306while (lastOffset < freeSpace1stTo2ndEnd)4307{4308// Find next non-null allocation or move nextAllocIndex to the end.4309while (nextAlloc1stIndex < suballoc1stCount &&4310suballocations1st[nextAlloc1stIndex].privateData == NULL)4311{4312++nextAlloc1stIndex;4313}43144315// Found non-null allocation.4316if (nextAlloc1stIndex < suballoc1stCount)4317{4318const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];43194320// 1. Process free space before this allocation.4321if (lastOffset < suballoc.offset)4322{4323// There is free space from lastOffset to suballoc.offset.4324const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4325AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4326}43274328// 2. Process this allocation.4329// There is allocation with suballoc.offset, suballoc.size.4330AddDetailedStatisticsAllocation(inoutStats, suballoc.size);43314332// 3. Prepare for next iteration.4333lastOffset = suballoc.offset + suballoc.size;4334++nextAlloc1stIndex;4335}4336// We are at the end.4337else4338{4339// There is free space from lastOffset to freeSpace1stTo2ndEnd.4340if (lastOffset < freeSpace1stTo2ndEnd)4341{4342const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;4343AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4344}43454346// End of loop.4347lastOffset = freeSpace1stTo2ndEnd;4348}4349}43504351if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4352{4353size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;4354while (lastOffset < size)4355{4356// Find next non-null allocation or move nextAllocIndex to the end.4357while (nextAlloc2ndIndex != SIZE_MAX &&4358suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4359{4360--nextAlloc2ndIndex;4361}43624363// Found non-null allocation.4364if (nextAlloc2ndIndex != SIZE_MAX)4365{4366const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];43674368// 1. Process free space before this allocation.4369if (lastOffset < suballoc.offset)4370{4371// There is free space from lastOffset to suballoc.offset.4372const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4373AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4374}43754376// 2. Process this allocation.4377// There is allocation with suballoc.offset, suballoc.size.4378AddDetailedStatisticsAllocation(inoutStats, suballoc.size);43794380// 3. Prepare for next iteration.4381lastOffset = suballoc.offset + suballoc.size;4382--nextAlloc2ndIndex;4383}4384// We are at the end.4385else4386{4387// There is free space from lastOffset to size.4388if (lastOffset < size)4389{4390const UINT64 unusedRangeSize = size - lastOffset;4391AddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);4392}43934394// End of loop.4395lastOffset = size;4396}4397}4398}4399}44004401void BlockMetadata_Linear::WriteAllocationInfoToJson(JsonWriter& json) const4402{4403const UINT64 size = GetSize();4404const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4405const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4406const size_t suballoc1stCount = suballocations1st.size();4407const size_t suballoc2ndCount = suballocations2nd.size();44084409// FIRST PASS44104411size_t unusedRangeCount = 0;4412UINT64 usedBytes = 0;44134414UINT64 lastOffset = 0;44154416size_t alloc2ndCount = 0;4417if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4418{4419const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;4420size_t nextAlloc2ndIndex = 0;4421while (lastOffset < freeSpace2ndTo1stEnd)4422{4423// Find next non-null allocation or move nextAlloc2ndIndex to the end.4424while (nextAlloc2ndIndex < suballoc2ndCount &&4425suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4426{4427++nextAlloc2ndIndex;4428}44294430// Found non-null allocation.4431if (nextAlloc2ndIndex < suballoc2ndCount)4432{4433const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];44344435// 1. Process free space before this allocation.4436if (lastOffset < suballoc.offset)4437{4438// There is free space from lastOffset to suballoc.offset.4439++unusedRangeCount;4440}44414442// 2. Process this allocation.4443// There is allocation with suballoc.offset, suballoc.size.4444++alloc2ndCount;4445usedBytes += suballoc.size;44464447// 3. Prepare for next iteration.4448lastOffset = suballoc.offset + suballoc.size;4449++nextAlloc2ndIndex;4450}4451// We are at the end.4452else4453{4454if (lastOffset < freeSpace2ndTo1stEnd)4455{4456// There is free space from lastOffset to freeSpace2ndTo1stEnd.4457++unusedRangeCount;4458}44594460// End of loop.4461lastOffset = freeSpace2ndTo1stEnd;4462}4463}4464}44654466size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;4467size_t alloc1stCount = 0;4468const UINT64 freeSpace1stTo2ndEnd =4469m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;4470while (lastOffset < freeSpace1stTo2ndEnd)4471{4472// Find next non-null allocation or move nextAllocIndex to the end.4473while (nextAlloc1stIndex < suballoc1stCount &&4474suballocations1st[nextAlloc1stIndex].privateData == NULL)4475{4476++nextAlloc1stIndex;4477}44784479// Found non-null allocation.4480if (nextAlloc1stIndex < suballoc1stCount)4481{4482const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];44834484// 1. Process free space before this allocation.4485if (lastOffset < suballoc.offset)4486{4487// There is free space from lastOffset to suballoc.offset.4488++unusedRangeCount;4489}44904491// 2. Process this allocation.4492// There is allocation with suballoc.offset, suballoc.size.4493++alloc1stCount;4494usedBytes += suballoc.size;44954496// 3. Prepare for next iteration.4497lastOffset = suballoc.offset + suballoc.size;4498++nextAlloc1stIndex;4499}4500// We are at the end.4501else4502{4503if (lastOffset < size)4504{4505// There is free space from lastOffset to freeSpace1stTo2ndEnd.4506++unusedRangeCount;4507}45084509// End of loop.4510lastOffset = freeSpace1stTo2ndEnd;4511}4512}45134514if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4515{4516size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;4517while (lastOffset < size)4518{4519// Find next non-null allocation or move nextAlloc2ndIndex to the end.4520while (nextAlloc2ndIndex != SIZE_MAX &&4521suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4522{4523--nextAlloc2ndIndex;4524}45254526// Found non-null allocation.4527if (nextAlloc2ndIndex != SIZE_MAX)4528{4529const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];45304531// 1. Process free space before this allocation.4532if (lastOffset < suballoc.offset)4533{4534// There is free space from lastOffset to suballoc.offset.4535++unusedRangeCount;4536}45374538// 2. Process this allocation.4539// There is allocation with suballoc.offset, suballoc.size.4540++alloc2ndCount;4541usedBytes += suballoc.size;45424543// 3. Prepare for next iteration.4544lastOffset = suballoc.offset + suballoc.size;4545--nextAlloc2ndIndex;4546}4547// We are at the end.4548else4549{4550if (lastOffset < size)4551{4552// There is free space from lastOffset to size.4553++unusedRangeCount;4554}45554556// End of loop.4557lastOffset = size;4558}4559}4560}45614562const UINT64 unusedBytes = size - usedBytes;4563PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);45644565// SECOND PASS4566lastOffset = 0;4567if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4568{4569const UINT64 freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;4570size_t nextAlloc2ndIndex = 0;4571while (lastOffset < freeSpace2ndTo1stEnd)4572{4573// Find next non-null allocation or move nextAlloc2ndIndex to the end.4574while (nextAlloc2ndIndex < suballoc2ndCount &&4575suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4576{4577++nextAlloc2ndIndex;4578}45794580// Found non-null allocation.4581if (nextAlloc2ndIndex < suballoc2ndCount)4582{4583const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];45844585// 1. Process free space before this allocation.4586if (lastOffset < suballoc.offset)4587{4588// There is free space from lastOffset to suballoc.offset.4589const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4590PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4591}45924593// 2. Process this allocation.4594// There is allocation with suballoc.offset, suballoc.size.4595PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);45964597// 3. Prepare for next iteration.4598lastOffset = suballoc.offset + suballoc.size;4599++nextAlloc2ndIndex;4600}4601// We are at the end.4602else4603{4604if (lastOffset < freeSpace2ndTo1stEnd)4605{4606// There is free space from lastOffset to freeSpace2ndTo1stEnd.4607const UINT64 unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;4608PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4609}46104611// End of loop.4612lastOffset = freeSpace2ndTo1stEnd;4613}4614}4615}46164617nextAlloc1stIndex = m_1stNullItemsBeginCount;4618while (lastOffset < freeSpace1stTo2ndEnd)4619{4620// Find next non-null allocation or move nextAllocIndex to the end.4621while (nextAlloc1stIndex < suballoc1stCount &&4622suballocations1st[nextAlloc1stIndex].privateData == NULL)4623{4624++nextAlloc1stIndex;4625}46264627// Found non-null allocation.4628if (nextAlloc1stIndex < suballoc1stCount)4629{4630const Suballocation& suballoc = suballocations1st[nextAlloc1stIndex];46314632// 1. Process free space before this allocation.4633if (lastOffset < suballoc.offset)4634{4635// There is free space from lastOffset to suballoc.offset.4636const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4637PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4638}46394640// 2. Process this allocation.4641// There is allocation with suballoc.offset, suballoc.size.4642PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);46434644// 3. Prepare for next iteration.4645lastOffset = suballoc.offset + suballoc.size;4646++nextAlloc1stIndex;4647}4648// We are at the end.4649else4650{4651if (lastOffset < freeSpace1stTo2ndEnd)4652{4653// There is free space from lastOffset to freeSpace1stTo2ndEnd.4654const UINT64 unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;4655PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4656}46574658// End of loop.4659lastOffset = freeSpace1stTo2ndEnd;4660}4661}46624663if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4664{4665size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;4666while (lastOffset < size)4667{4668// Find next non-null allocation or move nextAlloc2ndIndex to the end.4669while (nextAlloc2ndIndex != SIZE_MAX &&4670suballocations2nd[nextAlloc2ndIndex].privateData == NULL)4671{4672--nextAlloc2ndIndex;4673}46744675// Found non-null allocation.4676if (nextAlloc2ndIndex != SIZE_MAX)4677{4678const Suballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];46794680// 1. Process free space before this allocation.4681if (lastOffset < suballoc.offset)4682{4683// There is free space from lastOffset to suballoc.offset.4684const UINT64 unusedRangeSize = suballoc.offset - lastOffset;4685PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4686}46874688// 2. Process this allocation.4689// There is allocation with suballoc.offset, suballoc.size.4690PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.privateData);46914692// 3. Prepare for next iteration.4693lastOffset = suballoc.offset + suballoc.size;4694--nextAlloc2ndIndex;4695}4696// We are at the end.4697else4698{4699if (lastOffset < size)4700{4701// There is free space from lastOffset to size.4702const UINT64 unusedRangeSize = size - lastOffset;4703PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);4704}47054706// End of loop.4707lastOffset = size;4708}4709}4710}47114712PrintDetailedMap_End(json);4713}47144715void BlockMetadata_Linear::DebugLogAllAllocations() const4716{4717const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4718for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)4719if (it->type != SUBALLOCATION_TYPE_FREE)4720DebugLogAllocation(it->offset, it->size, it->privateData);47214722const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();4723for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)4724if (it->type != SUBALLOCATION_TYPE_FREE)4725DebugLogAllocation(it->offset, it->size, it->privateData);4726}47274728Suballocation& BlockMetadata_Linear::FindSuballocation(UINT64 offset) const4729{4730const SuballocationVectorType& suballocations1st = AccessSuballocations1st();4731const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();47324733Suballocation refSuballoc;4734refSuballoc.offset = offset;4735// Rest of members stays uninitialized intentionally for better performance.47364737// Item from the 1st vector.4738{4739const SuballocationVectorType::const_iterator it = BinaryFindSorted(4740suballocations1st.begin() + m_1stNullItemsBeginCount,4741suballocations1st.end(),4742refSuballoc,4743SuballocationOffsetLess());4744if (it != suballocations1st.end())4745{4746return const_cast<Suballocation&>(*it);4747}4748}47494750if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)4751{4752// Rest of members stays uninitialized intentionally for better performance.4753const SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?4754BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) :4755BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater());4756if (it != suballocations2nd.end())4757{4758return const_cast<Suballocation&>(*it);4759}4760}47614762D3D12MA_ASSERT(0 && "Allocation not found in linear allocator!");4763return const_cast<Suballocation&>(suballocations1st.back()); // Should never occur.4764}47654766bool BlockMetadata_Linear::ShouldCompact1st() const4767{4768const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;4769const size_t suballocCount = AccessSuballocations1st().size();4770return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;4771}47724773void BlockMetadata_Linear::CleanupAfterFree()4774{4775SuballocationVectorType& suballocations1st = AccessSuballocations1st();4776SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();47774778if (IsEmpty())4779{4780suballocations1st.clear();4781suballocations2nd.clear();4782m_1stNullItemsBeginCount = 0;4783m_1stNullItemsMiddleCount = 0;4784m_2ndNullItemsCount = 0;4785m_2ndVectorMode = SECOND_VECTOR_EMPTY;4786}4787else4788{4789const size_t suballoc1stCount = suballocations1st.size();4790const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;4791D3D12MA_ASSERT(nullItem1stCount <= suballoc1stCount);47924793// Find more null items at the beginning of 1st vector.4794while (m_1stNullItemsBeginCount < suballoc1stCount &&4795suballocations1st[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE)4796{4797++m_1stNullItemsBeginCount;4798--m_1stNullItemsMiddleCount;4799}48004801// Find more null items at the end of 1st vector.4802while (m_1stNullItemsMiddleCount > 0 &&4803suballocations1st.back().type == SUBALLOCATION_TYPE_FREE)4804{4805--m_1stNullItemsMiddleCount;4806suballocations1st.pop_back();4807}48084809// Find more null items at the end of 2nd vector.4810while (m_2ndNullItemsCount > 0 &&4811suballocations2nd.back().type == SUBALLOCATION_TYPE_FREE)4812{4813--m_2ndNullItemsCount;4814suballocations2nd.pop_back();4815}48164817// Find more null items at the beginning of 2nd vector.4818while (m_2ndNullItemsCount > 0 &&4819suballocations2nd[0].type == SUBALLOCATION_TYPE_FREE)4820{4821--m_2ndNullItemsCount;4822suballocations2nd.remove(0);4823}48244825if (ShouldCompact1st())4826{4827const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;4828size_t srcIndex = m_1stNullItemsBeginCount;4829for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)4830{4831while (suballocations1st[srcIndex].type == SUBALLOCATION_TYPE_FREE)4832{4833++srcIndex;4834}4835if (dstIndex != srcIndex)4836{4837suballocations1st[dstIndex] = suballocations1st[srcIndex];4838}4839++srcIndex;4840}4841suballocations1st.resize(nonNullItemCount);4842m_1stNullItemsBeginCount = 0;4843m_1stNullItemsMiddleCount = 0;4844}48454846// 2nd vector became empty.4847if (suballocations2nd.empty())4848{4849m_2ndVectorMode = SECOND_VECTOR_EMPTY;4850}48514852// 1st vector became empty.4853if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)4854{4855suballocations1st.clear();4856m_1stNullItemsBeginCount = 0;48574858if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4859{4860// Swap 1st with 2nd. Now 2nd is empty.4861m_2ndVectorMode = SECOND_VECTOR_EMPTY;4862m_1stNullItemsMiddleCount = m_2ndNullItemsCount;4863while (m_1stNullItemsBeginCount < suballocations2nd.size() &&4864suballocations2nd[m_1stNullItemsBeginCount].type == SUBALLOCATION_TYPE_FREE)4865{4866++m_1stNullItemsBeginCount;4867--m_1stNullItemsMiddleCount;4868}4869m_2ndNullItemsCount = 0;4870m_1stVectorIndex ^= 1;4871}4872}4873}48744875D3D12MA_HEAVY_ASSERT(Validate());4876}48774878bool BlockMetadata_Linear::CreateAllocationRequest_LowerAddress(4879UINT64 allocSize,4880UINT64 allocAlignment,4881AllocationRequest* pAllocationRequest)4882{4883const UINT64 blockSize = GetSize();4884SuballocationVectorType& suballocations1st = AccessSuballocations1st();4885SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();48864887if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)4888{4889// Try to allocate at the end of 1st vector.48904891UINT64 resultBaseOffset = 0;4892if (!suballocations1st.empty())4893{4894const Suballocation& lastSuballoc = suballocations1st.back();4895resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin();4896}48974898// Start from offset equal to beginning of free space.4899UINT64 resultOffset = resultBaseOffset;4900// Apply alignment.4901resultOffset = AlignUp(resultOffset, allocAlignment);49024903const UINT64 freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?4904suballocations2nd.back().offset : blockSize;49054906// There is enough free space at the end after alignment.4907if (resultOffset + allocSize + GetDebugMargin() <= freeSpaceEnd)4908{4909// All tests passed: Success.4910pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);4911// pAllocationRequest->item, customData unused.4912pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_1ST;4913return true;4914}4915}49164917// Wrap-around to end of 2nd vector. Try to allocate there, watching for the4918// beginning of 1st vector as the end of free space.4919if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4920{4921D3D12MA_ASSERT(!suballocations1st.empty());49224923UINT64 resultBaseOffset = 0;4924if (!suballocations2nd.empty())4925{4926const Suballocation& lastSuballoc = suballocations2nd.back();4927resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + GetDebugMargin();4928}49294930// Start from offset equal to beginning of free space.4931UINT64 resultOffset = resultBaseOffset;49324933// Apply alignment.4934resultOffset = AlignUp(resultOffset, allocAlignment);49354936size_t index1st = m_1stNullItemsBeginCount;4937// There is enough free space at the end after alignment.4938if ((index1st == suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= blockSize) ||4939(index1st < suballocations1st.size() && resultOffset + allocSize + GetDebugMargin() <= suballocations1st[index1st].offset))4940{4941// All tests passed: Success.4942pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);4943pAllocationRequest->algorithmData = ALLOC_REQUEST_END_OF_2ND;4944// pAllocationRequest->item, customData unused.4945return true;4946}4947}4948return false;4949}49504951bool BlockMetadata_Linear::CreateAllocationRequest_UpperAddress(4952UINT64 allocSize,4953UINT64 allocAlignment,4954AllocationRequest* pAllocationRequest)4955{4956const UINT64 blockSize = GetSize();4957SuballocationVectorType& suballocations1st = AccessSuballocations1st();4958SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();49594960if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)4961{4962D3D12MA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");4963return false;4964}49654966// Try to allocate before 2nd.back(), or end of block if 2nd.empty().4967if (allocSize > blockSize)4968{4969return false;4970}4971UINT64 resultBaseOffset = blockSize - allocSize;4972if (!suballocations2nd.empty())4973{4974const Suballocation& lastSuballoc = suballocations2nd.back();4975resultBaseOffset = lastSuballoc.offset - allocSize;4976if (allocSize > lastSuballoc.offset)4977{4978return false;4979}4980}49814982// Start from offset equal to end of free space.4983UINT64 resultOffset = resultBaseOffset;4984// Apply debugMargin at the end.4985if (GetDebugMargin() > 0)4986{4987if (resultOffset < GetDebugMargin())4988{4989return false;4990}4991resultOffset -= GetDebugMargin();4992}49934994// Apply alignment.4995resultOffset = AlignDown(resultOffset, allocAlignment);4996// There is enough free space.4997const UINT64 endOf1st = !suballocations1st.empty() ?4998suballocations1st.back().offset + suballocations1st.back().size : 0;49995000if (endOf1st + GetDebugMargin() <= resultOffset)5001{5002// All tests passed: Success.5003pAllocationRequest->allocHandle = (AllocHandle)(resultOffset + 1);5004// pAllocationRequest->item unused.5005pAllocationRequest->algorithmData = ALLOC_REQUEST_UPPER_ADDRESS;5006return true;5007}5008return false;5009}5010#endif // _D3D12MA_BLOCK_METADATA_LINEAR_FUNCTIONS5011#endif // _D3D12MA_BLOCK_METADATA_LINEAR50125013#ifndef _D3D12MA_BLOCK_METADATA_TLSF5014class BlockMetadata_TLSF : public BlockMetadata5015{5016public:5017BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual);5018virtual ~BlockMetadata_TLSF();50195020size_t GetAllocationCount() const override { return m_AllocCount; }5021size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }5022UINT64 GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }5023bool IsEmpty() const override { return m_NullBlock->offset == 0; }5024UINT64 GetAllocationOffset(AllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; };50255026void Init(UINT64 size) override;5027bool Validate() const override;5028void GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const override;50295030bool CreateAllocationRequest(5031UINT64 allocSize,5032UINT64 allocAlignment,5033bool upperAddress,5034UINT32 strategy,5035AllocationRequest* pAllocationRequest) override;50365037void Alloc(5038const AllocationRequest& request,5039UINT64 allocSize,5040void* privateData) override;50415042void Free(AllocHandle allocHandle) override;5043void Clear() override;50445045AllocHandle GetAllocationListBegin() const override;5046AllocHandle GetNextAllocation(AllocHandle prevAlloc) const override;5047UINT64 GetNextFreeRegionSize(AllocHandle alloc) const override;5048void* GetAllocationPrivateData(AllocHandle allocHandle) const override;5049void SetAllocationPrivateData(AllocHandle allocHandle, void* privateData) override;50505051void AddStatistics(Statistics& inoutStats) const override;5052void AddDetailedStatistics(DetailedStatistics& inoutStats) const override;5053void WriteAllocationInfoToJson(JsonWriter& json) const override;5054void DebugLogAllAllocations() const override;50555056private:5057// According to original paper it should be preferable 4 or 5:5058// M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"5059// http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf5060static const UINT8 SECOND_LEVEL_INDEX = 5;5061static const UINT16 SMALL_BUFFER_SIZE = 256;5062static const UINT INITIAL_BLOCK_ALLOC_COUNT = 16;5063static const UINT8 MEMORY_CLASS_SHIFT = 7;5064static const UINT8 MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;50655066class Block5067{5068public:5069UINT64 offset;5070UINT64 size;5071Block* prevPhysical;5072Block* nextPhysical;50735074void MarkFree() { prevFree = NULL; }5075void MarkTaken() { prevFree = this; }5076bool IsFree() const { return prevFree != this; }5077void*& PrivateData() { D3D12MA_HEAVY_ASSERT(!IsFree()); return privateData; }5078Block*& PrevFree() { return prevFree; }5079Block*& NextFree() { D3D12MA_HEAVY_ASSERT(IsFree()); return nextFree; }50805081private:5082Block* prevFree; // Address of the same block here indicates that block is taken5083union5084{5085Block* nextFree;5086void* privateData;5087};5088};50895090size_t m_AllocCount = 0;5091// Total number of free blocks besides null block5092size_t m_BlocksFreeCount = 0;5093// Total size of free blocks excluding null block5094UINT64 m_BlocksFreeSize = 0;5095UINT32 m_IsFreeBitmap = 0;5096UINT8 m_MemoryClasses = 0;5097UINT32 m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];5098UINT32 m_ListsCount = 0;5099/*5100* 0: 0-3 lists for small buffers5101* 1+: 0-(2^SLI-1) lists for normal buffers5102*/5103Block** m_FreeList = NULL;5104PoolAllocator<Block> m_BlockAllocator;5105Block* m_NullBlock = NULL;51065107UINT8 SizeToMemoryClass(UINT64 size) const;5108UINT16 SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const;5109UINT32 GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const;5110UINT32 GetListIndex(UINT64 size) const;51115112void RemoveFreeBlock(Block* block);5113void InsertFreeBlock(Block* block);5114void MergeBlock(Block* block, Block* prev);51155116Block* FindFreeBlock(UINT64 size, UINT32& listIndex) const;5117bool CheckBlock(5118Block& block,5119UINT32 listIndex,5120UINT64 allocSize,5121UINT64 allocAlignment,5122AllocationRequest* pAllocationRequest);51235124D3D12MA_CLASS_NO_COPY(BlockMetadata_TLSF)5125};51265127#ifndef _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS5128BlockMetadata_TLSF::BlockMetadata_TLSF(const ALLOCATION_CALLBACKS* allocationCallbacks, bool isVirtual)5129: BlockMetadata(allocationCallbacks, isVirtual),5130m_BlockAllocator(*allocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT)5131{5132D3D12MA_ASSERT(allocationCallbacks);5133}51345135BlockMetadata_TLSF::~BlockMetadata_TLSF()5136{5137D3D12MA_DELETE_ARRAY(*GetAllocs(), m_FreeList, m_ListsCount);5138}51395140void BlockMetadata_TLSF::Init(UINT64 size)5141{5142BlockMetadata::Init(size);51435144m_NullBlock = m_BlockAllocator.Alloc();5145m_NullBlock->size = size;5146m_NullBlock->offset = 0;5147m_NullBlock->prevPhysical = NULL;5148m_NullBlock->nextPhysical = NULL;5149m_NullBlock->MarkFree();5150m_NullBlock->NextFree() = NULL;5151m_NullBlock->PrevFree() = NULL;5152UINT8 memoryClass = SizeToMemoryClass(size);5153UINT16 sli = SizeToSecondIndex(size, memoryClass);5154m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;5155if (IsVirtual())5156m_ListsCount += 1UL << SECOND_LEVEL_INDEX;5157else5158m_ListsCount += 4;51595160m_MemoryClasses = memoryClass + 2;5161memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(UINT32));51625163m_FreeList = D3D12MA_NEW_ARRAY(*GetAllocs(), Block*, m_ListsCount);5164memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));5165}51665167bool BlockMetadata_TLSF::Validate() const5168{5169D3D12MA_VALIDATE(GetSumFreeSize() <= GetSize());51705171UINT64 calculatedSize = m_NullBlock->size;5172UINT64 calculatedFreeSize = m_NullBlock->size;5173size_t allocCount = 0;5174size_t freeCount = 0;51755176// Check integrity of free lists5177for (UINT32 list = 0; list < m_ListsCount; ++list)5178{5179Block* block = m_FreeList[list];5180if (block != NULL)5181{5182D3D12MA_VALIDATE(block->IsFree());5183D3D12MA_VALIDATE(block->PrevFree() == NULL);5184while (block->NextFree())5185{5186D3D12MA_VALIDATE(block->NextFree()->IsFree());5187D3D12MA_VALIDATE(block->NextFree()->PrevFree() == block);5188block = block->NextFree();5189}5190}5191}51925193D3D12MA_VALIDATE(m_NullBlock->nextPhysical == NULL);5194if (m_NullBlock->prevPhysical)5195{5196D3D12MA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);5197}51985199// Check all blocks5200UINT64 nextOffset = m_NullBlock->offset;5201for (Block* prev = m_NullBlock->prevPhysical; prev != NULL; prev = prev->prevPhysical)5202{5203D3D12MA_VALIDATE(prev->offset + prev->size == nextOffset);5204nextOffset = prev->offset;5205calculatedSize += prev->size;52065207UINT32 listIndex = GetListIndex(prev->size);5208if (prev->IsFree())5209{5210++freeCount;5211// Check if free block belongs to free list5212Block* freeBlock = m_FreeList[listIndex];5213D3D12MA_VALIDATE(freeBlock != NULL);52145215bool found = false;5216do5217{5218if (freeBlock == prev)5219found = true;52205221freeBlock = freeBlock->NextFree();5222} while (!found && freeBlock != NULL);52235224D3D12MA_VALIDATE(found);5225calculatedFreeSize += prev->size;5226}5227else5228{5229++allocCount;5230// Check if taken block is not on a free list5231Block* freeBlock = m_FreeList[listIndex];5232while (freeBlock)5233{5234D3D12MA_VALIDATE(freeBlock != prev);5235freeBlock = freeBlock->NextFree();5236}5237}52385239if (prev->prevPhysical)5240{5241D3D12MA_VALIDATE(prev->prevPhysical->nextPhysical == prev);5242}5243}52445245D3D12MA_VALIDATE(nextOffset == 0);5246D3D12MA_VALIDATE(calculatedSize == GetSize());5247D3D12MA_VALIDATE(calculatedFreeSize == GetSumFreeSize());5248D3D12MA_VALIDATE(allocCount == m_AllocCount);5249D3D12MA_VALIDATE(freeCount == m_BlocksFreeCount);52505251return true;5252}52535254void BlockMetadata_TLSF::GetAllocationInfo(AllocHandle allocHandle, VIRTUAL_ALLOCATION_INFO& outInfo) const5255{5256Block* block = (Block*)allocHandle;5257D3D12MA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");5258outInfo.Offset = block->offset;5259outInfo.Size = block->size;5260outInfo.pPrivateData = block->PrivateData();5261}52625263bool BlockMetadata_TLSF::CreateAllocationRequest(5264UINT64 allocSize,5265UINT64 allocAlignment,5266bool upperAddress,5267UINT32 strategy,5268AllocationRequest* pAllocationRequest)5269{5270D3D12MA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");5271D3D12MA_ASSERT(!upperAddress && "ALLOCATION_FLAG_UPPER_ADDRESS can be used only with linear algorithm.");5272D3D12MA_ASSERT(pAllocationRequest != NULL);5273D3D12MA_HEAVY_ASSERT(Validate());52745275allocSize += GetDebugMargin();5276// Quick check for too small pool5277if (allocSize > GetSumFreeSize())5278return false;52795280// If no free blocks in pool then check only null block5281if (m_BlocksFreeCount == 0)5282return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest);52835284// Round up to the next block5285UINT64 sizeForNextList = allocSize;5286UINT16 smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4);5287if (allocSize > SMALL_BUFFER_SIZE)5288{5289sizeForNextList += (1ULL << (BitScanMSB(allocSize) - SECOND_LEVEL_INDEX));5290}5291else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)5292sizeForNextList = SMALL_BUFFER_SIZE + 1;5293else5294sizeForNextList += smallSizeStep;52955296UINT32 nextListIndex = 0;5297UINT32 prevListIndex = 0;5298Block* nextListBlock = NULL;5299Block* prevListBlock = NULL;53005301// Check blocks according to strategies5302if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_TIME)5303{5304// Quick check for larger block first5305nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);5306if (nextListBlock != NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5307return true;53085309// If not fitted then null block5310if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))5311return true;53125313// Null block failed, search larger bucket5314while (nextListBlock)5315{5316if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5317return true;5318nextListBlock = nextListBlock->NextFree();5319}53205321// Failed again, check best fit bucket5322prevListBlock = FindFreeBlock(allocSize, prevListIndex);5323while (prevListBlock)5324{5325if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))5326return true;5327prevListBlock = prevListBlock->NextFree();5328}5329}5330else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_MEMORY)5331{5332// Check best fit bucket5333prevListBlock = FindFreeBlock(allocSize, prevListIndex);5334while (prevListBlock)5335{5336if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))5337return true;5338prevListBlock = prevListBlock->NextFree();5339}53405341// If failed check null block5342if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))5343return true;53445345// Check larger bucket5346nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);5347while (nextListBlock)5348{5349if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5350return true;5351nextListBlock = nextListBlock->NextFree();5352}5353}5354else if (strategy & ALLOCATION_FLAG_STRATEGY_MIN_OFFSET)5355{5356// Perform search from the start5357Vector<Block*> blockList(m_BlocksFreeCount, *GetAllocs());53585359size_t i = m_BlocksFreeCount;5360for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5361{5362if (block->IsFree() && block->size >= allocSize)5363blockList[--i] = block;5364}53655366for (; i < m_BlocksFreeCount; ++i)5367{5368Block& block = *blockList[i];5369if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, pAllocationRequest))5370return true;5371}53725373// If failed check null block5374if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))5375return true;53765377// Whole range searched, no more memory5378return false;5379}5380else5381{5382// Check larger bucket5383nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);5384while (nextListBlock)5385{5386if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5387return true;5388nextListBlock = nextListBlock->NextFree();5389}53905391// If failed check null block5392if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, pAllocationRequest))5393return true;53945395// Check best fit bucket5396prevListBlock = FindFreeBlock(allocSize, prevListIndex);5397while (prevListBlock)5398{5399if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, pAllocationRequest))5400return true;5401prevListBlock = prevListBlock->NextFree();5402}5403}54045405// Worst case, full search has to be done5406while (++nextListIndex < m_ListsCount)5407{5408nextListBlock = m_FreeList[nextListIndex];5409while (nextListBlock)5410{5411if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, pAllocationRequest))5412return true;5413nextListBlock = nextListBlock->NextFree();5414}5415}54165417// No more memory sadly5418return false;5419}54205421void BlockMetadata_TLSF::Alloc(5422const AllocationRequest& request,5423UINT64 allocSize,5424void* privateData)5425{5426// Get block and pop it from the free list5427Block* currentBlock = (Block*)request.allocHandle;5428UINT64 offset = request.algorithmData;5429D3D12MA_ASSERT(currentBlock != NULL);5430D3D12MA_ASSERT(currentBlock->offset <= offset);54315432if (currentBlock != m_NullBlock)5433RemoveFreeBlock(currentBlock);54345435// Append missing alignment to prev block or create new one5436UINT64 misssingAlignment = offset - currentBlock->offset;5437if (misssingAlignment)5438{5439Block* prevBlock = currentBlock->prevPhysical;5440D3D12MA_ASSERT(prevBlock != NULL && "There should be no missing alignment at offset 0!");54415442if (prevBlock->IsFree() && prevBlock->size != GetDebugMargin())5443{5444UINT32 oldList = GetListIndex(prevBlock->size);5445prevBlock->size += misssingAlignment;5446// Check if new size crosses list bucket5447if (oldList != GetListIndex(prevBlock->size))5448{5449prevBlock->size -= misssingAlignment;5450RemoveFreeBlock(prevBlock);5451prevBlock->size += misssingAlignment;5452InsertFreeBlock(prevBlock);5453}5454else5455m_BlocksFreeSize += misssingAlignment;5456}5457else5458{5459Block* newBlock = m_BlockAllocator.Alloc();5460currentBlock->prevPhysical = newBlock;5461prevBlock->nextPhysical = newBlock;5462newBlock->prevPhysical = prevBlock;5463newBlock->nextPhysical = currentBlock;5464newBlock->size = misssingAlignment;5465newBlock->offset = currentBlock->offset;5466newBlock->MarkTaken();54675468InsertFreeBlock(newBlock);5469}54705471currentBlock->size -= misssingAlignment;5472currentBlock->offset += misssingAlignment;5473}54745475UINT64 size = request.size + GetDebugMargin();5476if (currentBlock->size == size)5477{5478if (currentBlock == m_NullBlock)5479{5480// Setup new null block5481m_NullBlock = m_BlockAllocator.Alloc();5482m_NullBlock->size = 0;5483m_NullBlock->offset = currentBlock->offset + size;5484m_NullBlock->prevPhysical = currentBlock;5485m_NullBlock->nextPhysical = NULL;5486m_NullBlock->MarkFree();5487m_NullBlock->PrevFree() = NULL;5488m_NullBlock->NextFree() = NULL;5489currentBlock->nextPhysical = m_NullBlock;5490currentBlock->MarkTaken();5491}5492}5493else5494{5495D3D12MA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");54965497// Create new free block5498Block* newBlock = m_BlockAllocator.Alloc();5499newBlock->size = currentBlock->size - size;5500newBlock->offset = currentBlock->offset + size;5501newBlock->prevPhysical = currentBlock;5502newBlock->nextPhysical = currentBlock->nextPhysical;5503currentBlock->nextPhysical = newBlock;5504currentBlock->size = size;55055506if (currentBlock == m_NullBlock)5507{5508m_NullBlock = newBlock;5509m_NullBlock->MarkFree();5510m_NullBlock->NextFree() = NULL;5511m_NullBlock->PrevFree() = NULL;5512currentBlock->MarkTaken();5513}5514else5515{5516newBlock->nextPhysical->prevPhysical = newBlock;5517newBlock->MarkTaken();5518InsertFreeBlock(newBlock);5519}5520}5521currentBlock->PrivateData() = privateData;55225523if (GetDebugMargin() > 0)5524{5525currentBlock->size -= GetDebugMargin();5526Block* newBlock = m_BlockAllocator.Alloc();5527newBlock->size = GetDebugMargin();5528newBlock->offset = currentBlock->offset + currentBlock->size;5529newBlock->prevPhysical = currentBlock;5530newBlock->nextPhysical = currentBlock->nextPhysical;5531newBlock->MarkTaken();5532currentBlock->nextPhysical->prevPhysical = newBlock;5533currentBlock->nextPhysical = newBlock;5534InsertFreeBlock(newBlock);5535}5536++m_AllocCount;5537}55385539void BlockMetadata_TLSF::Free(AllocHandle allocHandle)5540{5541Block* block = (Block*)allocHandle;5542Block* next = block->nextPhysical;5543D3D12MA_ASSERT(!block->IsFree() && "Block is already free!");55445545--m_AllocCount;5546if (GetDebugMargin() > 0)5547{5548RemoveFreeBlock(next);5549MergeBlock(next, block);5550block = next;5551next = next->nextPhysical;5552}55535554// Try merging5555Block* prev = block->prevPhysical;5556if (prev != NULL && prev->IsFree() && prev->size != GetDebugMargin())5557{5558RemoveFreeBlock(prev);5559MergeBlock(block, prev);5560}55615562if (!next->IsFree())5563InsertFreeBlock(block);5564else if (next == m_NullBlock)5565MergeBlock(m_NullBlock, block);5566else5567{5568RemoveFreeBlock(next);5569MergeBlock(next, block);5570InsertFreeBlock(next);5571}5572}55735574void BlockMetadata_TLSF::Clear()5575{5576m_AllocCount = 0;5577m_BlocksFreeCount = 0;5578m_BlocksFreeSize = 0;5579m_IsFreeBitmap = 0;5580m_NullBlock->offset = 0;5581m_NullBlock->size = GetSize();5582Block* block = m_NullBlock->prevPhysical;5583m_NullBlock->prevPhysical = NULL;5584while (block)5585{5586Block* prev = block->prevPhysical;5587m_BlockAllocator.Free(block);5588block = prev;5589}5590memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));5591memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(UINT32));5592}55935594AllocHandle BlockMetadata_TLSF::GetAllocationListBegin() const5595{5596if (m_AllocCount == 0)5597return (AllocHandle)0;55985599for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)5600{5601if (!block->IsFree())5602return (AllocHandle)block;5603}5604D3D12MA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");5605return (AllocHandle)0;5606}56075608AllocHandle BlockMetadata_TLSF::GetNextAllocation(AllocHandle prevAlloc) const5609{5610Block* startBlock = (Block*)prevAlloc;5611D3D12MA_ASSERT(!startBlock->IsFree() && "Incorrect block!");56125613for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)5614{5615if (!block->IsFree())5616return (AllocHandle)block;5617}5618return (AllocHandle)0;5619}56205621UINT64 BlockMetadata_TLSF::GetNextFreeRegionSize(AllocHandle alloc) const5622{5623Block* block = (Block*)alloc;5624D3D12MA_ASSERT(!block->IsFree() && "Incorrect block!");56255626if (block->prevPhysical)5627return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;5628return 0;5629}56305631void* BlockMetadata_TLSF::GetAllocationPrivateData(AllocHandle allocHandle) const5632{5633Block* block = (Block*)allocHandle;5634D3D12MA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");5635return block->PrivateData();5636}56375638void BlockMetadata_TLSF::SetAllocationPrivateData(AllocHandle allocHandle, void* privateData)5639{5640Block* block = (Block*)allocHandle;5641D3D12MA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");5642block->PrivateData() = privateData;5643}56445645void BlockMetadata_TLSF::AddStatistics(Statistics& inoutStats) const5646{5647inoutStats.BlockCount++;5648inoutStats.AllocationCount += static_cast<UINT>(m_AllocCount);5649inoutStats.BlockBytes += GetSize();5650inoutStats.AllocationBytes += GetSize() - GetSumFreeSize();5651}56525653void BlockMetadata_TLSF::AddDetailedStatistics(DetailedStatistics& inoutStats) const5654{5655inoutStats.Stats.BlockCount++;5656inoutStats.Stats.BlockBytes += GetSize();56575658for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5659{5660if (block->IsFree())5661AddDetailedStatisticsUnusedRange(inoutStats, block->size);5662else5663AddDetailedStatisticsAllocation(inoutStats, block->size);5664}56655666if (m_NullBlock->size > 0)5667AddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);5668}56695670void BlockMetadata_TLSF::WriteAllocationInfoToJson(JsonWriter& json) const5671{5672size_t blockCount = m_AllocCount + m_BlocksFreeCount;5673Vector<Block*> blockList(blockCount, *GetAllocs());56745675size_t i = blockCount;5676if (m_NullBlock->size > 0)5677{5678++blockCount;5679blockList.push_back(m_NullBlock);5680}5681for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5682{5683blockList[--i] = block;5684}5685D3D12MA_ASSERT(i == 0);56865687PrintDetailedMap_Begin(json, GetSumFreeSize(), GetAllocationCount(), m_BlocksFreeCount + static_cast<bool>(m_NullBlock->size));5688for (; i < blockCount; ++i)5689{5690Block* block = blockList[i];5691if (block->IsFree())5692PrintDetailedMap_UnusedRange(json, block->offset, block->size);5693else5694PrintDetailedMap_Allocation(json, block->offset, block->size, block->PrivateData());5695}5696PrintDetailedMap_End(json);5697}56985699void BlockMetadata_TLSF::DebugLogAllAllocations() const5700{5701for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical)5702{5703if (!block->IsFree())5704{5705DebugLogAllocation(block->offset, block->size, block->PrivateData());5706}5707}5708}57095710UINT8 BlockMetadata_TLSF::SizeToMemoryClass(UINT64 size) const5711{5712if (size > SMALL_BUFFER_SIZE)5713return BitScanMSB(size) - MEMORY_CLASS_SHIFT;5714return 0;5715}57165717UINT16 BlockMetadata_TLSF::SizeToSecondIndex(UINT64 size, UINT8 memoryClass) const5718{5719if (memoryClass == 0)5720{5721if (IsVirtual())5722return static_cast<UINT16>((size - 1) / 8);5723else5724return static_cast<UINT16>((size - 1) / 64);5725}5726return static_cast<UINT16>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));5727}57285729UINT32 BlockMetadata_TLSF::GetListIndex(UINT8 memoryClass, UINT16 secondIndex) const5730{5731if (memoryClass == 0)5732return secondIndex;57335734const UINT32 index = static_cast<UINT32>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;5735if (IsVirtual())5736return index + (1 << SECOND_LEVEL_INDEX);5737else5738return index + 4;5739}57405741UINT32 BlockMetadata_TLSF::GetListIndex(UINT64 size) const5742{5743UINT8 memoryClass = SizeToMemoryClass(size);5744return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));5745}57465747void BlockMetadata_TLSF::RemoveFreeBlock(Block* block)5748{5749D3D12MA_ASSERT(block != m_NullBlock);5750D3D12MA_ASSERT(block->IsFree());57515752if (block->NextFree() != NULL)5753block->NextFree()->PrevFree() = block->PrevFree();5754if (block->PrevFree() != NULL)5755block->PrevFree()->NextFree() = block->NextFree();5756else5757{5758UINT8 memClass = SizeToMemoryClass(block->size);5759UINT16 secondIndex = SizeToSecondIndex(block->size, memClass);5760UINT32 index = GetListIndex(memClass, secondIndex);5761m_FreeList[index] = block->NextFree();5762if (block->NextFree() == NULL)5763{5764m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);5765if (m_InnerIsFreeBitmap[memClass] == 0)5766m_IsFreeBitmap &= ~(1UL << memClass);5767}5768}5769block->MarkTaken();5770block->PrivateData() = NULL;5771--m_BlocksFreeCount;5772m_BlocksFreeSize -= block->size;5773}57745775void BlockMetadata_TLSF::InsertFreeBlock(Block* block)5776{5777D3D12MA_ASSERT(block != m_NullBlock);5778D3D12MA_ASSERT(!block->IsFree() && "Cannot insert block twice!");57795780UINT8 memClass = SizeToMemoryClass(block->size);5781UINT16 secondIndex = SizeToSecondIndex(block->size, memClass);5782UINT32 index = GetListIndex(memClass, secondIndex);5783block->PrevFree() = NULL;5784block->NextFree() = m_FreeList[index];5785m_FreeList[index] = block;5786if (block->NextFree() != NULL)5787block->NextFree()->PrevFree() = block;5788else5789{5790m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;5791m_IsFreeBitmap |= 1UL << memClass;5792}5793++m_BlocksFreeCount;5794m_BlocksFreeSize += block->size;5795}57965797void BlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)5798{5799D3D12MA_ASSERT(block->prevPhysical == prev && "Cannot merge seperate physical regions!");5800D3D12MA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");58015802block->offset = prev->offset;5803block->size += prev->size;5804block->prevPhysical = prev->prevPhysical;5805if (block->prevPhysical)5806block->prevPhysical->nextPhysical = block;5807m_BlockAllocator.Free(prev);5808}58095810BlockMetadata_TLSF::Block* BlockMetadata_TLSF::FindFreeBlock(UINT64 size, UINT32& listIndex) const5811{5812UINT8 memoryClass = SizeToMemoryClass(size);5813UINT32 innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));5814if (!innerFreeMap)5815{5816// Check higher levels for avaiable blocks5817UINT32 freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));5818if (!freeMap)5819return NULL; // No more memory avaible58205821// Find lowest free region5822memoryClass = BitScanLSB(freeMap);5823innerFreeMap = m_InnerIsFreeBitmap[memoryClass];5824D3D12MA_ASSERT(innerFreeMap != 0);5825}5826// Find lowest free subregion5827listIndex = GetListIndex(memoryClass, BitScanLSB(innerFreeMap));5828return m_FreeList[listIndex];5829}58305831bool BlockMetadata_TLSF::CheckBlock(5832Block& block,5833UINT32 listIndex,5834UINT64 allocSize,5835UINT64 allocAlignment,5836AllocationRequest* pAllocationRequest)5837{5838D3D12MA_ASSERT(block.IsFree() && "Block is already taken!");58395840UINT64 alignedOffset = AlignUp(block.offset, allocAlignment);5841if (block.size < allocSize + alignedOffset - block.offset)5842return false;58435844// Alloc successful5845pAllocationRequest->allocHandle = (AllocHandle)█5846pAllocationRequest->size = allocSize - GetDebugMargin();5847pAllocationRequest->algorithmData = alignedOffset;58485849// Place block at the start of list if it's normal block5850if (listIndex != m_ListsCount && block.PrevFree())5851{5852block.PrevFree()->NextFree() = block.NextFree();5853if (block.NextFree())5854block.NextFree()->PrevFree() = block.PrevFree();5855block.PrevFree() = NULL;5856block.NextFree() = m_FreeList[listIndex];5857m_FreeList[listIndex] = █5858if (block.NextFree())5859block.NextFree()->PrevFree() = █5860}58615862return true;5863}5864#endif // _D3D12MA_BLOCK_METADATA_TLSF_FUNCTIONS5865#endif // _D3D12MA_BLOCK_METADATA_TLSF58665867#ifndef _D3D12MA_MEMORY_BLOCK5868/*5869Represents a single block of device memory (heap).5870Base class for inheritance.5871Thread-safety: This class must be externally synchronized.5872*/5873class MemoryBlock5874{5875public:5876// Creates the ID3D12Heap.5877MemoryBlock(5878AllocatorPimpl* allocator,5879const D3D12_HEAP_PROPERTIES& heapProps,5880D3D12_HEAP_FLAGS heapFlags,5881UINT64 size,5882UINT id);5883virtual ~MemoryBlock();58845885const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }5886D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }5887UINT64 GetSize() const { return m_Size; }5888UINT GetId() const { return m_Id; }5889ID3D12Heap* GetHeap() const { return m_Heap; }58905891protected:5892AllocatorPimpl* const m_Allocator;5893const D3D12_HEAP_PROPERTIES m_HeapProps;5894const D3D12_HEAP_FLAGS m_HeapFlags;5895const UINT64 m_Size;5896const UINT m_Id;58975898HRESULT Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures);58995900private:5901ID3D12Heap* m_Heap = NULL;59025903D3D12MA_CLASS_NO_COPY(MemoryBlock)5904};5905#endif // _D3D12MA_MEMORY_BLOCK59065907#ifndef _D3D12MA_NORMAL_BLOCK5908/*5909Represents a single block of device memory (heap) with all the data about its5910regions (aka suballocations, Allocation), assigned and free.5911Thread-safety: This class must be externally synchronized.5912*/5913class NormalBlock : public MemoryBlock5914{5915public:5916BlockMetadata* m_pMetadata;59175918NormalBlock(5919AllocatorPimpl* allocator,5920BlockVector* blockVector,5921const D3D12_HEAP_PROPERTIES& heapProps,5922D3D12_HEAP_FLAGS heapFlags,5923UINT64 size,5924UINT id);5925virtual ~NormalBlock();59265927BlockVector* GetBlockVector() const { return m_BlockVector; }59285929// 'algorithm' should be one of the *_ALGORITHM_* flags in enums POOL_FLAGS or VIRTUAL_BLOCK_FLAGS5930HRESULT Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures);59315932// Validates all data structures inside this object. If not valid, returns false.5933bool Validate() const;59345935private:5936BlockVector* m_BlockVector;59375938D3D12MA_CLASS_NO_COPY(NormalBlock)5939};5940#endif // _D3D12MA_NORMAL_BLOCK59415942#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS5943struct CommittedAllocationListItemTraits5944{5945using ItemType = Allocation;59465947static ItemType* GetPrev(const ItemType* item)5948{5949D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5950return item->m_Committed.prev;5951}5952static ItemType* GetNext(const ItemType* item)5953{5954D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5955return item->m_Committed.next;5956}5957static ItemType*& AccessPrev(ItemType* item)5958{5959D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5960return item->m_Committed.prev;5961}5962static ItemType*& AccessNext(ItemType* item)5963{5964D3D12MA_ASSERT(item->m_PackedData.GetType() == Allocation::TYPE_COMMITTED || item->m_PackedData.GetType() == Allocation::TYPE_HEAP);5965return item->m_Committed.next;5966}5967};5968#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_ITEM_TRAITS59695970#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST5971/*5972Stores linked list of Allocation objects that are of TYPE_COMMITTED or TYPE_HEAP.5973Thread-safe, synchronized internally.5974*/5975class CommittedAllocationList5976{5977public:5978CommittedAllocationList() = default;5979void Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool);5980~CommittedAllocationList();59815982D3D12_HEAP_TYPE GetHeapType() const { return m_HeapType; }5983PoolPimpl* GetPool() const { return m_Pool; }5984UINT GetMemorySegmentGroup(AllocatorPimpl* allocator) const;59855986void AddStatistics(Statistics& inoutStats);5987void AddDetailedStatistics(DetailedStatistics& inoutStats);5988// Writes JSON array with the list of allocations.5989void BuildStatsString(JsonWriter& json);59905991void Register(Allocation* alloc);5992void Unregister(Allocation* alloc);59935994private:5995using CommittedAllocationLinkedList = IntrusiveLinkedList<CommittedAllocationListItemTraits>;59965997bool m_UseMutex = true;5998D3D12_HEAP_TYPE m_HeapType = D3D12_HEAP_TYPE_CUSTOM;5999PoolPimpl* m_Pool = NULL;60006001D3D12MA_RW_MUTEX m_Mutex;6002CommittedAllocationLinkedList m_AllocationList;6003};6004#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST60056006#ifndef _D3D12M_COMMITTED_ALLOCATION_PARAMETERS6007struct CommittedAllocationParameters6008{6009CommittedAllocationList* m_List = NULL;6010D3D12_HEAP_PROPERTIES m_HeapProperties = {};6011D3D12_HEAP_FLAGS m_HeapFlags = D3D12_HEAP_FLAG_NONE;6012ID3D12ProtectedResourceSession* m_ProtectedSession = NULL;6013bool m_CanAlias = false;6014D3D12_RESIDENCY_PRIORITY m_ResidencyPriority = D3D12_RESIDENCY_PRIORITY_NONE;60156016bool IsValid() const { return m_List != NULL; }6017};6018#endif // _D3D12M_COMMITTED_ALLOCATION_PARAMETERS60196020// Simple variant data structure to hold all possible variations of ID3D12Device*::CreateCommittedResource* and ID3D12Device*::CreatePlacedResource* arguments6021struct CREATE_RESOURCE_PARAMS6022{6023CREATE_RESOURCE_PARAMS() = delete;6024CREATE_RESOURCE_PARAMS(6025const D3D12_RESOURCE_DESC* pResourceDesc,6026D3D12_RESOURCE_STATES InitialResourceState,6027const D3D12_CLEAR_VALUE* pOptimizedClearValue)6028: Variant(VARIANT_WITH_STATE)6029, pResourceDesc(pResourceDesc)6030, InitialResourceState(InitialResourceState)6031, pOptimizedClearValue(pOptimizedClearValue)6032{6033}6034#ifdef __ID3D12Device8_INTERFACE_DEFINED__6035CREATE_RESOURCE_PARAMS(6036const D3D12_RESOURCE_DESC1* pResourceDesc,6037D3D12_RESOURCE_STATES InitialResourceState,6038const D3D12_CLEAR_VALUE* pOptimizedClearValue)6039: Variant(VARIANT_WITH_STATE_AND_DESC1)6040, pResourceDesc1(pResourceDesc)6041, InitialResourceState(InitialResourceState)6042, pOptimizedClearValue(pOptimizedClearValue)6043{6044}6045#endif6046#ifdef __ID3D12Device10_INTERFACE_DEFINED__6047CREATE_RESOURCE_PARAMS(6048const D3D12_RESOURCE_DESC1* pResourceDesc,6049D3D12_BARRIER_LAYOUT InitialLayout,6050const D3D12_CLEAR_VALUE* pOptimizedClearValue,6051UINT32 NumCastableFormats,6052DXGI_FORMAT* pCastableFormats)6053: Variant(VARIANT_WITH_LAYOUT)6054, pResourceDesc1(pResourceDesc)6055, InitialLayout(InitialLayout)6056, pOptimizedClearValue(pOptimizedClearValue)6057, NumCastableFormats(NumCastableFormats)6058, pCastableFormats(pCastableFormats)6059{6060}6061#endif60626063enum VARIANT6064{6065VARIANT_INVALID = 0,6066VARIANT_WITH_STATE,6067VARIANT_WITH_STATE_AND_DESC1,6068VARIANT_WITH_LAYOUT6069};60706071VARIANT Variant = VARIANT_INVALID;60726073const D3D12_RESOURCE_DESC* GetResourceDesc() const6074{6075D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE);6076return pResourceDesc;6077}6078const D3D12_RESOURCE_DESC*& AccessResourceDesc()6079{6080D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE);6081return pResourceDesc;6082}6083const D3D12_RESOURCE_DESC* GetBaseResourceDesc() const6084{6085// D3D12_RESOURCE_DESC1 can be cast to D3D12_RESOURCE_DESC by discarding the new members at the end.6086return pResourceDesc;6087}6088D3D12_RESOURCE_STATES GetInitialResourceState() const6089{6090D3D12MA_ASSERT(Variant < VARIANT_WITH_LAYOUT);6091return InitialResourceState;6092}6093const D3D12_CLEAR_VALUE* GetOptimizedClearValue() const6094{6095return pOptimizedClearValue;6096}60976098#ifdef __ID3D12Device8_INTERFACE_DEFINED__6099const D3D12_RESOURCE_DESC1* GetResourceDesc1() const6100{6101D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1);6102return pResourceDesc1;6103}6104const D3D12_RESOURCE_DESC1*& AccessResourceDesc1()6105{6106D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1);6107return pResourceDesc1;6108}6109#endif61106111#ifdef __ID3D12Device10_INTERFACE_DEFINED__6112D3D12_BARRIER_LAYOUT GetInitialLayout() const6113{6114D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);6115return InitialLayout;6116}6117UINT32 GetNumCastableFormats() const6118{6119D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);6120return NumCastableFormats;6121}6122DXGI_FORMAT* GetCastableFormats() const6123{6124D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT);6125return pCastableFormats;6126}6127#endif61286129private:6130union6131{6132const D3D12_RESOURCE_DESC* pResourceDesc;6133#ifdef __ID3D12Device8_INTERFACE_DEFINED__6134const D3D12_RESOURCE_DESC1* pResourceDesc1;6135#endif6136};6137union6138{6139D3D12_RESOURCE_STATES InitialResourceState;6140#ifdef __ID3D12Device10_INTERFACE_DEFINED__6141D3D12_BARRIER_LAYOUT InitialLayout;6142#endif6143};6144const D3D12_CLEAR_VALUE* pOptimizedClearValue;6145#ifdef __ID3D12Device10_INTERFACE_DEFINED__6146UINT32 NumCastableFormats;6147DXGI_FORMAT* pCastableFormats;6148#endif6149};61506151#ifndef _D3D12MA_BLOCK_VECTOR6152/*6153Sequence of NormalBlock. Represents memory blocks allocated for a specific6154heap type and possibly resource type (if only Tier 1 is supported).61556156Synchronized internally with a mutex.6157*/6158class BlockVector6159{6160friend class DefragmentationContextPimpl;6161D3D12MA_CLASS_NO_COPY(BlockVector)6162public:6163BlockVector(6164AllocatorPimpl* hAllocator,6165const D3D12_HEAP_PROPERTIES& heapProps,6166D3D12_HEAP_FLAGS heapFlags,6167UINT64 preferredBlockSize,6168size_t minBlockCount,6169size_t maxBlockCount,6170bool explicitBlockSize,6171UINT64 minAllocationAlignment,6172UINT32 algorithm,6173bool denyMsaaTextures,6174ID3D12ProtectedResourceSession* pProtectedSession,6175D3D12_RESIDENCY_PRIORITY residencyPriority);6176~BlockVector();6177D3D12_RESIDENCY_PRIORITY GetResidencyPriority() const { return m_ResidencyPriority; }61786179const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }6180D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }6181UINT64 GetPreferredBlockSize() const { return m_PreferredBlockSize; }6182UINT32 GetAlgorithm() const { return m_Algorithm; }6183bool DeniesMsaaTextures() const { return m_DenyMsaaTextures; }6184// To be used only while the m_Mutex is locked. Used during defragmentation.6185size_t GetBlockCount() const { return m_Blocks.size(); }6186// To be used only while the m_Mutex is locked. Used during defragmentation.6187NormalBlock* GetBlock(size_t index) const { return m_Blocks[index]; }6188D3D12MA_RW_MUTEX& GetMutex() { return m_Mutex; }61896190HRESULT CreateMinBlocks();6191bool IsEmpty();61926193HRESULT Allocate(6194UINT64 size,6195UINT64 alignment,6196const ALLOCATION_DESC& allocDesc,6197size_t allocationCount,6198Allocation** pAllocations);61996200void Free(Allocation* hAllocation);62016202HRESULT CreateResource(6203UINT64 size,6204UINT64 alignment,6205const ALLOCATION_DESC& allocDesc,6206const CREATE_RESOURCE_PARAMS& createParams,6207Allocation** ppAllocation,6208REFIID riidResource,6209void** ppvResource);62106211void AddStatistics(Statistics& inoutStats);6212void AddDetailedStatistics(DetailedStatistics& inoutStats);62136214void WriteBlockInfoToJson(JsonWriter& json);62156216private:6217AllocatorPimpl* const m_hAllocator;6218const D3D12_HEAP_PROPERTIES m_HeapProps;6219const D3D12_HEAP_FLAGS m_HeapFlags;6220const UINT64 m_PreferredBlockSize;6221const size_t m_MinBlockCount;6222const size_t m_MaxBlockCount;6223const bool m_ExplicitBlockSize;6224const UINT64 m_MinAllocationAlignment;6225const UINT32 m_Algorithm;6226const bool m_DenyMsaaTextures;6227ID3D12ProtectedResourceSession* const m_ProtectedSession;6228const D3D12_RESIDENCY_PRIORITY m_ResidencyPriority;6229/* There can be at most one allocation that is completely empty - a6230hysteresis to avoid pessimistic case of alternating creation and destruction6231of a ID3D12Heap. */6232bool m_HasEmptyBlock;6233D3D12MA_RW_MUTEX m_Mutex;6234// Incrementally sorted by sumFreeSize, ascending.6235Vector<NormalBlock*> m_Blocks;6236UINT m_NextBlockId;6237bool m_IncrementalSort = true;62386239// Disable incremental sorting when freeing allocations6240void SetIncrementalSort(bool val) { m_IncrementalSort = val; }62416242UINT64 CalcSumBlockSize() const;6243UINT64 CalcMaxBlockSize() const;62446245// Finds and removes given block from vector.6246void Remove(NormalBlock* pBlock);62476248// Performs single step in sorting m_Blocks. They may not be fully sorted6249// after this call.6250void IncrementallySortBlocks();6251void SortByFreeSize();62526253HRESULT AllocatePage(6254UINT64 size,6255UINT64 alignment,6256const ALLOCATION_DESC& allocDesc,6257Allocation** pAllocation);62586259HRESULT AllocateFromBlock(6260NormalBlock* pBlock,6261UINT64 size,6262UINT64 alignment,6263ALLOCATION_FLAGS allocFlags,6264void* pPrivateData,6265UINT32 strategy,6266Allocation** pAllocation);62676268HRESULT CommitAllocationRequest(6269AllocationRequest& allocRequest,6270NormalBlock* pBlock,6271UINT64 size,6272UINT64 alignment,6273void* pPrivateData,6274Allocation** pAllocation);62756276HRESULT CreateBlock(6277UINT64 blockSize,6278size_t* pNewBlockIndex);6279};6280#endif // _D3D12MA_BLOCK_VECTOR62816282#ifndef _D3D12MA_CURRENT_BUDGET_DATA6283class CurrentBudgetData6284{6285public:6286bool ShouldUpdateBudget() const { return m_OperationsSinceBudgetFetch >= 30; }62876288void GetStatistics(Statistics& outStats, UINT group) const;6289void GetBudget(bool useMutex,6290UINT64* outLocalUsage, UINT64* outLocalBudget,6291UINT64* outNonLocalUsage, UINT64* outNonLocalBudget);62926293#if D3D12MA_DXGI_1_46294HRESULT UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex);6295#endif62966297void AddAllocation(UINT group, UINT64 allocationBytes);6298void RemoveAllocation(UINT group, UINT64 allocationBytes);62996300void AddBlock(UINT group, UINT64 blockBytes);6301void RemoveBlock(UINT group, UINT64 blockBytes);63026303private:6304D3D12MA_ATOMIC_UINT32 m_BlockCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6305D3D12MA_ATOMIC_UINT32 m_AllocationCount[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6306D3D12MA_ATOMIC_UINT64 m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6307D3D12MA_ATOMIC_UINT64 m_AllocationBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};63086309D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch = {0};6310D3D12MA_RW_MUTEX m_BudgetMutex;6311UINT64 m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6312UINT64 m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6313UINT64 m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {};6314};63156316#ifndef _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS6317void CurrentBudgetData::GetStatistics(Statistics& outStats, UINT group) const6318{6319outStats.BlockCount = m_BlockCount[group];6320outStats.AllocationCount = m_AllocationCount[group];6321outStats.BlockBytes = m_BlockBytes[group];6322outStats.AllocationBytes = m_AllocationBytes[group];6323}63246325void CurrentBudgetData::GetBudget(bool useMutex,6326UINT64* outLocalUsage, UINT64* outLocalBudget,6327UINT64* outNonLocalUsage, UINT64* outNonLocalBudget)6328{6329MutexLockRead lockRead(m_BudgetMutex, useMutex);63306331if (outLocalUsage)6332{6333const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];6334const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];6335const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];6336*outLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ?6337D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0;6338}6339if (outLocalBudget)6340*outLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY];63416342if (outNonLocalUsage)6343{6344const UINT64 D3D12Usage = m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];6345const UINT64 blockBytes = m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];6346const UINT64 blockBytesAtD3D12Fetch = m_BlockBytesAtD3D12Fetch[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];6347*outNonLocalUsage = D3D12Usage + blockBytes > blockBytesAtD3D12Fetch ?6348D3D12Usage + blockBytes - blockBytesAtD3D12Fetch : 0;6349}6350if (outNonLocalBudget)6351*outNonLocalBudget = m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY];6352}63536354#if D3D12MA_DXGI_1_46355HRESULT CurrentBudgetData::UpdateBudget(IDXGIAdapter3* adapter3, bool useMutex)6356{6357D3D12MA_ASSERT(adapter3);63586359DXGI_QUERY_VIDEO_MEMORY_INFO infoLocal = {};6360DXGI_QUERY_VIDEO_MEMORY_INFO infoNonLocal = {};6361const HRESULT hrLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &infoLocal);6362const HRESULT hrNonLocal = adapter3->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &infoNonLocal);63636364if (SUCCEEDED(hrLocal) || SUCCEEDED(hrNonLocal))6365{6366MutexLockWrite lockWrite(m_BudgetMutex, useMutex);63676368if (SUCCEEDED(hrLocal))6369{6370m_D3D12Usage[0] = infoLocal.CurrentUsage;6371m_D3D12Budget[0] = infoLocal.Budget;6372}6373if (SUCCEEDED(hrNonLocal))6374{6375m_D3D12Usage[1] = infoNonLocal.CurrentUsage;6376m_D3D12Budget[1] = infoNonLocal.Budget;6377}63786379m_BlockBytesAtD3D12Fetch[0] = m_BlockBytes[0];6380m_BlockBytesAtD3D12Fetch[1] = m_BlockBytes[1];6381m_OperationsSinceBudgetFetch = 0;6382}63836384return FAILED(hrLocal) ? hrLocal : hrNonLocal;6385}6386#endif // #if D3D12MA_DXGI_1_463876388void CurrentBudgetData::AddAllocation(UINT group, UINT64 allocationBytes)6389{6390++m_AllocationCount[group];6391m_AllocationBytes[group] += allocationBytes;6392++m_OperationsSinceBudgetFetch;6393}63946395void CurrentBudgetData::RemoveAllocation(UINT group, UINT64 allocationBytes)6396{6397D3D12MA_ASSERT(m_AllocationBytes[group] >= allocationBytes);6398D3D12MA_ASSERT(m_AllocationCount[group] > 0);6399m_AllocationBytes[group] -= allocationBytes;6400--m_AllocationCount[group];6401++m_OperationsSinceBudgetFetch;6402}64036404void CurrentBudgetData::AddBlock(UINT group, UINT64 blockBytes)6405{6406++m_BlockCount[group];6407m_BlockBytes[group] += blockBytes;6408++m_OperationsSinceBudgetFetch;6409}64106411void CurrentBudgetData::RemoveBlock(UINT group, UINT64 blockBytes)6412{6413D3D12MA_ASSERT(m_BlockBytes[group] >= blockBytes);6414D3D12MA_ASSERT(m_BlockCount[group] > 0);6415m_BlockBytes[group] -= blockBytes;6416--m_BlockCount[group];6417++m_OperationsSinceBudgetFetch;6418}6419#endif // _D3D12MA_CURRENT_BUDGET_DATA_FUNCTIONS6420#endif // _D3D12MA_CURRENT_BUDGET_DATA64216422#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL6423class DefragmentationContextPimpl6424{6425D3D12MA_CLASS_NO_COPY(DefragmentationContextPimpl)6426public:6427DefragmentationContextPimpl(6428AllocatorPimpl* hAllocator,6429const DEFRAGMENTATION_DESC& desc,6430BlockVector* poolVector);6431~DefragmentationContextPimpl();64326433void GetStats(DEFRAGMENTATION_STATS& outStats) { outStats = m_GlobalStats; }6434const ALLOCATION_CALLBACKS& GetAllocs() const { return m_Moves.GetAllocs(); }64356436HRESULT DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo);6437HRESULT DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo);64386439private:6440// Max number of allocations to ignore due to size constraints before ending single pass6441static const UINT8 MAX_ALLOCS_TO_IGNORE = 16;6442enum class CounterStatus { Pass, Ignore, End };64436444struct FragmentedBlock6445{6446UINT32 data;6447NormalBlock* block;6448};6449struct StateBalanced6450{6451UINT64 avgFreeSize = 0;6452UINT64 avgAllocSize = UINT64_MAX;6453};6454struct MoveAllocationData6455{6456UINT64 size;6457UINT64 alignment;6458ALLOCATION_FLAGS flags;6459DEFRAGMENTATION_MOVE move = {};6460};64616462const UINT64 m_MaxPassBytes;6463const UINT32 m_MaxPassAllocations;64646465Vector<DEFRAGMENTATION_MOVE> m_Moves;64666467UINT8 m_IgnoredAllocs = 0;6468UINT32 m_Algorithm;6469UINT32 m_BlockVectorCount;6470BlockVector* m_PoolBlockVector;6471BlockVector** m_pBlockVectors;6472size_t m_ImmovableBlockCount = 0;6473DEFRAGMENTATION_STATS m_GlobalStats = { 0 };6474DEFRAGMENTATION_STATS m_PassStats = { 0 };6475void* m_AlgorithmState = NULL;64766477static MoveAllocationData GetMoveData(AllocHandle handle, BlockMetadata* metadata);6478CounterStatus CheckCounters(UINT64 bytes);6479bool IncrementCounters(UINT64 bytes);6480bool ReallocWithinBlock(BlockVector& vector, NormalBlock* block);6481bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector);64826483bool ComputeDefragmentation(BlockVector& vector, size_t index);6484bool ComputeDefragmentation_Fast(BlockVector& vector);6485bool ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update);6486bool ComputeDefragmentation_Full(BlockVector& vector);64876488void UpdateVectorStatistics(BlockVector& vector, StateBalanced& state);6489};6490#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL64916492#ifndef _D3D12MA_POOL_PIMPL6493class PoolPimpl6494{6495friend class Allocator;6496friend struct PoolListItemTraits;6497public:6498PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc);6499~PoolPimpl();65006501AllocatorPimpl* GetAllocator() const { return m_Allocator; }6502const POOL_DESC& GetDesc() const { return m_Desc; }6503bool SupportsCommittedAllocations() const { return m_Desc.BlockSize == 0; }6504LPCWSTR GetName() const { return m_Name; }65056506BlockVector* GetBlockVector() { return m_BlockVector; }6507CommittedAllocationList* GetCommittedAllocationList() { return SupportsCommittedAllocations() ? &m_CommittedAllocations : NULL; }65086509HRESULT Init();6510void GetStatistics(Statistics& outStats);6511void CalculateStatistics(DetailedStatistics& outStats);6512void AddDetailedStatistics(DetailedStatistics& inoutStats);6513void SetName(LPCWSTR Name);65146515private:6516AllocatorPimpl* m_Allocator; // Externally owned object.6517POOL_DESC m_Desc;6518BlockVector* m_BlockVector; // Owned object.6519CommittedAllocationList m_CommittedAllocations;6520wchar_t* m_Name;6521PoolPimpl* m_PrevPool = NULL;6522PoolPimpl* m_NextPool = NULL;65236524void FreeName();6525};65266527struct PoolListItemTraits6528{6529using ItemType = PoolPimpl;6530static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }6531static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }6532static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }6533static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }6534};6535#endif // _D3D12MA_POOL_PIMPL653665376538#ifndef _D3D12MA_ALLOCATOR_PIMPL6539class AllocatorPimpl6540{6541friend class Allocator;6542friend class Pool;6543public:6544std::atomic_uint32_t m_RefCount = {1};6545CurrentBudgetData m_Budget;65466547AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc);6548~AllocatorPimpl();65496550ID3D12Device* GetDevice() const { return m_Device; }6551#ifdef __ID3D12Device1_INTERFACE_DEFINED__6552ID3D12Device1* GetDevice1() const { return m_Device1; }6553#endif6554#ifdef __ID3D12Device4_INTERFACE_DEFINED__6555ID3D12Device4* GetDevice4() const { return m_Device4; }6556#endif6557#ifdef __ID3D12Device8_INTERFACE_DEFINED__6558ID3D12Device8* GetDevice8() const { return m_Device8; }6559#endif6560// Shortcut for "Allocation Callbacks", because this function is called so often.6561const ALLOCATION_CALLBACKS& GetAllocs() const { return m_AllocationCallbacks; }6562const D3D12_FEATURE_DATA_D3D12_OPTIONS& GetD3D12Options() const { return m_D3D12Options; }6563BOOL IsUMA() const { return m_D3D12Architecture.UMA; }6564BOOL IsCacheCoherentUMA() const { return m_D3D12Architecture.CacheCoherentUMA; }6565bool SupportsResourceHeapTier2() const { return m_D3D12Options.ResourceHeapTier >= D3D12_RESOURCE_HEAP_TIER_2; }6566bool UseMutex() const { return m_UseMutex; }6567AllocationObjectAllocator& GetAllocationObjectAllocator() { return m_AllocationObjectAllocator; }6568UINT GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }6569/*6570If SupportsResourceHeapTier2():65710: D3D12_HEAP_TYPE_DEFAULT65721: D3D12_HEAP_TYPE_UPLOAD65732: D3D12_HEAP_TYPE_READBACK6574else:65750: D3D12_HEAP_TYPE_DEFAULT + buffer65761: D3D12_HEAP_TYPE_DEFAULT + texture65772: D3D12_HEAP_TYPE_DEFAULT + texture RT or DS65783: D3D12_HEAP_TYPE_UPLOAD + buffer65794: D3D12_HEAP_TYPE_UPLOAD + texture65805: D3D12_HEAP_TYPE_UPLOAD + texture RT or DS65816: D3D12_HEAP_TYPE_READBACK + buffer65827: D3D12_HEAP_TYPE_READBACK + texture65838: D3D12_HEAP_TYPE_READBACK + texture RT or DS6584*/6585UINT GetDefaultPoolCount() const { return SupportsResourceHeapTier2() ? 3 : 9; }6586BlockVector** GetDefaultPools() { return m_BlockVectors; }65876588HRESULT Init(const ALLOCATOR_DESC& desc);6589bool HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const;6590UINT StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const;6591UINT HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const;6592UINT64 GetMemoryCapacity(UINT memorySegmentGroup) const;65936594HRESULT CreatePlacedResourceWrap(6595ID3D12Heap *pHeap,6596UINT64 HeapOffset,6597const CREATE_RESOURCE_PARAMS& createParams,6598REFIID riidResource,6599void** ppvResource);66006601HRESULT CreateResource(6602const ALLOCATION_DESC* pAllocDesc,6603const CREATE_RESOURCE_PARAMS& createParams,6604Allocation** ppAllocation,6605REFIID riidResource,6606void** ppvResource);66076608HRESULT CreateAliasingResource(6609Allocation* pAllocation,6610UINT64 AllocationLocalOffset,6611const CREATE_RESOURCE_PARAMS& createParams,6612REFIID riidResource,6613void** ppvResource);66146615HRESULT AllocateMemory(6616const ALLOCATION_DESC* pAllocDesc,6617const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,6618Allocation** ppAllocation);66196620// Unregisters allocation from the collection of dedicated allocations.6621// Allocation object must be deleted externally afterwards.6622void FreeCommittedMemory(Allocation* allocation);6623// Unregisters allocation from the collection of placed allocations.6624// Allocation object must be deleted externally afterwards.6625void FreePlacedMemory(Allocation* allocation);6626// Unregisters allocation from the collection of dedicated allocations and destroys associated heap.6627// Allocation object must be deleted externally afterwards.6628void FreeHeapMemory(Allocation* allocation);66296630void SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const;66316632void SetCurrentFrameIndex(UINT frameIndex);6633// For more deailed stats use outCutomHeaps to access statistics divided into L0 and L1 group6634void CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCutomHeaps[2] = NULL);66356636void GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget);6637void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType);66386639void BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap);6640void FreeStatsString(WCHAR* pStatsString);66416642private:6643using PoolList = IntrusiveLinkedList<PoolListItemTraits>;66446645const bool m_UseMutex;6646const bool m_AlwaysCommitted;6647const bool m_MsaaAlwaysCommitted;6648bool m_DefaultPoolsNotZeroed = false;6649ID3D12Device* m_Device; // AddRef6650#ifdef __ID3D12Device1_INTERFACE_DEFINED__6651ID3D12Device1* m_Device1 = NULL; // AddRef, optional6652#endif6653#ifdef __ID3D12Device4_INTERFACE_DEFINED__6654ID3D12Device4* m_Device4 = NULL; // AddRef, optional6655#endif6656#ifdef __ID3D12Device8_INTERFACE_DEFINED__6657ID3D12Device8* m_Device8 = NULL; // AddRef, optional6658#endif6659#ifdef __ID3D12Device10_INTERFACE_DEFINED__6660ID3D12Device10* m_Device10 = NULL; // AddRef, optional6661#endif6662IDXGIAdapter* m_Adapter; // AddRef6663#if D3D12MA_DXGI_1_46664IDXGIAdapter3* m_Adapter3 = NULL; // AddRef, optional6665#endif6666UINT64 m_PreferredBlockSize;6667ALLOCATION_CALLBACKS m_AllocationCallbacks;6668D3D12MA_ATOMIC_UINT32 m_CurrentFrameIndex;6669DXGI_ADAPTER_DESC m_AdapterDesc;6670D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Options;6671D3D12_FEATURE_DATA_ARCHITECTURE m_D3D12Architecture;6672AllocationObjectAllocator m_AllocationObjectAllocator;66736674D3D12MA_RW_MUTEX m_PoolsMutex[HEAP_TYPE_COUNT];6675PoolList m_Pools[HEAP_TYPE_COUNT];6676// Default pools.6677BlockVector* m_BlockVectors[DEFAULT_POOL_MAX_COUNT];6678CommittedAllocationList m_CommittedAllocations[STANDARD_HEAP_TYPE_COUNT];66796680/*6681Heuristics that decides whether a resource should better be placed in its own,6682dedicated allocation (committed resource rather than placed resource).6683*/6684template<typename D3D12_RESOURCE_DESC_T>6685static bool PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc);66866687// Allocates and registers new committed resource with implicit heap, as dedicated allocation.6688// Creates and returns Allocation object and optionally D3D12 resource.6689HRESULT AllocateCommittedResource(6690const CommittedAllocationParameters& committedAllocParams,6691UINT64 resourceSize, bool withinBudget, void* pPrivateData,6692const CREATE_RESOURCE_PARAMS& createParams,6693Allocation** ppAllocation, REFIID riidResource, void** ppvResource);66946695// Allocates and registers new heap without any resources placed in it, as dedicated allocation.6696// Creates and returns Allocation object.6697HRESULT AllocateHeap(6698const CommittedAllocationParameters& committedAllocParams,6699const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget,6700void* pPrivateData, Allocation** ppAllocation);67016702template<typename D3D12_RESOURCE_DESC_T>6703HRESULT CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,6704const D3D12_RESOURCE_DESC_T* resDesc, // Optional6705BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted);67066707// Returns UINT32_MAX if index cannot be calculcated.6708UINT CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const;6709void CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const;67106711// Registers Pool object in m_Pools.6712void RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);6713// Unregisters Pool object from m_Pools.6714void UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType);67156716HRESULT UpdateD3D12Budget();67176718D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const;6719#ifdef __ID3D12Device8_INTERFACE_DEFINED__6720D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const;6721#endif67226723template<typename D3D12_RESOURCE_DESC_T>6724D3D12_RESOURCE_ALLOCATION_INFO GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const;67256726bool NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size);67276728// Writes object { } with data of given budget.6729static void WriteBudgetToJson(JsonWriter& json, const Budget& budget);6730};67316732#ifndef _D3D12MA_ALLOCATOR_PIMPL_FUNCTINOS6733AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc)6734: m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0),6735m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0),6736m_MsaaAlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0),6737m_Device(desc.pDevice),6738m_Adapter(desc.pAdapter),6739m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE),6740m_AllocationCallbacks(allocationCallbacks),6741m_CurrentFrameIndex(0),6742// Below this line don't use allocationCallbacks but m_AllocationCallbacks!!!6743m_AllocationObjectAllocator(m_AllocationCallbacks)6744{6745// desc.pAllocationCallbacks intentionally ignored here, preprocessed by CreateAllocator.6746ZeroMemory(&m_D3D12Options, sizeof(m_D3D12Options));6747ZeroMemory(&m_D3D12Architecture, sizeof(m_D3D12Architecture));67486749ZeroMemory(m_BlockVectors, sizeof(m_BlockVectors));67506751for (UINT i = 0; i < STANDARD_HEAP_TYPE_COUNT; ++i)6752{6753m_CommittedAllocations[i].Init(6754m_UseMutex,6755(D3D12_HEAP_TYPE)(D3D12_HEAP_TYPE_DEFAULT + i),6756NULL); // pool6757}67586759m_Device->AddRef();6760m_Adapter->AddRef();6761}67626763HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc)6764{6765#if D3D12MA_DXGI_1_46766desc.pAdapter->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Adapter3));6767#endif67686769#ifdef __ID3D12Device1_INTERFACE_DEFINED__6770m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device1));6771#endif67726773#ifdef __ID3D12Device4_INTERFACE_DEFINED__6774m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device4));6775#endif67766777#ifdef __ID3D12Device8_INTERFACE_DEFINED__6778m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device8));67796780if((desc.Flags & ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED) != 0)6781{6782D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = {};6783if(SUCCEEDED(m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, sizeof(options7))))6784{6785// DEFAULT_POOLS_NOT_ZEROED both supported and enabled by the user.6786m_DefaultPoolsNotZeroed = true;6787}6788}6789#endif67906791#ifdef __ID3D12Device10_INTERFACE_DEFINED__6792m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device10));6793#endif67946795HRESULT hr = m_Adapter->GetDesc(&m_AdapterDesc);6796if (FAILED(hr))6797{6798return hr;6799}68006801hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &m_D3D12Options, sizeof(m_D3D12Options));6802if (FAILED(hr))6803{6804return hr;6805}6806#ifdef D3D12MA_FORCE_RESOURCE_HEAP_TIER6807m_D3D12Options.ResourceHeapTier = (D3D12MA_FORCE_RESOURCE_HEAP_TIER);6808#endif68096810hr = m_Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &m_D3D12Architecture, sizeof(m_D3D12Architecture));6811if (FAILED(hr))6812{6813m_D3D12Architecture.UMA = FALSE;6814m_D3D12Architecture.CacheCoherentUMA = FALSE;6815}68166817D3D12_HEAP_PROPERTIES heapProps = {};6818const UINT defaultPoolCount = GetDefaultPoolCount();6819for (UINT i = 0; i < defaultPoolCount; ++i)6820{6821D3D12_HEAP_FLAGS heapFlags;6822CalcDefaultPoolParams(heapProps.Type, heapFlags, i);68236824#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE6825if(m_DefaultPoolsNotZeroed)6826{6827heapFlags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;6828}6829#endif68306831m_BlockVectors[i] = D3D12MA_NEW(GetAllocs(), BlockVector)(6832this, // hAllocator6833heapProps, // heapType6834heapFlags, // heapFlags6835m_PreferredBlockSize,68360, // minBlockCount6837SIZE_MAX, // maxBlockCount6838false, // explicitBlockSize6839D3D12MA_DEBUG_ALIGNMENT, // minAllocationAlignment68400, // Default algorithm,6841m_MsaaAlwaysCommitted,6842NULL, // pProtectedSession6843D3D12_RESIDENCY_PRIORITY_NONE); // residencyPriority6844// No need to call m_pBlockVectors[i]->CreateMinBlocks here, becase minBlockCount is 0.6845}68466847#if D3D12MA_DXGI_1_46848UpdateD3D12Budget();6849#endif68506851return S_OK;6852}68536854AllocatorPimpl::~AllocatorPimpl()6855{6856#ifdef __ID3D12Device10_INTERFACE_DEFINED__6857SAFE_RELEASE(m_Device10);6858#endif6859#ifdef __ID3D12Device8_INTERFACE_DEFINED__6860SAFE_RELEASE(m_Device8);6861#endif6862#ifdef __ID3D12Device4_INTERFACE_DEFINED__6863SAFE_RELEASE(m_Device4);6864#endif6865#ifdef __ID3D12Device1_INTERFACE_DEFINED__6866SAFE_RELEASE(m_Device1);6867#endif6868#if D3D12MA_DXGI_1_46869SAFE_RELEASE(m_Adapter3);6870#endif6871SAFE_RELEASE(m_Adapter);6872SAFE_RELEASE(m_Device);68736874for (UINT i = DEFAULT_POOL_MAX_COUNT; i--; )6875{6876D3D12MA_DELETE(GetAllocs(), m_BlockVectors[i]);6877}68786879for (UINT i = HEAP_TYPE_COUNT; i--; )6880{6881if (!m_Pools[i].IsEmpty())6882{6883D3D12MA_ASSERT(0 && "Unfreed pools found!");6884}6885}6886}68876888bool AllocatorPimpl::HeapFlagsFulfillResourceHeapTier(D3D12_HEAP_FLAGS flags) const6889{6890if (SupportsResourceHeapTier2())6891{6892return true;6893}6894else6895{6896const bool allowBuffers = (flags & D3D12_HEAP_FLAG_DENY_BUFFERS) == 0;6897const bool allowRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) == 0;6898const bool allowNonRtDsTextures = (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) == 0;6899const uint8_t allowedGroupCount = (allowBuffers ? 1 : 0) + (allowRtDsTextures ? 1 : 0) + (allowNonRtDsTextures ? 1 : 0);6900return allowedGroupCount == 1;6901}6902}69036904UINT AllocatorPimpl::StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE heapType) const6905{6906D3D12MA_ASSERT(IsHeapTypeStandard(heapType));6907if (IsUMA())6908return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY;6909return heapType == D3D12_HEAP_TYPE_DEFAULT ?6910DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY;6911}69126913UINT AllocatorPimpl::HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const6914{6915if (IsUMA())6916return DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY;6917if (heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_UNKNOWN)6918return StandardHeapTypeToMemorySegmentGroup(heapProps.Type);6919return heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_L1 ?6920DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY;6921}69226923UINT64 AllocatorPimpl::GetMemoryCapacity(UINT memorySegmentGroup) const6924{6925switch (memorySegmentGroup)6926{6927case DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY:6928return IsUMA() ?6929m_AdapterDesc.DedicatedVideoMemory + m_AdapterDesc.SharedSystemMemory : m_AdapterDesc.DedicatedVideoMemory;6930case DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY:6931return IsUMA() ? 0 : m_AdapterDesc.SharedSystemMemory;6932default:6933D3D12MA_ASSERT(0);6934return UINT64_MAX;6935}6936}69376938HRESULT AllocatorPimpl::CreatePlacedResourceWrap(6939ID3D12Heap *pHeap,6940UINT64 HeapOffset,6941const CREATE_RESOURCE_PARAMS& createParams,6942REFIID riidResource,6943void** ppvResource)6944{6945#ifdef __ID3D12Device10_INTERFACE_DEFINED__6946if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)6947{6948if (!m_Device10)6949{6950return E_NOINTERFACE;6951}6952return m_Device10->CreatePlacedResource2(pHeap, HeapOffset,6953createParams.GetResourceDesc1(), createParams.GetInitialLayout(),6954createParams.GetOptimizedClearValue(), createParams.GetNumCastableFormats(),6955createParams.GetCastableFormats(), riidResource, ppvResource);6956} else6957#endif6958#ifdef __ID3D12Device8_INTERFACE_DEFINED__6959if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)6960{6961if (!m_Device8)6962{6963return E_NOINTERFACE;6964}6965return m_Device8->CreatePlacedResource1(pHeap, HeapOffset,6966createParams.GetResourceDesc1(), createParams.GetInitialResourceState(),6967createParams.GetOptimizedClearValue(), riidResource, ppvResource);6968} else6969#endif6970if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)6971{6972return m_Device->CreatePlacedResource(pHeap, HeapOffset,6973createParams.GetResourceDesc(), createParams.GetInitialResourceState(),6974createParams.GetOptimizedClearValue(), riidResource, ppvResource);6975}6976else6977{6978D3D12MA_ASSERT(0);6979return E_INVALIDARG;6980}6981}698269836984HRESULT AllocatorPimpl::CreateResource(6985const ALLOCATION_DESC* pAllocDesc,6986const CREATE_RESOURCE_PARAMS& createParams,6987Allocation** ppAllocation,6988REFIID riidResource,6989void** ppvResource)6990{6991D3D12MA_ASSERT(pAllocDesc && createParams.GetBaseResourceDesc() && ppAllocation);69926993*ppAllocation = NULL;6994if (ppvResource)6995{6996*ppvResource = NULL;6997}69986999CREATE_RESOURCE_PARAMS finalCreateParams = createParams;7000D3D12_RESOURCE_DESC finalResourceDesc;7001#ifdef __ID3D12Device8_INTERFACE_DEFINED__7002D3D12_RESOURCE_DESC1 finalResourceDesc1;7003#endif7004D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo;7005if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)7006{7007finalResourceDesc = *createParams.GetResourceDesc();7008finalCreateParams.AccessResourceDesc() = &finalResourceDesc;7009resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);7010}7011#ifdef __ID3D12Device8_INTERFACE_DEFINED__7012else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7013{7014if (!m_Device8)7015{7016return E_NOINTERFACE;7017}7018finalResourceDesc1 = *createParams.GetResourceDesc1();7019finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;7020resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);7021}7022#endif7023#ifdef __ID3D12Device10_INTERFACE_DEFINED__7024else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)7025{7026if (!m_Device10)7027{7028return E_NOINTERFACE;7029}7030finalResourceDesc1 = *createParams.GetResourceDesc1();7031finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;7032resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);7033}7034#endif7035else7036{7037D3D12MA_ASSERT(0);7038return E_INVALIDARG;7039}7040D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));7041D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);70427043BlockVector* blockVector = NULL;7044CommittedAllocationParameters committedAllocationParams = {};7045bool preferCommitted = false;70467047HRESULT hr;7048#ifdef __ID3D12Device8_INTERFACE_DEFINED__7049if (createParams.Variant >= CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7050{7051hr = CalcAllocationParams<D3D12_RESOURCE_DESC1>(*pAllocDesc, resAllocInfo.SizeInBytes,7052createParams.GetResourceDesc1(),7053blockVector, committedAllocationParams, preferCommitted);7054}7055else7056#endif7057{7058hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, resAllocInfo.SizeInBytes,7059createParams.GetResourceDesc(),7060blockVector, committedAllocationParams, preferCommitted);7061}7062if (FAILED(hr))7063return hr;70647065const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0;7066hr = E_INVALIDARG;7067if (committedAllocationParams.IsValid() && preferCommitted)7068{7069hr = AllocateCommittedResource(committedAllocationParams,7070resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData,7071finalCreateParams, ppAllocation, riidResource, ppvResource);7072if (SUCCEEDED(hr))7073return hr;7074}7075if (blockVector != NULL)7076{7077hr = blockVector->CreateResource(resAllocInfo.SizeInBytes, resAllocInfo.Alignment,7078*pAllocDesc, finalCreateParams,7079ppAllocation, riidResource, ppvResource);7080if (SUCCEEDED(hr))7081return hr;7082}7083if (committedAllocationParams.IsValid() && !preferCommitted)7084{7085hr = AllocateCommittedResource(committedAllocationParams,7086resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData,7087finalCreateParams, ppAllocation, riidResource, ppvResource);7088if (SUCCEEDED(hr))7089return hr;7090}7091return hr;7092}70937094HRESULT AllocatorPimpl::AllocateMemory(7095const ALLOCATION_DESC* pAllocDesc,7096const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,7097Allocation** ppAllocation)7098{7099*ppAllocation = NULL;71007101BlockVector* blockVector = NULL;7102CommittedAllocationParameters committedAllocationParams = {};7103bool preferCommitted = false;7104HRESULT hr = CalcAllocationParams<D3D12_RESOURCE_DESC>(*pAllocDesc, pAllocInfo->SizeInBytes,7105NULL, // pResDesc7106blockVector, committedAllocationParams, preferCommitted);7107if (FAILED(hr))7108return hr;71097110const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0;7111hr = E_INVALIDARG;7112if (committedAllocationParams.IsValid() && preferCommitted)7113{7114hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation);7115if (SUCCEEDED(hr))7116return hr;7117}7118if (blockVector != NULL)7119{7120hr = blockVector->Allocate(pAllocInfo->SizeInBytes, pAllocInfo->Alignment,7121*pAllocDesc, 1, (Allocation**)ppAllocation);7122if (SUCCEEDED(hr))7123return hr;7124}7125if (committedAllocationParams.IsValid() && !preferCommitted)7126{7127hr = AllocateHeap(committedAllocationParams, *pAllocInfo, withinBudget, pAllocDesc->pPrivateData, ppAllocation);7128if (SUCCEEDED(hr))7129return hr;7130}7131return hr;7132}71337134HRESULT AllocatorPimpl::CreateAliasingResource(7135Allocation* pAllocation,7136UINT64 AllocationLocalOffset,7137const CREATE_RESOURCE_PARAMS& createParams,7138REFIID riidResource,7139void** ppvResource)7140{7141*ppvResource = NULL;71427143CREATE_RESOURCE_PARAMS finalCreateParams = createParams;7144D3D12_RESOURCE_DESC finalResourceDesc;7145#ifdef __ID3D12Device8_INTERFACE_DEFINED__7146D3D12_RESOURCE_DESC1 finalResourceDesc1;7147#endif7148D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo;7149if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)7150{7151finalResourceDesc = *createParams.GetResourceDesc();7152finalCreateParams.AccessResourceDesc() = &finalResourceDesc;7153resAllocInfo = GetResourceAllocationInfo(finalResourceDesc);7154}7155#ifdef __ID3D12Device8_INTERFACE_DEFINED__7156else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7157{7158if (!m_Device8)7159{7160return E_NOINTERFACE;7161}7162finalResourceDesc1 = *createParams.GetResourceDesc1();7163finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;7164resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);7165}7166#endif7167#ifdef __ID3D12Device10_INTERFACE_DEFINED__7168else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)7169{7170if (!m_Device10)7171{7172return E_NOINTERFACE;7173}7174finalResourceDesc1 = *createParams.GetResourceDesc1();7175finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1;7176resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1);7177}7178#endif7179else7180{7181D3D12MA_ASSERT(0);7182return E_INVALIDARG;7183}7184D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment));7185D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0);71867187ID3D12Heap* const existingHeap = pAllocation->GetHeap();7188const UINT64 existingOffset = pAllocation->GetOffset();7189const UINT64 existingSize = pAllocation->GetSize();7190const UINT64 newOffset = existingOffset + AllocationLocalOffset;71917192if (existingHeap == NULL ||7193AllocationLocalOffset + resAllocInfo.SizeInBytes > existingSize ||7194newOffset % resAllocInfo.Alignment != 0)7195{7196return E_INVALIDARG;7197}71987199return CreatePlacedResourceWrap(existingHeap, newOffset, finalCreateParams, riidResource, ppvResource);7200}72017202void AllocatorPimpl::FreeCommittedMemory(Allocation* allocation)7203{7204D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_COMMITTED);72057206CommittedAllocationList* const allocList = allocation->m_Committed.list;7207allocList->Unregister(allocation);72087209const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this);7210const UINT64 allocSize = allocation->GetSize();7211m_Budget.RemoveAllocation(memSegmentGroup, allocSize);7212m_Budget.RemoveBlock(memSegmentGroup, allocSize);7213}72147215void AllocatorPimpl::FreePlacedMemory(Allocation* allocation)7216{7217D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_PLACED);72187219NormalBlock* const block = allocation->m_Placed.block;7220D3D12MA_ASSERT(block);7221BlockVector* const blockVector = block->GetBlockVector();7222D3D12MA_ASSERT(blockVector);7223m_Budget.RemoveAllocation(HeapPropertiesToMemorySegmentGroup(block->GetHeapProperties()), allocation->GetSize());7224blockVector->Free(allocation);7225}72267227void AllocatorPimpl::FreeHeapMemory(Allocation* allocation)7228{7229D3D12MA_ASSERT(allocation && allocation->m_PackedData.GetType() == Allocation::TYPE_HEAP);72307231CommittedAllocationList* const allocList = allocation->m_Committed.list;7232allocList->Unregister(allocation);7233SAFE_RELEASE(allocation->m_Heap.heap);72347235const UINT memSegmentGroup = allocList->GetMemorySegmentGroup(this);7236const UINT64 allocSize = allocation->GetSize();7237m_Budget.RemoveAllocation(memSegmentGroup, allocSize);7238m_Budget.RemoveBlock(memSegmentGroup, allocSize);7239}72407241void AllocatorPimpl::SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const7242{7243#ifdef __ID3D12Device1_INTERFACE_DEFINED__7244if (priority != D3D12_RESIDENCY_PRIORITY_NONE && m_Device1)7245{7246// Intentionally ignoring the result.7247m_Device1->SetResidencyPriority(1, &obj, &priority);7248}7249#endif7250}72517252void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex)7253{7254m_CurrentFrameIndex.store(frameIndex);72557256#if D3D12MA_DXGI_1_47257UpdateD3D12Budget();7258#endif7259}72607261void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCutomHeaps[2])7262{7263// Init stats7264for (size_t i = 0; i < HEAP_TYPE_COUNT; i++)7265ClearDetailedStatistics(outStats.HeapType[i]);7266for (size_t i = 0; i < DXGI_MEMORY_SEGMENT_GROUP_COUNT; i++)7267ClearDetailedStatistics(outStats.MemorySegmentGroup[i]);7268ClearDetailedStatistics(outStats.Total);7269if (outCutomHeaps)7270{7271ClearDetailedStatistics(outCutomHeaps[0]);7272ClearDetailedStatistics(outCutomHeaps[1]);7273}72747275// Process default pools. 3 standard heap types only. Add them to outStats.HeapType[i].7276if (SupportsResourceHeapTier2())7277{7278// DEFAULT, UPLOAD, READBACK.7279for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)7280{7281BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex];7282D3D12MA_ASSERT(pBlockVector);7283pBlockVector->AddDetailedStatistics(outStats.HeapType[heapTypeIndex]);7284}7285}7286else7287{7288// DEFAULT, UPLOAD, READBACK.7289for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)7290{7291for (size_t heapSubType = 0; heapSubType < 3; ++heapSubType)7292{7293BlockVector* const pBlockVector = m_BlockVectors[heapTypeIndex * 3 + heapSubType];7294D3D12MA_ASSERT(pBlockVector);7295pBlockVector->AddDetailedStatistics(outStats.HeapType[heapTypeIndex]);7296}7297}7298}72997300// Sum them up to memory segment groups.7301AddDetailedStatistics(7302outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_DEFAULT)],7303outStats.HeapType[0]);7304AddDetailedStatistics(7305outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_UPLOAD)],7306outStats.HeapType[1]);7307AddDetailedStatistics(7308outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(D3D12_HEAP_TYPE_READBACK)],7309outStats.HeapType[2]);73107311// Process custom pools.7312DetailedStatistics tmpStats;7313for (size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)7314{7315MutexLockRead lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);7316PoolList& poolList = m_Pools[heapTypeIndex];7317for (PoolPimpl* pool = poolList.Front(); pool != NULL; pool = poolList.GetNext(pool))7318{7319const D3D12_HEAP_PROPERTIES& poolHeapProps = pool->GetDesc().HeapProperties;7320ClearDetailedStatistics(tmpStats);7321pool->AddDetailedStatistics(tmpStats);7322AddDetailedStatistics(7323outStats.HeapType[heapTypeIndex], tmpStats);73247325UINT memorySegment = HeapPropertiesToMemorySegmentGroup(poolHeapProps);7326AddDetailedStatistics(7327outStats.MemorySegmentGroup[memorySegment], tmpStats);73287329if (outCutomHeaps)7330AddDetailedStatistics(outCutomHeaps[memorySegment], tmpStats);7331}7332}73337334// Process committed allocations. 3 standard heap types only.7335for (UINT heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)7336{7337ClearDetailedStatistics(tmpStats);7338m_CommittedAllocations[heapTypeIndex].AddDetailedStatistics(tmpStats);7339AddDetailedStatistics(7340outStats.HeapType[heapTypeIndex], tmpStats);7341AddDetailedStatistics(7342outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(IndexToHeapType(heapTypeIndex))], tmpStats);7343}73447345// Sum up memory segment groups to totals.7346AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[0]);7347AddDetailedStatistics(outStats.Total, outStats.MemorySegmentGroup[1]);73487349D3D12MA_ASSERT(outStats.Total.Stats.BlockCount ==7350outStats.MemorySegmentGroup[0].Stats.BlockCount + outStats.MemorySegmentGroup[1].Stats.BlockCount);7351D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount ==7352outStats.MemorySegmentGroup[0].Stats.AllocationCount + outStats.MemorySegmentGroup[1].Stats.AllocationCount);7353D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes ==7354outStats.MemorySegmentGroup[0].Stats.BlockBytes + outStats.MemorySegmentGroup[1].Stats.BlockBytes);7355D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes ==7356outStats.MemorySegmentGroup[0].Stats.AllocationBytes + outStats.MemorySegmentGroup[1].Stats.AllocationBytes);7357D3D12MA_ASSERT(outStats.Total.UnusedRangeCount ==7358outStats.MemorySegmentGroup[0].UnusedRangeCount + outStats.MemorySegmentGroup[1].UnusedRangeCount);73597360D3D12MA_ASSERT(outStats.Total.Stats.BlockCount ==7361outStats.HeapType[0].Stats.BlockCount + outStats.HeapType[1].Stats.BlockCount +7362outStats.HeapType[2].Stats.BlockCount + outStats.HeapType[3].Stats.BlockCount);7363D3D12MA_ASSERT(outStats.Total.Stats.AllocationCount ==7364outStats.HeapType[0].Stats.AllocationCount + outStats.HeapType[1].Stats.AllocationCount +7365outStats.HeapType[2].Stats.AllocationCount + outStats.HeapType[3].Stats.AllocationCount);7366D3D12MA_ASSERT(outStats.Total.Stats.BlockBytes ==7367outStats.HeapType[0].Stats.BlockBytes + outStats.HeapType[1].Stats.BlockBytes +7368outStats.HeapType[2].Stats.BlockBytes + outStats.HeapType[3].Stats.BlockBytes);7369D3D12MA_ASSERT(outStats.Total.Stats.AllocationBytes ==7370outStats.HeapType[0].Stats.AllocationBytes + outStats.HeapType[1].Stats.AllocationBytes +7371outStats.HeapType[2].Stats.AllocationBytes + outStats.HeapType[3].Stats.AllocationBytes);7372D3D12MA_ASSERT(outStats.Total.UnusedRangeCount ==7373outStats.HeapType[0].UnusedRangeCount + outStats.HeapType[1].UnusedRangeCount +7374outStats.HeapType[2].UnusedRangeCount + outStats.HeapType[3].UnusedRangeCount);7375}73767377void AllocatorPimpl::GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget)7378{7379if (outLocalBudget)7380m_Budget.GetStatistics(outLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY);7381if (outNonLocalBudget)7382m_Budget.GetStatistics(outNonLocalBudget->Stats, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY);73837384#if D3D12MA_DXGI_1_47385if (m_Adapter3)7386{7387if (!m_Budget.ShouldUpdateBudget())7388{7389m_Budget.GetBudget(m_UseMutex,7390outLocalBudget ? &outLocalBudget->UsageBytes : NULL,7391outLocalBudget ? &outLocalBudget->BudgetBytes : NULL,7392outNonLocalBudget ? &outNonLocalBudget->UsageBytes : NULL,7393outNonLocalBudget ? &outNonLocalBudget->BudgetBytes : NULL);7394}7395else7396{7397UpdateD3D12Budget();7398GetBudget(outLocalBudget, outNonLocalBudget); // Recursion7399}7400}7401else7402#endif7403{7404if (outLocalBudget)7405{7406outLocalBudget->UsageBytes = outLocalBudget->Stats.BlockBytes;7407outLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_LOCAL_COPY) * 8 / 10; // 80% heuristics.7408}7409if (outNonLocalBudget)7410{7411outNonLocalBudget->UsageBytes = outNonLocalBudget->Stats.BlockBytes;7412outNonLocalBudget->BudgetBytes = GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL_COPY) * 8 / 10; // 80% heuristics.7413}7414}7415}74167417void AllocatorPimpl::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType)7418{7419switch (heapType)7420{7421case D3D12_HEAP_TYPE_DEFAULT:7422GetBudget(&outBudget, NULL);7423break;7424case D3D12_HEAP_TYPE_UPLOAD:7425case D3D12_HEAP_TYPE_READBACK:7426GetBudget(NULL, &outBudget);7427break;7428default: D3D12MA_ASSERT(0);7429}7430}74317432void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)7433{7434StringBuilder sb(GetAllocs());7435{7436Budget localBudget = {}, nonLocalBudget = {};7437GetBudget(&localBudget, &nonLocalBudget);74387439TotalStatistics stats;7440DetailedStatistics customHeaps[2];7441CalculateStatistics(stats, customHeaps);74427443JsonWriter json(GetAllocs(), sb);7444json.BeginObject();7445{7446json.WriteString(L"General");7447json.BeginObject();7448{7449json.WriteString(L"API");7450json.WriteString(L"Direct3D 12");74517452json.WriteString(L"GPU");7453json.WriteString(m_AdapterDesc.Description);74547455json.WriteString(L"DedicatedVideoMemory");7456json.WriteNumber((UINT64)m_AdapterDesc.DedicatedVideoMemory);7457json.WriteString(L"DedicatedSystemMemory");7458json.WriteNumber((UINT64)m_AdapterDesc.DedicatedSystemMemory);7459json.WriteString(L"SharedSystemMemory");7460json.WriteNumber((UINT64)m_AdapterDesc.SharedSystemMemory);74617462json.WriteString(L"ResourceHeapTier");7463json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceHeapTier));74647465json.WriteString(L"ResourceBindingTier");7466json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceBindingTier));74677468json.WriteString(L"TiledResourcesTier");7469json.WriteNumber(static_cast<UINT>(m_D3D12Options.TiledResourcesTier));74707471json.WriteString(L"TileBasedRenderer");7472json.WriteBool(m_D3D12Architecture.TileBasedRenderer);74737474json.WriteString(L"UMA");7475json.WriteBool(m_D3D12Architecture.UMA);7476json.WriteString(L"CacheCoherentUMA");7477json.WriteBool(m_D3D12Architecture.CacheCoherentUMA);7478}7479json.EndObject();7480}7481{7482json.WriteString(L"Total");7483json.AddDetailedStatisticsInfoObject(stats.Total);7484}7485{7486json.WriteString(L"MemoryInfo");7487json.BeginObject();7488{7489json.WriteString(L"L0");7490json.BeginObject();7491{7492json.WriteString(L"Budget");7493WriteBudgetToJson(json, IsUMA() ? localBudget : nonLocalBudget); // When UMA device only L0 present as local74947495json.WriteString(L"Stats");7496json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[!IsUMA()]);74977498json.WriteString(L"MemoryPools");7499json.BeginObject();7500{7501if (IsUMA())7502{7503json.WriteString(L"DEFAULT");7504json.BeginObject();7505{7506json.WriteString(L"Stats");7507json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);7508}7509json.EndObject();7510}7511json.WriteString(L"UPLOAD");7512json.BeginObject();7513{7514json.WriteString(L"Stats");7515json.AddDetailedStatisticsInfoObject(stats.HeapType[1]);7516}7517json.EndObject();75187519json.WriteString(L"READBACK");7520json.BeginObject();7521{7522json.WriteString(L"Stats");7523json.AddDetailedStatisticsInfoObject(stats.HeapType[2]);7524}7525json.EndObject();75267527json.WriteString(L"CUSTOM");7528json.BeginObject();7529{7530json.WriteString(L"Stats");7531json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]);7532}7533json.EndObject();7534}7535json.EndObject();7536}7537json.EndObject();7538if (!IsUMA())7539{7540json.WriteString(L"L1");7541json.BeginObject();7542{7543json.WriteString(L"Budget");7544WriteBudgetToJson(json, localBudget);75457546json.WriteString(L"Stats");7547json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[0]);75487549json.WriteString(L"MemoryPools");7550json.BeginObject();7551{7552json.WriteString(L"DEFAULT");7553json.BeginObject();7554{7555json.WriteString(L"Stats");7556json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);7557}7558json.EndObject();75597560json.WriteString(L"CUSTOM");7561json.BeginObject();7562{7563json.WriteString(L"Stats");7564json.AddDetailedStatisticsInfoObject(customHeaps[0]);7565}7566json.EndObject();7567}7568json.EndObject();7569}7570json.EndObject();7571}7572}7573json.EndObject();7574}75757576if (detailedMap)7577{7578const auto writeHeapInfo = [&](BlockVector* blockVector, CommittedAllocationList* committedAllocs, bool customHeap)7579{7580D3D12MA_ASSERT(blockVector);75817582D3D12_HEAP_FLAGS flags = blockVector->GetHeapFlags();7583json.WriteString(L"Flags");7584json.BeginArray(true);7585{7586if (flags & D3D12_HEAP_FLAG_SHARED)7587json.WriteString(L"HEAP_FLAG_SHARED");7588if (flags & D3D12_HEAP_FLAG_ALLOW_DISPLAY)7589json.WriteString(L"HEAP_FLAG_ALLOW_DISPLAY");7590if (flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER)7591json.WriteString(L"HEAP_FLAG_CROSS_ADAPTER");7592if (flags & D3D12_HEAP_FLAG_HARDWARE_PROTECTED)7593json.WriteString(L"HEAP_FLAG_HARDWARE_PROTECTED");7594if (flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH)7595json.WriteString(L"HEAP_FLAG_ALLOW_WRITE_WATCH");7596if (flags & D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS)7597json.WriteString(L"HEAP_FLAG_ALLOW_SHADER_ATOMICS");7598#ifdef __ID3D12Device8_INTERFACE_DEFINED__7599if (flags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT)7600json.WriteString(L"HEAP_FLAG_CREATE_NOT_RESIDENT");7601if (flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED)7602json.WriteString(L"HEAP_FLAG_CREATE_NOT_ZEROED");7603#endif76047605if (flags & D3D12_HEAP_FLAG_DENY_BUFFERS)7606json.WriteString(L"HEAP_FLAG_DENY_BUFFERS");7607if (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES)7608json.WriteString(L"HEAP_FLAG_DENY_RT_DS_TEXTURES");7609if (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES)7610json.WriteString(L"HEAP_FLAG_DENY_NON_RT_DS_TEXTURES");76117612flags &= ~(D3D12_HEAP_FLAG_SHARED7613| D3D12_HEAP_FLAG_DENY_BUFFERS7614| D3D12_HEAP_FLAG_ALLOW_DISPLAY7615| D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER7616| D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES7617| D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES7618| D3D12_HEAP_FLAG_HARDWARE_PROTECTED7619| D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH7620| D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS);7621#ifdef __ID3D12Device8_INTERFACE_DEFINED__7622flags &= ~(D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT7623| D3D12_HEAP_FLAG_CREATE_NOT_ZEROED);7624#endif7625if (flags != 0)7626json.WriteNumber((UINT)flags);76277628if (customHeap)7629{7630const D3D12_HEAP_PROPERTIES& properties = blockVector->GetHeapProperties();7631switch (properties.MemoryPoolPreference)7632{7633default:7634D3D12MA_ASSERT(0);7635case D3D12_MEMORY_POOL_UNKNOWN:7636json.WriteString(L"MEMORY_POOL_UNKNOWN");7637break;7638case D3D12_MEMORY_POOL_L0:7639json.WriteString(L"MEMORY_POOL_L0");7640break;7641case D3D12_MEMORY_POOL_L1:7642json.WriteString(L"MEMORY_POOL_L1");7643break;7644}7645switch (properties.CPUPageProperty)7646{7647default:7648D3D12MA_ASSERT(0);7649case D3D12_CPU_PAGE_PROPERTY_UNKNOWN:7650json.WriteString(L"CPU_PAGE_PROPERTY_UNKNOWN");7651break;7652case D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE:7653json.WriteString(L"CPU_PAGE_PROPERTY_NOT_AVAILABLE");7654break;7655case D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE:7656json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_COMBINE");7657break;7658case D3D12_CPU_PAGE_PROPERTY_WRITE_BACK:7659json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_BACK");7660break;7661}7662}7663}7664json.EndArray();76657666json.WriteString(L"PreferredBlockSize");7667json.WriteNumber(blockVector->GetPreferredBlockSize());76687669json.WriteString(L"Blocks");7670blockVector->WriteBlockInfoToJson(json);76717672json.WriteString(L"DedicatedAllocations");7673json.BeginArray();7674if (committedAllocs)7675committedAllocs->BuildStatsString(json);7676json.EndArray();7677};76787679json.WriteString(L"DefaultPools");7680json.BeginObject();7681{7682if (SupportsResourceHeapTier2())7683{7684for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)7685{7686json.WriteString(HeapTypeNames[heapType]);7687json.BeginObject();7688writeHeapInfo(m_BlockVectors[heapType], m_CommittedAllocations + heapType, false);7689json.EndObject();7690}7691}7692else7693{7694for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)7695{7696for (uint8_t heapSubType = 0; heapSubType < 3; ++heapSubType)7697{7698static const WCHAR* const heapSubTypeName[] = {7699L" - Buffers",7700L" - Textures",7701L" - Textures RT/DS",7702};7703json.BeginString(HeapTypeNames[heapType]);7704json.EndString(heapSubTypeName[heapSubType]);77057706json.BeginObject();7707writeHeapInfo(m_BlockVectors[heapType + heapSubType], m_CommittedAllocations + heapType, false);7708json.EndObject();7709}7710}7711}7712}7713json.EndObject();77147715json.WriteString(L"CustomPools");7716json.BeginObject();7717for (uint8_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)7718{7719MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex);7720auto* item = m_Pools[heapTypeIndex].Front();7721if (item != NULL)7722{7723size_t index = 0;7724json.WriteString(HeapTypeNames[heapTypeIndex]);7725json.BeginArray();7726do7727{7728json.BeginObject();7729json.WriteString(L"Name");7730json.BeginString();7731json.ContinueString(index++);7732if (item->GetName())7733{7734json.ContinueString(L" - ");7735json.ContinueString(item->GetName());7736}7737json.EndString();77387739writeHeapInfo(item->GetBlockVector(), item->GetCommittedAllocationList(), heapTypeIndex == 3);7740json.EndObject();7741} while ((item = PoolList::GetNext(item)) != NULL);7742json.EndArray();7743}7744}7745json.EndObject();7746}7747json.EndObject();7748}77497750const size_t length = sb.GetLength();7751WCHAR* result = AllocateArray<WCHAR>(GetAllocs(), length + 2);7752result[0] = 0xFEFF;7753memcpy(result + 1, sb.GetData(), length * sizeof(WCHAR));7754result[length + 1] = L'\0';7755*ppStatsString = result;7756}77577758void AllocatorPimpl::FreeStatsString(WCHAR* pStatsString)7759{7760D3D12MA_ASSERT(pStatsString);7761Free(GetAllocs(), pStatsString);7762}77637764template<typename D3D12_RESOURCE_DESC_T>7765bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& resourceDesc)7766{7767// Intentional. It may change in the future.7768return false;7769}77707771HRESULT AllocatorPimpl::AllocateCommittedResource(7772const CommittedAllocationParameters& committedAllocParams,7773UINT64 resourceSize, bool withinBudget, void* pPrivateData,7774const CREATE_RESOURCE_PARAMS& createParams,7775Allocation** ppAllocation, REFIID riidResource, void** ppvResource)7776{7777D3D12MA_ASSERT(committedAllocParams.IsValid());77787779HRESULT hr;7780ID3D12Resource* res = NULL;7781// Allocate aliasing memory with explicit heap7782if (committedAllocParams.m_CanAlias)7783{7784D3D12_RESOURCE_ALLOCATION_INFO heapAllocInfo = {};7785heapAllocInfo.SizeInBytes = resourceSize;7786heapAllocInfo.Alignment = HeapFlagsToAlignment(committedAllocParams.m_HeapFlags, m_MsaaAlwaysCommitted);7787hr = AllocateHeap(committedAllocParams, heapAllocInfo, withinBudget, pPrivateData, ppAllocation);7788if (SUCCEEDED(hr))7789{7790hr = CreatePlacedResourceWrap((*ppAllocation)->GetHeap(), 0,7791createParams, D3D12MA_IID_PPV_ARGS(&res));7792if (SUCCEEDED(hr))7793{7794if (ppvResource != NULL)7795hr = res->QueryInterface(riidResource, ppvResource);7796if (SUCCEEDED(hr))7797{7798(*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc());7799return hr;7800}7801res->Release();7802}7803FreeHeapMemory(*ppAllocation);7804}7805return hr;7806}78077808if (withinBudget &&7809!NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, resourceSize))7810{7811return E_OUTOFMEMORY;7812}78137814/* D3D12 ERROR:7815* ID3D12Device::CreateCommittedResource:7816* When creating a committed resource, D3D12_HEAP_FLAGS must not have either7817* D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES,7818* D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES,7819* nor D3D12_HEAP_FLAG_DENY_BUFFERS set.7820* These flags will be set automatically to correspond with the committed resource type.7821*7822* [ STATE_CREATION ERROR #640: CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS]7823*/78247825#ifdef __ID3D12Device10_INTERFACE_DEFINED__7826if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT)7827{7828if (!m_Device10)7829{7830return E_NOINTERFACE;7831}7832hr = m_Device10->CreateCommittedResource3(7833&committedAllocParams.m_HeapProperties,7834committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7835createParams.GetResourceDesc1(), createParams.GetInitialLayout(),7836createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7837createParams.GetNumCastableFormats(), createParams.GetCastableFormats(),7838D3D12MA_IID_PPV_ARGS(&res));7839} else7840#endif7841#ifdef __ID3D12Device8_INTERFACE_DEFINED__7842if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1)7843{7844if (!m_Device8)7845{7846return E_NOINTERFACE;7847}7848hr = m_Device8->CreateCommittedResource2(7849&committedAllocParams.m_HeapProperties,7850committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7851createParams.GetResourceDesc1(), createParams.GetInitialResourceState(),7852createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7853D3D12MA_IID_PPV_ARGS(&res));7854} else7855#endif7856if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE)7857{7858#ifdef __ID3D12Device4_INTERFACE_DEFINED__7859if (m_Device4)7860{7861hr = m_Device4->CreateCommittedResource1(7862&committedAllocParams.m_HeapProperties,7863committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7864createParams.GetResourceDesc(), createParams.GetInitialResourceState(),7865createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession,7866D3D12MA_IID_PPV_ARGS(&res));7867}7868else7869#endif7870{7871if (committedAllocParams.m_ProtectedSession == NULL)7872{7873hr = m_Device->CreateCommittedResource(7874&committedAllocParams.m_HeapProperties,7875committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS,7876createParams.GetResourceDesc(), createParams.GetInitialResourceState(),7877createParams.GetOptimizedClearValue(), D3D12MA_IID_PPV_ARGS(&res));7878}7879else7880hr = E_NOINTERFACE;7881}7882}7883else7884{7885D3D12MA_ASSERT(0);7886return E_INVALIDARG;7887}78887889if (SUCCEEDED(hr))7890{7891SetResidencyPriority(res, committedAllocParams.m_ResidencyPriority);78927893if (ppvResource != NULL)7894{7895hr = res->QueryInterface(riidResource, ppvResource);7896}7897if (SUCCEEDED(hr))7898{7899BOOL wasZeroInitialized = TRUE;7900#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE7901if((committedAllocParams.m_HeapFlags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) != 0)7902{7903wasZeroInitialized = FALSE;7904}7905#endif79067907Allocation* alloc = m_AllocationObjectAllocator.Allocate(7908this, resourceSize, createParams.GetBaseResourceDesc()->Alignment, wasZeroInitialized);7909alloc->InitCommitted(committedAllocParams.m_List);7910alloc->SetResourcePointer(res, createParams.GetBaseResourceDesc());7911alloc->SetPrivateData(pPrivateData);79127913*ppAllocation = alloc;79147915committedAllocParams.m_List->Register(alloc);79167917const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties);7918m_Budget.AddBlock(memSegmentGroup, resourceSize);7919m_Budget.AddAllocation(memSegmentGroup, resourceSize);7920}7921else7922{7923res->Release();7924}7925}7926return hr;7927}79287929HRESULT AllocatorPimpl::AllocateHeap(7930const CommittedAllocationParameters& committedAllocParams,7931const D3D12_RESOURCE_ALLOCATION_INFO& allocInfo, bool withinBudget,7932void* pPrivateData, Allocation** ppAllocation)7933{7934D3D12MA_ASSERT(committedAllocParams.IsValid());79357936*ppAllocation = nullptr;79377938if (withinBudget &&7939!NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, allocInfo.SizeInBytes))7940{7941return E_OUTOFMEMORY;7942}79437944D3D12_HEAP_DESC heapDesc = {};7945heapDesc.SizeInBytes = allocInfo.SizeInBytes;7946heapDesc.Properties = committedAllocParams.m_HeapProperties;7947heapDesc.Alignment = allocInfo.Alignment;7948heapDesc.Flags = committedAllocParams.m_HeapFlags;79497950HRESULT hr;7951ID3D12Heap* heap = nullptr;7952#ifdef __ID3D12Device4_INTERFACE_DEFINED__7953if (m_Device4)7954hr = m_Device4->CreateHeap1(&heapDesc, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&heap));7955else7956#endif7957{7958if (committedAllocParams.m_ProtectedSession == NULL)7959hr = m_Device->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&heap));7960else7961hr = E_NOINTERFACE;7962}79637964if (SUCCEEDED(hr))7965{7966SetResidencyPriority(heap, committedAllocParams.m_ResidencyPriority);79677968BOOL wasZeroInitialized = TRUE;7969#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE7970if((heapDesc.Flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) != 0)7971{7972wasZeroInitialized = FALSE;7973}7974#endif79757976(*ppAllocation) = m_AllocationObjectAllocator.Allocate(this, allocInfo.SizeInBytes, allocInfo.Alignment, wasZeroInitialized);7977(*ppAllocation)->InitHeap(committedAllocParams.m_List, heap);7978(*ppAllocation)->SetPrivateData(pPrivateData);7979committedAllocParams.m_List->Register(*ppAllocation);79807981const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties);7982m_Budget.AddBlock(memSegmentGroup, allocInfo.SizeInBytes);7983m_Budget.AddAllocation(memSegmentGroup, allocInfo.SizeInBytes);7984}7985return hr;7986}79877988template<typename D3D12_RESOURCE_DESC_T>7989HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, UINT64 allocSize,7990const D3D12_RESOURCE_DESC_T* resDesc,7991BlockVector*& outBlockVector, CommittedAllocationParameters& outCommittedAllocationParams, bool& outPreferCommitted)7992{7993outBlockVector = NULL;7994outCommittedAllocationParams = CommittedAllocationParameters();7995outPreferCommitted = false;79967997bool msaaAlwaysCommitted;7998if (allocDesc.CustomPool != NULL)7999{8000PoolPimpl* const pool = allocDesc.CustomPool->m_Pimpl;80018002msaaAlwaysCommitted = pool->GetBlockVector()->DeniesMsaaTextures();8003outBlockVector = pool->GetBlockVector();80048005const auto& desc = pool->GetDesc();8006outCommittedAllocationParams.m_ProtectedSession = desc.pProtectedSession;8007outCommittedAllocationParams.m_HeapProperties = desc.HeapProperties;8008outCommittedAllocationParams.m_HeapFlags = desc.HeapFlags;8009outCommittedAllocationParams.m_List = pool->GetCommittedAllocationList();8010outCommittedAllocationParams.m_ResidencyPriority = pool->GetDesc().ResidencyPriority;8011}8012else8013{8014if (!IsHeapTypeStandard(allocDesc.HeapType))8015{8016return E_INVALIDARG;8017}8018msaaAlwaysCommitted = m_MsaaAlwaysCommitted;80198020outCommittedAllocationParams.m_HeapProperties = StandardHeapTypeToHeapProperties(allocDesc.HeapType);8021outCommittedAllocationParams.m_HeapFlags = allocDesc.ExtraHeapFlags;8022outCommittedAllocationParams.m_List = &m_CommittedAllocations[HeapTypeToIndex(allocDesc.HeapType)];8023// outCommittedAllocationParams.m_ResidencyPriority intentionally left with default value.80248025const ResourceClass resourceClass = (resDesc != NULL) ?8026ResourceDescToResourceClass(*resDesc) : HeapFlagsToResourceClass(allocDesc.ExtraHeapFlags);8027const UINT defaultPoolIndex = CalcDefaultPoolIndex(allocDesc, resourceClass);8028if (defaultPoolIndex != UINT32_MAX)8029{8030outBlockVector = m_BlockVectors[defaultPoolIndex];8031const UINT64 preferredBlockSize = outBlockVector->GetPreferredBlockSize();8032if (allocSize > preferredBlockSize)8033{8034outBlockVector = NULL;8035}8036else if (allocSize > preferredBlockSize / 2)8037{8038// Heuristics: Allocate committed memory if requested size if greater than half of preferred block size.8039outPreferCommitted = true;8040}8041}80428043const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS;8044if (outBlockVector != NULL && extraHeapFlags != 0)8045{8046outBlockVector = NULL;8047}8048}80498050if ((allocDesc.Flags & ALLOCATION_FLAG_COMMITTED) != 0 ||8051m_AlwaysCommitted)8052{8053outBlockVector = NULL;8054}8055if ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) != 0)8056{8057outCommittedAllocationParams.m_List = NULL;8058}8059outCommittedAllocationParams.m_CanAlias = allocDesc.Flags & ALLOCATION_FLAG_CAN_ALIAS;80608061if (resDesc != NULL)8062{8063if (resDesc->SampleDesc.Count > 1 && msaaAlwaysCommitted)8064outBlockVector = NULL;8065if (!outPreferCommitted && PrefersCommittedAllocation(*resDesc))8066outPreferCommitted = true;8067}80688069return (outBlockVector != NULL || outCommittedAllocationParams.m_List != NULL) ? S_OK : E_INVALIDARG;8070}80718072UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const8073{8074D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS;80758076#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE8077// If allocator was created with ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED, also ignore8078// D3D12_HEAP_FLAG_CREATE_NOT_ZEROED.8079if(m_DefaultPoolsNotZeroed)8080{8081extraHeapFlags &= ~D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;8082}8083#endif80848085if (extraHeapFlags != 0)8086{8087return UINT32_MAX;8088}80898090UINT poolIndex = UINT_MAX;8091switch (allocDesc.HeapType)8092{8093case D3D12_HEAP_TYPE_DEFAULT: poolIndex = 0; break;8094case D3D12_HEAP_TYPE_UPLOAD: poolIndex = 1; break;8095case D3D12_HEAP_TYPE_READBACK: poolIndex = 2; break;8096default: D3D12MA_ASSERT(0);8097}80988099if (SupportsResourceHeapTier2())8100return poolIndex;8101else8102{8103switch (resourceClass)8104{8105case ResourceClass::Buffer:8106return poolIndex * 3;8107case ResourceClass::Non_RT_DS_Texture:8108return poolIndex * 3 + 1;8109case ResourceClass::RT_DS_Texture:8110return poolIndex * 3 + 2;8111default:8112return UINT32_MAX;8113}8114}8115}81168117void AllocatorPimpl::CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_HEAP_FLAGS& outHeapFlags, UINT index) const8118{8119outHeapType = D3D12_HEAP_TYPE_DEFAULT;8120outHeapFlags = D3D12_HEAP_FLAG_NONE;81218122if (!SupportsResourceHeapTier2())8123{8124switch (index % 3)8125{8126case 0:8127outHeapFlags = D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;8128break;8129case 1:8130outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;8131break;8132case 2:8133outHeapFlags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES;8134break;8135}81368137index /= 3;8138}81398140switch (index)8141{8142case 0:8143outHeapType = D3D12_HEAP_TYPE_DEFAULT;8144break;8145case 1:8146outHeapType = D3D12_HEAP_TYPE_UPLOAD;8147break;8148case 2:8149outHeapType = D3D12_HEAP_TYPE_READBACK;8150break;8151default:8152D3D12MA_ASSERT(0);8153}8154}81558156void AllocatorPimpl::RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)8157{8158const UINT heapTypeIndex = HeapTypeToIndex(heapType);81598160MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);8161m_Pools[heapTypeIndex].PushBack(pool->m_Pimpl);8162}81638164void AllocatorPimpl::UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType)8165{8166const UINT heapTypeIndex = HeapTypeToIndex(heapType);81678168MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex);8169m_Pools[heapTypeIndex].Remove(pool->m_Pimpl);8170}81718172HRESULT AllocatorPimpl::UpdateD3D12Budget()8173{8174#if D3D12MA_DXGI_1_48175if (m_Adapter3)8176return m_Budget.UpdateBudget(m_Adapter3, m_UseMutex);8177else8178return E_NOINTERFACE;8179#else8180return S_OK;8181#endif8182}81838184D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const8185{8186#if defined(_MSC_VER) || !defined(_WIN32)8187return m_Device->GetResourceAllocationInfo(0, 1, &resourceDesc);8188#else8189D3D12_RESOURCE_ALLOCATION_INFO ret;8190m_Device->GetResourceAllocationInfo(&ret, 0, 1, &resourceDesc);8191return ret;8192#endif8193}81948195#ifdef __ID3D12Device8_INTERFACE_DEFINED__8196D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC1& resourceDesc) const8197{8198D3D12MA_ASSERT(m_Device8 != NULL);8199D3D12_RESOURCE_ALLOCATION_INFO1 info1Unused;8200#if defined(_MSC_VER) || !defined(_WIN32)8201return m_Device8->GetResourceAllocationInfo2(0, 1, &resourceDesc, &info1Unused);8202#else8203D3D12_RESOURCE_ALLOCATION_INFO ret;8204m_Device8->GetResourceAllocationInfo2(&ret, 0, 1, &resourceDesc, &info1Unused);8205return ret;8206#endif8207}8208#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__82098210template<typename D3D12_RESOURCE_DESC_T>8211D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const8212{8213/* Optional optimization: Microsoft documentation says:8214https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getresourceallocationinfo82158216Your application can forgo using GetResourceAllocationInfo for buffer resources8217(D3D12_RESOURCE_DIMENSION_BUFFER). Buffers have the same size on all adapters,8218which is merely the smallest multiple of 64KB that's greater or equal to8219D3D12_RESOURCE_DESC::Width.8220*/8221if (inOutResourceDesc.Alignment == 0 &&8222inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)8223{8224return {8225AlignUp<UINT64>(inOutResourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), // SizeInBytes8226D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT }; // Alignment8227}82288229#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT8230if (inOutResourceDesc.Alignment == 0 &&8231inOutResourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&8232(inOutResourceDesc.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) == 08233#if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT == 18234&& CanUseSmallAlignment(inOutResourceDesc)8235#endif8236)8237{8238/*8239The algorithm here is based on Microsoft sample: "Small Resources Sample"8240https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/Samples/Desktop/D3D12SmallResources8241*/8242const UINT64 smallAlignmentToTry = inOutResourceDesc.SampleDesc.Count > 1 ?8243D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT :8244D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;8245inOutResourceDesc.Alignment = smallAlignmentToTry;8246const D3D12_RESOURCE_ALLOCATION_INFO smallAllocInfo = GetResourceAllocationInfoNative(inOutResourceDesc);8247// Check if alignment requested has been granted.8248if (smallAllocInfo.Alignment == smallAlignmentToTry)8249{8250return smallAllocInfo;8251}8252inOutResourceDesc.Alignment = 0; // Restore original8253}8254#endif // #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT82558256return GetResourceAllocationInfoNative(inOutResourceDesc);8257}82588259bool AllocatorPimpl::NewAllocationWithinBudget(D3D12_HEAP_TYPE heapType, UINT64 size)8260{8261Budget budget = {};8262GetBudgetForHeapType(budget, heapType);8263return budget.UsageBytes + size <= budget.BudgetBytes;8264}82658266void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget)8267{8268json.BeginObject();8269{8270json.WriteString(L"BudgetBytes");8271json.WriteNumber(budget.BudgetBytes);8272json.WriteString(L"UsageBytes");8273json.WriteNumber(budget.UsageBytes);8274}8275json.EndObject();8276}82778278#endif // _D3D12MA_ALLOCATOR_PIMPL8279#endif // _D3D12MA_ALLOCATOR_PIMPL82808281#ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL8282class VirtualBlockPimpl8283{8284public:8285const ALLOCATION_CALLBACKS m_AllocationCallbacks;8286const UINT64 m_Size;8287BlockMetadata* m_Metadata;82888289VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc);8290~VirtualBlockPimpl();8291};82928293#ifndef _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS8294VirtualBlockPimpl::VirtualBlockPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc)8295: m_AllocationCallbacks(allocationCallbacks), m_Size(desc.Size)8296{8297switch (desc.Flags & VIRTUAL_BLOCK_FLAG_ALGORITHM_MASK)8298{8299case VIRTUAL_BLOCK_FLAG_ALGORITHM_LINEAR:8300m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_Linear)(&m_AllocationCallbacks, true);8301break;8302default:8303D3D12MA_ASSERT(0);8304case 0:8305m_Metadata = D3D12MA_NEW(allocationCallbacks, BlockMetadata_TLSF)(&m_AllocationCallbacks, true);8306break;8307}8308m_Metadata->Init(m_Size);8309}83108311VirtualBlockPimpl::~VirtualBlockPimpl()8312{8313D3D12MA_DELETE(m_AllocationCallbacks, m_Metadata);8314}8315#endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL_FUNCTIONS8316#endif // _D3D12MA_VIRTUAL_BLOCK_PIMPL831783188319#ifndef _D3D12MA_MEMORY_BLOCK_FUNCTIONS8320MemoryBlock::MemoryBlock(8321AllocatorPimpl* allocator,8322const D3D12_HEAP_PROPERTIES& heapProps,8323D3D12_HEAP_FLAGS heapFlags,8324UINT64 size,8325UINT id)8326: m_Allocator(allocator),8327m_HeapProps(heapProps),8328m_HeapFlags(heapFlags),8329m_Size(size),8330m_Id(id) {}83318332MemoryBlock::~MemoryBlock()8333{8334if (m_Heap)8335{8336m_Heap->Release();8337m_Allocator->m_Budget.RemoveBlock(8338m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size);8339}8340}83418342HRESULT MemoryBlock::Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures)8343{8344D3D12MA_ASSERT(m_Heap == NULL && m_Size > 0);83458346D3D12_HEAP_DESC heapDesc = {};8347heapDesc.SizeInBytes = m_Size;8348heapDesc.Properties = m_HeapProps;8349heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags, denyMsaaTextures);8350heapDesc.Flags = m_HeapFlags;83518352HRESULT hr;8353#ifdef __ID3D12Device4_INTERFACE_DEFINED__8354ID3D12Device4* const device4 = m_Allocator->GetDevice4();8355if (device4)8356hr = m_Allocator->GetDevice4()->CreateHeap1(&heapDesc, pProtectedSession, D3D12MA_IID_PPV_ARGS(&m_Heap));8357else8358#endif8359{8360if (pProtectedSession == NULL)8361hr = m_Allocator->GetDevice()->CreateHeap(&heapDesc, D3D12MA_IID_PPV_ARGS(&m_Heap));8362else8363hr = E_NOINTERFACE;8364}83658366if (SUCCEEDED(hr))8367{8368m_Allocator->m_Budget.AddBlock(8369m_Allocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), m_Size);8370}8371return hr;8372}8373#endif // _D3D12MA_MEMORY_BLOCK_FUNCTIONS83748375#ifndef _D3D12MA_NORMAL_BLOCK_FUNCTIONS8376NormalBlock::NormalBlock(8377AllocatorPimpl* allocator,8378BlockVector* blockVector,8379const D3D12_HEAP_PROPERTIES& heapProps,8380D3D12_HEAP_FLAGS heapFlags,8381UINT64 size,8382UINT id)8383: MemoryBlock(allocator, heapProps, heapFlags, size, id),8384m_pMetadata(NULL),8385m_BlockVector(blockVector) {}83868387NormalBlock::~NormalBlock()8388{8389if (m_pMetadata != NULL)8390{8391// Define macro D3D12MA_DEBUG_LOG to receive the list of the unfreed allocations.8392if (!m_pMetadata->IsEmpty())8393m_pMetadata->DebugLogAllAllocations();83948395// THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY!8396// Hitting it means you have some memory leak - unreleased Allocation objects.8397D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");83988399D3D12MA_DELETE(m_Allocator->GetAllocs(), m_pMetadata);8400}8401}84028403HRESULT NormalBlock::Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures)8404{8405HRESULT hr = MemoryBlock::Init(pProtectedSession, denyMsaaTextures);8406if (FAILED(hr))8407{8408return hr;8409}84108411switch (algorithm)8412{8413case POOL_FLAG_ALGORITHM_LINEAR:8414m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_Linear)(&m_Allocator->GetAllocs(), false);8415break;8416default:8417D3D12MA_ASSERT(0);8418case 0:8419m_pMetadata = D3D12MA_NEW(m_Allocator->GetAllocs(), BlockMetadata_TLSF)(&m_Allocator->GetAllocs(), false);8420break;8421}8422m_pMetadata->Init(m_Size);84238424return hr;8425}84268427bool NormalBlock::Validate() const8428{8429D3D12MA_VALIDATE(GetHeap() &&8430m_pMetadata &&8431m_pMetadata->GetSize() != 0 &&8432m_pMetadata->GetSize() == GetSize());8433return m_pMetadata->Validate();8434}8435#endif // _D3D12MA_NORMAL_BLOCK_FUNCTIONS84368437#ifndef _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS8438void CommittedAllocationList::Init(bool useMutex, D3D12_HEAP_TYPE heapType, PoolPimpl* pool)8439{8440m_UseMutex = useMutex;8441m_HeapType = heapType;8442m_Pool = pool;8443}84448445CommittedAllocationList::~CommittedAllocationList()8446{8447if (!m_AllocationList.IsEmpty())8448{8449D3D12MA_ASSERT(0 && "Unfreed committed allocations found!");8450}8451}84528453UINT CommittedAllocationList::GetMemorySegmentGroup(AllocatorPimpl* allocator) const8454{8455if (m_Pool)8456return allocator->HeapPropertiesToMemorySegmentGroup(m_Pool->GetDesc().HeapProperties);8457else8458return allocator->StandardHeapTypeToMemorySegmentGroup(m_HeapType);8459}84608461void CommittedAllocationList::AddStatistics(Statistics& inoutStats)8462{8463MutexLockRead lock(m_Mutex, m_UseMutex);84648465for (Allocation* alloc = m_AllocationList.Front();8466alloc != NULL; alloc = m_AllocationList.GetNext(alloc))8467{8468const UINT64 size = alloc->GetSize();8469inoutStats.BlockCount++;8470inoutStats.AllocationCount++;8471inoutStats.BlockBytes += size;8472inoutStats.AllocationBytes += size;8473}8474}84758476void CommittedAllocationList::AddDetailedStatistics(DetailedStatistics& inoutStats)8477{8478MutexLockRead lock(m_Mutex, m_UseMutex);84798480for (Allocation* alloc = m_AllocationList.Front();8481alloc != NULL; alloc = m_AllocationList.GetNext(alloc))8482{8483const UINT64 size = alloc->GetSize();8484inoutStats.Stats.BlockCount++;8485inoutStats.Stats.BlockBytes += size;8486AddDetailedStatisticsAllocation(inoutStats, size);8487}8488}84898490void CommittedAllocationList::BuildStatsString(JsonWriter& json)8491{8492MutexLockRead lock(m_Mutex, m_UseMutex);84938494for (Allocation* alloc = m_AllocationList.Front();8495alloc != NULL; alloc = m_AllocationList.GetNext(alloc))8496{8497json.BeginObject(true);8498json.AddAllocationToObject(*alloc);8499json.EndObject();8500}8501}85028503void CommittedAllocationList::Register(Allocation* alloc)8504{8505MutexLockWrite lock(m_Mutex, m_UseMutex);8506m_AllocationList.PushBack(alloc);8507}85088509void CommittedAllocationList::Unregister(Allocation* alloc)8510{8511MutexLockWrite lock(m_Mutex, m_UseMutex);8512m_AllocationList.Remove(alloc);8513}8514#endif // _D3D12MA_COMMITTED_ALLOCATION_LIST_FUNCTIONS85158516#ifndef _D3D12MA_BLOCK_VECTOR_FUNCTIONS8517BlockVector::BlockVector(8518AllocatorPimpl* hAllocator,8519const D3D12_HEAP_PROPERTIES& heapProps,8520D3D12_HEAP_FLAGS heapFlags,8521UINT64 preferredBlockSize,8522size_t minBlockCount,8523size_t maxBlockCount,8524bool explicitBlockSize,8525UINT64 minAllocationAlignment,8526UINT32 algorithm,8527bool denyMsaaTextures,8528ID3D12ProtectedResourceSession* pProtectedSession,8529D3D12_RESIDENCY_PRIORITY residencyPriority)8530: m_hAllocator(hAllocator),8531m_HeapProps(heapProps),8532m_HeapFlags(heapFlags),8533m_PreferredBlockSize(preferredBlockSize),8534m_MinBlockCount(minBlockCount),8535m_MaxBlockCount(maxBlockCount),8536m_ExplicitBlockSize(explicitBlockSize),8537m_MinAllocationAlignment(minAllocationAlignment),8538m_Algorithm(algorithm),8539m_DenyMsaaTextures(denyMsaaTextures),8540m_ProtectedSession(pProtectedSession),8541m_ResidencyPriority(residencyPriority),8542m_HasEmptyBlock(false),8543m_Blocks(hAllocator->GetAllocs()),8544m_NextBlockId(0) {}85458546BlockVector::~BlockVector()8547{8548for (size_t i = m_Blocks.size(); i--; )8549{8550D3D12MA_DELETE(m_hAllocator->GetAllocs(), m_Blocks[i]);8551}8552}85538554HRESULT BlockVector::CreateMinBlocks()8555{8556for (size_t i = 0; i < m_MinBlockCount; ++i)8557{8558HRESULT hr = CreateBlock(m_PreferredBlockSize, NULL);8559if (FAILED(hr))8560{8561return hr;8562}8563}8564return S_OK;8565}85668567bool BlockVector::IsEmpty()8568{8569MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());8570return m_Blocks.empty();8571}85728573HRESULT BlockVector::Allocate(8574UINT64 size,8575UINT64 alignment,8576const ALLOCATION_DESC& allocDesc,8577size_t allocationCount,8578Allocation** pAllocations)8579{8580size_t allocIndex;8581HRESULT hr = S_OK;85828583{8584MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());8585for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)8586{8587hr = AllocatePage(8588size,8589alignment,8590allocDesc,8591pAllocations + allocIndex);8592if (FAILED(hr))8593{8594break;8595}8596}8597}85988599if (FAILED(hr))8600{8601// Free all already created allocations.8602while (allocIndex--)8603{8604Free(pAllocations[allocIndex]);8605}8606ZeroMemory(pAllocations, sizeof(Allocation*) * allocationCount);8607}86088609return hr;8610}86118612void BlockVector::Free(Allocation* hAllocation)8613{8614NormalBlock* pBlockToDelete = NULL;86158616bool budgetExceeded = false;8617if (IsHeapTypeStandard(m_HeapProps.Type))8618{8619Budget budget = {};8620m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type);8621budgetExceeded = budget.UsageBytes >= budget.BudgetBytes;8622}86238624// Scope for lock.8625{8626MutexLockWrite lock(m_Mutex, m_hAllocator->UseMutex());86278628NormalBlock* pBlock = hAllocation->m_Placed.block;86298630pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());8631D3D12MA_HEAVY_ASSERT(pBlock->Validate());86328633const size_t blockCount = m_Blocks.size();8634// pBlock became empty after this deallocation.8635if (pBlock->m_pMetadata->IsEmpty())8636{8637// Already has empty Allocation. We don't want to have two, so delete this one.8638if ((m_HasEmptyBlock || budgetExceeded) &&8639blockCount > m_MinBlockCount)8640{8641pBlockToDelete = pBlock;8642Remove(pBlock);8643}8644// We now have first empty block.8645else8646{8647m_HasEmptyBlock = true;8648}8649}8650// pBlock didn't become empty, but we have another empty block - find and free that one.8651// (This is optional, heuristics.)8652else if (m_HasEmptyBlock && blockCount > m_MinBlockCount)8653{8654NormalBlock* pLastBlock = m_Blocks.back();8655if (pLastBlock->m_pMetadata->IsEmpty())8656{8657pBlockToDelete = pLastBlock;8658m_Blocks.pop_back();8659m_HasEmptyBlock = false;8660}8661}86628663IncrementallySortBlocks();8664}86658666// Destruction of a free Allocation. Deferred until this point, outside of mutex8667// lock, for performance reason.8668if (pBlockToDelete != NULL)8669{8670D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlockToDelete);8671}8672}86738674HRESULT BlockVector::CreateResource(8675UINT64 size,8676UINT64 alignment,8677const ALLOCATION_DESC& allocDesc,8678const CREATE_RESOURCE_PARAMS& createParams,8679Allocation** ppAllocation,8680REFIID riidResource,8681void** ppvResource)8682{8683HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation);8684if (SUCCEEDED(hr))8685{8686ID3D12Resource* res = NULL;8687hr = m_hAllocator->CreatePlacedResourceWrap(8688(*ppAllocation)->m_Placed.block->GetHeap(),8689(*ppAllocation)->GetOffset(),8690createParams,8691D3D12MA_IID_PPV_ARGS(&res));8692if (SUCCEEDED(hr))8693{8694if (ppvResource != NULL)8695{8696hr = res->QueryInterface(riidResource, ppvResource);8697}8698if (SUCCEEDED(hr))8699{8700(*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc());8701}8702else8703{8704res->Release();8705SAFE_RELEASE(*ppAllocation);8706}8707}8708else8709{8710SAFE_RELEASE(*ppAllocation);8711}8712}8713return hr;8714}87158716void BlockVector::AddStatistics(Statistics& inoutStats)8717{8718MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());87198720for (size_t i = 0; i < m_Blocks.size(); ++i)8721{8722const NormalBlock* const pBlock = m_Blocks[i];8723D3D12MA_ASSERT(pBlock);8724D3D12MA_HEAVY_ASSERT(pBlock->Validate());8725pBlock->m_pMetadata->AddStatistics(inoutStats);8726}8727}87288729void BlockVector::AddDetailedStatistics(DetailedStatistics& inoutStats)8730{8731MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());87328733for (size_t i = 0; i < m_Blocks.size(); ++i)8734{8735const NormalBlock* const pBlock = m_Blocks[i];8736D3D12MA_ASSERT(pBlock);8737D3D12MA_HEAVY_ASSERT(pBlock->Validate());8738pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);8739}8740}87418742void BlockVector::WriteBlockInfoToJson(JsonWriter& json)8743{8744MutexLockRead lock(m_Mutex, m_hAllocator->UseMutex());87458746json.BeginObject();87478748for (size_t i = 0, count = m_Blocks.size(); i < count; ++i)8749{8750const NormalBlock* const pBlock = m_Blocks[i];8751D3D12MA_ASSERT(pBlock);8752D3D12MA_HEAVY_ASSERT(pBlock->Validate());8753json.BeginString();8754json.ContinueString(pBlock->GetId());8755json.EndString();87568757json.BeginObject();8758pBlock->m_pMetadata->WriteAllocationInfoToJson(json);8759json.EndObject();8760}87618762json.EndObject();8763}87648765UINT64 BlockVector::CalcSumBlockSize() const8766{8767UINT64 result = 0;8768for (size_t i = m_Blocks.size(); i--; )8769{8770result += m_Blocks[i]->m_pMetadata->GetSize();8771}8772return result;8773}87748775UINT64 BlockVector::CalcMaxBlockSize() const8776{8777UINT64 result = 0;8778for (size_t i = m_Blocks.size(); i--; )8779{8780result = D3D12MA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());8781if (result >= m_PreferredBlockSize)8782{8783break;8784}8785}8786return result;8787}87888789void BlockVector::Remove(NormalBlock* pBlock)8790{8791for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)8792{8793if (m_Blocks[blockIndex] == pBlock)8794{8795m_Blocks.remove(blockIndex);8796return;8797}8798}8799D3D12MA_ASSERT(0);8800}88018802void BlockVector::IncrementallySortBlocks()8803{8804if (!m_IncrementalSort)8805return;8806// Bubble sort only until first swap.8807for (size_t i = 1; i < m_Blocks.size(); ++i)8808{8809if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())8810{8811D3D12MA_SWAP(m_Blocks[i - 1], m_Blocks[i]);8812return;8813}8814}8815}88168817void BlockVector::SortByFreeSize()8818{8819D3D12MA_SORT(m_Blocks.begin(), m_Blocks.end(),8820[](auto* b1, auto* b2)8821{8822return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();8823});8824}88258826HRESULT BlockVector::AllocatePage(8827UINT64 size,8828UINT64 alignment,8829const ALLOCATION_DESC& allocDesc,8830Allocation** pAllocation)8831{8832// Early reject: requested allocation size is larger that maximum block size for this block vector.8833if (size + D3D12MA_DEBUG_MARGIN > m_PreferredBlockSize)8834{8835return E_OUTOFMEMORY;8836}88378838UINT64 freeMemory = UINT64_MAX;8839if (IsHeapTypeStandard(m_HeapProps.Type))8840{8841Budget budget = {};8842m_hAllocator->GetBudgetForHeapType(budget, m_HeapProps.Type);8843freeMemory = (budget.UsageBytes < budget.BudgetBytes) ? (budget.BudgetBytes - budget.UsageBytes) : 0;8844}88458846const bool canCreateNewBlock =8847((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) == 0) &&8848(m_Blocks.size() < m_MaxBlockCount) &&8849// Even if we don't have to stay within budget with this allocation, when the8850// budget would be exceeded, we don't want to allocate new blocks, but always8851// create resources as committed.8852freeMemory >= size;88538854// 1. Search existing allocations8855{8856// Forward order in m_Blocks - prefer blocks with smallest amount of free space.8857for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)8858{8859NormalBlock* const pCurrBlock = m_Blocks[blockIndex];8860D3D12MA_ASSERT(pCurrBlock);8861HRESULT hr = AllocateFromBlock(8862pCurrBlock,8863size,8864alignment,8865allocDesc.Flags,8866allocDesc.pPrivateData,8867allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK,8868pAllocation);8869if (SUCCEEDED(hr))8870{8871return hr;8872}8873}8874}88758876// 2. Try to create new block.8877if (canCreateNewBlock)8878{8879// Calculate optimal size for new block.8880UINT64 newBlockSize = m_PreferredBlockSize;8881UINT newBlockSizeShift = 0;88828883if (!m_ExplicitBlockSize)8884{8885// Allocate 1/8, 1/4, 1/2 as first blocks.8886const UINT64 maxExistingBlockSize = CalcMaxBlockSize();8887for (UINT i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)8888{8889const UINT64 smallerNewBlockSize = newBlockSize / 2;8890if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)8891{8892newBlockSize = smallerNewBlockSize;8893++newBlockSizeShift;8894}8895else8896{8897break;8898}8899}8900}89018902size_t newBlockIndex = 0;8903HRESULT hr = newBlockSize <= freeMemory ?8904CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;8905// Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.8906if (!m_ExplicitBlockSize)8907{8908while (FAILED(hr) && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)8909{8910const UINT64 smallerNewBlockSize = newBlockSize / 2;8911if (smallerNewBlockSize >= size)8912{8913newBlockSize = smallerNewBlockSize;8914++newBlockSizeShift;8915hr = newBlockSize <= freeMemory ?8916CreateBlock(newBlockSize, &newBlockIndex) : E_OUTOFMEMORY;8917}8918else8919{8920break;8921}8922}8923}89248925if (SUCCEEDED(hr))8926{8927NormalBlock* const pBlock = m_Blocks[newBlockIndex];8928D3D12MA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);89298930hr = AllocateFromBlock(8931pBlock,8932size,8933alignment,8934allocDesc.Flags,8935allocDesc.pPrivateData,8936allocDesc.Flags & ALLOCATION_FLAG_STRATEGY_MASK,8937pAllocation);8938if (SUCCEEDED(hr))8939{8940return hr;8941}8942else8943{8944// Allocation from new block failed, possibly due to D3D12MA_DEBUG_MARGIN or alignment.8945return E_OUTOFMEMORY;8946}8947}8948}89498950return E_OUTOFMEMORY;8951}89528953HRESULT BlockVector::AllocateFromBlock(8954NormalBlock* pBlock,8955UINT64 size,8956UINT64 alignment,8957ALLOCATION_FLAGS allocFlags,8958void* pPrivateData,8959UINT32 strategy,8960Allocation** pAllocation)8961{8962alignment = D3D12MA_MAX(alignment, m_MinAllocationAlignment);89638964AllocationRequest currRequest = {};8965if (pBlock->m_pMetadata->CreateAllocationRequest(8966size,8967alignment,8968allocFlags & ALLOCATION_FLAG_UPPER_ADDRESS,8969strategy,8970&currRequest))8971{8972return CommitAllocationRequest(currRequest, pBlock, size, alignment, pPrivateData, pAllocation);8973}8974return E_OUTOFMEMORY;8975}89768977HRESULT BlockVector::CommitAllocationRequest(8978AllocationRequest& allocRequest,8979NormalBlock* pBlock,8980UINT64 size,8981UINT64 alignment,8982void* pPrivateData,8983Allocation** pAllocation)8984{8985// We no longer have an empty Allocation.8986if (pBlock->m_pMetadata->IsEmpty())8987m_HasEmptyBlock = false;89888989*pAllocation = m_hAllocator->GetAllocationObjectAllocator().Allocate(m_hAllocator, size, alignment, allocRequest.zeroInitialized);8990pBlock->m_pMetadata->Alloc(allocRequest, size, *pAllocation);89918992(*pAllocation)->InitPlaced(allocRequest.allocHandle, pBlock);8993(*pAllocation)->SetPrivateData(pPrivateData);89948995D3D12MA_HEAVY_ASSERT(pBlock->Validate());8996m_hAllocator->m_Budget.AddAllocation(m_hAllocator->HeapPropertiesToMemorySegmentGroup(m_HeapProps), size);89978998return S_OK;8999}90009001HRESULT BlockVector::CreateBlock(9002UINT64 blockSize,9003size_t* pNewBlockIndex)9004{9005NormalBlock* const pBlock = D3D12MA_NEW(m_hAllocator->GetAllocs(), NormalBlock)(9006m_hAllocator,9007this,9008m_HeapProps,9009m_HeapFlags,9010blockSize,9011m_NextBlockId++);9012HRESULT hr = pBlock->Init(m_Algorithm, m_ProtectedSession, m_DenyMsaaTextures);9013if (FAILED(hr))9014{9015D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlock);9016return hr;9017}90189019m_hAllocator->SetResidencyPriority(pBlock->GetHeap(), m_ResidencyPriority);90209021m_Blocks.push_back(pBlock);9022if (pNewBlockIndex != NULL)9023{9024*pNewBlockIndex = m_Blocks.size() - 1;9025}90269027return hr;9028}9029#endif // _D3D12MA_BLOCK_VECTOR_FUNCTIONS90309031#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS9032DefragmentationContextPimpl::DefragmentationContextPimpl(9033AllocatorPimpl* hAllocator,9034const DEFRAGMENTATION_DESC& desc,9035BlockVector* poolVector)9036: m_MaxPassBytes(desc.MaxBytesPerPass == 0 ? UINT64_MAX : desc.MaxBytesPerPass),9037m_MaxPassAllocations(desc.MaxAllocationsPerPass == 0 ? UINT32_MAX : desc.MaxAllocationsPerPass),9038m_Moves(hAllocator->GetAllocs())9039{9040m_Algorithm = desc.Flags & DEFRAGMENTATION_FLAG_ALGORITHM_MASK;90419042if (poolVector != NULL)9043{9044m_BlockVectorCount = 1;9045m_PoolBlockVector = poolVector;9046m_pBlockVectors = &m_PoolBlockVector;9047m_PoolBlockVector->SetIncrementalSort(false);9048m_PoolBlockVector->SortByFreeSize();9049}9050else9051{9052m_BlockVectorCount = hAllocator->GetDefaultPoolCount();9053m_PoolBlockVector = NULL;9054m_pBlockVectors = hAllocator->GetDefaultPools();9055for (UINT32 i = 0; i < m_BlockVectorCount; ++i)9056{9057BlockVector* vector = m_pBlockVectors[i];9058if (vector != NULL)9059{9060vector->SetIncrementalSort(false);9061vector->SortByFreeSize();9062}9063}9064}90659066switch (m_Algorithm)9067{9068case 0: // Default algorithm9069m_Algorithm = DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED;9070case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:9071{9072m_AlgorithmState = D3D12MA_NEW_ARRAY(hAllocator->GetAllocs(), StateBalanced, m_BlockVectorCount);9073break;9074}9075}9076}90779078DefragmentationContextPimpl::~DefragmentationContextPimpl()9079{9080if (m_PoolBlockVector != NULL)9081m_PoolBlockVector->SetIncrementalSort(true);9082else9083{9084for (UINT32 i = 0; i < m_BlockVectorCount; ++i)9085{9086BlockVector* vector = m_pBlockVectors[i];9087if (vector != NULL)9088vector->SetIncrementalSort(true);9089}9090}90919092if (m_AlgorithmState)9093{9094switch (m_Algorithm)9095{9096case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:9097D3D12MA_DELETE_ARRAY(m_Moves.GetAllocs(), reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);9098break;9099default:9100D3D12MA_ASSERT(0);9101}9102}9103}91049105HRESULT DefragmentationContextPimpl::DefragmentPassBegin(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo)9106{9107if (m_PoolBlockVector != NULL)9108{9109MutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->m_hAllocator->UseMutex());91109111if (m_PoolBlockVector->GetBlockCount() > 1)9112ComputeDefragmentation(*m_PoolBlockVector, 0);9113else if (m_PoolBlockVector->GetBlockCount() == 1)9114ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));91159116// Setup index into block vector9117for (size_t i = 0; i < m_Moves.size(); ++i)9118m_Moves[i].pDstTmpAllocation->SetPrivateData(0);9119}9120else9121{9122for (UINT32 i = 0; i < m_BlockVectorCount; ++i)9123{9124if (m_pBlockVectors[i] != NULL)9125{9126MutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->m_hAllocator->UseMutex());91279128bool end = false;9129size_t movesOffset = m_Moves.size();9130if (m_pBlockVectors[i]->GetBlockCount() > 1)9131{9132end = ComputeDefragmentation(*m_pBlockVectors[i], i);9133}9134else if (m_pBlockVectors[i]->GetBlockCount() == 1)9135{9136end = ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0));9137}91389139// Setup index into block vector9140for (; movesOffset < m_Moves.size(); ++movesOffset)9141m_Moves[movesOffset].pDstTmpAllocation->SetPrivateData(reinterpret_cast<void*>(static_cast<uintptr_t>(i)));91429143if (end)9144break;9145}9146}9147}91489149moveInfo.MoveCount = static_cast<UINT32>(m_Moves.size());9150if (moveInfo.MoveCount > 0)9151{9152moveInfo.pMoves = m_Moves.data();9153return S_FALSE;9154}91559156moveInfo.pMoves = NULL;9157return S_OK;9158}91599160HRESULT DefragmentationContextPimpl::DefragmentPassEnd(DEFRAGMENTATION_PASS_MOVE_INFO& moveInfo)9161{9162D3D12MA_ASSERT(moveInfo.MoveCount > 0 ? moveInfo.pMoves != NULL : true);91639164HRESULT result = S_OK;9165Vector<FragmentedBlock> immovableBlocks(m_Moves.GetAllocs());91669167for (uint32_t i = 0; i < moveInfo.MoveCount; ++i)9168{9169DEFRAGMENTATION_MOVE& move = moveInfo.pMoves[i];9170size_t prevCount = 0, currentCount = 0;9171UINT64 freedBlockSize = 0;91729173UINT32 vectorIndex;9174BlockVector* vector;9175if (m_PoolBlockVector != NULL)9176{9177vectorIndex = 0;9178vector = m_PoolBlockVector;9179}9180else9181{9182vectorIndex = static_cast<UINT32>(reinterpret_cast<uintptr_t>(move.pDstTmpAllocation->GetPrivateData()));9183vector = m_pBlockVectors[vectorIndex];9184D3D12MA_ASSERT(vector != NULL);9185}91869187switch (move.Operation)9188{9189case DEFRAGMENTATION_MOVE_OPERATION_COPY:9190{9191move.pSrcAllocation->SwapBlockAllocation(move.pDstTmpAllocation);91929193// Scope for locks, Free have it's own lock9194{9195MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9196prevCount = vector->GetBlockCount();9197freedBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize();9198}9199move.pDstTmpAllocation->Release();9200{9201MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9202currentCount = vector->GetBlockCount();9203}92049205result = S_FALSE;9206break;9207}9208case DEFRAGMENTATION_MOVE_OPERATION_IGNORE:9209{9210m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize();9211--m_PassStats.AllocationsMoved;9212move.pDstTmpAllocation->Release();92139214NormalBlock* newBlock = move.pSrcAllocation->GetBlock();9215bool notPresent = true;9216for (const FragmentedBlock& block : immovableBlocks)9217{9218if (block.block == newBlock)9219{9220notPresent = false;9221break;9222}9223}9224if (notPresent)9225immovableBlocks.push_back({ vectorIndex, newBlock });9226break;9227}9228case DEFRAGMENTATION_MOVE_OPERATION_DESTROY:9229{9230m_PassStats.BytesMoved -= move.pSrcAllocation->GetSize();9231--m_PassStats.AllocationsMoved;9232// Scope for locks, Free have it's own lock9233{9234MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9235prevCount = vector->GetBlockCount();9236freedBlockSize = move.pSrcAllocation->GetBlock()->m_pMetadata->GetSize();9237}9238move.pSrcAllocation->Release();9239{9240MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9241currentCount = vector->GetBlockCount();9242}9243freedBlockSize *= prevCount - currentCount;92449245UINT64 dstBlockSize;9246{9247MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9248dstBlockSize = move.pDstTmpAllocation->GetBlock()->m_pMetadata->GetSize();9249}9250move.pDstTmpAllocation->Release();9251{9252MutexLockRead lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());9253freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());9254currentCount = vector->GetBlockCount();9255}92569257result = S_FALSE;9258break;9259}9260default:9261D3D12MA_ASSERT(0);9262}92639264if (prevCount > currentCount)9265{9266size_t freedBlocks = prevCount - currentCount;9267m_PassStats.HeapsFreed += static_cast<UINT32>(freedBlocks);9268m_PassStats.BytesFreed += freedBlockSize;9269}9270}9271moveInfo.MoveCount = 0;9272moveInfo.pMoves = NULL;9273m_Moves.clear();92749275// Update stats9276m_GlobalStats.AllocationsMoved += m_PassStats.AllocationsMoved;9277m_GlobalStats.BytesFreed += m_PassStats.BytesFreed;9278m_GlobalStats.BytesMoved += m_PassStats.BytesMoved;9279m_GlobalStats.HeapsFreed += m_PassStats.HeapsFreed;9280m_PassStats = { 0 };92819282// Move blocks with immovable allocations according to algorithm9283if (immovableBlocks.size() > 0)9284{9285// Move to the begining9286for (const FragmentedBlock& block : immovableBlocks)9287{9288BlockVector* vector = m_pBlockVectors[block.data];9289MutexLockWrite lock(vector->GetMutex(), vector->m_hAllocator->UseMutex());92909291for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)9292{9293if (vector->GetBlock(i) == block.block)9294{9295D3D12MA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);9296break;9297}9298}9299}9300}9301return result;9302}93039304bool DefragmentationContextPimpl::ComputeDefragmentation(BlockVector& vector, size_t index)9305{9306switch (m_Algorithm)9307{9308case DEFRAGMENTATION_FLAG_ALGORITHM_FAST:9309return ComputeDefragmentation_Fast(vector);9310default:9311D3D12MA_ASSERT(0);9312case DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED:9313return ComputeDefragmentation_Balanced(vector, index, true);9314case DEFRAGMENTATION_FLAG_ALGORITHM_FULL:9315return ComputeDefragmentation_Full(vector);9316}9317}93189319DefragmentationContextPimpl::MoveAllocationData DefragmentationContextPimpl::GetMoveData(9320AllocHandle handle, BlockMetadata* metadata)9321{9322MoveAllocationData moveData;9323moveData.move.pSrcAllocation = (Allocation*)metadata->GetAllocationPrivateData(handle);9324moveData.size = moveData.move.pSrcAllocation->GetSize();9325moveData.alignment = moveData.move.pSrcAllocation->GetAlignment();9326moveData.flags = ALLOCATION_FLAG_NONE;93279328return moveData;9329}93309331DefragmentationContextPimpl::CounterStatus DefragmentationContextPimpl::CheckCounters(UINT64 bytes)9332{9333// Ignore allocation if will exceed max size for copy9334if (m_PassStats.BytesMoved + bytes > m_MaxPassBytes)9335{9336if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)9337return CounterStatus::Ignore;9338else9339return CounterStatus::End;9340}9341return CounterStatus::Pass;9342}93439344bool DefragmentationContextPimpl::IncrementCounters(UINT64 bytes)9345{9346m_PassStats.BytesMoved += bytes;9347// Early return when max found9348if (++m_PassStats.AllocationsMoved >= m_MaxPassAllocations || m_PassStats.BytesMoved >= m_MaxPassBytes)9349{9350D3D12MA_ASSERT((m_PassStats.AllocationsMoved == m_MaxPassAllocations ||9351m_PassStats.BytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!");9352return true;9353}9354return false;9355}93569357bool DefragmentationContextPimpl::ReallocWithinBlock(BlockVector& vector, NormalBlock* block)9358{9359BlockMetadata* metadata = block->m_pMetadata;93609361for (AllocHandle handle = metadata->GetAllocationListBegin();9362handle != (AllocHandle)0;9363handle = metadata->GetNextAllocation(handle))9364{9365MoveAllocationData moveData = GetMoveData(handle, metadata);9366// Ignore newly created allocations by defragmentation algorithm9367if (moveData.move.pSrcAllocation->GetPrivateData() == this)9368continue;9369switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9370{9371case CounterStatus::Ignore:9372continue;9373case CounterStatus::End:9374return true;9375default:9376D3D12MA_ASSERT(0);9377case CounterStatus::Pass:9378break;9379}93809381UINT64 offset = moveData.move.pSrcAllocation->GetOffset();9382if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)9383{9384AllocationRequest request = {};9385if (metadata->CreateAllocationRequest(9386moveData.size,9387moveData.alignment,9388false,9389ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,9390&request))9391{9392if (metadata->GetAllocationOffset(request.allocHandle) < offset)9393{9394if (SUCCEEDED(vector.CommitAllocationRequest(9395request,9396block,9397moveData.size,9398moveData.alignment,9399this,9400&moveData.move.pDstTmpAllocation)))9401{9402m_Moves.push_back(moveData.move);9403if (IncrementCounters(moveData.size))9404return true;9405}9406}9407}9408}9409}9410return false;9411}94129413bool DefragmentationContextPimpl::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, BlockVector& vector)9414{9415for (; start < end; ++start)9416{9417NormalBlock* dstBlock = vector.GetBlock(start);9418if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)9419{9420if (SUCCEEDED(vector.AllocateFromBlock(dstBlock,9421data.size,9422data.alignment,9423data.flags,9424this,94250,9426&data.move.pDstTmpAllocation)))9427{9428m_Moves.push_back(data.move);9429if (IncrementCounters(data.size))9430return true;9431break;9432}9433}9434}9435return false;9436}94379438bool DefragmentationContextPimpl::ComputeDefragmentation_Fast(BlockVector& vector)9439{9440// Move only between blocks94419442// Go through allocations in last blocks and try to fit them inside first ones9443for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)9444{9445BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;94469447for (AllocHandle handle = metadata->GetAllocationListBegin();9448handle != (AllocHandle)0;9449handle = metadata->GetNextAllocation(handle))9450{9451MoveAllocationData moveData = GetMoveData(handle, metadata);9452// Ignore newly created allocations by defragmentation algorithm9453if (moveData.move.pSrcAllocation->GetPrivateData() == this)9454continue;9455switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9456{9457case CounterStatus::Ignore:9458continue;9459case CounterStatus::End:9460return true;9461default:9462D3D12MA_ASSERT(0);9463case CounterStatus::Pass:9464break;9465}94669467// Check all previous blocks for free space9468if (AllocInOtherBlock(0, i, moveData, vector))9469return true;9470}9471}9472return false;9473}94749475bool DefragmentationContextPimpl::ComputeDefragmentation_Balanced(BlockVector& vector, size_t index, bool update)9476{9477// Go over every allocation and try to fit it in previous blocks at lowest offsets,9478// if not possible: realloc within single block to minimize offset (exclude offset == 0),9479// but only if there are noticable gaps between them (some heuristic, ex. average size of allocation in block)9480D3D12MA_ASSERT(m_AlgorithmState != NULL);94819482StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];9483if (update && vectorState.avgAllocSize == UINT64_MAX)9484UpdateVectorStatistics(vector, vectorState);94859486const size_t startMoveCount = m_Moves.size();9487UINT64 minimalFreeRegion = vectorState.avgFreeSize / 2;9488for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)9489{9490NormalBlock* block = vector.GetBlock(i);9491BlockMetadata* metadata = block->m_pMetadata;9492UINT64 prevFreeRegionSize = 0;94939494for (AllocHandle handle = metadata->GetAllocationListBegin();9495handle != (AllocHandle)0;9496handle = metadata->GetNextAllocation(handle))9497{9498MoveAllocationData moveData = GetMoveData(handle, metadata);9499// Ignore newly created allocations by defragmentation algorithm9500if (moveData.move.pSrcAllocation->GetPrivateData() == this)9501continue;9502switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9503{9504case CounterStatus::Ignore:9505continue;9506case CounterStatus::End:9507return true;9508default:9509D3D12MA_ASSERT(0);9510case CounterStatus::Pass:9511break;9512}95139514// Check all previous blocks for free space9515const size_t prevMoveCount = m_Moves.size();9516if (AllocInOtherBlock(0, i, moveData, vector))9517return true;95189519UINT64 nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);9520// If no room found then realloc within block for lower offset9521UINT64 offset = moveData.move.pSrcAllocation->GetOffset();9522if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)9523{9524// Check if realloc will make sense9525if (prevFreeRegionSize >= minimalFreeRegion ||9526nextFreeRegionSize >= minimalFreeRegion ||9527moveData.size <= vectorState.avgFreeSize ||9528moveData.size <= vectorState.avgAllocSize)9529{9530AllocationRequest request = {};9531if (metadata->CreateAllocationRequest(9532moveData.size,9533moveData.alignment,9534false,9535ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,9536&request))9537{9538if (metadata->GetAllocationOffset(request.allocHandle) < offset)9539{9540if (SUCCEEDED(vector.CommitAllocationRequest(9541request,9542block,9543moveData.size,9544moveData.alignment,9545this,9546&moveData.move.pDstTmpAllocation)))9547{9548m_Moves.push_back(moveData.move);9549if (IncrementCounters(moveData.size))9550return true;9551}9552}9553}9554}9555}9556prevFreeRegionSize = nextFreeRegionSize;9557}9558}95599560// No moves perfomed, update statistics to current vector state9561if (startMoveCount == m_Moves.size() && !update)9562{9563vectorState.avgAllocSize = UINT64_MAX;9564return ComputeDefragmentation_Balanced(vector, index, false);9565}9566return false;9567}95689569bool DefragmentationContextPimpl::ComputeDefragmentation_Full(BlockVector& vector)9570{9571// Go over every allocation and try to fit it in previous blocks at lowest offsets,9572// if not possible: realloc within single block to minimize offset (exclude offset == 0)95739574for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)9575{9576NormalBlock* block = vector.GetBlock(i);9577BlockMetadata* metadata = block->m_pMetadata;95789579for (AllocHandle handle = metadata->GetAllocationListBegin();9580handle != (AllocHandle)0;9581handle = metadata->GetNextAllocation(handle))9582{9583MoveAllocationData moveData = GetMoveData(handle, metadata);9584// Ignore newly created allocations by defragmentation algorithm9585if (moveData.move.pSrcAllocation->GetPrivateData() == this)9586continue;9587switch (CheckCounters(moveData.move.pSrcAllocation->GetSize()))9588{9589case CounterStatus::Ignore:9590continue;9591case CounterStatus::End:9592return true;9593default:9594D3D12MA_ASSERT(0);9595case CounterStatus::Pass:9596break;9597}95989599// Check all previous blocks for free space9600const size_t prevMoveCount = m_Moves.size();9601if (AllocInOtherBlock(0, i, moveData, vector))9602return true;96039604// If no room found then realloc within block for lower offset9605UINT64 offset = moveData.move.pSrcAllocation->GetOffset();9606if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)9607{9608AllocationRequest request = {};9609if (metadata->CreateAllocationRequest(9610moveData.size,9611moveData.alignment,9612false,9613ALLOCATION_FLAG_STRATEGY_MIN_OFFSET,9614&request))9615{9616if (metadata->GetAllocationOffset(request.allocHandle) < offset)9617{9618if (SUCCEEDED(vector.CommitAllocationRequest(9619request,9620block,9621moveData.size,9622moveData.alignment,9623this,9624&moveData.move.pDstTmpAllocation)))9625{9626m_Moves.push_back(moveData.move);9627if (IncrementCounters(moveData.size))9628return true;9629}9630}9631}9632}9633}9634}9635return false;9636}96379638void DefragmentationContextPimpl::UpdateVectorStatistics(BlockVector& vector, StateBalanced& state)9639{9640size_t allocCount = 0;9641size_t freeCount = 0;9642state.avgFreeSize = 0;9643state.avgAllocSize = 0;96449645for (size_t i = 0; i < vector.GetBlockCount(); ++i)9646{9647BlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;96489649allocCount += metadata->GetAllocationCount();9650freeCount += metadata->GetFreeRegionsCount();9651state.avgFreeSize += metadata->GetSumFreeSize();9652state.avgAllocSize += metadata->GetSize();9653}96549655state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;9656state.avgFreeSize /= freeCount;9657}9658#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_PIMPL_FUNCTIONS96599660#ifndef _D3D12MA_POOL_PIMPL_FUNCTIONS9661PoolPimpl::PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc)9662: m_Allocator(allocator),9663m_Desc(desc),9664m_BlockVector(NULL),9665m_Name(NULL)9666{9667const bool explicitBlockSize = desc.BlockSize != 0;9668const UINT64 preferredBlockSize = explicitBlockSize ? desc.BlockSize : D3D12MA_DEFAULT_BLOCK_SIZE;9669UINT maxBlockCount = desc.MaxBlockCount != 0 ? desc.MaxBlockCount : UINT_MAX;96709671#ifndef __ID3D12Device4_INTERFACE_DEFINED__9672D3D12MA_ASSERT(m_Desc.pProtectedSession == NULL);9673#endif96749675m_BlockVector = D3D12MA_NEW(allocator->GetAllocs(), BlockVector)(9676allocator, desc.HeapProperties, desc.HeapFlags,9677preferredBlockSize,9678desc.MinBlockCount, maxBlockCount,9679explicitBlockSize,9680D3D12MA_MAX(desc.MinAllocationAlignment, (UINT64)D3D12MA_DEBUG_ALIGNMENT),9681(desc.Flags & POOL_FLAG_ALGORITHM_MASK) != 0,9682(desc.Flags & POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0,9683desc.pProtectedSession,9684desc.ResidencyPriority);9685}96869687PoolPimpl::~PoolPimpl()9688{9689D3D12MA_ASSERT(m_PrevPool == NULL && m_NextPool == NULL);9690FreeName();9691D3D12MA_DELETE(m_Allocator->GetAllocs(), m_BlockVector);9692}96939694HRESULT PoolPimpl::Init()9695{9696m_CommittedAllocations.Init(m_Allocator->UseMutex(), m_Desc.HeapProperties.Type, this);9697return m_BlockVector->CreateMinBlocks();9698}96999700void PoolPimpl::GetStatistics(Statistics& outStats)9701{9702ClearStatistics(outStats);9703m_BlockVector->AddStatistics(outStats);9704m_CommittedAllocations.AddStatistics(outStats);9705}97069707void PoolPimpl::CalculateStatistics(DetailedStatistics& outStats)9708{9709ClearDetailedStatistics(outStats);9710AddDetailedStatistics(outStats);9711}97129713void PoolPimpl::AddDetailedStatistics(DetailedStatistics& inoutStats)9714{9715m_BlockVector->AddDetailedStatistics(inoutStats);9716m_CommittedAllocations.AddDetailedStatistics(inoutStats);9717}97189719void PoolPimpl::SetName(LPCWSTR Name)9720{9721FreeName();97229723if (Name)9724{9725const size_t nameCharCount = wcslen(Name) + 1;9726m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);9727memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));9728}9729}97309731void PoolPimpl::FreeName()9732{9733if (m_Name)9734{9735const size_t nameCharCount = wcslen(m_Name) + 1;9736D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);9737m_Name = NULL;9738}9739}9740#endif // _D3D12MA_POOL_PIMPL_FUNCTIONS974197429743#ifndef _D3D12MA_PUBLIC_INTERFACE9744HRESULT CreateAllocator(const ALLOCATOR_DESC* pDesc, Allocator** ppAllocator)9745{9746if (!pDesc || !ppAllocator || !pDesc->pDevice || !pDesc->pAdapter ||9747!(pDesc->PreferredBlockSize == 0 || (pDesc->PreferredBlockSize >= 16 && pDesc->PreferredBlockSize < 0x10000000000ull)))9748{9749D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateAllocator.");9750return E_INVALIDARG;9751}97529753D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK97549755ALLOCATION_CALLBACKS allocationCallbacks;9756SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);97579758*ppAllocator = D3D12MA_NEW(allocationCallbacks, Allocator)(allocationCallbacks, *pDesc);9759HRESULT hr = (*ppAllocator)->m_Pimpl->Init(*pDesc);9760if (FAILED(hr))9761{9762D3D12MA_DELETE(allocationCallbacks, *ppAllocator);9763*ppAllocator = NULL;9764}9765return hr;9766}97679768HRESULT CreateVirtualBlock(const VIRTUAL_BLOCK_DESC* pDesc, VirtualBlock** ppVirtualBlock)9769{9770if (!pDesc || !ppVirtualBlock)9771{9772D3D12MA_ASSERT(0 && "Invalid arguments passed to CreateVirtualBlock.");9773return E_INVALIDARG;9774}97759776D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK97779778ALLOCATION_CALLBACKS allocationCallbacks;9779SetupAllocationCallbacks(allocationCallbacks, pDesc->pAllocationCallbacks);97809781*ppVirtualBlock = D3D12MA_NEW(allocationCallbacks, VirtualBlock)(allocationCallbacks, *pDesc);9782return S_OK;9783}97849785#ifndef _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS9786HRESULT STDMETHODCALLTYPE IUnknownImpl::QueryInterface(REFIID riid, void** ppvObject)9787{9788if (ppvObject == NULL)9789return E_POINTER;9790if (riid == IID_IUnknown)9791{9792++m_RefCount;9793*ppvObject = this;9794return S_OK;9795}9796*ppvObject = NULL;9797return E_NOINTERFACE;9798}97999800ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef()9801{9802return ++m_RefCount;9803}98049805ULONG STDMETHODCALLTYPE IUnknownImpl::Release()9806{9807D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK98089809const uint32_t newRefCount = --m_RefCount;9810if (newRefCount == 0)9811ReleaseThis();9812return newRefCount;9813}9814#endif // _D3D12MA_IUNKNOWN_IMPL_FUNCTIONS98159816#ifndef _D3D12MA_ALLOCATION_FUNCTIONS9817void Allocation::PackedData::SetType(Type type)9818{9819const UINT u = (UINT)type;9820D3D12MA_ASSERT(u < (1u << 2));9821m_Type = u;9822}98239824void Allocation::PackedData::SetResourceDimension(D3D12_RESOURCE_DIMENSION resourceDimension)9825{9826const UINT u = (UINT)resourceDimension;9827D3D12MA_ASSERT(u < (1u << 3));9828m_ResourceDimension = u;9829}98309831void Allocation::PackedData::SetResourceFlags(D3D12_RESOURCE_FLAGS resourceFlags)9832{9833const UINT u = (UINT)resourceFlags;9834D3D12MA_ASSERT(u < (1u << 24));9835m_ResourceFlags = u;9836}98379838void Allocation::PackedData::SetTextureLayout(D3D12_TEXTURE_LAYOUT textureLayout)9839{9840const UINT u = (UINT)textureLayout;9841D3D12MA_ASSERT(u < (1u << 9));9842m_TextureLayout = u;9843}98449845UINT64 Allocation::GetOffset() const9846{9847switch (m_PackedData.GetType())9848{9849case TYPE_COMMITTED:9850case TYPE_HEAP:9851return 0;9852case TYPE_PLACED:9853return m_Placed.block->m_pMetadata->GetAllocationOffset(m_Placed.allocHandle);9854default:9855D3D12MA_ASSERT(0);9856return 0;9857}9858}98599860void Allocation::SetResource(ID3D12Resource* pResource)9861{9862if (pResource != m_Resource)9863{9864if (m_Resource)9865m_Resource->Release();9866m_Resource = pResource;9867if (m_Resource)9868m_Resource->AddRef();9869}9870}98719872ID3D12Heap* Allocation::GetHeap() const9873{9874switch (m_PackedData.GetType())9875{9876case TYPE_COMMITTED:9877return NULL;9878case TYPE_PLACED:9879return m_Placed.block->GetHeap();9880case TYPE_HEAP:9881return m_Heap.heap;9882default:9883D3D12MA_ASSERT(0);9884return 0;9885}9886}98879888void Allocation::SetName(LPCWSTR Name)9889{9890FreeName();98919892if (Name)9893{9894const size_t nameCharCount = wcslen(Name) + 1;9895m_Name = D3D12MA_NEW_ARRAY(m_Allocator->GetAllocs(), WCHAR, nameCharCount);9896memcpy(m_Name, Name, nameCharCount * sizeof(WCHAR));9897}9898}98999900void Allocation::ReleaseThis()9901{9902if (this == NULL)9903{9904return;9905}99069907SAFE_RELEASE(m_Resource);99089909switch (m_PackedData.GetType())9910{9911case TYPE_COMMITTED:9912m_Allocator->FreeCommittedMemory(this);9913break;9914case TYPE_PLACED:9915m_Allocator->FreePlacedMemory(this);9916break;9917case TYPE_HEAP:9918m_Allocator->FreeHeapMemory(this);9919break;9920}99219922FreeName();99239924m_Allocator->GetAllocationObjectAllocator().Free(this);9925}99269927Allocation::Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, BOOL wasZeroInitialized)9928: m_Allocator{ allocator },9929m_Size{ size },9930m_Alignment{ alignment },9931m_Resource{ NULL },9932m_pPrivateData{ NULL },9933m_Name{ NULL }9934{9935D3D12MA_ASSERT(allocator);99369937m_PackedData.SetType(TYPE_COUNT);9938m_PackedData.SetResourceDimension(D3D12_RESOURCE_DIMENSION_UNKNOWN);9939m_PackedData.SetResourceFlags(D3D12_RESOURCE_FLAG_NONE);9940m_PackedData.SetTextureLayout(D3D12_TEXTURE_LAYOUT_UNKNOWN);9941m_PackedData.SetWasZeroInitialized(wasZeroInitialized);9942}99439944void Allocation::InitCommitted(CommittedAllocationList* list)9945{9946m_PackedData.SetType(TYPE_COMMITTED);9947m_Committed.list = list;9948m_Committed.prev = NULL;9949m_Committed.next = NULL;9950}99519952void Allocation::InitPlaced(AllocHandle allocHandle, NormalBlock* block)9953{9954m_PackedData.SetType(TYPE_PLACED);9955m_Placed.allocHandle = allocHandle;9956m_Placed.block = block;9957}99589959void Allocation::InitHeap(CommittedAllocationList* list, ID3D12Heap* heap)9960{9961m_PackedData.SetType(TYPE_HEAP);9962m_Heap.list = list;9963m_Committed.prev = NULL;9964m_Committed.next = NULL;9965m_Heap.heap = heap;9966}99679968void Allocation::SwapBlockAllocation(Allocation* allocation)9969{9970D3D12MA_ASSERT(allocation != NULL);9971D3D12MA_ASSERT(m_PackedData.GetType() == TYPE_PLACED);9972D3D12MA_ASSERT(allocation->m_PackedData.GetType() == TYPE_PLACED);99739974D3D12MA_SWAP(m_Resource, allocation->m_Resource);9975m_PackedData.SetWasZeroInitialized(allocation->m_PackedData.WasZeroInitialized());9976m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, allocation);9977D3D12MA_SWAP(m_Placed, allocation->m_Placed);9978m_Placed.block->m_pMetadata->SetAllocationPrivateData(m_Placed.allocHandle, this);9979}99809981AllocHandle Allocation::GetAllocHandle() const9982{9983switch (m_PackedData.GetType())9984{9985case TYPE_COMMITTED:9986case TYPE_HEAP:9987return (AllocHandle)0;9988case TYPE_PLACED:9989return m_Placed.allocHandle;9990default:9991D3D12MA_ASSERT(0);9992return (AllocHandle)0;9993}9994}99959996NormalBlock* Allocation::GetBlock()9997{9998switch (m_PackedData.GetType())9999{10000case TYPE_COMMITTED:10001case TYPE_HEAP:10002return NULL;10003case TYPE_PLACED:10004return m_Placed.block;10005default:10006D3D12MA_ASSERT(0);10007return NULL;10008}10009}1001010011template<typename D3D12_RESOURCE_DESC_T>10012void Allocation::SetResourcePointer(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc)10013{10014D3D12MA_ASSERT(m_Resource == NULL && pResourceDesc);10015m_Resource = resource;10016m_PackedData.SetResourceDimension(pResourceDesc->Dimension);10017m_PackedData.SetResourceFlags(pResourceDesc->Flags);10018m_PackedData.SetTextureLayout(pResourceDesc->Layout);10019}1002010021void Allocation::FreeName()10022{10023if (m_Name)10024{10025const size_t nameCharCount = wcslen(m_Name) + 1;10026D3D12MA_DELETE_ARRAY(m_Allocator->GetAllocs(), m_Name, nameCharCount);10027m_Name = NULL;10028}10029}10030#endif // _D3D12MA_ALLOCATION_FUNCTIONS1003110032#ifndef _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS10033HRESULT DefragmentationContext::BeginPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo)10034{10035D3D12MA_ASSERT(pPassInfo);10036return m_Pimpl->DefragmentPassBegin(*pPassInfo);10037}1003810039HRESULT DefragmentationContext::EndPass(DEFRAGMENTATION_PASS_MOVE_INFO* pPassInfo)10040{10041D3D12MA_ASSERT(pPassInfo);10042return m_Pimpl->DefragmentPassEnd(*pPassInfo);10043}1004410045void DefragmentationContext::GetStats(DEFRAGMENTATION_STATS* pStats)10046{10047D3D12MA_ASSERT(pStats);10048m_Pimpl->GetStats(*pStats);10049}1005010051void DefragmentationContext::ReleaseThis()10052{10053if (this == NULL)10054{10055return;10056}1005710058D3D12MA_DELETE(m_Pimpl->GetAllocs(), this);10059}1006010061DefragmentationContext::DefragmentationContext(AllocatorPimpl* allocator,10062const DEFRAGMENTATION_DESC& desc,10063BlockVector* poolVector)10064: m_Pimpl(D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContextPimpl)(allocator, desc, poolVector)) {}1006510066DefragmentationContext::~DefragmentationContext()10067{10068D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);10069}10070#endif // _D3D12MA_DEFRAGMENTATION_CONTEXT_FUNCTIONS1007110072#ifndef _D3D12MA_POOL_FUNCTIONS10073POOL_DESC Pool::GetDesc() const10074{10075return m_Pimpl->GetDesc();10076}1007710078void Pool::GetStatistics(Statistics* pStats)10079{10080D3D12MA_ASSERT(pStats);10081D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10082m_Pimpl->GetStatistics(*pStats);10083}1008410085void Pool::CalculateStatistics(DetailedStatistics* pStats)10086{10087D3D12MA_ASSERT(pStats);10088D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10089m_Pimpl->CalculateStatistics(*pStats);10090}1009110092void Pool::SetName(LPCWSTR Name)10093{10094D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10095m_Pimpl->SetName(Name);10096}1009710098LPCWSTR Pool::GetName() const10099{10100return m_Pimpl->GetName();10101}1010210103HRESULT Pool::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext)10104{10105D3D12MA_ASSERT(pDesc && ppContext);1010610107// Check for support10108if (m_Pimpl->GetBlockVector()->GetAlgorithm() & POOL_FLAG_ALGORITHM_LINEAR)10109return E_NOINTERFACE;1011010111AllocatorPimpl* allocator = m_Pimpl->GetAllocator();10112*ppContext = D3D12MA_NEW(allocator->GetAllocs(), DefragmentationContext)(allocator, *pDesc, m_Pimpl->GetBlockVector());10113return S_OK;10114}1011510116void Pool::ReleaseThis()10117{10118if (this == NULL)10119{10120return;10121}1012210123D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), this);10124}1012510126Pool::Pool(Allocator* allocator, const POOL_DESC& desc)10127: m_Pimpl(D3D12MA_NEW(allocator->m_Pimpl->GetAllocs(), PoolPimpl)(allocator->m_Pimpl, desc)) {}1012810129Pool::~Pool()10130{10131m_Pimpl->GetAllocator()->UnregisterPool(this, m_Pimpl->GetDesc().HeapProperties.Type);1013210133D3D12MA_DELETE(m_Pimpl->GetAllocator()->GetAllocs(), m_Pimpl);10134}10135#endif // _D3D12MA_POOL_FUNCTIONS1013610137#ifndef _D3D12MA_ALLOCATOR_FUNCTIONS10138const D3D12_FEATURE_DATA_D3D12_OPTIONS& Allocator::GetD3D12Options() const10139{10140return m_Pimpl->GetD3D12Options();10141}1014210143BOOL Allocator::IsUMA() const10144{10145return m_Pimpl->IsUMA();10146}1014710148BOOL Allocator::IsCacheCoherentUMA() const10149{10150return m_Pimpl->IsCacheCoherentUMA();10151}1015210153UINT64 Allocator::GetMemoryCapacity(UINT memorySegmentGroup) const10154{10155return m_Pimpl->GetMemoryCapacity(memorySegmentGroup);10156}1015710158HRESULT Allocator::CreateResource(10159const ALLOCATION_DESC* pAllocDesc,10160const D3D12_RESOURCE_DESC* pResourceDesc,10161D3D12_RESOURCE_STATES InitialResourceState,10162const D3D12_CLEAR_VALUE* pOptimizedClearValue,10163Allocation** ppAllocation,10164REFIID riidResource,10165void** ppvResource)10166{10167if (!pAllocDesc || !pResourceDesc || !ppAllocation)10168{10169D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource.");10170return E_INVALIDARG;10171}10172D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10173return m_Pimpl->CreateResource(10174pAllocDesc,10175CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),10176ppAllocation,10177riidResource,10178ppvResource);10179}1018010181#ifdef __ID3D12Device8_INTERFACE_DEFINED__10182HRESULT Allocator::CreateResource2(10183const ALLOCATION_DESC* pAllocDesc,10184const D3D12_RESOURCE_DESC1* pResourceDesc,10185D3D12_RESOURCE_STATES InitialResourceState,10186const D3D12_CLEAR_VALUE* pOptimizedClearValue,10187Allocation** ppAllocation,10188REFIID riidResource,10189void** ppvResource)10190{10191if (!pAllocDesc || !pResourceDesc || !ppAllocation)10192{10193D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource2.");10194return E_INVALIDARG;10195}10196D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10197return m_Pimpl->CreateResource(10198pAllocDesc,10199CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),10200ppAllocation,10201riidResource,10202ppvResource);10203}10204#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__1020510206#ifdef __ID3D12Device10_INTERFACE_DEFINED__10207HRESULT Allocator::CreateResource3(10208const ALLOCATION_DESC* pAllocDesc,10209const D3D12_RESOURCE_DESC1* pResourceDesc,10210D3D12_BARRIER_LAYOUT InitialLayout,10211const D3D12_CLEAR_VALUE* pOptimizedClearValue,10212UINT32 NumCastableFormats,10213DXGI_FORMAT* pCastableFormats,10214Allocation** ppAllocation,10215REFIID riidResource,10216void** ppvResource)10217{10218if (!pAllocDesc || !pResourceDesc || !ppAllocation)10219{10220D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource3.");10221return E_INVALIDARG;10222}10223D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10224return m_Pimpl->CreateResource(10225pAllocDesc,10226CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats),10227ppAllocation,10228riidResource,10229ppvResource);10230}10231#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__1023210233HRESULT Allocator::AllocateMemory(10234const ALLOCATION_DESC* pAllocDesc,10235const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo,10236Allocation** ppAllocation)10237{10238if (!ValidateAllocateMemoryParameters(pAllocDesc, pAllocInfo, ppAllocation))10239{10240D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::AllocateMemory.");10241return E_INVALIDARG;10242}10243D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10244return m_Pimpl->AllocateMemory(pAllocDesc, pAllocInfo, ppAllocation);10245}1024610247HRESULT Allocator::CreateAliasingResource(10248Allocation* pAllocation,10249UINT64 AllocationLocalOffset,10250const D3D12_RESOURCE_DESC* pResourceDesc,10251D3D12_RESOURCE_STATES InitialResourceState,10252const D3D12_CLEAR_VALUE* pOptimizedClearValue,10253REFIID riidResource,10254void** ppvResource)10255{10256if (!pAllocation || !pResourceDesc || !ppvResource)10257{10258D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");10259return E_INVALIDARG;10260}10261D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10262return m_Pimpl->CreateAliasingResource(10263pAllocation,10264AllocationLocalOffset,10265CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),10266riidResource,10267ppvResource);10268}1026910270#ifdef __ID3D12Device8_INTERFACE_DEFINED__10271HRESULT Allocator::CreateAliasingResource1(10272Allocation* pAllocation,10273UINT64 AllocationLocalOffset,10274const D3D12_RESOURCE_DESC1* pResourceDesc,10275D3D12_RESOURCE_STATES InitialResourceState,10276const D3D12_CLEAR_VALUE* pOptimizedClearValue,10277REFIID riidResource,10278void** ppvResource)10279{10280if (!pAllocation || !pResourceDesc || !ppvResource)10281{10282D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");10283return E_INVALIDARG;10284}10285D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10286return m_Pimpl->CreateAliasingResource(10287pAllocation,10288AllocationLocalOffset,10289CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue),10290riidResource,10291ppvResource);10292}10293#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__1029410295#ifdef __ID3D12Device10_INTERFACE_DEFINED__10296HRESULT Allocator::CreateAliasingResource2(10297Allocation* pAllocation,10298UINT64 AllocationLocalOffset,10299const D3D12_RESOURCE_DESC1* pResourceDesc,10300D3D12_BARRIER_LAYOUT InitialLayout,10301const D3D12_CLEAR_VALUE* pOptimizedClearValue,10302UINT32 NumCastableFormats,10303DXGI_FORMAT* pCastableFormats,10304REFIID riidResource,10305void** ppvResource)10306{10307if (!pAllocation || !pResourceDesc || !ppvResource)10308{10309D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource.");10310return E_INVALIDARG;10311}10312D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10313return m_Pimpl->CreateAliasingResource(10314pAllocation,10315AllocationLocalOffset,10316CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats),10317riidResource,10318ppvResource);10319}10320#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__1032110322HRESULT Allocator::CreatePool(10323const POOL_DESC* pPoolDesc,10324Pool** ppPool)10325{10326if (!pPoolDesc || !ppPool ||10327(pPoolDesc->MaxBlockCount > 0 && pPoolDesc->MaxBlockCount < pPoolDesc->MinBlockCount) ||10328(pPoolDesc->MinAllocationAlignment > 0 && !IsPow2(pPoolDesc->MinAllocationAlignment)))10329{10330D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreatePool.");10331return E_INVALIDARG;10332}10333if (!m_Pimpl->HeapFlagsFulfillResourceHeapTier(pPoolDesc->HeapFlags))10334{10335D3D12MA_ASSERT(0 && "Invalid pPoolDesc->HeapFlags passed to Allocator::CreatePool. Did you forget to handle ResourceHeapTier=1?");10336return E_INVALIDARG;10337}10338D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10339* ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc);10340HRESULT hr = (*ppPool)->m_Pimpl->Init();10341if (SUCCEEDED(hr))10342{10343m_Pimpl->RegisterPool(*ppPool, pPoolDesc->HeapProperties.Type);10344}10345else10346{10347D3D12MA_DELETE(m_Pimpl->GetAllocs(), *ppPool);10348*ppPool = NULL;10349}10350return hr;10351}1035210353void Allocator::SetCurrentFrameIndex(UINT frameIndex)10354{10355D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10356m_Pimpl->SetCurrentFrameIndex(frameIndex);10357}1035810359void Allocator::GetBudget(Budget* pLocalBudget, Budget* pNonLocalBudget)10360{10361if (pLocalBudget == NULL && pNonLocalBudget == NULL)10362{10363return;10364}10365D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10366m_Pimpl->GetBudget(pLocalBudget, pNonLocalBudget);10367}1036810369void Allocator::CalculateStatistics(TotalStatistics* pStats)10370{10371D3D12MA_ASSERT(pStats);10372D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10373m_Pimpl->CalculateStatistics(*pStats);10374}1037510376void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const10377{10378D3D12MA_ASSERT(ppStatsString);10379D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10380m_Pimpl->BuildStatsString(ppStatsString, DetailedMap);10381}1038210383void Allocator::FreeStatsString(WCHAR* pStatsString) const10384{10385if (pStatsString != NULL)10386{10387D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10388m_Pimpl->FreeStatsString(pStatsString);10389}10390}1039110392void Allocator::BeginDefragmentation(const DEFRAGMENTATION_DESC* pDesc, DefragmentationContext** ppContext)10393{10394D3D12MA_ASSERT(pDesc && ppContext);1039510396*ppContext = D3D12MA_NEW(m_Pimpl->GetAllocs(), DefragmentationContext)(m_Pimpl, *pDesc, NULL);10397}1039810399void Allocator::ReleaseThis()10400{10401// Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.10402const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->GetAllocs();10403D3D12MA_DELETE(allocationCallbacksCopy, this);10404}1040510406Allocator::Allocator(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc)10407: m_Pimpl(D3D12MA_NEW(allocationCallbacks, AllocatorPimpl)(allocationCallbacks, desc)) {}1040810409Allocator::~Allocator()10410{10411D3D12MA_DELETE(m_Pimpl->GetAllocs(), m_Pimpl);10412}10413#endif // _D3D12MA_ALLOCATOR_FUNCTIONS1041410415#ifndef _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS10416BOOL VirtualBlock::IsEmpty() const10417{10418D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10419return m_Pimpl->m_Metadata->IsEmpty() ? TRUE : FALSE;10420}1042110422void VirtualBlock::GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOCATION_INFO* pInfo) const10423{10424D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0 && pInfo);1042510426D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10427m_Pimpl->m_Metadata->GetAllocationInfo(allocation.AllocHandle, *pInfo);10428}1042910430HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, VirtualAllocation* pAllocation, UINT64* pOffset)10431{10432if (!pDesc || !pAllocation || pDesc->Size == 0 || !IsPow2(pDesc->Alignment))10433{10434D3D12MA_ASSERT(0 && "Invalid arguments passed to VirtualBlock::Allocate.");10435return E_INVALIDARG;10436}1043710438D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK1043910440const UINT64 alignment = pDesc->Alignment != 0 ? pDesc->Alignment : 1;10441AllocationRequest allocRequest = {};10442if (m_Pimpl->m_Metadata->CreateAllocationRequest(10443pDesc->Size,10444alignment,10445pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_UPPER_ADDRESS,10446pDesc->Flags & VIRTUAL_ALLOCATION_FLAG_STRATEGY_MASK,10447&allocRequest))10448{10449m_Pimpl->m_Metadata->Alloc(allocRequest, pDesc->Size, pDesc->pPrivateData);10450D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10451pAllocation->AllocHandle = allocRequest.allocHandle;1045210453if (pOffset)10454*pOffset = m_Pimpl->m_Metadata->GetAllocationOffset(allocRequest.allocHandle);10455return S_OK;10456}1045710458pAllocation->AllocHandle = (AllocHandle)0;10459if (pOffset)10460*pOffset = UINT64_MAX;1046110462return E_OUTOFMEMORY;10463}1046410465void VirtualBlock::FreeAllocation(VirtualAllocation allocation)10466{10467if (allocation.AllocHandle == (AllocHandle)0)10468return;1046910470D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK1047110472m_Pimpl->m_Metadata->Free(allocation.AllocHandle);10473D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10474}1047510476void VirtualBlock::Clear()10477{10478D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK1047910480m_Pimpl->m_Metadata->Clear();10481D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10482}1048310484void VirtualBlock::SetAllocationPrivateData(VirtualAllocation allocation, void* pPrivateData)10485{10486D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0);1048710488D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10489m_Pimpl->m_Metadata->SetAllocationPrivateData(allocation.AllocHandle, pPrivateData);10490}1049110492void VirtualBlock::GetStatistics(Statistics* pStats) const10493{10494D3D12MA_ASSERT(pStats);10495D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10496D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10497ClearStatistics(*pStats);10498m_Pimpl->m_Metadata->AddStatistics(*pStats);10499}1050010501void VirtualBlock::CalculateStatistics(DetailedStatistics* pStats) const10502{10503D3D12MA_ASSERT(pStats);10504D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10505D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10506ClearDetailedStatistics(*pStats);10507m_Pimpl->m_Metadata->AddDetailedStatistics(*pStats);10508}1050910510void VirtualBlock::BuildStatsString(WCHAR** ppStatsString) const10511{10512D3D12MA_ASSERT(ppStatsString);1051310514D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK1051510516StringBuilder sb(m_Pimpl->m_AllocationCallbacks);10517{10518JsonWriter json(m_Pimpl->m_AllocationCallbacks, sb);10519D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate());10520json.BeginObject();10521m_Pimpl->m_Metadata->WriteAllocationInfoToJson(json);10522json.EndObject();10523} // Scope for JsonWriter1052410525const size_t length = sb.GetLength();10526WCHAR* result = AllocateArray<WCHAR>(m_Pimpl->m_AllocationCallbacks, length + 1);10527memcpy(result, sb.GetData(), length * sizeof(WCHAR));10528result[length] = L'\0';10529*ppStatsString = result;10530}1053110532void VirtualBlock::FreeStatsString(WCHAR* pStatsString) const10533{10534if (pStatsString != NULL)10535{10536D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK10537D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString);10538}10539}1054010541void VirtualBlock::ReleaseThis()10542{10543// Copy is needed because otherwise we would call destructor and invalidate the structure with callbacks before using it to free memory.10544const ALLOCATION_CALLBACKS allocationCallbacksCopy = m_Pimpl->m_AllocationCallbacks;10545D3D12MA_DELETE(allocationCallbacksCopy, this);10546}1054710548VirtualBlock::VirtualBlock(const ALLOCATION_CALLBACKS& allocationCallbacks, const VIRTUAL_BLOCK_DESC& desc)10549: m_Pimpl(D3D12MA_NEW(allocationCallbacks, VirtualBlockPimpl)(allocationCallbacks, desc)) {}1055010551VirtualBlock::~VirtualBlock()10552{10553// THIS IS AN IMPORTANT ASSERT!10554// Hitting it means you have some memory leak - unreleased allocations in this virtual block.10555D3D12MA_ASSERT(m_Pimpl->m_Metadata->IsEmpty() && "Some allocations were not freed before destruction of this virtual block!");1055610557D3D12MA_DELETE(m_Pimpl->m_AllocationCallbacks, m_Pimpl);10558}10559#endif // _D3D12MA_VIRTUAL_BLOCK_FUNCTIONS10560#endif // _D3D12MA_PUBLIC_INTERFACE10561} // namespace D3D12MA105621056310564