From bf75bbe8486a749ec161f653384e112fbb7bd701 Mon Sep 17 00:00:00 2001 From: JordanTheToaster Date: Wed, 6 Dec 2023 10:34:36 +0000 Subject: [PATCH] 3rdparty: Update D3D12 memory allocator Updates D3D12 memory allocator to latest master commit. --- 3rdparty/d3d12memalloc/CHANGELOG.md | 22 +- 3rdparty/d3d12memalloc/README.md | 4 +- .../d3d12memalloc/include/D3D12MemAlloc.h | 231 +- 3rdparty/d3d12memalloc/src/.editorconfig | 5 + 3rdparty/d3d12memalloc/src/CMakeLists.txt | 159 + 3rdparty/d3d12memalloc/src/Common.cpp | 176 + 3rdparty/d3d12memalloc/src/Common.h | 392 ++ 3rdparty/d3d12memalloc/src/D3D12MemAlloc.cpp | 1638 +++++-- 3rdparty/d3d12memalloc/src/D3D12Sample.cpp | 1825 +++++++ 3rdparty/d3d12memalloc/src/Doxyfile | 2778 +++++++++++ .../src/Shaders/FxBatchCompilerScript.fxbc | 19 + 3rdparty/d3d12memalloc/src/Shaders/PS.hlsl | 40 + .../d3d12memalloc/src/Shaders/PS_Compiled.h | 203 + 3rdparty/d3d12memalloc/src/Shaders/VS.hlsl | 46 + .../d3d12memalloc/src/Shaders/VS_Compiled.h | 220 + 3rdparty/d3d12memalloc/src/Tests.cpp | 4217 +++++++++++++++++ 3rdparty/d3d12memalloc/src/Tests.h | 35 + 17 files changed, 11468 insertions(+), 542 deletions(-) create mode 100644 3rdparty/d3d12memalloc/src/.editorconfig create mode 100644 3rdparty/d3d12memalloc/src/CMakeLists.txt create mode 100644 3rdparty/d3d12memalloc/src/Common.cpp create mode 100644 3rdparty/d3d12memalloc/src/Common.h create mode 100644 3rdparty/d3d12memalloc/src/D3D12Sample.cpp create mode 100644 3rdparty/d3d12memalloc/src/Doxyfile create mode 100644 3rdparty/d3d12memalloc/src/Shaders/FxBatchCompilerScript.fxbc create mode 100644 3rdparty/d3d12memalloc/src/Shaders/PS.hlsl create mode 100644 3rdparty/d3d12memalloc/src/Shaders/PS_Compiled.h create mode 100644 3rdparty/d3d12memalloc/src/Shaders/VS.hlsl create mode 100644 3rdparty/d3d12memalloc/src/Shaders/VS_Compiled.h create mode 100644 3rdparty/d3d12memalloc/src/Tests.cpp create mode 100644 3rdparty/d3d12memalloc/src/Tests.h diff --git a/3rdparty/d3d12memalloc/CHANGELOG.md b/3rdparty/d3d12memalloc/CHANGELOG.md index 9e69431e1f836b..bc027d3bf825c0 100644 --- a/3rdparty/d3d12memalloc/CHANGELOG.md +++ b/3rdparty/d3d12memalloc/CHANGELOG.md @@ -1,3 +1,23 @@ -# 1.0.0 (2019-09-02) +# 2.0.1 (2022-04-05) + +A maintenance release with some bug fixes and improvements. There are no changes in the library API. + +- Fixed an assert failing when detailed JSON dump was made while a custom pool was present with specified string name (#36, thanks @rbertin-aso). +- Fixed image height calculation in JSON dump visualization tool "GpuMemDumpVis.py" (#37, thanks @rbertin-aso). +- Added JSON Schema for JSON dump format - see file "tools\GpuMemDumpVis\GpuMemDump.schema.json". +- Added documentation section "Resource reference counting". + +# 2.0.0 (2022-03-25) + +So much has changed since the first release that it doesn’t make much sense to compare the differences. Here are the most important features that the library now provides: + +- Powerful custom pools, which give an opportunity to not only keep certain resources together, reserve some minimum or limit the maximum amount of memory they can take, but also to pass additional allocation parameters unavailable to simple allocations. Among them, probably the most interesting is `POOL_DESC::HeapProperties`, which allows you to specify parameters of a custom memory type, which may be useful on UMA platforms. Committed allocations can now also be created in custom pools. +- The API for statistics and budget has been redesigned - see structures `Statistics`, `Budget`, `DetailedStatistics`, `TotalStatistics`. +- The library exposes its core allocation algorithm via the “virtual allocator” interface. This can be used to allocate pieces of custom memory or whatever you like, even something completely unrelated to graphics. +- The allocation algorithm has been replaced with the new, more efficient TLSF. +- Added support for defragmentation. +- Objects of the library can be used with smart pointers designed for COM objects. + +# 1.0.0 (2019-09-02) First published version. diff --git a/3rdparty/d3d12memalloc/README.md b/3rdparty/d3d12memalloc/README.md index 65a9ff59ca63c4..81c6be18aa4ec0 100644 --- a/3rdparty/d3d12memalloc/README.md +++ b/3rdparty/d3d12memalloc/README.md @@ -41,7 +41,7 @@ Additional features: - Statistics: Obtain brief or detailed statistics about the amount of memory used, unused, number of allocated heaps, number of allocations etc. - globally and per memory heap type. Current memory usage and budget as reported by the system can also be queried. - Debug annotations: Associate custom `void* pPrivateData` and debug `LPCWSTR pName` with each allocation. - JSON dump: Obtain a string in JSON format with detailed map of internal state, including list of allocations, their string names, and gaps between them. -- Convert this JSON dump into a picture to visualize your memory using attached Python script. +- Convert this JSON dump into a picture to visualize your memory. See [tools/GpuMemDumpVis](tools/GpuMemDumpVis/README.md). - Virtual allocator - an API that exposes the core allocation algorithm to be used without allocating real GPU memory, to allocate your own stuff, e.g. sub-allocate pieces of one large buffer. # Prerequisites @@ -104,10 +104,12 @@ For more information see [NOTICES.txt](NOTICES.txt). # Software using this library - **[The Forge](https://github.com/ConfettiFX/The-Forge)** - cross-platform rendering framework. Apache License 2.0. +- **[Wicked Engine](https://github.com/turanszkij/WickedEngine)** - 3D engine with modern graphics [Some other projects on GitHub](https://github.com/search?q=D3D12MemAlloc.h&type=Code) and some game development studios that use DX12 in their games. # See also +- **[Vcpkg](https://github.com/Microsoft/vcpkg)** dependency manager from Microsoft offers a port of this library that is easy to install. - **[Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/)** - equivalent library for Vulkan. License: MIT. - **[TerraFX.Interop.D3D12MemoryAllocator](https://github.com/terrafx/terrafx.interop.d3d12memoryallocator)** - interop bindings for this library for C#, as used by [TerraFX](https://github.com/terrafx/terrafx). License: MIT. diff --git a/3rdparty/d3d12memalloc/include/D3D12MemAlloc.h b/3rdparty/d3d12memalloc/include/D3D12MemAlloc.h index 4efbb50115c48e..4e87bf0277474b 100644 --- a/3rdparty/d3d12memalloc/include/D3D12MemAlloc.h +++ b/3rdparty/d3d12memalloc/include/D3D12MemAlloc.h @@ -24,9 +24,9 @@ /** \mainpage D3D12 Memory Allocator -Version 2.0.0-development (2021-07-26) +Version 2.1.0-development (2023-07-05) -Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. \n +Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved. \n License: MIT Documentation of all members: D3D12MemAlloc.h @@ -36,6 +36,7 @@ Documentation of all members: D3D12MemAlloc.h - \subpage quick_start - [Project setup](@ref quick_start_project_setup) - [Creating resources](@ref quick_start_creating_resources) + - [Resource reference counting](@ref quick_start_resource_reference_counting) - [Mapping memory](@ref quick_start_mapping_memory) - \subpage custom_pools - \subpage defragmentation @@ -50,7 +51,7 @@ Documentation of all members: D3D12MemAlloc.h - [Thread safety](@ref general_considerations_thread_safety) - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility) - [Features not supported](@ref general_considerations_features_not_supported) - + \section main_see_also See also - [Product page on GPUOpen](https://gpuopen.com/gaming-product/d3d12-memory-allocator/) @@ -58,9 +59,18 @@ Documentation of all members: D3D12MemAlloc.h */ // If using this library on a platform different than Windows PC or want to use different version of DXGI, -// you should include D3D12-compatible headers before this library on your own and define this macro. +// you should include D3D12-compatible headers before this library on your own and define +// D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED. +// Alternatively, if you are targeting the open sourced DirectX headers, defining D3D12MA_USING_DIRECTX_HEADERS +// will include them rather the ones provided by the Windows SDK. #ifndef D3D12MA_D3D12_HEADERS_ALREADY_INCLUDED - #include + #if defined(D3D12MA_USING_DIRECTX_HEADERS) + #include + #include + #else + #include + #endif + #include #endif @@ -132,6 +142,18 @@ If providing your own implementation, you need to implement a subset of std::ato // Forward declaration if ID3D12ProtectedResourceSession is not defined inside the headers (older SDK, pre ID3D12Device4) struct ID3D12ProtectedResourceSession; +// Define this enum even if SDK doesn't provide it, to simplify the API. +#ifndef __ID3D12Device1_INTERFACE_DEFINED__ +typedef enum D3D12_RESIDENCY_PRIORITY +{ + D3D12_RESIDENCY_PRIORITY_MINIMUM = 0x28000000, + D3D12_RESIDENCY_PRIORITY_LOW = 0x50000000, + D3D12_RESIDENCY_PRIORITY_NORMAL = 0x78000000, + D3D12_RESIDENCY_PRIORITY_HIGH = 0xa0010000, + D3D12_RESIDENCY_PRIORITY_MAXIMUM = 0xc8000000 +} D3D12_RESIDENCY_PRIORITY; +#endif + namespace D3D12MA { class D3D12MA_API IUnknownImpl : public IUnknown @@ -144,7 +166,7 @@ class D3D12MA_API IUnknownImpl : public IUnknown protected: virtual void ReleaseThis() { delete this; } private: - D3D12MA_ATOMIC_UINT32 m_RefCount{1}; + D3D12MA_ATOMIC_UINT32 m_RefCount = {1}; }; } // namespace D3D12MA @@ -226,8 +248,6 @@ enum ALLOCATION_FLAGS /** Create allocation only if additional memory required for it, if any, won't exceed memory budget. Otherwise return `E_OUTOFMEMORY`. - - \warning Currently this feature is not fully implemented yet. */ ALLOCATION_FLAG_WITHIN_BUDGET = 0x4, @@ -237,7 +257,6 @@ enum ALLOCATION_FLAGS */ ALLOCATION_FLAG_UPPER_ADDRESS = 0x8, - /** Set this flag if the allocated memory will have aliasing resources. Use this when calling D3D12MA::Allocator::CreateResource() and similar to @@ -306,7 +325,6 @@ struct ALLOCATION_DESC /** \brief Custom pool to place the new resource in. Optional. When not NULL, the resource will be created inside specified custom pool. - It will then never be created as committed. */ Pool* CustomPool; /// Custom general-purpose pointer that will be stored in D3D12MA::Allocation. @@ -570,7 +588,6 @@ class D3D12MA_API Allocation : public IUnknownImpl UINT64 m_Size; UINT64 m_Alignment; ID3D12Resource* m_Resource; - UINT m_CreationFrameIndex; void* m_pPrivateData; wchar_t* m_Name; @@ -637,7 +654,7 @@ class D3D12MA_API Allocation : public IUnknownImpl AllocHandle GetAllocHandle() const; NormalBlock* GetBlock(); template - void SetResource(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc); + void SetResourcePointer(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc); void FreeName(); D3D12MA_CLASS_NO_COPY(Allocation) @@ -838,6 +855,14 @@ enum POOL_FLAGS */ POOL_FLAG_ALGORITHM_LINEAR = 0x1, + /** \brief Optimization, allocate MSAA textures as committed resources always. + + Specify this flag to create MSAA textures with implicit heaps, as if they were created + with flag D3D12MA::ALLOCATION_FLAG_COMMITTED. Usage of this flags enables pool to create its heaps + on smaller alignment not suitable for MSAA textures. + */ + POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED = 0x2, + // Bit mask to extract only `ALGORITHM` bits from entire set of flags. POOL_FLAG_ALGORITHM_MASK = POOL_FLAG_ALGORITHM_LINEAR }; @@ -895,6 +920,29 @@ struct POOL_DESC Valid only if ID3D12Device4 interface is present in current Windows SDK! */ ID3D12ProtectedResourceSession* pProtectedSession; + /** \brief Residency priority to be set for all allocations made in this pool. Optional. + + Set this parameter to one of the possible enum values e.g. `D3D12_RESIDENCY_PRIORITY_HIGH` + to apply specific residency priority to all allocations made in this pool: + `ID3D12Heap` memory blocks used to sub-allocate for placed resources, as well as + committed resources or heaps created when D3D12MA::ALLOCATION_FLAG_COMMITTED is used. + This can increase/decrease chance that the memory will be pushed out from VRAM + to system RAM when the system runs out of memory, which is invisible to the developer + using D3D12 API while it can degrade performance. + + Priority is set using function `ID3D12Device1::SetResidencyPriority`. + It is performed only when `ID3D12Device1` interface is defined and successfully obtained. + Otherwise, this parameter is ignored. + + This parameter is optional. If you set it to `D3D12_RESIDENCY_PRIORITY(0)`, + residency priority will not be set for allocations made in this pool. + + There is no equivalent parameter for allocations made in default pools. + If you want to set residency priority for such allocation, you need to do it manually: + allocate with D3D12MA::ALLOCATION_FLAG_COMMITTED and call + `ID3D12Device1::SetResidencyPriority`, passing `allocation->GetResource()`. + */ + D3D12_RESIDENCY_PRIORITY ResidencyPriority; }; /** \brief Custom memory pool @@ -1009,6 +1057,14 @@ enum ALLOCATOR_FLAGS Only avaiable if `ID3D12Device8` is present. Otherwise, the flag is ignored. */ ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED = 0x4, + + /** \brief Optimization, allocate MSAA textures as committed resources always. + + Specify this flag to create MSAA textures with implicit heaps, as if they were created + with flag D3D12MA::ALLOCATION_FLAG_COMMITTED. Usage of this flags enables all default pools + to create its heaps on smaller alignment not suitable for MSAA textures. + */ + ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED = 0x8, }; /// \brief Parameters of created Allocator object. To be used with CreateAllocator(). @@ -1144,7 +1200,26 @@ class D3D12MA_API Allocator : public IUnknownImpl Allocation** ppAllocation, REFIID riidResource, void** ppvResource); -#endif // #ifdef __ID3D12Device4_INTERFACE_DEFINED__ +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + /** \brief Similar to Allocator::CreateResource2, but there are initial layout instead of state and + castable formats list + + It internally uses `ID3D12Device10::CreateCommittedResource3` or `ID3D12Device10::CreatePlacedResource2`. + + To work correctly, `ID3D12Device10` interface must be available in the current system. Otherwise, `E_NOINTERFACE` is returned. + */ + HRESULT CreateResource3(const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_BARRIER_LAYOUT InitialLayout, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + UINT32 NumCastableFormats, + DXGI_FORMAT* pCastableFormats, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource); +#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__ /** \brief Allocates memory without creating any resource placed in it. @@ -1201,6 +1276,41 @@ class D3D12MA_API Allocator : public IUnknownImpl REFIID riidResource, void** ppvResource); +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + /** \brief Similar to Allocator::CreateAliasingResource, but supports new structure `D3D12_RESOURCE_DESC1`. + + It internally uses `ID3D12Device8::CreatePlacedResource1`. + + To work correctly, `ID3D12Device8` interface must be available in the current system. Otherwise, `E_NOINTERFACE` is returned. + */ + HRESULT CreateAliasingResource1(Allocation* pAllocation, + UINT64 AllocationLocalOffset, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + REFIID riidResource, + void** ppvResource); +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + /** \brief Similar to Allocator::CreateAliasingResource1, but there are initial layout instead of state and + castable formats list + + It internally uses `ID3D12Device10::CreatePlacedResource2`. + + To work correctly, `ID3D12Device10` interface must be available in the current system. Otherwise, `E_NOINTERFACE` is returned. + */ + HRESULT CreateAliasingResource2(Allocation* pAllocation, + UINT64 AllocationLocalOffset, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_BARRIER_LAYOUT InitialLayout, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + UINT32 NumCastableFormats, + DXGI_FORMAT* pCastableFormats, + REFIID riidResource, + void** ppvResource); +#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__ + /** \brief Creates custom pool. */ HRESULT CreatePool( @@ -1223,7 +1333,7 @@ class D3D12MA_API Allocator : public IUnknownImpl - `pNonLocalBudget` returns the budget of the system memory available for D3D12 resources. - When IsUMA() `== TRUE` (integrated graphics chip): - `pLocalBudget` returns the budget of the shared memory available for all D3D12 resources. - All memory is considered "local". + All memory is considered "local". - `pNonLocalBudget` is not applicable and returns zeros. This function is called "get" not "calculate" because it is very fast, suitable to be called @@ -1246,8 +1356,9 @@ class D3D12MA_API Allocator : public IUnknownImpl */ void CalculateStatistics(TotalStatistics* pStats); - /// Builds and returns statistics as a string in JSON format. - /** @param[out] ppStatsString Must be freed using Allocator::FreeStatsString. + /** \brief Builds and returns statistics as a string in JSON format. + * + @param[out] ppStatsString Must be freed using Allocator::FreeStatsString. @param DetailedMap `TRUE` to include full list of allocations (can make the string quite long), `FALSE` to only return statistics. */ void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const; @@ -1559,9 +1670,9 @@ to be passed along with `D3D12_RESOURCE_DESC` and other parameters for created resource. This structure describes parameters of the desired memory allocation, including choice of `D3D12_HEAP_TYPE`. -The function also returns a new object of type D3D12MA::Allocation, created along -with usual `ID3D12Resource`. It represents allocated memory and can be queried -for size, offset, `ID3D12Resource`, and `ID3D12Heap` if needed. +The function returns a new object of type D3D12MA::Allocation. +It represents allocated memory and can be queried for size, offset, `ID3D12Heap`. +It also holds a reference to the `ID3D12Resource`, which can be accessed by calling D3D12MA::Allocation::GetResource(). \code D3D12_RESOURCE_DESC resourceDesc = {}; @@ -1580,7 +1691,6 @@ resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; D3D12MA::ALLOCATION_DESC allocationDesc = {}; allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; -D3D12Resource* resource; D3D12MA::Allocation* allocation; HRESULT hr = allocator->CreateResource( &allocationDesc, @@ -1588,15 +1698,16 @@ HRESULT hr = allocator->CreateResource( D3D12_RESOURCE_STATE_COPY_DEST, NULL, &allocation, - IID_PPV_ARGS(&resource)); + IID_NULL, NULL); + +// Use allocation->GetResource()... \endcode -You need to remember both resource and allocation objects and destroy them -separately when no longer needed. +You need to release the allocation object when no longer needed. +This will also release the D3D12 resource. \code allocation->Release(); -resource->Release(); \endcode The advantage of using the allocator instead of creating committed resource, and @@ -1619,6 +1730,65 @@ they can be kept together. By using this library, you don't need to handle this manually. +\section quick_start_resource_reference_counting Resource reference counting + +`ID3D12Resource` and other interfaces of Direct3D 12 use COM, so they are reference-counted. +Objects of this library are reference-counted as well. +An object of type D3D12MA::Allocation remembers the resource (buffer or texture) +that was created together with this memory allocation +and holds a reference to the `ID3D12Resource` object. +(Note this is a difference to Vulkan Memory Allocator, where a `VmaAllocation` object has no connection +with the buffer or image that was created with it.) +Thus, it is important to manage the resource reference counter properly. + +The simplest use case is shown in the code snippet above. +When only D3D12MA::Allocation object is obtained from a function call like D3D12MA::Allocator::CreateResource, +it remembers the `ID3D12Resource` that was created with it and holds a reference to it. +The resource can be obtained by calling `allocation->GetResource()`, which doesn't increment the resource +reference counter. +Calling `allocation->Release()` will decrease the resource reference counter, which is = 1 in this case, +so the resource will be released. + +Second option is to retrieve a pointer to the resource along with D3D12MA::Allocation. +Last parameters of the resource creation function can be used for this purpose. + +\code +D3D12MA::Allocation* allocation; +ID3D12Resource* resource; +HRESULT hr = allocator->CreateResource( + &allocationDesc, + &resourceDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + NULL, + &allocation, + IID_PPV_ARGS(&resource)); + +// Use resource... +\endcode + +In this case, returned pointer `resource` is equal to `allocation->GetResource()`, +but the creation function additionally increases resource reference counter for the purpose of returning it from this call +(it actually calls `QueryInterface` internally), so the resource will have the counter = 2. +The resource then need to be released along with the allocation, in this particular order, +to make sure the resource is destroyed before its memory heap can potentially be freed. + +\code +resource->Release(); +allocation->Release(); +\endcode + +More advanced use cases are possible when we consider that an D3D12MA::Allocation object can just hold +a reference to any resource. +It can be changed by calling D3D12MA::Allocation::SetResource. This function +releases the old resource and calls `AddRef` on the new one. + +Special care must be taken when performing defragmentation. +The new resource created at the destination place should be set as `pass.pMoves[i].pDstTmpAllocation->SetResource(newRes)`, +but it is moved to the source allocation at end of the defragmentation pass, +while the old resource accessible through `pass.pMoves[i].pSrcAllocation->GetResource()` is then released. +For more information, see documentation chapter \ref defragmentation. + + \section quick_start_mapping_memory Mapping memory The process of getting regular CPU-side pointer to the memory of a resource in @@ -1892,9 +2062,20 @@ You can perform the defragmentation incrementally to limit the number of allocat in each pass, e.g. to call it in sync with render frames and not to experience too big hitches. See members: D3D12MA::DEFRAGMENTATION_DESC::MaxBytesPerPass, D3D12MA::DEFRAGMENTATION_DESC::MaxAllocationsPerPass. -It is also safe to perform the defragmentation asynchronously to render frames and other Direct3D 12 and %D3D12MA +Thread safety: +It is safe to perform the defragmentation asynchronously to render frames and other Direct3D 12 and %D3D12MA usage, possibly from multiple threads, with the exception that allocations returned in D3D12MA::DEFRAGMENTATION_PASS_MOVE_INFO::pMoves shouldn't be released until the defragmentation pass is ended. +During the call to D3D12MA::DefragmentationContext::BeginPass(), any operations on the memory pool +affected by the defragmentation are blocked by a mutex. + +What it means in practice is that you shouldn't free any allocations from the defragmented pool +since the moment a call to `BeginPass` begins. Otherwise, a thread performing the `allocation->Release()` +would block for the time `BeginPass` executes and then free the allocation when it finishes, while the allocation +could have ended up on the list of allocations to move. +A solution to freeing allocations during defragmentation is to find such allocation on the list +`pass.pMoves[i]` and set its operation to D3D12MA::DEFRAGMENTATION_MOVE_OPERATION_DESTROY instead of +calling `allocation->Release()`, or simply deferring the release to the time after defragmentation finished. Mapping is out of scope of this library and so it is not preserved after an allocation is moved during defragmentation. You need to map the new resource yourself if needed. diff --git a/3rdparty/d3d12memalloc/src/.editorconfig b/3rdparty/d3d12memalloc/src/.editorconfig new file mode 100644 index 00000000000000..1b9f7aaac143e7 --- /dev/null +++ b/3rdparty/d3d12memalloc/src/.editorconfig @@ -0,0 +1,5 @@ +root = true + +[**.{cpp,h}] +indent_style = space +indent_size = 4 diff --git a/3rdparty/d3d12memalloc/src/CMakeLists.txt b/3rdparty/d3d12memalloc/src/CMakeLists.txt new file mode 100644 index 00000000000000..7d8d42f2f0b0b0 --- /dev/null +++ b/3rdparty/d3d12memalloc/src/CMakeLists.txt @@ -0,0 +1,159 @@ +set(D3D12MA_LIBRARY_SOURCE_FILES + D3D12MemAlloc.cpp + "${PROJECT_SOURCE_DIR}/include/D3D12MemAlloc.h" +) + +if(WIN32 AND ${CMAKE_GENERATOR} MATCHES "Visual Studio.*") + set(D3D12MA_LIBRARY_SOURCE_FILES ${D3D12MA_LIBRARY_SOURCE_FILES} D3D12MemAlloc.natvis) +endif() + +add_library(D3D12MemoryAllocator ${D3D12MA_LIBRARY_SOURCE_FILES}) + +set_target_properties( + D3D12MemoryAllocator PROPERTIES + + CXX_EXTENSIONS OFF + # Use C++14 + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + + OUTPUT_NAME "D3D12MA" + # Postfix for different profiles + DEBUG_POSTFIX "d" + RELWITHDEBINFO_POSTFIX "rd" + MINSIZEREL_POSTFIX "s" +) + +target_include_directories(D3D12MemoryAllocator PUBLIC + "${PROJECT_SOURCE_DIR}/include" +) + +target_link_libraries(D3D12MemoryAllocator PUBLIC + d3d12.lib + dxgi.lib + dxguid.lib +) + +if(BUILD_SHARED_LIBS) + target_compile_definitions(D3D12MemoryAllocator PRIVATE + D3D12MA_EXPORTS + ) + + target_compile_definitions(D3D12MemoryAllocator INTERFACE + D3D12MA_IMPORTS + ) +endif() + +install(TARGETS D3D12MemoryAllocator + RUNTIME DESTINATION "bin" + ARCHIVE DESTINATION "lib" + LIBRARY DESTINATION "lib") +install(FILES "${PROJECT_SOURCE_DIR}/include/D3D12MemAlloc.h" DESTINATION "include") + +if(D3D12MA_BUILD_SAMPLE) + if(WIN32) + set(SHADER_DIR "Shaders") + + set(D3D12_SAMPLE_SOURCE_FILES + Common.cpp + Common.h + Tests.cpp + Tests.h + D3D12Sample.cpp + ) + + set(VERTEX_SHADERS + "${SHADER_DIR}/VS.hlsl" + ) + + set(PIXEL_SHADERS + "${SHADER_DIR}/PS.hlsl" + ) + + set( SHADERS + ${VERTEX_SHADERS} + ${PIXEL_SHADERS} + ) + + source_group("Resources\\Shaders" FILES ${SHADERS}) + + set_source_files_properties(${VERTEX_SHADERS} + PROPERTIES + VS_SHADER_TYPE Vertex + VS_SETTINGS "ExcludedFromBuild=true" + ) + + set_source_files_properties( ${PIXEL_SHADERS} + PROPERTIES + VS_SHADER_TYPE Pixel + VS_SETTINGS "ExcludedFromBuild=true" + ) + + add_executable(D3D12Sample ${D3D12_SAMPLE_SOURCE_FILES} ${SHADERS}) + + add_dependencies(D3D12Sample D3D12MemoryAllocator) + + # Visual Studio specific settings + if(${CMAKE_GENERATOR} MATCHES "Visual Studio.*") + # Use Unicode instead of multibyte set + add_compile_definitions(UNICODE _UNICODE) + + # Set VmaSample as startup project + set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT "D3D12Sample") + + # Enable multithreaded compiling + target_compile_options(D3D12Sample PRIVATE "/MP") + + # Set working directory for Visual Studio debugger + set_target_properties( + D3D12Sample + PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/bin" + ) + endif() + + set_target_properties( + D3D12Sample PROPERTIES + + CXX_EXTENSIONS OFF + # Use C++14 + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON + ) + + target_link_libraries( + D3D12Sample + + PRIVATE D3D12MemoryAllocator + PUBLIC d3d12.lib + PUBLIC dxgi.lib + PUBLIC dxguid.lib + PUBLIC Shlwapi.lib + ) + else() + message(STATUS "D3D12Sample application is not supported to Linux") + endif() +endif() + +set(D3D12MA_AGILITY_SDK_DIRECTORY "" CACHE STRING "Path to unpacked DX12 Agility SDK. Leave empty to compile without it.") +option(D3D12MA_AGILITY_SDK_PREVIEW "Set if DX12 Agility SDK is preview version." OFF) +if(D3D12MA_AGILITY_SDK_DIRECTORY) + if(EXISTS "${D3D12MA_AGILITY_SDK_DIRECTORY}/build/native/include/d3d12.h") + message(STATUS "DX12 Agility SDK used from \"${D3D12MA_AGILITY_SDK_DIRECTORY}\".") + target_compile_definitions(D3D12MemoryAllocator PRIVATE D3D12MA_USE_AGILITY_SDK=1) + target_include_directories(D3D12MemoryAllocator BEFORE PRIVATE "${D3D12MA_AGILITY_SDK_DIRECTORY}/build/native/include") + if(D3D12MA_AGILITY_SDK_PREVIEW) + target_compile_definitions(D3D12MemoryAllocator PRIVATE D3D12MA_USE_AGILITY_SDK_PREVIEW=1) + endif() + if(${D3D12MA_BUILD_SAMPLE} AND ${WIN32}) + target_compile_definitions(D3D12Sample PRIVATE D3D12MA_USE_AGILITY_SDK=1) + target_include_directories(D3D12Sample BEFORE PRIVATE "${D3D12MA_AGILITY_SDK_DIRECTORY}/build/native/include") + if(D3D12MA_AGILITY_SDK_PREVIEW) + target_compile_definitions(D3D12Sample PRIVATE D3D12MA_USE_AGILITY_SDK_PREVIEW=1) + endif() + endif() + else() + message(FATAL_ERROR "DX12 Agility SDK not found - cannot find file \"${D3D12MA_AGILITY_SDK_DIRECTORY}/build/native/include/d3d12.h\".") + endif() +else() + message(STATUS "DX12 Agility SDK not used.") +endif() diff --git a/3rdparty/d3d12memalloc/src/Common.cpp b/3rdparty/d3d12memalloc/src/Common.cpp new file mode 100644 index 00000000000000..2ec8c1bbb18a7e --- /dev/null +++ b/3rdparty/d3d12memalloc/src/Common.cpp @@ -0,0 +1,176 @@ +// +// Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include "Common.h" + +void ReadFile(std::vector& out, const wchar_t* fileName) +{ + std::ifstream file(fileName, std::ios::ate | std::ios::binary); + assert(file.is_open()); + size_t fileSize = (size_t)file.tellg(); + if(fileSize > 0) + { + out.resize(fileSize); + file.seekg(0); + file.read(out.data(), fileSize); + } + else + out.clear(); +} + +void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize) +{ + FILE* f = nullptr; + _wfopen_s(&f, filePath, L"wb"); + if(f) + { + fwrite(data, 1, dataSize, f); + fclose(f); + } + else + assert(0); +} + +void SetConsoleColor(CONSOLE_COLOR color) +{ + WORD attr = 0; + switch(color) + { + case CONSOLE_COLOR::INFO: + attr = FOREGROUND_INTENSITY;; + break; + case CONSOLE_COLOR::NORMAL: + attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + break; + case CONSOLE_COLOR::WARNING: + attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + break; + case CONSOLE_COLOR::ERROR_: + attr = FOREGROUND_RED | FOREGROUND_INTENSITY; + break; + default: + assert(0); + } + + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(out, attr); +} + +void PrintMessage(CONSOLE_COLOR color, const char* msg) +{ + if(color != CONSOLE_COLOR::NORMAL) + SetConsoleColor(color); + + printf("%s\n", msg); + + if (color != CONSOLE_COLOR::NORMAL) + SetConsoleColor(CONSOLE_COLOR::NORMAL); +} + +void PrintMessage(CONSOLE_COLOR color, const wchar_t* msg) +{ + if(color != CONSOLE_COLOR::NORMAL) + SetConsoleColor(color); + + wprintf(L"%s\n", msg); + + if (color != CONSOLE_COLOR::NORMAL) + SetConsoleColor(CONSOLE_COLOR::NORMAL); +} + +static const size_t CONSOLE_SMALL_BUF_SIZE = 256; + +void PrintMessageV(CONSOLE_COLOR color, const char* format, va_list argList) +{ + size_t dstLen = (size_t)::_vscprintf(format, argList); + if(dstLen) + { + bool useSmallBuf = dstLen < CONSOLE_SMALL_BUF_SIZE; + char smallBuf[CONSOLE_SMALL_BUF_SIZE]; + std::vector bigBuf(useSmallBuf ? 0 : dstLen + 1); + char* bufPtr = useSmallBuf ? smallBuf : bigBuf.data(); + ::vsprintf_s(bufPtr, dstLen + 1, format, argList); + PrintMessage(color, bufPtr); + } +} + +void PrintMessageV(CONSOLE_COLOR color, const wchar_t* format, va_list argList) +{ + size_t dstLen = (size_t)::_vcwprintf(format, argList); + if(dstLen) + { + bool useSmallBuf = dstLen < CONSOLE_SMALL_BUF_SIZE; + wchar_t smallBuf[CONSOLE_SMALL_BUF_SIZE]; + std::vector bigBuf(useSmallBuf ? 0 : dstLen + 1); + wchar_t* bufPtr = useSmallBuf ? smallBuf : bigBuf.data(); + ::vswprintf_s(bufPtr, dstLen + 1, format, argList); + PrintMessage(color, bufPtr); + } +} + +void PrintMessageF(CONSOLE_COLOR color, const char* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(color, format, argList); + va_end(argList); +} + +void PrintMessageF(CONSOLE_COLOR color, const wchar_t* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(color, format, argList); + va_end(argList); +} + +void PrintWarningF(const char* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); + va_end(argList); +} + +void PrintWarningF(const wchar_t* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); + va_end(argList); +} + +void PrintErrorF(const char* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); + va_end(argList); +} + +void PrintErrorF(const wchar_t* format, ...) +{ + va_list argList; + va_start(argList, format); + PrintMessageV(CONSOLE_COLOR::WARNING, format, argList); + va_end(argList); +} diff --git a/3rdparty/d3d12memalloc/src/Common.h b/3rdparty/d3d12memalloc/src/Common.h new file mode 100644 index 00000000000000..9658bab63df559 --- /dev/null +++ b/3rdparty/d3d12memalloc/src/Common.h @@ -0,0 +1,392 @@ +// +// Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Microsoft::WRL::ComPtr; + +#include +#include +#include +#include + +typedef std::chrono::high_resolution_clock::time_point time_point; +typedef std::chrono::high_resolution_clock::duration duration; + +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x +#define LINE_STRING STRINGIZE(__LINE__) +#define CHECK_BOOL(expr) do { if(!(expr)) { \ + assert(0 && #expr); \ + throw std::runtime_error(__FILE__ "(" LINE_STRING "): ( " #expr " ) == false"); \ + } } while(false) +#define CHECK_HR(expr) do { if(FAILED(expr)) { \ + assert(0 && #expr); \ + throw std::runtime_error(__FILE__ "(" LINE_STRING "): FAILED( " #expr " )"); \ + } } while(false) + +const uint32_t VENDOR_ID_AMD = 0x1002; +const uint32_t VENDOR_ID_NVIDIA = 0x10DE; +const uint32_t VENDOR_ID_INTEL = 0x8086; + +template +inline constexpr T CeilDiv(T x, T y) +{ + return (x+y-1) / y; +} +template +inline constexpr T RoundDiv(T x, T y) +{ + return (x+y/(T)2) / y; +} + +template +inline constexpr T AlignUp(T val, T align) +{ + return (val + align - 1) / align * align; +} + +static const float PI = 3.14159265358979323846264338327950288419716939937510582f; + +static const D3D12_RANGE EMPTY_RANGE = {0, 0}; + +struct vec2 +{ + float x, y; + + vec2() { } + vec2(float x, float y) : x(x), y(y) { } + + float& operator[](uint32_t index) { return *(&x + index); } + const float& operator[](uint32_t index) const { return *(&x + index); } + + vec2 operator+(const vec2& rhs) const { return vec2(x + rhs.x, y + rhs.y); } + vec2 operator-(const vec2& rhs) const { return vec2(x - rhs.x, y - rhs.y); } + vec2 operator*(float s) const { return vec2(x * s, y * s); } + + vec2 Normalized() const + { + return (*this) * (1.f / sqrt(x * x + y * y)); + } +}; + +struct vec3 +{ + float x, y, z; + + vec3() { } + vec3(float x, float y, float z) : x(x), y(y), z(z) { } + + float& operator[](uint32_t index) { return *(&x + index); } + const float& operator[](uint32_t index) const { return *(&x + index); } + + vec3 operator+(const vec3& rhs) const { return vec3(x + rhs.x, y + rhs.y, z + rhs.z); } + vec3 operator-(const vec3& rhs) const { return vec3(x - rhs.x, y - rhs.y, z - rhs.z); } + vec3 operator*(float s) const { return vec3(x * s, y * s, z * s); } + + vec3 Normalized() const + { + return (*this) * (1.f / sqrt(x * x + y * y + z * z)); + } +}; + +inline float Dot(const vec3& lhs, const vec3& rhs) +{ + return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; +} +inline vec3 Cross(const vec3& lhs, const vec3& rhs) +{ + return vec3( + lhs.y * rhs.z - lhs.z * rhs.y, + lhs.z * rhs.x - lhs.x * rhs.z, + lhs.x * rhs.y - lhs.y * rhs.x); +} + +struct vec4 +{ + float x, y, z, w; + + vec4() { } + vec4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) { } + vec4(const vec3& v, float w) : x(v.x), y(v.y), z(v.z), w(w) { } + + float& operator[](uint32_t index) { return *(&x + index); } + const float& operator[](uint32_t index) const { return *(&x + index); } + + vec4 operator+(const vec4& rhs) const { return vec4(x + rhs.x, y + rhs.y, z + rhs.z, w + rhs.w); } + vec4 operator-(const vec4& rhs) const { return vec4(x - rhs.x, y - rhs.y, z - rhs.z, w - rhs.w); } + vec4 operator*(float s) const { return vec4(x * s, y * s, z * s, w * s); } +}; + +struct mat4 +{ + union + { + struct + { + float _11, _12, _13, _14; + float _21, _22, _23, _24; + float _31, _32, _33, _34; + float _41, _42, _43, _44; + }; + float m[4][4]; // [row][column] + }; + + mat4() { } + + mat4( + float _11, float _12, float _13, float _14, + float _21, float _22, float _23, float _24, + float _31, float _32, float _33, float _34, + float _41, float _42, float _43, float _44) : + _11(_11), _12(_12), _13(_13), _14(_14), + _21(_21), _22(_22), _23(_23), _24(_24), + _31(_31), _32(_32), _33(_33), _34(_34), + _41(_41), _42(_42), _43(_43), _44(_44) + { + } + + mat4( + const vec4& row1, + const vec4& row2, + const vec4& row3, + const vec4& row4) : + _11(row1.x), _12(row1.y), _13(row1.z), _14(row1.w), + _21(row2.x), _22(row2.y), _23(row2.z), _24(row2.w), + _31(row3.x), _32(row3.y), _33(row3.z), _34(row3.w), + _41(row4.x), _42(row4.y), _43(row4.z), _44(row4.w) + { + } + + mat4(const float* data) : + _11(data[ 0]), _12(data[ 1]), _13(data[ 2]), _14(data[ 3]), + _21(data[ 4]), _22(data[ 5]), _23(data[ 6]), _24(data[ 7]), + _31(data[ 8]), _32(data[ 9]), _33(data[10]), _34(data[11]), + _41(data[12]), _42(data[13]), _43(data[14]), _44(data[15]) + { + } + + mat4 operator*(const mat4 &rhs) const + { + return mat4( + _11 * rhs._11 + _12 * rhs._21 + _13 * rhs._31 + _14 * rhs._41, + _11 * rhs._12 + _12 * rhs._22 + _13 * rhs._32 + _14 * rhs._42, + _11 * rhs._13 + _12 * rhs._23 + _13 * rhs._33 + _14 * rhs._43, + _11 * rhs._14 + _12 * rhs._24 + _13 * rhs._34 + _14 * rhs._44, + + _21 * rhs._11 + _22 * rhs._21 + _23 * rhs._31 + _24 * rhs._41, + _21 * rhs._12 + _22 * rhs._22 + _23 * rhs._32 + _24 * rhs._42, + _21 * rhs._13 + _22 * rhs._23 + _23 * rhs._33 + _24 * rhs._43, + _21 * rhs._14 + _22 * rhs._24 + _23 * rhs._34 + _24 * rhs._44, + + _31 * rhs._11 + _32 * rhs._21 + _33 * rhs._31 + _34 * rhs._41, + _31 * rhs._12 + _32 * rhs._22 + _33 * rhs._32 + _34 * rhs._42, + _31 * rhs._13 + _32 * rhs._23 + _33 * rhs._33 + _34 * rhs._43, + _31 * rhs._14 + _32 * rhs._24 + _33 * rhs._34 + _34 * rhs._44, + + _41 * rhs._11 + _42 * rhs._21 + _43 * rhs._31 + _44 * rhs._41, + _41 * rhs._12 + _42 * rhs._22 + _43 * rhs._32 + _44 * rhs._42, + _41 * rhs._13 + _42 * rhs._23 + _43 * rhs._33 + _44 * rhs._43, + _41 * rhs._14 + _42 * rhs._24 + _43 * rhs._34 + _44 * rhs._44); + } + + static mat4 Identity() + { + return mat4( + 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f); + } + + static mat4 Translation(const vec3& v) + { + return mat4( + 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + v.x, v.y, v.z, 1.f); + } + + static mat4 Scaling(float s) + { + return mat4( + s, 0.f, 0.f, 0.f, + 0.f, s, 0.f, 0.f, + 0.f, 0.f, s, 0.f, + 0.f, 0.f, 0.f, 1.f); + } + + static mat4 Scaling(const vec3& s) + { + return mat4( + s.x, 0.f, 0.f, 0.f, + 0.f, s.y, 0.f, 0.f, + 0.f, 0.f, s.z, 0.f, + 0.f, 0.f, 0.f, 1.f); + } + + static mat4 RotationX(float angle) + { + const float s = sin(angle), c = cos(angle); + return mat4( + 1.f, 0.f, 0.f, 0.f, + 0.f, c, s, 0.f, + 0.f, -s, c, 0.f, + 0.f, 0.f, 0.f, 1.f); + } + + static mat4 RotationY(float angle) + { + const float s = sin(angle), c = cos(angle); + return mat4( + c, s, 0.f, 0.f, + -s, c, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f); + } + + static mat4 RotationZ(float angle) + { + const float s = sin(angle), c = cos(angle); + return mat4( + c, 0.f, -s, 0.f, + 0.f, 1.f, 0.f, 0.f, + s, 0.f, c, 0.f, + 0.f, 0.f, 0.f, 1.f); + } + + static mat4 Perspective(float fovY, float aspectRatio, float zNear, float zFar) + { + float yScale = 1.0f / tan(fovY * 0.5f); + float xScale = yScale / aspectRatio; + return mat4( + xScale, 0.0f, 0.0f, 0.0f, + 0.0f, yScale, 0.0f, 0.0f, + 0.0f, 0.0f, zFar / (zFar - zNear), 1.0f, + 0.0f, 0.0f, -zNear * zFar / (zFar - zNear), 0.0f); + } + + static mat4 LookAt(vec3 at, vec3 eye, vec3 up) + { + vec3 zAxis = (at - eye).Normalized(); + vec3 xAxis = Cross(up, zAxis).Normalized(); + vec3 yAxis = Cross(zAxis, xAxis); + return mat4( + xAxis.x, yAxis.x, zAxis.x, 0.0f, + xAxis.y, yAxis.y, zAxis.y, 0.0f, + xAxis.z, yAxis.z, zAxis.z, 0.0f, + -Dot(xAxis, eye), -Dot(yAxis, eye), -Dot(zAxis, eye), 1.0f); + } + + mat4 Transposed() const + { + return mat4( + _11, _21, _31, _41, + _12, _22, _32, _42, + _13, _23, _33, _43, + _14, _24, _34, _44); + } +}; + +class RandomNumberGenerator +{ +public: + RandomNumberGenerator() : m_Value{GetTickCount()} {} + RandomNumberGenerator(uint32_t seed) : m_Value{seed} { } + void Seed(uint32_t seed) { m_Value = seed; } + uint32_t Generate() { return GenerateFast() ^ (GenerateFast() >> 7); } + bool GenerateBool() { return (GenerateFast() & 0x4) != 0; } + +private: + uint32_t m_Value; + uint32_t GenerateFast() { return m_Value = (m_Value * 196314165 + 907633515); } +}; + +// Wrapper for RandomNumberGenerator compatible with STL "UniformRandomNumberGenerator" idea. +struct MyUniformRandomNumberGenerator +{ + typedef uint32_t result_type; + MyUniformRandomNumberGenerator(RandomNumberGenerator& gen) : m_Gen(gen) { } + static uint32_t min() { return 0; } + static uint32_t max() { return UINT32_MAX; } + uint32_t operator()() { return m_Gen.Generate(); } + +private: + RandomNumberGenerator& m_Gen; +}; + +void ReadFile(std::vector& out, const wchar_t* fileName); +void SaveFile(const wchar_t* filePath, const void* data, size_t dataSize); + +enum class CONSOLE_COLOR +{ + INFO, + NORMAL, + WARNING, + ERROR_, + COUNT +}; + +void SetConsoleColor(CONSOLE_COLOR color); + +void PrintMessage(CONSOLE_COLOR color, const char* msg); +void PrintMessage(CONSOLE_COLOR color, const wchar_t* msg); + +inline void Print(const char* msg) { PrintMessage(CONSOLE_COLOR::NORMAL, msg); } +inline void Print(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::NORMAL, msg); } +inline void PrintWarning(const char* msg) { PrintMessage(CONSOLE_COLOR::WARNING, msg); } +inline void PrintWarning(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::WARNING, msg); } +inline void PrintError(const char* msg) { PrintMessage(CONSOLE_COLOR::ERROR_, msg); } +inline void PrintError(const wchar_t* msg) { PrintMessage(CONSOLE_COLOR::ERROR_, msg); } + +void PrintMessageV(CONSOLE_COLOR color, const char* format, va_list argList); +void PrintMessageV(CONSOLE_COLOR color, const wchar_t* format, va_list argList); +void PrintMessageF(CONSOLE_COLOR color, const char* format, ...); +void PrintMessageF(CONSOLE_COLOR color, const wchar_t* format, ...); +void PrintWarningF(const char* format, ...); +void PrintWarningF(const wchar_t* format, ...); +void PrintErrorF(const char* format, ...); +void PrintErrorF(const wchar_t* format, ...); + diff --git a/3rdparty/d3d12memalloc/src/D3D12MemAlloc.cpp b/3rdparty/d3d12memalloc/src/D3D12MemAlloc.cpp index 896cceeb6195ac..21c178269f2190 100644 --- a/3rdparty/d3d12memalloc/src/D3D12MemAlloc.cpp +++ b/3rdparty/d3d12memalloc/src/D3D12MemAlloc.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include // for _aligned_malloc, _aligned_free #ifndef _WIN32 #include @@ -106,6 +107,16 @@ especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs. #define D3D12MA_DEFAULT_BLOCK_SIZE (64ull * 1024 * 1024) #endif +#ifndef D3D12MA_DEBUG_LOG + #define D3D12MA_DEBUG_LOG(format, ...) + /* + #define D3D12MA_DEBUG_LOG(format, ...) do { \ + wprintf(format, __VA_ARGS__); \ + wprintf(L"\n"); \ + } while(false) + */ +#endif + #endif // _D3D12MA_CONFIGURATION //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -117,6 +128,10 @@ especially to test compatibility with D3D12_RESOURCE_HEAP_TIER_1 on modern GPUs. #define D3D12MA_IID_PPV_ARGS(ppType) __uuidof(**(ppType)), reinterpret_cast(ppType) +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + #define D3D12MA_CREATE_NOT_ZEROED_AVAILABLE 1 +#endif + namespace D3D12MA { static constexpr UINT HEAP_TYPE_COUNT = 4; @@ -133,10 +148,18 @@ static const WCHAR* const HeapTypeNames[] = L"READBACK", L"CUSTOM", }; +static const WCHAR* const StandardHeapTypeNames[] = +{ + L"DEFAULT", + L"UPLOAD", + L"READBACK", +}; static const D3D12_HEAP_FLAGS RESOURCE_CLASS_HEAP_FLAGS = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES; +static const D3D12_RESIDENCY_PRIORITY D3D12_RESIDENCY_PRIORITY_NONE = D3D12_RESIDENCY_PRIORITY(0); + #ifndef _D3D12MA_ENUM_DECLARATIONS // Local copy of this enum, as it is provided only by , so it may not be available. @@ -377,7 +400,15 @@ template static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; } template static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; } - + +static WCHAR HexDigitToChar(UINT8 digit) +{ + if(digit < 10) + return L'0' + digit; + else + return L'A' + (digit - 10); +} + /* Performs binary search and returns iterator to first element that is greater or equal to `key`, according to comparison `cmp`. @@ -427,26 +458,29 @@ static IterT BinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& va return end; } -static UINT HeapTypeToIndex(D3D12_HEAP_TYPE type) +static UINT StandardHeapTypeToIndex(D3D12_HEAP_TYPE type) { switch (type) { case D3D12_HEAP_TYPE_DEFAULT: return 0; case D3D12_HEAP_TYPE_UPLOAD: return 1; case D3D12_HEAP_TYPE_READBACK: return 2; - case D3D12_HEAP_TYPE_CUSTOM: return 3; default: D3D12MA_ASSERT(0); return UINT_MAX; } } -static D3D12_HEAP_TYPE IndexToHeapType(UINT heapTypeIndex) +static D3D12_HEAP_TYPE IndexToStandardHeapType(UINT heapTypeIndex) { - D3D12MA_ASSERT(heapTypeIndex < 4); - // D3D12_HEAP_TYPE_DEFAULT starts at 1. - return (D3D12_HEAP_TYPE)(heapTypeIndex + 1); + switch(heapTypeIndex) + { + case 0: return D3D12_HEAP_TYPE_DEFAULT; + case 1: return D3D12_HEAP_TYPE_UPLOAD; + case 2: return D3D12_HEAP_TYPE_READBACK; + default: D3D12MA_ASSERT(0); return D3D12_HEAP_TYPE_CUSTOM; + } } -static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags) +static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags, bool denyMsaaTextures) { /* Documentation of D3D12_HEAP_DESC structure says: @@ -459,6 +493,9 @@ static UINT64 HeapFlagsToAlignment(D3D12_HEAP_FLAGS flags) https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_heap_desc */ + if (denyMsaaTextures) + return D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT; + const D3D12_HEAP_FLAGS denyAllTexturesFlags = D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES; const bool canContainAnyTextures = @@ -888,6 +925,7 @@ class Vector public: using value_type = T; using iterator = T*; + using const_iterator = const T*; // allocationCallbacks externally owned, must outlive this object. Vector(const ALLOCATION_CALLBACKS& allocationCallbacks); @@ -904,13 +942,10 @@ class Vector iterator begin() { return m_pArray; } iterator end() { return m_pArray + m_Count; } - iterator rend() { return begin() - 1; } - iterator rbegin() { return end() - 1; } - - const iterator cbegin() const { return m_pArray; } - const iterator cend() const { return m_pArray + m_Count; } - const iterator crbegin() const { return cend() - 1; } - const iterator crend() const { return cbegin() - 1; } + const_iterator cbegin() const { return m_pArray; } + const_iterator cend() const { return m_pArray + m_Count; } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } void push_front(const T& src) { insert(0, src); } void push_back(const T& src); @@ -1177,6 +1212,7 @@ class StringBuilder void AddNewLine() { Add(L'\n'); } void AddNumber(UINT num); void AddNumber(UINT64 num); + void AddPointer(const void* ptr); private: Vector m_Data; @@ -1221,6 +1257,22 @@ void StringBuilder::AddNumber(UINT64 num) while (num); Add(p); } + +void StringBuilder::AddPointer(const void* ptr) +{ + WCHAR buf[21]; + uintptr_t num = (uintptr_t)ptr; + buf[20] = L'\0'; + WCHAR *p = &buf[20]; + do + { + *--p = HexDigitToChar((UINT8)(num & 0xF)); + num >>= 4; + } + while (num); + Add(p); +} + #endif // _D3D12MA_STRING_BUILDER_FUNCTIONS #endif // _D3D12MA_STRING_BUILDER @@ -1264,6 +1316,7 @@ class JsonWriter // Posts next part of an open string. The number is converted to decimal characters. void ContinueString(UINT num); void ContinueString(UINT64 num); + void ContinueString_Pointer(const void* ptr); // Posts next part of an open string. Pointer value is converted to characters // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00 // void ContinueString_Pointer(const void* ptr); @@ -1449,6 +1502,12 @@ void JsonWriter::ContinueString(UINT64 num) m_SB.AddNumber(num); } +void JsonWriter::ContinueString_Pointer(const void* ptr) +{ + D3D12MA_ASSERT(m_InsideString); + m_SB.AddPointer(ptr); +} + void JsonWriter::EndString(LPCWSTR pStr) { D3D12MA_ASSERT(m_InsideString); @@ -1511,61 +1570,63 @@ void JsonWriter::AddAllocationToObject(const Allocation& alloc) break; default: D3D12MA_ASSERT(0); break; } + WriteString(L"Size"); WriteNumber(alloc.GetSize()); + WriteString(L"Usage"); + WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags()); + + void* privateData = alloc.GetPrivateData(); + if (privateData) + { + WriteString(L"CustomData"); + BeginString(); + ContinueString_Pointer(privateData); + EndString(); + } + LPCWSTR name = alloc.GetName(); if (name != NULL) { WriteString(L"Name"); WriteString(name); } - if (alloc.m_PackedData.GetResourceFlags()) - { - WriteString(L"Flags"); - WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags()); - } if (alloc.m_PackedData.GetTextureLayout()) { WriteString(L"Layout"); WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout()); } - if (alloc.m_CreationFrameIndex) - { - WriteString(L"CreationFrameIndex"); - WriteNumber(alloc.m_CreationFrameIndex); - } } void JsonWriter::AddDetailedStatisticsInfoObject(const DetailedStatistics& stats) { BeginObject(); + WriteString(L"BlockCount"); WriteNumber(stats.Stats.BlockCount); - WriteString(L"AllocationCount"); - WriteNumber(stats.Stats.AllocationCount); - WriteString(L"UnusedRangeCount"); - WriteNumber(stats.UnusedRangeCount); WriteString(L"BlockBytes"); WriteNumber(stats.Stats.BlockBytes); + WriteString(L"AllocationCount"); + WriteNumber(stats.Stats.AllocationCount); WriteString(L"AllocationBytes"); WriteNumber(stats.Stats.AllocationBytes); + WriteString(L"UnusedRangeCount"); + WriteNumber(stats.UnusedRangeCount); - WriteString(L"AllocationSize"); - BeginObject(true); - WriteString(L"Min"); - WriteNumber(stats.AllocationSizeMin); - WriteString(L"Max"); - WriteNumber(stats.AllocationSizeMax); - EndObject(); - - WriteString(L"UnusedRangeSize"); - BeginObject(true); - WriteString(L"Min"); - WriteNumber(stats.UnusedRangeSizeMin); - WriteString(L"Max"); - WriteNumber(stats.UnusedRangeSizeMax); - EndObject(); - + if (stats.Stats.AllocationCount > 1) + { + WriteString(L"AllocationSizeMin"); + WriteNumber(stats.AllocationSizeMin); + WriteString(L"AllocationSizeMax"); + WriteNumber(stats.AllocationSizeMax); + } + if (stats.UnusedRangeCount > 1) + { + WriteString(L"UnusedRangeSizeMin"); + WriteNumber(stats.UnusedRangeSizeMin); + WriteString(L"UnusedRangeSizeMax"); + WriteNumber(stats.UnusedRangeSizeMax); + } EndObject(); } @@ -2806,7 +2867,7 @@ struct AllocationRequest UINT64 sumFreeSize; // Sum size of free items that overlap with proposed allocation. UINT64 sumItemSize; // Sum size of items to make lost that overlap with proposed allocation. SuballocationList::iterator item; - BOOL zeroInitialized; + BOOL zeroInitialized = FALSE; // TODO Implement proper handling in TLSF and Linear, using ZeroInitializedRange class. }; #endif // _D3D12MA_ALLOCATION_REQUEST @@ -2930,11 +2991,13 @@ class BlockMetadata virtual void AddStatistics(Statistics& inoutStats) const = 0; virtual void AddDetailedStatistics(DetailedStatistics& inoutStats) const = 0; virtual void WriteAllocationInfoToJson(JsonWriter& json) const = 0; + virtual void DebugLogAllAllocations() const = 0; protected: const ALLOCATION_CALLBACKS* GetAllocs() const { return m_pAllocationCallbacks; } UINT64 GetDebugMargin() const { return IsVirtual() ? 0 : D3D12MA_DEBUG_MARGIN; } + void DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const; void PrintDetailedMap_Begin(JsonWriter& json, UINT64 unusedBytes, size_t allocationCount, @@ -2962,11 +3025,28 @@ BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bo D3D12MA_ASSERT(allocationCallbacks); } +void BlockMetadata::DebugLogAllocation(UINT64 offset, UINT64 size, void* privateData) const +{ + if (IsVirtual()) + { + D3D12MA_DEBUG_LOG(L"UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p", offset, size, privateData); + } + else + { + D3D12MA_ASSERT(privateData != NULL); + Allocation* allocation = reinterpret_cast(privateData); + + privateData = allocation->GetPrivateData(); + LPCWSTR name = allocation->GetName(); + + D3D12MA_DEBUG_LOG(L"UNFREED ALLOCATION; Offset: %llu; Size: %llu; PrivateData: %p; Name: %s", + offset, size, privateData, name ? name : L"D3D12MA_Empty"); + } +} + void BlockMetadata::PrintDetailedMap_Begin(JsonWriter& json, UINT64 unusedBytes, size_t allocationCount, size_t unusedRangeCount) const { - json.BeginObject(); - json.WriteString(L"TotalBytes"); json.WriteNumber(GetSize()); @@ -2974,10 +3054,10 @@ void BlockMetadata::PrintDetailedMap_Begin(JsonWriter& json, json.WriteNumber(unusedBytes); json.WriteString(L"Allocations"); - json.WriteNumber(allocationCount); + json.WriteNumber((UINT64)allocationCount); json.WriteString(L"UnusedRanges"); - json.WriteNumber(unusedRangeCount); + json.WriteNumber((UINT64)unusedRangeCount); json.WriteString(L"Suballocations"); json.BeginArray(); @@ -2993,13 +3073,11 @@ void BlockMetadata::PrintDetailedMap_Allocation(JsonWriter& json, if (IsVirtual()) { - json.WriteString(L"Type"); - json.WriteString(L"ALLOCATION"); json.WriteString(L"Size"); json.WriteNumber(size); if (privateData) { - json.WriteString(L"PrivateData"); + json.WriteString(L"CustomData"); json.WriteNumber((uintptr_t)privateData); } } @@ -3032,7 +3110,6 @@ void BlockMetadata::PrintDetailedMap_UnusedRange(JsonWriter& json, void BlockMetadata::PrintDetailedMap_End(JsonWriter& json) const { json.EndArray(); - json.EndObject(); } #endif // _D3D12MA_BLOCK_METADATA_FUNCTIONS #endif // _D3D12MA_BLOCK_METADATA @@ -3682,6 +3759,7 @@ class BlockMetadata_Linear : public BlockMetadata void AddStatistics(Statistics& inoutStats) const override; void AddDetailedStatistics(DetailedStatistics& inoutStats) const override; void WriteAllocationInfoToJson(JsonWriter& json) const override; + void DebugLogAllAllocations() const override; private: /* @@ -3820,7 +3898,7 @@ bool BlockMetadata_Linear::Validate() const { if (!IsVirtual()) { - D3D12MA_VALIDATE((UINT64)alloc->GetAllocHandle() == suballoc.offset); + D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset); D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size); } sumUsedSize += suballoc.size; @@ -3862,7 +3940,7 @@ bool BlockMetadata_Linear::Validate() const { if (!IsVirtual()) { - D3D12MA_VALIDATE((UINT64)alloc->GetAllocHandle() == suballoc.offset); + D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset); D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size); } sumUsedSize += suballoc.size; @@ -3896,7 +3974,7 @@ bool BlockMetadata_Linear::Validate() const { if (!IsVirtual()) { - D3D12MA_VALIDATE((UINT64)alloc->GetAllocHandle() == suballoc.offset); + D3D12MA_VALIDATE(GetAllocationOffset(alloc->GetAllocHandle()) == suballoc.offset); D3D12MA_VALIDATE(alloc->GetSize() == suballoc.size); } sumUsedSize += suballoc.size; @@ -4638,6 +4716,19 @@ void BlockMetadata_Linear::WriteAllocationInfoToJson(JsonWriter& json) const PrintDetailedMap_End(json); } +void BlockMetadata_Linear::DebugLogAllAllocations() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it) + if (it->type != SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(it->offset, it->size, it->privateData); + + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it) + if (it->type != SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(it->offset, it->size, it->privateData); +} + Suballocation& BlockMetadata_Linear::FindSuballocation(UINT64 offset) const { const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); @@ -4649,31 +4740,31 @@ Suballocation& BlockMetadata_Linear::FindSuballocation(UINT64 offset) const // Item from the 1st vector. { - const SuballocationVectorType::iterator it = BinaryFindSorted( - suballocations1st.cbegin() + m_1stNullItemsBeginCount, - suballocations1st.cend(), + const SuballocationVectorType::const_iterator it = BinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), refSuballoc, SuballocationOffsetLess()); - if (it != suballocations1st.cend()) + if (it != suballocations1st.end()) { - return *it; + return const_cast(*it); } } if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) { // Rest of members stays uninitialized intentionally for better performance. - const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? - BinaryFindSorted(suballocations2nd.cbegin(), suballocations2nd.cend(), refSuballoc, SuballocationOffsetLess()) : - BinaryFindSorted(suballocations2nd.cbegin(), suballocations2nd.cend(), refSuballoc, SuballocationOffsetGreater()); - if (it != suballocations2nd.cend()) + const SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetLess()) : + BinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, SuballocationOffsetGreater()); + if (it != suballocations2nd.end()) { - return *it; + return const_cast(*it); } } D3D12MA_ASSERT(0 && "Allocation not found in linear allocator!"); - return *suballocations1st.crbegin(); // Should never occur. + return const_cast(suballocations1st.back()); // Should never occur. } bool BlockMetadata_Linear::ShouldCompact1st() const @@ -4964,6 +5055,7 @@ class BlockMetadata_TLSF : public BlockMetadata void AddStatistics(Statistics& inoutStats) const override; void AddDetailedStatistics(DetailedStatistics& inoutStats) const override; void WriteAllocationInfoToJson(JsonWriter& json) const override; + void DebugLogAllAllocations() const override; private: // According to original paper it should be preferable 4 or 5: @@ -5195,7 +5287,7 @@ bool BlockMetadata_TLSF::CreateAllocationRequest( // Round up to the next block UINT64 sizeForNextList = allocSize; - UINT64 smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4); + UINT16 smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4); if (allocSize > SMALL_BUFFER_SIZE) { sizeForNextList += (1ULL << (BitScanMSB(allocSize) - SECOND_LEVEL_INDEX)); @@ -5603,11 +5695,22 @@ void BlockMetadata_TLSF::WriteAllocationInfoToJson(JsonWriter& json) const if (block->IsFree()) PrintDetailedMap_UnusedRange(json, block->offset, block->size); else - PrintDetailedMap_Allocation(json, block->size, block->offset, block->PrivateData()); + PrintDetailedMap_Allocation(json, block->offset, block->size, block->PrivateData()); } PrintDetailedMap_End(json); } +void BlockMetadata_TLSF::DebugLogAllAllocations() const +{ + for (Block* block = m_NullBlock->prevPhysical; block != NULL; block = block->prevPhysical) + { + if (!block->IsFree()) + { + DebugLogAllocation(block->offset, block->size, block->PrivateData()); + } + } +} + UINT8 BlockMetadata_TLSF::SizeToMemoryClass(UINT64 size) const { if (size > SMALL_BUFFER_SIZE) @@ -5796,7 +5899,7 @@ class MemoryBlock const UINT64 m_Size; const UINT m_Id; - HRESULT Init(ID3D12ProtectedResourceSession* pProtectedSession); + HRESULT Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures); private: ID3D12Heap* m_Heap = NULL; @@ -5828,7 +5931,7 @@ class NormalBlock : public MemoryBlock BlockVector* GetBlockVector() const { return m_BlockVector; } // 'algorithm' should be one of the *_ALGORITHM_* flags in enums POOL_FLAGS or VIRTUAL_BLOCK_FLAGS - HRESULT Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession); + HRESULT Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures); // Validates all data structures inside this object. If not valid, returns false. bool Validate() const; @@ -5911,11 +6014,144 @@ struct CommittedAllocationParameters D3D12_HEAP_PROPERTIES m_HeapProperties = {}; D3D12_HEAP_FLAGS m_HeapFlags = D3D12_HEAP_FLAG_NONE; ID3D12ProtectedResourceSession* m_ProtectedSession = NULL; + bool m_CanAlias = false; + D3D12_RESIDENCY_PRIORITY m_ResidencyPriority = D3D12_RESIDENCY_PRIORITY_NONE; bool IsValid() const { return m_List != NULL; } }; #endif // _D3D12M_COMMITTED_ALLOCATION_PARAMETERS +// Simple variant data structure to hold all possible variations of ID3D12Device*::CreateCommittedResource* and ID3D12Device*::CreatePlacedResource* arguments +struct CREATE_RESOURCE_PARAMS +{ + CREATE_RESOURCE_PARAMS() = delete; + CREATE_RESOURCE_PARAMS( + const D3D12_RESOURCE_DESC* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue) + : Variant(VARIANT_WITH_STATE) + , pResourceDesc(pResourceDesc) + , InitialResourceState(InitialResourceState) + , pOptimizedClearValue(pOptimizedClearValue) + { + } +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + CREATE_RESOURCE_PARAMS( + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue) + : Variant(VARIANT_WITH_STATE_AND_DESC1) + , pResourceDesc1(pResourceDesc) + , InitialResourceState(InitialResourceState) + , pOptimizedClearValue(pOptimizedClearValue) + { + } +#endif +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + CREATE_RESOURCE_PARAMS( + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_BARRIER_LAYOUT InitialLayout, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + UINT32 NumCastableFormats, + DXGI_FORMAT* pCastableFormats) + : Variant(VARIANT_WITH_LAYOUT) + , pResourceDesc1(pResourceDesc) + , InitialLayout(InitialLayout) + , pOptimizedClearValue(pOptimizedClearValue) + , NumCastableFormats(NumCastableFormats) + , pCastableFormats(pCastableFormats) + { + } +#endif + + enum VARIANT + { + VARIANT_INVALID = 0, + VARIANT_WITH_STATE, + VARIANT_WITH_STATE_AND_DESC1, + VARIANT_WITH_LAYOUT + }; + + VARIANT Variant = VARIANT_INVALID; + + const D3D12_RESOURCE_DESC* GetResourceDesc() const + { + D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE); + return pResourceDesc; + } + const D3D12_RESOURCE_DESC*& AccessResourceDesc() + { + D3D12MA_ASSERT(Variant == VARIANT_WITH_STATE); + return pResourceDesc; + } + const D3D12_RESOURCE_DESC* GetBaseResourceDesc() const + { + // D3D12_RESOURCE_DESC1 can be cast to D3D12_RESOURCE_DESC by discarding the new members at the end. + return pResourceDesc; + } + D3D12_RESOURCE_STATES GetInitialResourceState() const + { + D3D12MA_ASSERT(Variant < VARIANT_WITH_LAYOUT); + return InitialResourceState; + } + const D3D12_CLEAR_VALUE* GetOptimizedClearValue() const + { + return pOptimizedClearValue; + } + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + const D3D12_RESOURCE_DESC1* GetResourceDesc1() const + { + D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1); + return pResourceDesc1; + } + const D3D12_RESOURCE_DESC1*& AccessResourceDesc1() + { + D3D12MA_ASSERT(Variant >= VARIANT_WITH_STATE_AND_DESC1); + return pResourceDesc1; + } +#endif + +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + D3D12_BARRIER_LAYOUT GetInitialLayout() const + { + D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT); + return InitialLayout; + } + UINT32 GetNumCastableFormats() const + { + D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT); + return NumCastableFormats; + } + DXGI_FORMAT* GetCastableFormats() const + { + D3D12MA_ASSERT(Variant >= VARIANT_WITH_LAYOUT); + return pCastableFormats; + } +#endif + +private: + union + { + const D3D12_RESOURCE_DESC* pResourceDesc; +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + const D3D12_RESOURCE_DESC1* pResourceDesc1; +#endif + }; + union + { + D3D12_RESOURCE_STATES InitialResourceState; +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + D3D12_BARRIER_LAYOUT InitialLayout; +#endif + }; + const D3D12_CLEAR_VALUE* pOptimizedClearValue; +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + UINT32 NumCastableFormats; + DXGI_FORMAT* pCastableFormats; +#endif +}; + #ifndef _D3D12MA_BLOCK_VECTOR /* Sequence of NormalBlock. Represents memory blocks allocated for a specific @@ -5938,12 +6174,17 @@ class BlockVector bool explicitBlockSize, UINT64 minAllocationAlignment, UINT32 algorithm, - ID3D12ProtectedResourceSession* pProtectedSession); + bool denyMsaaTextures, + ID3D12ProtectedResourceSession* pProtectedSession, + D3D12_RESIDENCY_PRIORITY residencyPriority); ~BlockVector(); + D3D12_RESIDENCY_PRIORITY GetResidencyPriority() const { return m_ResidencyPriority; } const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; } + D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; } UINT64 GetPreferredBlockSize() const { return m_PreferredBlockSize; } UINT32 GetAlgorithm() const { return m_Algorithm; } + bool DeniesMsaaTextures() const { return m_DenyMsaaTextures; } // To be used only while the m_Mutex is locked. Used during defragmentation. size_t GetBlockCount() const { return m_Blocks.size(); } // To be used only while the m_Mutex is locked. Used during defragmentation. @@ -5966,25 +6207,10 @@ class BlockVector UINT64 size, UINT64 alignment, const ALLOCATION_DESC& allocDesc, - const D3D12_RESOURCE_DESC& resourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE *pOptimizedClearValue, - Allocation** ppAllocation, - REFIID riidResource, - void** ppvResource); - -#ifdef __ID3D12Device8_INTERFACE_DEFINED__ - HRESULT CreateResource2( - UINT64 size, - UINT64 alignment, - const ALLOCATION_DESC& allocDesc, - const D3D12_RESOURCE_DESC1& resourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE *pOptimizedClearValue, + const CREATE_RESOURCE_PARAMS& createParams, Allocation** ppAllocation, REFIID riidResource, void** ppvResource); -#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ void AddStatistics(Statistics& inoutStats); void AddDetailedStatistics(DetailedStatistics& inoutStats); @@ -6001,7 +6227,9 @@ class BlockVector const bool m_ExplicitBlockSize; const UINT64 m_MinAllocationAlignment; const UINT32 m_Algorithm; + const bool m_DenyMsaaTextures; ID3D12ProtectedResourceSession* const m_ProtectedSession; + const D3D12_RESIDENCY_PRIORITY m_ResidencyPriority; /* There can be at most one allocation that is completely empty - a hysteresis to avoid pessimistic case of alternating creation and destruction of a ID3D12Heap. */ @@ -6082,7 +6310,7 @@ class CurrentBudgetData D3D12MA_ATOMIC_UINT64 m_BlockBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; D3D12MA_ATOMIC_UINT64 m_AllocationBytes[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; - D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch{0}; + D3D12MA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch = {0}; D3D12MA_RW_MUTEX m_BudgetMutex; UINT64 m_D3D12Usage[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; UINT64 m_D3D12Budget[DXGI_MEMORY_SEGMENT_GROUP_COUNT] = {}; @@ -6317,13 +6545,16 @@ class AllocatorPimpl friend class Allocator; friend class Pool; public: - std::atomic_uint32_t m_RefCount{1}; + std::atomic_uint32_t m_RefCount = {1}; CurrentBudgetData m_Budget; AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc); ~AllocatorPimpl(); ID3D12Device* GetDevice() const { return m_Device; } +#ifdef __ID3D12Device1_INTERFACE_DEFINED__ + ID3D12Device1* GetDevice1() const { return m_Device1; } +#endif #ifdef __ID3D12Device4_INTERFACE_DEFINED__ ID3D12Device4* GetDevice4() const { return m_Device4; } #endif @@ -6364,32 +6595,24 @@ class AllocatorPimpl UINT HeapPropertiesToMemorySegmentGroup(const D3D12_HEAP_PROPERTIES& heapProps) const; UINT64 GetMemoryCapacity(UINT memorySegmentGroup) const; - HRESULT CreateResource( - const ALLOCATION_DESC* pAllocDesc, - const D3D12_RESOURCE_DESC* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE *pOptimizedClearValue, - Allocation** ppAllocation, + HRESULT CreatePlacedResourceWrap( + ID3D12Heap *pHeap, + UINT64 HeapOffset, + const CREATE_RESOURCE_PARAMS& createParams, REFIID riidResource, void** ppvResource); -#ifdef __ID3D12Device8_INTERFACE_DEFINED__ - HRESULT CreateResource2( + HRESULT CreateResource( const ALLOCATION_DESC* pAllocDesc, - const D3D12_RESOURCE_DESC1* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE *pOptimizedClearValue, + const CREATE_RESOURCE_PARAMS& createParams, Allocation** ppAllocation, REFIID riidResource, void** ppvResource); -#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ HRESULT CreateAliasingResource( Allocation* pAllocation, UINT64 AllocationLocalOffset, - const D3D12_RESOURCE_DESC* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE *pOptimizedClearValue, + const CREATE_RESOURCE_PARAMS& createParams, REFIID riidResource, void** ppvResource); @@ -6408,14 +6631,16 @@ class AllocatorPimpl // Allocation object must be deleted externally afterwards. void FreeHeapMemory(Allocation* allocation); - void SetCurrentFrameIndex(UINT frameIndex); + void SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const; - void CalculateStatistics(TotalStatistics& outStats); + void SetCurrentFrameIndex(UINT frameIndex); + // For more deailed stats use outCustomHeaps to access statistics divided into L0 and L1 group + void CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCustomHeaps[2] = NULL); void GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget); void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType); - void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap); + void BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap); void FreeStatsString(WCHAR* pStatsString); private: @@ -6423,12 +6648,20 @@ class AllocatorPimpl const bool m_UseMutex; const bool m_AlwaysCommitted; + const bool m_MsaaAlwaysCommitted; + bool m_DefaultPoolsNotZeroed = false; ID3D12Device* m_Device; // AddRef +#ifdef __ID3D12Device1_INTERFACE_DEFINED__ + ID3D12Device1* m_Device1 = NULL; // AddRef, optional +#endif #ifdef __ID3D12Device4_INTERFACE_DEFINED__ ID3D12Device4* m_Device4 = NULL; // AddRef, optional #endif #ifdef __ID3D12Device8_INTERFACE_DEFINED__ ID3D12Device8* m_Device8 = NULL; // AddRef, optional +#endif +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + ID3D12Device10* m_Device10 = NULL; // AddRef, optional #endif IDXGIAdapter* m_Adapter; // AddRef #if D3D12MA_DXGI_1_4 @@ -6460,18 +6693,8 @@ class AllocatorPimpl HRESULT AllocateCommittedResource( const CommittedAllocationParameters& committedAllocParams, UINT64 resourceSize, bool withinBudget, void* pPrivateData, - const D3D12_RESOURCE_DESC* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE *pOptimizedClearValue, - Allocation** ppAllocation, REFIID riidResource, void** ppvResource); - -#ifdef __ID3D12Device8_INTERFACE_DEFINED__ - HRESULT AllocateCommittedResource2( - const CommittedAllocationParameters& committedAllocParams, - UINT64 resourceSize, bool withinBudget, void* pPrivateData, - const D3D12_RESOURCE_DESC1* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE *pOptimizedClearValue, + const CREATE_RESOURCE_PARAMS& createParams, Allocation** ppAllocation, REFIID riidResource, void** ppvResource); -#endif // Allocates and registers new heap without any resources placed in it, as dedicated allocation. // Creates and returns Allocation object. @@ -6514,6 +6737,7 @@ class AllocatorPimpl AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, const ALLOCATOR_DESC& desc) : m_UseMutex((desc.Flags & ALLOCATOR_FLAG_SINGLETHREADED) == 0), m_AlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_ALWAYS_COMMITTED) != 0), + m_MsaaAlwaysCommitted((desc.Flags & ALLOCATOR_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0), m_Device(desc.pDevice), m_Adapter(desc.pAdapter), m_PreferredBlockSize(desc.PreferredBlockSize != 0 ? desc.PreferredBlockSize : D3D12MA_DEFAULT_BLOCK_SIZE), @@ -6532,7 +6756,7 @@ AllocatorPimpl::AllocatorPimpl(const ALLOCATION_CALLBACKS& allocationCallbacks, { m_CommittedAllocations[i].Init( m_UseMutex, - (D3D12_HEAP_TYPE)(D3D12_HEAP_TYPE_DEFAULT + i), + IndexToStandardHeapType(i), NULL); // pool } @@ -6546,12 +6770,30 @@ HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc) desc.pAdapter->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Adapter3)); #endif +#ifdef __ID3D12Device1_INTERFACE_DEFINED__ + m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device1)); +#endif + #ifdef __ID3D12Device4_INTERFACE_DEFINED__ m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device4)); #endif #ifdef __ID3D12Device8_INTERFACE_DEFINED__ m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device8)); + + if((desc.Flags & ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED) != 0) + { + D3D12_FEATURE_DATA_D3D12_OPTIONS7 options7 = {}; + if(SUCCEEDED(m_Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &options7, sizeof(options7)))) + { + // DEFAULT_POOLS_NOT_ZEROED both supported and enabled by the user. + m_DefaultPoolsNotZeroed = true; + } + } +#endif + +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + m_Device->QueryInterface(D3D12MA_IID_PPV_ARGS(&m_Device10)); #endif HRESULT hr = m_Adapter->GetDesc(&m_AdapterDesc); @@ -6583,9 +6825,11 @@ HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc) D3D12_HEAP_FLAGS heapFlags; CalcDefaultPoolParams(heapProps.Type, heapFlags, i); -#ifdef __ID3D12Device8_INTERFACE_DEFINED__ - if (desc.Flags & ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED) +#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE + if(m_DefaultPoolsNotZeroed) + { heapFlags |= D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; + } #endif m_BlockVectors[i] = D3D12MA_NEW(GetAllocs(), BlockVector)( @@ -6597,8 +6841,10 @@ HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc) SIZE_MAX, // maxBlockCount false, // explicitBlockSize D3D12MA_DEBUG_ALIGNMENT, // minAllocationAlignment - 0, // Default algorithm - NULL); // pProtectedSession + 0, // Default algorithm, + m_MsaaAlwaysCommitted, + NULL, // pProtectedSession + D3D12_RESIDENCY_PRIORITY_NONE); // residencyPriority // No need to call m_pBlockVectors[i]->CreateMinBlocks here, becase minBlockCount is 0. } @@ -6611,12 +6857,18 @@ HRESULT AllocatorPimpl::Init(const ALLOCATOR_DESC& desc) AllocatorPimpl::~AllocatorPimpl() { +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + SAFE_RELEASE(m_Device10); +#endif #ifdef __ID3D12Device8_INTERFACE_DEFINED__ SAFE_RELEASE(m_Device8); #endif #ifdef __ID3D12Device4_INTERFACE_DEFINED__ SAFE_RELEASE(m_Device4); #endif +#ifdef __ID3D12Device1_INTERFACE_DEFINED__ + SAFE_RELEASE(m_Device1); +#endif #if D3D12MA_DXGI_1_4 SAFE_RELEASE(m_Adapter3); #endif @@ -6687,16 +6939,60 @@ UINT64 AllocatorPimpl::GetMemoryCapacity(UINT memorySegmentGroup) const } } +HRESULT AllocatorPimpl::CreatePlacedResourceWrap( + ID3D12Heap *pHeap, + UINT64 HeapOffset, + const CREATE_RESOURCE_PARAMS& createParams, + REFIID riidResource, + void** ppvResource) +{ +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT) + { + if (!m_Device10) + { + return E_NOINTERFACE; + } + return m_Device10->CreatePlacedResource2(pHeap, HeapOffset, + createParams.GetResourceDesc1(), createParams.GetInitialLayout(), + createParams.GetOptimizedClearValue(), createParams.GetNumCastableFormats(), + createParams.GetCastableFormats(), riidResource, ppvResource); + } else +#endif +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1) + { + if (!m_Device8) + { + return E_NOINTERFACE; + } + return m_Device8->CreatePlacedResource1(pHeap, HeapOffset, + createParams.GetResourceDesc1(), createParams.GetInitialResourceState(), + createParams.GetOptimizedClearValue(), riidResource, ppvResource); + } else +#endif + if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE) + { + return m_Device->CreatePlacedResource(pHeap, HeapOffset, + createParams.GetResourceDesc(), createParams.GetInitialResourceState(), + createParams.GetOptimizedClearValue(), riidResource, ppvResource); + } + else + { + D3D12MA_ASSERT(0); + return E_INVALIDARG; + } +} + + HRESULT AllocatorPimpl::CreateResource( const ALLOCATION_DESC* pAllocDesc, - const D3D12_RESOURCE_DESC* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE* pOptimizedClearValue, + const CREATE_RESOURCE_PARAMS& createParams, Allocation** ppAllocation, REFIID riidResource, void** ppvResource) { - D3D12MA_ASSERT(pAllocDesc && pResourceDesc && ppAllocation); + D3D12MA_ASSERT(pAllocDesc && createParams.GetBaseResourceDesc() && ppAllocation); *ppAllocation = NULL; if (ppvResource) @@ -6704,17 +7000,69 @@ HRESULT AllocatorPimpl::CreateResource( *ppvResource = NULL; } - D3D12_RESOURCE_DESC finalResourceDesc = *pResourceDesc; - D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(finalResourceDesc); + CREATE_RESOURCE_PARAMS finalCreateParams = createParams; + D3D12_RESOURCE_DESC finalResourceDesc; +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + D3D12_RESOURCE_DESC1 finalResourceDesc1; +#endif + D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo; + if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE) + { + finalResourceDesc = *createParams.GetResourceDesc(); + finalCreateParams.AccessResourceDesc() = &finalResourceDesc; + resAllocInfo = GetResourceAllocationInfo(finalResourceDesc); + } +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1) + { + if (!m_Device8) + { + return E_NOINTERFACE; + } + finalResourceDesc1 = *createParams.GetResourceDesc1(); + finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1; + resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1); + } +#endif +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT) + { + if (!m_Device10) + { + return E_NOINTERFACE; + } + finalResourceDesc1 = *createParams.GetResourceDesc1(); + finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1; + resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1); + } +#endif + else + { + D3D12MA_ASSERT(0); + return E_INVALIDARG; + } D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment)); D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0); BlockVector* blockVector = NULL; CommittedAllocationParameters committedAllocationParams = {}; bool preferCommitted = false; - HRESULT hr = CalcAllocationParams(*pAllocDesc, resAllocInfo.SizeInBytes, - pResourceDesc, - blockVector, committedAllocationParams, preferCommitted); + + HRESULT hr; +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + if (createParams.Variant >= CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1) + { + hr = CalcAllocationParams(*pAllocDesc, resAllocInfo.SizeInBytes, + createParams.GetResourceDesc1(), + blockVector, committedAllocationParams, preferCommitted); + } + else +#endif + { + hr = CalcAllocationParams(*pAllocDesc, resAllocInfo.SizeInBytes, + createParams.GetResourceDesc(), + blockVector, committedAllocationParams, preferCommitted); + } if (FAILED(hr)) return hr; @@ -6724,16 +7072,14 @@ HRESULT AllocatorPimpl::CreateResource( { hr = AllocateCommittedResource(committedAllocationParams, resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData, - &finalResourceDesc, InitialResourceState, pOptimizedClearValue, - ppAllocation, riidResource, ppvResource); + finalCreateParams, ppAllocation, riidResource, ppvResource); if (SUCCEEDED(hr)) return hr; } if (blockVector != NULL) { hr = blockVector->CreateResource(resAllocInfo.SizeInBytes, resAllocInfo.Alignment, - *pAllocDesc, finalResourceDesc, - InitialResourceState, pOptimizedClearValue, + *pAllocDesc, finalCreateParams, ppAllocation, riidResource, ppvResource); if (SUCCEEDED(hr)) return hr; @@ -6742,83 +7088,13 @@ HRESULT AllocatorPimpl::CreateResource( { hr = AllocateCommittedResource(committedAllocationParams, resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData, - &finalResourceDesc, InitialResourceState, pOptimizedClearValue, - ppAllocation, riidResource, ppvResource); + finalCreateParams, ppAllocation, riidResource, ppvResource); if (SUCCEEDED(hr)) return hr; } return hr; } -#ifdef __ID3D12Device8_INTERFACE_DEFINED__ -HRESULT AllocatorPimpl::CreateResource2( - const ALLOCATION_DESC* pAllocDesc, - const D3D12_RESOURCE_DESC1* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE* pOptimizedClearValue, - Allocation** ppAllocation, - REFIID riidResource, - void** ppvResource) -{ - D3D12MA_ASSERT(pAllocDesc && pResourceDesc && ppAllocation); - - *ppAllocation = NULL; - if (ppvResource) - { - *ppvResource = NULL; - } - if (m_Device8 == NULL) - { - return E_NOINTERFACE; - } - - D3D12_RESOURCE_DESC1 finalResourceDesc = *pResourceDesc; - D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(finalResourceDesc); - D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment)); - D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0); - - BlockVector* blockVector = NULL; - CommittedAllocationParameters committedAllocationParams = {}; - bool preferCommitted = false; - HRESULT hr = CalcAllocationParams(*pAllocDesc, resAllocInfo.SizeInBytes, - pResourceDesc, - blockVector, committedAllocationParams, preferCommitted); - if (FAILED(hr)) - return hr; - - const bool withinBudget = (pAllocDesc->Flags & ALLOCATION_FLAG_WITHIN_BUDGET) != 0; - hr = E_INVALIDARG; - if (committedAllocationParams.IsValid() && preferCommitted) - { - hr = AllocateCommittedResource2(committedAllocationParams, - resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData, - &finalResourceDesc, InitialResourceState, pOptimizedClearValue, - ppAllocation, riidResource, ppvResource); - if (SUCCEEDED(hr)) - return hr; - } - if (blockVector != NULL) - { - hr = blockVector->CreateResource2(resAllocInfo.SizeInBytes, resAllocInfo.Alignment, - *pAllocDesc, finalResourceDesc, - InitialResourceState, pOptimizedClearValue, - ppAllocation, riidResource, ppvResource); - if (SUCCEEDED(hr)) - return hr; - } - if (committedAllocationParams.IsValid() && !preferCommitted) - { - hr = AllocateCommittedResource2(committedAllocationParams, - resAllocInfo.SizeInBytes, withinBudget, pAllocDesc->pPrivateData, - &finalResourceDesc, InitialResourceState, pOptimizedClearValue, - ppAllocation, riidResource, ppvResource); - if (SUCCEEDED(hr)) - return hr; - } - return hr; -} -#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ - HRESULT AllocatorPimpl::AllocateMemory( const ALLOCATION_DESC* pAllocDesc, const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo, @@ -6862,16 +7138,53 @@ HRESULT AllocatorPimpl::AllocateMemory( HRESULT AllocatorPimpl::CreateAliasingResource( Allocation* pAllocation, UINT64 AllocationLocalOffset, - const D3D12_RESOURCE_DESC* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE* pOptimizedClearValue, + const CREATE_RESOURCE_PARAMS& createParams, REFIID riidResource, void** ppvResource) { *ppvResource = NULL; - D3D12_RESOURCE_DESC resourceDesc2 = *pResourceDesc; - D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo = GetResourceAllocationInfo(resourceDesc2); + CREATE_RESOURCE_PARAMS finalCreateParams = createParams; + D3D12_RESOURCE_DESC finalResourceDesc; +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + D3D12_RESOURCE_DESC1 finalResourceDesc1; +#endif + D3D12_RESOURCE_ALLOCATION_INFO resAllocInfo; + if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE) + { + finalResourceDesc = *createParams.GetResourceDesc(); + finalCreateParams.AccessResourceDesc() = &finalResourceDesc; + resAllocInfo = GetResourceAllocationInfo(finalResourceDesc); + } +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1) + { + if (!m_Device8) + { + return E_NOINTERFACE; + } + finalResourceDesc1 = *createParams.GetResourceDesc1(); + finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1; + resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1); + } +#endif +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + else if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT) + { + if (!m_Device10) + { + return E_NOINTERFACE; + } + finalResourceDesc1 = *createParams.GetResourceDesc1(); + finalCreateParams.AccessResourceDesc1() = &finalResourceDesc1; + resAllocInfo = GetResourceAllocationInfo(finalResourceDesc1); + } +#endif + else + { + D3D12MA_ASSERT(0); + return E_INVALIDARG; + } D3D12MA_ASSERT(IsPow2(resAllocInfo.Alignment)); D3D12MA_ASSERT(resAllocInfo.SizeInBytes > 0); @@ -6887,14 +7200,7 @@ HRESULT AllocatorPimpl::CreateAliasingResource( return E_INVALIDARG; } - return m_Device->CreatePlacedResource( - existingHeap, - newOffset, - &resourceDesc2, - InitialResourceState, - pOptimizedClearValue, - riidResource, - ppvResource); + return CreatePlacedResourceWrap(existingHeap, newOffset, finalCreateParams, riidResource, ppvResource); } void AllocatorPimpl::FreeCommittedMemory(Allocation* allocation) @@ -6936,6 +7242,17 @@ void AllocatorPimpl::FreeHeapMemory(Allocation* allocation) m_Budget.RemoveBlock(memSegmentGroup, allocSize); } +void AllocatorPimpl::SetResidencyPriority(ID3D12Pageable* obj, D3D12_RESIDENCY_PRIORITY priority) const +{ +#ifdef __ID3D12Device1_INTERFACE_DEFINED__ + if (priority != D3D12_RESIDENCY_PRIORITY_NONE && m_Device1) + { + // Intentionally ignoring the result. + m_Device1->SetResidencyPriority(1, &obj, &priority); + } +#endif +} + void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex) { m_CurrentFrameIndex.store(frameIndex); @@ -6945,7 +7262,7 @@ void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex) #endif } -void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats) +void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCustomHeaps[2]) { // Init stats for (size_t i = 0; i < HEAP_TYPE_COUNT; i++) @@ -6953,6 +7270,11 @@ void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats) for (size_t i = 0; i < DXGI_MEMORY_SEGMENT_GROUP_COUNT; i++) ClearDetailedStatistics(outStats.MemorySegmentGroup[i]); ClearDetailedStatistics(outStats.Total); + if (outCustomHeaps) + { + ClearDetailedStatistics(outCustomHeaps[0]); + ClearDetailedStatistics(outCustomHeaps[1]); + } // Process default pools. 3 standard heap types only. Add them to outStats.HeapType[i]. if (SupportsResourceHeapTier2()) @@ -7003,8 +7325,13 @@ void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats) pool->AddDetailedStatistics(tmpStats); AddDetailedStatistics( outStats.HeapType[heapTypeIndex], tmpStats); + + UINT memorySegment = HeapPropertiesToMemorySegmentGroup(poolHeapProps); AddDetailedStatistics( - outStats.MemorySegmentGroup[HeapPropertiesToMemorySegmentGroup(poolHeapProps)], tmpStats); + outStats.MemorySegmentGroup[memorySegment], tmpStats); + + if (outCustomHeaps) + AddDetailedStatistics(outCustomHeaps[memorySegment], tmpStats); } } @@ -7016,7 +7343,7 @@ void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats) AddDetailedStatistics( outStats.HeapType[heapTypeIndex], tmpStats); AddDetailedStatistics( - outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(IndexToHeapType(heapTypeIndex))], tmpStats); + outStats.MemorySegmentGroup[StandardHeapTypeToMemorySegmentGroup(IndexToStandardHeapType(heapTypeIndex))], tmpStats); } // Sum up memory segment groups to totals. @@ -7106,155 +7433,329 @@ void AllocatorPimpl::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE hea } } -void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) +void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) { StringBuilder sb(GetAllocs()); { - JsonWriter json(GetAllocs(), sb); - Budget localBudget = {}, nonLocalBudget = {}; GetBudget(&localBudget, &nonLocalBudget); TotalStatistics stats; - CalculateStatistics(stats); + DetailedStatistics customHeaps[2]; + CalculateStatistics(stats, customHeaps); + JsonWriter json(GetAllocs(), sb); json.BeginObject(); - - json.WriteString(L"Total"); - json.AddDetailedStatisticsInfoObject(stats.Total); - for (size_t heapType = 0; heapType < HEAP_TYPE_COUNT; ++heapType) { - json.WriteString(HeapTypeNames[heapType]); - json.AddDetailedStatisticsInfoObject(stats.HeapType[heapType]); + json.WriteString(L"General"); + json.BeginObject(); + { + json.WriteString(L"API"); + json.WriteString(L"Direct3D 12"); + + json.WriteString(L"GPU"); + json.WriteString(m_AdapterDesc.Description); + + json.WriteString(L"DedicatedVideoMemory"); + json.WriteNumber((UINT64)m_AdapterDesc.DedicatedVideoMemory); + json.WriteString(L"DedicatedSystemMemory"); + json.WriteNumber((UINT64)m_AdapterDesc.DedicatedSystemMemory); + json.WriteString(L"SharedSystemMemory"); + json.WriteNumber((UINT64)m_AdapterDesc.SharedSystemMemory); + + json.WriteString(L"ResourceHeapTier"); + json.WriteNumber(static_cast(m_D3D12Options.ResourceHeapTier)); + + json.WriteString(L"ResourceBindingTier"); + json.WriteNumber(static_cast(m_D3D12Options.ResourceBindingTier)); + + json.WriteString(L"TiledResourcesTier"); + json.WriteNumber(static_cast(m_D3D12Options.TiledResourcesTier)); + + json.WriteString(L"TileBasedRenderer"); + json.WriteBool(m_D3D12Architecture.TileBasedRenderer); + + json.WriteString(L"UMA"); + json.WriteBool(m_D3D12Architecture.UMA); + json.WriteString(L"CacheCoherentUMA"); + json.WriteBool(m_D3D12Architecture.CacheCoherentUMA); + } + json.EndObject(); } - - json.WriteString(L"Budget"); - json.BeginObject(); { - json.WriteString(L"Local"); - WriteBudgetToJson(json, localBudget); - json.WriteString(L"NonLocal"); - WriteBudgetToJson(json, nonLocalBudget); + json.WriteString(L"Total"); + json.AddDetailedStatisticsInfoObject(stats.Total); } - json.EndObject(); - - if (DetailedMap) { - json.WriteString(L"DetailedMap"); + json.WriteString(L"MemoryInfo"); json.BeginObject(); + { + json.WriteString(L"L0"); + json.BeginObject(); + { + json.WriteString(L"Budget"); + WriteBudgetToJson(json, IsUMA() ? localBudget : nonLocalBudget); // When UMA device only L0 present as local - json.WriteString(L"DefaultPools"); - json.BeginObject(); + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[!IsUMA()]); - if (SupportsResourceHeapTier2()) - { - for (size_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType) + json.WriteString(L"MemoryPools"); + json.BeginObject(); + { + if (IsUMA()) + { + json.WriteString(L"DEFAULT"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.HeapType[0]); + } + json.EndObject(); + } + json.WriteString(L"UPLOAD"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.HeapType[1]); + } + json.EndObject(); + + json.WriteString(L"READBACK"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.HeapType[2]); + } + json.EndObject(); + + json.WriteString(L"CUSTOM"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]); + } + json.EndObject(); + } + json.EndObject(); + } + json.EndObject(); + if (!IsUMA()) { - json.WriteString(HeapTypeNames[heapType]); + json.WriteString(L"L1"); json.BeginObject(); + { + json.WriteString(L"Budget"); + WriteBudgetToJson(json, localBudget); - json.WriteString(L"Blocks"); + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[0]); - BlockVector* blockVector = m_BlockVectors[heapType]; - D3D12MA_ASSERT(blockVector); - blockVector->WriteBlockInfoToJson(json); + json.WriteString(L"MemoryPools"); + json.BeginObject(); + { + json.WriteString(L"DEFAULT"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(stats.HeapType[0]); + } + json.EndObject(); - json.EndObject(); // heap name + json.WriteString(L"CUSTOM"); + json.BeginObject(); + { + json.WriteString(L"Stats"); + json.AddDetailedStatisticsInfoObject(customHeaps[0]); + } + json.EndObject(); + } + json.EndObject(); + } + json.EndObject(); } } - else + json.EndObject(); + } + + if (detailedMap) + { + const auto writeHeapInfo = [&](BlockVector* blockVector, CommittedAllocationList* committedAllocs, bool customHeap) { - for (size_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType) - { - for (size_t heapSubType = 0; heapSubType < 3; ++heapSubType) - { - static const WCHAR* const heapSubTypeName[] = { - L" + buffer", - L" + texture", - L" + texture RT or DS", - }; - json.BeginString(); - json.ContinueString(HeapTypeNames[heapType]); - json.ContinueString(heapSubTypeName[heapSubType]); - json.EndString(); - json.BeginObject(); + D3D12MA_ASSERT(blockVector); - json.WriteString(L"Blocks"); + D3D12_HEAP_FLAGS flags = blockVector->GetHeapFlags(); + json.WriteString(L"Flags"); + json.BeginArray(true); + { + if (flags & D3D12_HEAP_FLAG_SHARED) + json.WriteString(L"HEAP_FLAG_SHARED"); + if (flags & D3D12_HEAP_FLAG_ALLOW_DISPLAY) + json.WriteString(L"HEAP_FLAG_ALLOW_DISPLAY"); + if (flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER) + json.WriteString(L"HEAP_FLAG_CROSS_ADAPTER"); + if (flags & D3D12_HEAP_FLAG_HARDWARE_PROTECTED) + json.WriteString(L"HEAP_FLAG_HARDWARE_PROTECTED"); + if (flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH) + json.WriteString(L"HEAP_FLAG_ALLOW_WRITE_WATCH"); + if (flags & D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS) + json.WriteString(L"HEAP_FLAG_ALLOW_SHADER_ATOMICS"); +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + if (flags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT) + json.WriteString(L"HEAP_FLAG_CREATE_NOT_RESIDENT"); + if (flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) + json.WriteString(L"HEAP_FLAG_CREATE_NOT_ZEROED"); +#endif - BlockVector* blockVector = m_BlockVectors[heapType * 3 + heapSubType]; - D3D12MA_ASSERT(blockVector); - blockVector->WriteBlockInfoToJson(json); + if (flags & D3D12_HEAP_FLAG_DENY_BUFFERS) + json.WriteString(L"HEAP_FLAG_DENY_BUFFERS"); + if (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES) + json.WriteString(L"HEAP_FLAG_DENY_RT_DS_TEXTURES"); + if (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES) + json.WriteString(L"HEAP_FLAG_DENY_NON_RT_DS_TEXTURES"); + + flags &= ~(D3D12_HEAP_FLAG_SHARED + | D3D12_HEAP_FLAG_DENY_BUFFERS + | D3D12_HEAP_FLAG_ALLOW_DISPLAY + | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER + | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES + | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES + | D3D12_HEAP_FLAG_HARDWARE_PROTECTED + | D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH + | D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS); +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + flags &= ~(D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT + | D3D12_HEAP_FLAG_CREATE_NOT_ZEROED); +#endif + if (flags != 0) + json.WriteNumber((UINT)flags); - json.EndObject(); // heap name + if (customHeap) + { + const D3D12_HEAP_PROPERTIES& properties = blockVector->GetHeapProperties(); + switch (properties.MemoryPoolPreference) + { + default: + D3D12MA_ASSERT(0); + case D3D12_MEMORY_POOL_UNKNOWN: + json.WriteString(L"MEMORY_POOL_UNKNOWN"); + break; + case D3D12_MEMORY_POOL_L0: + json.WriteString(L"MEMORY_POOL_L0"); + break; + case D3D12_MEMORY_POOL_L1: + json.WriteString(L"MEMORY_POOL_L1"); + break; + } + switch (properties.CPUPageProperty) + { + default: + D3D12MA_ASSERT(0); + case D3D12_CPU_PAGE_PROPERTY_UNKNOWN: + json.WriteString(L"CPU_PAGE_PROPERTY_UNKNOWN"); + break; + case D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE: + json.WriteString(L"CPU_PAGE_PROPERTY_NOT_AVAILABLE"); + break; + case D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE: + json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_COMBINE"); + break; + case D3D12_CPU_PAGE_PROPERTY_WRITE_BACK: + json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_BACK"); + break; + } } } - } + json.EndArray(); - json.EndObject(); // DefaultPools + json.WriteString(L"PreferredBlockSize"); + json.WriteNumber(blockVector->GetPreferredBlockSize()); - json.WriteString(L"CommittedAllocations"); - json.BeginObject(); + json.WriteString(L"Blocks"); + blockVector->WriteBlockInfoToJson(json); - for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex) - { - json.WriteString(HeapTypeNames[heapTypeIndex]); + json.WriteString(L"DedicatedAllocations"); json.BeginArray(); - m_CommittedAllocations[heapTypeIndex].BuildStatsString(json); + if (committedAllocs) + committedAllocs->BuildStatsString(json); json.EndArray(); - } + }; - json.EndObject(); // CommittedAllocations - - json.WriteString(L"Pools"); + json.WriteString(L"DefaultPools"); json.BeginObject(); - - for (size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex) { - json.WriteString(HeapTypeNames[heapTypeIndex]); - json.BeginArray(); - MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex); - size_t index = 0; - for (auto* item = m_Pools[heapTypeIndex].Front(); item != nullptr; item = PoolList::GetNext(item)) + if (SupportsResourceHeapTier2()) { - json.BeginObject(); - json.WriteString(L"Name"); - if (item->GetName() != nullptr) + for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType) { - json.WriteString(item->GetName()); + json.WriteString(StandardHeapTypeNames[heapType]); + json.BeginObject(); + writeHeapInfo(m_BlockVectors[heapType], m_CommittedAllocations + heapType, false); + json.EndObject(); } - else + } + else + { + for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType) { - json.BeginString(); - json.ContinueString(index); - json.EndString(); + for (uint8_t heapSubType = 0; heapSubType < 3; ++heapSubType) + { + static const WCHAR* const heapSubTypeName[] = { + L" - Buffers", + L" - Textures", + L" - Textures RT/DS", + }; + json.BeginString(StandardHeapTypeNames[heapType]); + json.EndString(heapSubTypeName[heapSubType]); + + json.BeginObject(); + writeHeapInfo(m_BlockVectors[heapType * 3 + heapSubType], m_CommittedAllocations + heapType, false); + json.EndObject(); + } } - ++index; - - json.WriteString(L"Blocks"); - item->GetBlockVector()->WriteBlockInfoToJson(json); + } + } + json.EndObject(); - json.WriteString(L"CommittedAllocations"); + json.WriteString(L"CustomPools"); + json.BeginObject(); + for (uint8_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex) + { + MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex); + auto* item = m_Pools[heapTypeIndex].Front(); + if (item != NULL) + { + size_t index = 0; + json.WriteString(HeapTypeNames[heapTypeIndex]); json.BeginArray(); - if (item->SupportsCommittedAllocations()) - item->GetCommittedAllocationList()->BuildStatsString(json); - json.EndArray(); + do + { + json.BeginObject(); + json.WriteString(L"Name"); + json.BeginString(); + json.ContinueString(index++); + if (item->GetName()) + { + json.ContinueString(L" - "); + json.ContinueString(item->GetName()); + } + json.EndString(); - json.EndObject(); + writeHeapInfo(item->GetBlockVector(), item->GetCommittedAllocationList(), heapTypeIndex == 3); + json.EndObject(); + } while ((item = PoolList::GetNext(item)) != NULL); + json.EndArray(); } - json.EndArray(); } - - json.EndObject(); // Pools - - json.EndObject(); // DetailedMap + json.EndObject(); } json.EndObject(); } const size_t length = sb.GetLength(); - WCHAR* result = AllocateArray(GetAllocs(), length + 1); - memcpy(result, sb.GetData(), length * sizeof(WCHAR)); - result[length] = L'\0'; + WCHAR* result = AllocateArray(GetAllocs(), length + 2); + result[0] = 0xFEFF; + memcpy(result + 1, sb.GetData(), length * sizeof(WCHAR)); + result[length + 1] = L'\0'; *ppStatsString = result; } @@ -7274,19 +7775,46 @@ bool AllocatorPimpl::PrefersCommittedAllocation(const D3D12_RESOURCE_DESC_T& res HRESULT AllocatorPimpl::AllocateCommittedResource( const CommittedAllocationParameters& committedAllocParams, UINT64 resourceSize, bool withinBudget, void* pPrivateData, - const D3D12_RESOURCE_DESC* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE* pOptimizedClearValue, + const CREATE_RESOURCE_PARAMS& createParams, Allocation** ppAllocation, REFIID riidResource, void** ppvResource) { D3D12MA_ASSERT(committedAllocParams.IsValid()); + HRESULT hr; + ID3D12Resource* res = NULL; + // Allocate aliasing memory with explicit heap + if (committedAllocParams.m_CanAlias) + { + D3D12_RESOURCE_ALLOCATION_INFO heapAllocInfo = {}; + heapAllocInfo.SizeInBytes = resourceSize; + heapAllocInfo.Alignment = HeapFlagsToAlignment(committedAllocParams.m_HeapFlags, m_MsaaAlwaysCommitted); + hr = AllocateHeap(committedAllocParams, heapAllocInfo, withinBudget, pPrivateData, ppAllocation); + if (SUCCEEDED(hr)) + { + hr = CreatePlacedResourceWrap((*ppAllocation)->GetHeap(), 0, + createParams, D3D12MA_IID_PPV_ARGS(&res)); + if (SUCCEEDED(hr)) + { + if (ppvResource != NULL) + hr = res->QueryInterface(riidResource, ppvResource); + if (SUCCEEDED(hr)) + { + (*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc()); + return hr; + } + res->Release(); + } + FreeHeapMemory(*ppAllocation); + } + return hr; + } + if (withinBudget && !NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, resourceSize)) { return E_OUTOFMEMORY; } - ID3D12Resource* res = NULL; /* D3D12 ERROR: * ID3D12Device::CreateCommittedResource: * When creating a committed resource, D3D12_HEAP_FLAGS must not have either @@ -7297,100 +7825,93 @@ HRESULT AllocatorPimpl::AllocateCommittedResource( * * [ STATE_CREATION ERROR #640: CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS] */ - HRESULT hr; -#ifdef __ID3D12Device4_INTERFACE_DEFINED__ - if (m_Device4) - { - hr = m_Device4->CreateCommittedResource1( - &committedAllocParams.m_HeapProperties, - committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, - pResourceDesc, InitialResourceState, - pOptimizedClearValue, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&res)); - } - else -#endif + +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ + if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_LAYOUT) { - if (committedAllocParams.m_ProtectedSession == NULL) + if (!m_Device10) { - hr = m_Device->CreateCommittedResource( + return E_NOINTERFACE; + } + hr = m_Device10->CreateCommittedResource3( &committedAllocParams.m_HeapProperties, committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, - pResourceDesc, InitialResourceState, - pOptimizedClearValue, D3D12MA_IID_PPV_ARGS(&res)); - } - else - hr = E_NOINTERFACE; - } - - if (SUCCEEDED(hr)) + createParams.GetResourceDesc1(), createParams.GetInitialLayout(), + createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession, + createParams.GetNumCastableFormats(), createParams.GetCastableFormats(), + D3D12MA_IID_PPV_ARGS(&res)); + } else +#endif +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ + if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE_AND_DESC1) { - if (ppvResource != NULL) + if (!m_Device8) { - hr = res->QueryInterface(riidResource, ppvResource); + return E_NOINTERFACE; } - if (SUCCEEDED(hr)) + hr = m_Device8->CreateCommittedResource2( + &committedAllocParams.m_HeapProperties, + committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, + createParams.GetResourceDesc1(), createParams.GetInitialResourceState(), + createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession, + D3D12MA_IID_PPV_ARGS(&res)); + } else +#endif + if (createParams.Variant == CREATE_RESOURCE_PARAMS::VARIANT_WITH_STATE) + { +#ifdef __ID3D12Device4_INTERFACE_DEFINED__ + if (m_Device4) { - const BOOL wasZeroInitialized = TRUE; - Allocation* alloc = m_AllocationObjectAllocator.Allocate(this, resourceSize, pResourceDesc->Alignment, wasZeroInitialized); - alloc->InitCommitted(committedAllocParams.m_List); - alloc->SetResource(res, pResourceDesc); - alloc->SetPrivateData(pPrivateData); - - *ppAllocation = alloc; - - committedAllocParams.m_List->Register(alloc); - - const UINT memSegmentGroup = HeapPropertiesToMemorySegmentGroup(committedAllocParams.m_HeapProperties); - m_Budget.AddBlock(memSegmentGroup, resourceSize); - m_Budget.AddAllocation(memSegmentGroup, resourceSize); + hr = m_Device4->CreateCommittedResource1( + &committedAllocParams.m_HeapProperties, + committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, + createParams.GetResourceDesc(), createParams.GetInitialResourceState(), + createParams.GetOptimizedClearValue(), committedAllocParams.m_ProtectedSession, + D3D12MA_IID_PPV_ARGS(&res)); } else +#endif { - res->Release(); + if (committedAllocParams.m_ProtectedSession == NULL) + { + hr = m_Device->CreateCommittedResource( + &committedAllocParams.m_HeapProperties, + committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, + createParams.GetResourceDesc(), createParams.GetInitialResourceState(), + createParams.GetOptimizedClearValue(), D3D12MA_IID_PPV_ARGS(&res)); + } + else + hr = E_NOINTERFACE; } } - return hr; -} - -#ifdef __ID3D12Device8_INTERFACE_DEFINED__ -HRESULT AllocatorPimpl::AllocateCommittedResource2( - const CommittedAllocationParameters& committedAllocParams, - UINT64 resourceSize, bool withinBudget, void* pPrivateData, - const D3D12_RESOURCE_DESC1* pResourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, const D3D12_CLEAR_VALUE* pOptimizedClearValue, - Allocation** ppAllocation, REFIID riidResource, void** ppvResource) -{ - D3D12MA_ASSERT(committedAllocParams.IsValid()); - - if (m_Device8 == NULL) - { - return E_NOINTERFACE; - } - - if (withinBudget && - !NewAllocationWithinBudget(committedAllocParams.m_HeapProperties.Type, resourceSize)) + else { - return E_OUTOFMEMORY; + D3D12MA_ASSERT(0); + return E_INVALIDARG; } - ID3D12Resource* res = NULL; - HRESULT hr = m_Device8->CreateCommittedResource2( - &committedAllocParams.m_HeapProperties, - committedAllocParams.m_HeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS, // D3D12 ERROR: ID3D12Device::CreateCommittedResource: When creating a committed resource, D3D12_HEAP_FLAGS must not have either D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES, D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES, nor D3D12_HEAP_FLAG_DENY_BUFFERS set. These flags will be set automatically to correspond with the committed resource type. [ STATE_CREATION ERROR #640: CREATERESOURCEANDHEAP_INVALIDHEAPMISCFLAGS] - pResourceDesc, InitialResourceState, - pOptimizedClearValue, committedAllocParams.m_ProtectedSession, D3D12MA_IID_PPV_ARGS(&res)); if (SUCCEEDED(hr)) { + SetResidencyPriority(res, committedAllocParams.m_ResidencyPriority); + if (ppvResource != NULL) { hr = res->QueryInterface(riidResource, ppvResource); } if (SUCCEEDED(hr)) { - const BOOL wasZeroInitialized = TRUE; - Allocation* alloc = m_AllocationObjectAllocator.Allocate(this, resourceSize, pResourceDesc->Alignment, wasZeroInitialized); + BOOL wasZeroInitialized = TRUE; +#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE + if((committedAllocParams.m_HeapFlags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) != 0) + { + wasZeroInitialized = FALSE; + } +#endif + + Allocation* alloc = m_AllocationObjectAllocator.Allocate( + this, resourceSize, createParams.GetBaseResourceDesc()->Alignment, wasZeroInitialized); alloc->InitCommitted(committedAllocParams.m_List); - alloc->SetResource(res, pResourceDesc); + alloc->SetResourcePointer(res, createParams.GetBaseResourceDesc()); alloc->SetPrivateData(pPrivateData); *ppAllocation = alloc; @@ -7408,7 +7929,6 @@ HRESULT AllocatorPimpl::AllocateCommittedResource2( } return hr; } -#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ HRESULT AllocatorPimpl::AllocateHeap( const CommittedAllocationParameters& committedAllocParams, @@ -7447,7 +7967,16 @@ HRESULT AllocatorPimpl::AllocateHeap( if (SUCCEEDED(hr)) { - const BOOL wasZeroInitialized = TRUE; + SetResidencyPriority(heap, committedAllocParams.m_ResidencyPriority); + + BOOL wasZeroInitialized = TRUE; +#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE + if((heapDesc.Flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED) != 0) + { + wasZeroInitialized = FALSE; + } +#endif + (*ppAllocation) = m_AllocationObjectAllocator.Allocate(this, allocInfo.SizeInBytes, allocInfo.Alignment, wasZeroInitialized); (*ppAllocation)->InitHeap(committedAllocParams.m_List, heap); (*ppAllocation)->SetPrivateData(pPrivateData); @@ -7469,16 +7998,20 @@ HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, U outCommittedAllocationParams = CommittedAllocationParameters(); outPreferCommitted = false; + bool msaaAlwaysCommitted; if (allocDesc.CustomPool != NULL) { PoolPimpl* const pool = allocDesc.CustomPool->m_Pimpl; + msaaAlwaysCommitted = pool->GetBlockVector()->DeniesMsaaTextures(); outBlockVector = pool->GetBlockVector(); - outCommittedAllocationParams.m_ProtectedSession = pool->GetDesc().pProtectedSession; - outCommittedAllocationParams.m_HeapProperties = pool->GetDesc().HeapProperties; - outCommittedAllocationParams.m_HeapFlags = pool->GetDesc().HeapFlags; + const auto& desc = pool->GetDesc(); + outCommittedAllocationParams.m_ProtectedSession = desc.pProtectedSession; + outCommittedAllocationParams.m_HeapProperties = desc.HeapProperties; + outCommittedAllocationParams.m_HeapFlags = desc.HeapFlags; outCommittedAllocationParams.m_List = pool->GetCommittedAllocationList(); + outCommittedAllocationParams.m_ResidencyPriority = pool->GetDesc().ResidencyPriority; } else { @@ -7486,10 +8019,12 @@ HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, U { return E_INVALIDARG; } + msaaAlwaysCommitted = m_MsaaAlwaysCommitted; outCommittedAllocationParams.m_HeapProperties = StandardHeapTypeToHeapProperties(allocDesc.HeapType); outCommittedAllocationParams.m_HeapFlags = allocDesc.ExtraHeapFlags; - outCommittedAllocationParams.m_List = &m_CommittedAllocations[HeapTypeToIndex(allocDesc.HeapType)]; + outCommittedAllocationParams.m_List = &m_CommittedAllocations[StandardHeapTypeToIndex(allocDesc.HeapType)]; + // outCommittedAllocationParams.m_ResidencyPriority intentionally left with default value. const ResourceClass resourceClass = (resDesc != NULL) ? ResourceDescToResourceClass(*resDesc) : HeapFlagsToResourceClass(allocDesc.ExtraHeapFlags); @@ -7521,14 +8056,18 @@ HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, U { outBlockVector = NULL; } - if ((allocDesc.Flags & (ALLOCATION_FLAG_NEVER_ALLOCATE | ALLOCATION_FLAG_CAN_ALIAS)) != 0) + if ((allocDesc.Flags & ALLOCATION_FLAG_NEVER_ALLOCATE) != 0) { outCommittedAllocationParams.m_List = NULL; } + outCommittedAllocationParams.m_CanAlias = allocDesc.Flags & ALLOCATION_FLAG_CAN_ALIAS; - if (resDesc != NULL && !outPreferCommitted && PrefersCommittedAllocation(*resDesc)) + if (resDesc != NULL) { - outPreferCommitted = true; + if (resDesc->SampleDesc.Count > 1 && msaaAlwaysCommitted) + outBlockVector = NULL; + if (!outPreferCommitted && PrefersCommittedAllocation(*resDesc)) + outPreferCommitted = true; } return (outBlockVector != NULL || outCommittedAllocationParams.m_List != NULL) ? S_OK : E_INVALIDARG; @@ -7536,7 +8075,17 @@ HRESULT AllocatorPimpl::CalcAllocationParams(const ALLOCATION_DESC& allocDesc, U UINT AllocatorPimpl::CalcDefaultPoolIndex(const ALLOCATION_DESC& allocDesc, ResourceClass resourceClass) const { - const D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS; + D3D12_HEAP_FLAGS extraHeapFlags = allocDesc.ExtraHeapFlags & ~RESOURCE_CLASS_HEAP_FLAGS; + +#if D3D12MA_CREATE_NOT_ZEROED_AVAILABLE + // If allocator was created with ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED, also ignore + // D3D12_HEAP_FLAG_CREATE_NOT_ZEROED. + if(m_DefaultPoolsNotZeroed) + { + extraHeapFlags &= ~D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; + } +#endif + if (extraHeapFlags != 0) { return UINT32_MAX; @@ -7610,7 +8159,7 @@ void AllocatorPimpl::CalcDefaultPoolParams(D3D12_HEAP_TYPE& outHeapType, D3D12_H void AllocatorPimpl::RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType) { - const UINT heapTypeIndex = HeapTypeToIndex(heapType); + const UINT heapTypeIndex = (UINT)heapType - 1; MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex); m_Pools[heapTypeIndex].PushBack(pool->m_Pimpl); @@ -7618,7 +8167,7 @@ void AllocatorPimpl::RegisterPool(Pool* pool, D3D12_HEAP_TYPE heapType) void AllocatorPimpl::UnregisterPool(Pool* pool, D3D12_HEAP_TYPE heapType) { - const UINT heapTypeIndex = HeapTypeToIndex(heapType); + const UINT heapTypeIndex = (UINT)heapType - 1; MutexLockWrite lock(m_PoolsMutex[heapTypeIndex], m_UseMutex); m_Pools[heapTypeIndex].Remove(pool->m_Pimpl); @@ -7638,7 +8187,14 @@ HRESULT AllocatorPimpl::UpdateD3D12Budget() D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(const D3D12_RESOURCE_DESC& resourceDesc) const { + // This is how new D3D12 headers define GetResourceAllocationInfo function - + // different signature depending on these macros. +#if defined(_MSC_VER) || !defined(_WIN32) return m_Device->GetResourceAllocationInfo(0, 1, &resourceDesc); +#else + D3D12_RESOURCE_ALLOCATION_INFO retVal; + return *m_Device->GetResourceAllocationInfo(&retVal, 0, 1, &resourceDesc); +#endif } #ifdef __ID3D12Device8_INTERFACE_DEFINED__ @@ -7646,13 +8202,22 @@ D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfoNative(c { D3D12MA_ASSERT(m_Device8 != NULL); D3D12_RESOURCE_ALLOCATION_INFO1 info1Unused; + + // This is how new D3D12 headers define GetResourceAllocationInfo function - + // different signature depending on these macros. +#if defined(_MSC_VER) || !defined(_WIN32) return m_Device8->GetResourceAllocationInfo2(0, 1, &resourceDesc, &info1Unused); +#else + D3D12_RESOURCE_ALLOCATION_INFO retVal; + return *m_Device8->GetResourceAllocationInfo2(&retVal, 0, 1, &resourceDesc, &info1Unused); +#endif } #endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ template D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_RESOURCE_DESC_T& inOutResourceDesc) const { +#ifdef __ID3D12Device1_INTERFACE_DEFINED__ /* Optional optimization: Microsoft documentation says: https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-getresourceallocationinfo @@ -7668,6 +8233,7 @@ D3D12_RESOURCE_ALLOCATION_INFO AllocatorPimpl::GetResourceAllocationInfo(D3D12_R AlignUp(inOutResourceDesc.Width, D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT), // SizeInBytes D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT }; // Alignment } +#endif // #ifdef __ID3D12Device1_INTERFACE_DEFINED__ #if D3D12MA_USE_SMALL_RESOURCE_PLACEMENT_ALIGNMENT if (inOutResourceDesc.Alignment == 0 && @@ -7710,21 +8276,14 @@ void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget) { json.BeginObject(); { - json.WriteString(L"BlockCount"); - json.WriteNumber(budget.Stats.BlockCount); - json.WriteString(L"AllocationCount"); - json.WriteNumber(budget.Stats.AllocationCount); - json.WriteString(L"BlockBytes"); - json.WriteNumber(budget.Stats.BlockBytes); - json.WriteString(L"AllocationBytes"); - json.WriteNumber(budget.Stats.AllocationBytes); - json.WriteString(L"UsageBytes"); - json.WriteNumber(budget.UsageBytes); json.WriteString(L"BudgetBytes"); json.WriteNumber(budget.BudgetBytes); + json.WriteString(L"UsageBytes"); + json.WriteNumber(budget.UsageBytes); } json.EndObject(); } + #endif // _D3D12MA_ALLOCATOR_PIMPL #endif // _D3D12MA_ALLOCATOR_PIMPL @@ -7789,14 +8348,14 @@ MemoryBlock::~MemoryBlock() } } -HRESULT MemoryBlock::Init(ID3D12ProtectedResourceSession* pProtectedSession) +HRESULT MemoryBlock::Init(ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures) { D3D12MA_ASSERT(m_Heap == NULL && m_Size > 0); D3D12_HEAP_DESC heapDesc = {}; heapDesc.SizeInBytes = m_Size; heapDesc.Properties = m_HeapProps; - heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags); + heapDesc.Alignment = HeapFlagsToAlignment(m_HeapFlags, denyMsaaTextures); heapDesc.Flags = m_HeapFlags; HRESULT hr; @@ -7838,6 +8397,10 @@ NormalBlock::~NormalBlock() { if (m_pMetadata != NULL) { + // Define macro D3D12MA_DEBUG_LOG to receive the list of the unfreed allocations. + if (!m_pMetadata->IsEmpty()) + m_pMetadata->DebugLogAllAllocations(); + // THIS IS THE MOST IMPORTANT ASSERT IN THE ENTIRE LIBRARY! // Hitting it means you have some memory leak - unreleased Allocation objects. D3D12MA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!"); @@ -7846,9 +8409,9 @@ NormalBlock::~NormalBlock() } } -HRESULT NormalBlock::Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession) +HRESULT NormalBlock::Init(UINT32 algorithm, ID3D12ProtectedResourceSession* pProtectedSession, bool denyMsaaTextures) { - HRESULT hr = MemoryBlock::Init(pProtectedSession); + HRESULT hr = MemoryBlock::Init(pProtectedSession, denyMsaaTextures); if (FAILED(hr)) { return hr; @@ -7970,7 +8533,9 @@ BlockVector::BlockVector( bool explicitBlockSize, UINT64 minAllocationAlignment, UINT32 algorithm, - ID3D12ProtectedResourceSession* pProtectedSession) + bool denyMsaaTextures, + ID3D12ProtectedResourceSession* pProtectedSession, + D3D12_RESIDENCY_PRIORITY residencyPriority) : m_hAllocator(hAllocator), m_HeapProps(heapProps), m_HeapFlags(heapFlags), @@ -7980,7 +8545,9 @@ BlockVector::BlockVector( m_ExplicitBlockSize(explicitBlockSize), m_MinAllocationAlignment(minAllocationAlignment), m_Algorithm(algorithm), + m_DenyMsaaTextures(denyMsaaTextures), m_ProtectedSession(pProtectedSession), + m_ResidencyPriority(residencyPriority), m_HasEmptyBlock(false), m_Blocks(hAllocator->GetAllocs()), m_NextBlockId(0) {} @@ -8117,76 +8684,19 @@ HRESULT BlockVector::CreateResource( UINT64 size, UINT64 alignment, const ALLOCATION_DESC& allocDesc, - const D3D12_RESOURCE_DESC& resourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE* pOptimizedClearValue, - Allocation** ppAllocation, - REFIID riidResource, - void** ppvResource) -{ - HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation); - if (SUCCEEDED(hr)) - { - ID3D12Resource* res = NULL; - hr = m_hAllocator->GetDevice()->CreatePlacedResource( - (*ppAllocation)->m_Placed.block->GetHeap(), - (*ppAllocation)->GetOffset(), - &resourceDesc, - InitialResourceState, - pOptimizedClearValue, - D3D12MA_IID_PPV_ARGS(&res)); - if (SUCCEEDED(hr)) - { - if (ppvResource != NULL) - { - hr = res->QueryInterface(riidResource, ppvResource); - } - if (SUCCEEDED(hr)) - { - (*ppAllocation)->SetResource(res, &resourceDesc); - } - else - { - res->Release(); - SAFE_RELEASE(*ppAllocation); - } - } - else - { - SAFE_RELEASE(*ppAllocation); - } - } - return hr; -} - -#ifdef __ID3D12Device8_INTERFACE_DEFINED__ -HRESULT BlockVector::CreateResource2( - UINT64 size, - UINT64 alignment, - const ALLOCATION_DESC& allocDesc, - const D3D12_RESOURCE_DESC1& resourceDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE* pOptimizedClearValue, + const CREATE_RESOURCE_PARAMS& createParams, Allocation** ppAllocation, REFIID riidResource, void** ppvResource) { - ID3D12Device8* const device8 = m_hAllocator->GetDevice8(); - if (device8 == NULL) - { - return E_NOINTERFACE; - } - HRESULT hr = Allocate(size, alignment, allocDesc, 1, ppAllocation); if (SUCCEEDED(hr)) { ID3D12Resource* res = NULL; - hr = device8->CreatePlacedResource1( + hr = m_hAllocator->CreatePlacedResourceWrap( (*ppAllocation)->m_Placed.block->GetHeap(), (*ppAllocation)->GetOffset(), - &resourceDesc, - InitialResourceState, - pOptimizedClearValue, + createParams, D3D12MA_IID_PPV_ARGS(&res)); if (SUCCEEDED(hr)) { @@ -8196,7 +8706,7 @@ HRESULT BlockVector::CreateResource2( } if (SUCCEEDED(hr)) { - (*ppAllocation)->SetResource(res, &resourceDesc); + (*ppAllocation)->SetResourcePointer(res, createParams.GetBaseResourceDesc()); } else { @@ -8211,7 +8721,6 @@ HRESULT BlockVector::CreateResource2( } return hr; } -#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ void BlockVector::AddStatistics(Statistics& inoutStats) { @@ -8254,7 +8763,9 @@ void BlockVector::WriteBlockInfoToJson(JsonWriter& json) json.ContinueString(pBlock->GetId()); json.EndString(); + json.BeginObject(); pBlock->m_pMetadata->WriteAllocationInfoToJson(json); + json.EndObject(); } json.EndObject(); @@ -8507,13 +9018,15 @@ HRESULT BlockVector::CreateBlock( m_HeapFlags, blockSize, m_NextBlockId++); - HRESULT hr = pBlock->Init(m_Algorithm, m_ProtectedSession); + HRESULT hr = pBlock->Init(m_Algorithm, m_ProtectedSession, m_DenyMsaaTextures); if (FAILED(hr)) { D3D12MA_DELETE(m_hAllocator->GetAllocs(), pBlock); return hr; } + m_hAllocator->SetResidencyPriority(pBlock->GetHeap(), m_ResidencyPriority); + m_Blocks.push_back(pBlock); if (pNewBlockIndex != NULL) { @@ -8843,8 +9356,8 @@ bool DefragmentationContextPimpl::IncrementCounters(UINT64 bytes) // Early return when max found if (++m_PassStats.AllocationsMoved >= m_MaxPassAllocations || m_PassStats.BytesMoved >= m_MaxPassBytes) { - D3D12MA_ASSERT(m_PassStats.AllocationsMoved == m_MaxPassAllocations || - m_PassStats.BytesMoved == m_MaxPassBytes && "Exceeded maximal pass threshold!"); + D3D12MA_ASSERT((m_PassStats.AllocationsMoved == m_MaxPassAllocations || + m_PassStats.BytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!"); return true; } return false; @@ -9174,8 +9687,10 @@ PoolPimpl::PoolPimpl(AllocatorPimpl* allocator, const POOL_DESC& desc) desc.MinBlockCount, maxBlockCount, explicitBlockSize, D3D12MA_MAX(desc.MinAllocationAlignment, (UINT64)D3D12MA_DEBUG_ALIGNMENT), - desc.Flags & POOL_FLAG_ALGORITHM_MASK, - desc.pProtectedSession); + (desc.Flags & POOL_FLAG_ALGORITHM_MASK) != 0, + (desc.Flags & POOL_FLAG_MSAA_TEXTURES_ALWAYS_COMMITTED) != 0, + desc.pProtectedSession, + desc.ResidencyPriority); } PoolPimpl::~PoolPimpl() @@ -9300,7 +9815,7 @@ ULONG STDMETHODCALLTYPE IUnknownImpl::Release() { D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - const uint32_t newRefCount = --m_RefCount; + const uint32_t newRefCount = --m_RefCount; if (newRefCount == 0) ReleaseThis(); return newRefCount; @@ -9423,7 +9938,7 @@ Allocation::Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, m_Size{ size }, m_Alignment{ alignment }, m_Resource{ NULL }, - m_CreationFrameIndex{ allocator->GetCurrentFrameIndex() }, + m_pPrivateData{ NULL }, m_Name{ NULL } { D3D12MA_ASSERT(allocator); @@ -9503,7 +10018,7 @@ NormalBlock* Allocation::GetBlock() } template -void Allocation::SetResource(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc) +void Allocation::SetResourcePointer(ID3D12Resource* resource, const D3D12_RESOURCE_DESC_T* pResourceDesc) { D3D12MA_ASSERT(m_Resource == NULL && pResourceDesc); m_Resource = resource; @@ -9573,20 +10088,20 @@ void Pool::GetStatistics(Statistics* pStats) { D3D12MA_ASSERT(pStats); D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->GetStatistics(*pStats); + m_Pimpl->GetStatistics(*pStats); } void Pool::CalculateStatistics(DetailedStatistics* pStats) { D3D12MA_ASSERT(pStats); D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->CalculateStatistics(*pStats); + m_Pimpl->CalculateStatistics(*pStats); } void Pool::SetName(LPCWSTR Name) { D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->SetName(Name); + m_Pimpl->SetName(Name); } LPCWSTR Pool::GetName() const @@ -9664,7 +10179,12 @@ HRESULT Allocator::CreateResource( return E_INVALIDARG; } D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - return m_Pimpl->CreateResource(pAllocDesc, pResourceDesc, InitialResourceState, pOptimizedClearValue, ppAllocation, riidResource, ppvResource); + return m_Pimpl->CreateResource( + pAllocDesc, + CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue), + ppAllocation, + riidResource, + ppvResource); } #ifdef __ID3D12Device8_INTERFACE_DEFINED__ @@ -9683,10 +10203,42 @@ HRESULT Allocator::CreateResource2( return E_INVALIDARG; } D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - return m_Pimpl->CreateResource2(pAllocDesc, pResourceDesc, InitialResourceState, pOptimizedClearValue, ppAllocation, riidResource, ppvResource); + return m_Pimpl->CreateResource( + pAllocDesc, + CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue), + ppAllocation, + riidResource, + ppvResource); } #endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ +HRESULT Allocator::CreateResource3( + const ALLOCATION_DESC* pAllocDesc, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_BARRIER_LAYOUT InitialLayout, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + UINT32 NumCastableFormats, + DXGI_FORMAT* pCastableFormats, + Allocation** ppAllocation, + REFIID riidResource, + void** ppvResource) +{ + if (!pAllocDesc || !pResourceDesc || !ppAllocation) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateResource3."); + return E_INVALIDARG; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + return m_Pimpl->CreateResource( + pAllocDesc, + CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats), + ppAllocation, + riidResource, + ppvResource); +} +#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__ + HRESULT Allocator::AllocateMemory( const ALLOCATION_DESC* pAllocDesc, const D3D12_RESOURCE_ALLOCATION_INFO* pAllocInfo, @@ -9716,8 +10268,65 @@ HRESULT Allocator::CreateAliasingResource( return E_INVALIDARG; } D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - return m_Pimpl->CreateAliasingResource(pAllocation, AllocationLocalOffset, pResourceDesc, InitialResourceState, pOptimizedClearValue, riidResource, ppvResource); + return m_Pimpl->CreateAliasingResource( + pAllocation, + AllocationLocalOffset, + CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue), + riidResource, + ppvResource); +} + +#ifdef __ID3D12Device8_INTERFACE_DEFINED__ +HRESULT Allocator::CreateAliasingResource1( + Allocation* pAllocation, + UINT64 AllocationLocalOffset, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + REFIID riidResource, + void** ppvResource) +{ + if (!pAllocation || !pResourceDesc || !ppvResource) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource."); + return E_INVALIDARG; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + return m_Pimpl->CreateAliasingResource( + pAllocation, + AllocationLocalOffset, + CREATE_RESOURCE_PARAMS(pResourceDesc, InitialResourceState, pOptimizedClearValue), + riidResource, + ppvResource); } +#endif // #ifdef __ID3D12Device8_INTERFACE_DEFINED__ + +#ifdef __ID3D12Device10_INTERFACE_DEFINED__ +HRESULT Allocator::CreateAliasingResource2( + Allocation* pAllocation, + UINT64 AllocationLocalOffset, + const D3D12_RESOURCE_DESC1* pResourceDesc, + D3D12_BARRIER_LAYOUT InitialLayout, + const D3D12_CLEAR_VALUE* pOptimizedClearValue, + UINT32 NumCastableFormats, + DXGI_FORMAT* pCastableFormats, + REFIID riidResource, + void** ppvResource) +{ + if (!pAllocation || !pResourceDesc || !ppvResource) + { + D3D12MA_ASSERT(0 && "Invalid arguments passed to Allocator::CreateAliasingResource."); + return E_INVALIDARG; + } + D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK + return m_Pimpl->CreateAliasingResource( + pAllocation, + AllocationLocalOffset, + CREATE_RESOURCE_PARAMS(pResourceDesc, InitialLayout, pOptimizedClearValue, NumCastableFormats, pCastableFormats), + riidResource, + ppvResource); +} +#endif // #ifdef __ID3D12Device10_INTERFACE_DEFINED__ HRESULT Allocator::CreatePool( const POOL_DESC* pPoolDesc, @@ -9736,7 +10345,7 @@ HRESULT Allocator::CreatePool( return E_INVALIDARG; } D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - * ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc); + * ppPool = D3D12MA_NEW(m_Pimpl->GetAllocs(), Pool)(this, *pPoolDesc); HRESULT hr = (*ppPool)->m_Pimpl->Init(); if (SUCCEEDED(hr)) { @@ -9753,7 +10362,7 @@ HRESULT Allocator::CreatePool( void Allocator::SetCurrentFrameIndex(UINT frameIndex) { D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->SetCurrentFrameIndex(frameIndex); + m_Pimpl->SetCurrentFrameIndex(frameIndex); } void Allocator::GetBudget(Budget* pLocalBudget, Budget* pNonLocalBudget) @@ -9763,21 +10372,21 @@ void Allocator::GetBudget(Budget* pLocalBudget, Budget* pNonLocalBudget) return; } D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->GetBudget(pLocalBudget, pNonLocalBudget); + m_Pimpl->GetBudget(pLocalBudget, pNonLocalBudget); } void Allocator::CalculateStatistics(TotalStatistics* pStats) { D3D12MA_ASSERT(pStats); D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->CalculateStatistics(*pStats); + m_Pimpl->CalculateStatistics(*pStats); } void Allocator::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const { D3D12MA_ASSERT(ppStatsString); D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->BuildStatsString(ppStatsString, DetailedMap); + m_Pimpl->BuildStatsString(ppStatsString, DetailedMap); } void Allocator::FreeStatsString(WCHAR* pStatsString) const @@ -9785,7 +10394,7 @@ void Allocator::FreeStatsString(WCHAR* pStatsString) const if (pStatsString != NULL) { D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->FreeStatsString(pStatsString); + m_Pimpl->FreeStatsString(pStatsString); } } @@ -9816,8 +10425,7 @@ Allocator::~Allocator() BOOL VirtualBlock::IsEmpty() const { D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - - return m_Pimpl->m_Metadata->IsEmpty() ? TRUE : FALSE; + return m_Pimpl->m_Metadata->IsEmpty() ? TRUE : FALSE; } void VirtualBlock::GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOCATION_INFO* pInfo) const @@ -9825,8 +10433,7 @@ void VirtualBlock::GetAllocationInfo(VirtualAllocation allocation, VIRTUAL_ALLOC D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0 && pInfo); D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - - m_Pimpl->m_Metadata->GetAllocationInfo(allocation.AllocHandle, *pInfo); + m_Pimpl->m_Metadata->GetAllocationInfo(allocation.AllocHandle, *pInfo); } HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, VirtualAllocation* pAllocation, UINT64* pOffset) @@ -9839,7 +10446,7 @@ HRESULT VirtualBlock::Allocate(const VIRTUAL_ALLOCATION_DESC* pDesc, VirtualAllo D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - const UINT64 alignment = pDesc->Alignment != 0 ? pDesc->Alignment : 1; + const UINT64 alignment = pDesc->Alignment != 0 ? pDesc->Alignment : 1; AllocationRequest allocRequest = {}; if (m_Pimpl->m_Metadata->CreateAllocationRequest( pDesc->Size, @@ -9871,7 +10478,7 @@ void VirtualBlock::FreeAllocation(VirtualAllocation allocation) D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->m_Metadata->Free(allocation.AllocHandle); + m_Pimpl->m_Metadata->Free(allocation.AllocHandle); D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); } @@ -9879,7 +10486,7 @@ void VirtualBlock::Clear() { D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - m_Pimpl->m_Metadata->Clear(); + m_Pimpl->m_Metadata->Clear(); D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); } @@ -9888,15 +10495,14 @@ void VirtualBlock::SetAllocationPrivateData(VirtualAllocation allocation, void* D3D12MA_ASSERT(allocation.AllocHandle != (AllocHandle)0); D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - - m_Pimpl->m_Metadata->SetAllocationPrivateData(allocation.AllocHandle, pPrivateData); + m_Pimpl->m_Metadata->SetAllocationPrivateData(allocation.AllocHandle, pPrivateData); } void VirtualBlock::GetStatistics(Statistics* pStats) const { D3D12MA_ASSERT(pStats); D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); + D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); ClearStatistics(*pStats); m_Pimpl->m_Metadata->AddStatistics(*pStats); } @@ -9905,7 +10511,7 @@ void VirtualBlock::CalculateStatistics(DetailedStatistics* pStats) const { D3D12MA_ASSERT(pStats); D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); + D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); ClearDetailedStatistics(*pStats); m_Pimpl->m_Metadata->AddDetailedStatistics(*pStats); } @@ -9916,11 +10522,13 @@ void VirtualBlock::BuildStatsString(WCHAR** ppStatsString) const D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - StringBuilder sb(m_Pimpl->m_AllocationCallbacks); + StringBuilder sb(m_Pimpl->m_AllocationCallbacks); { JsonWriter json(m_Pimpl->m_AllocationCallbacks, sb); D3D12MA_HEAVY_ASSERT(m_Pimpl->m_Metadata->Validate()); + json.BeginObject(); m_Pimpl->m_Metadata->WriteAllocationInfoToJson(json); + json.EndObject(); } // Scope for JsonWriter const size_t length = sb.GetLength(); @@ -9935,7 +10543,7 @@ void VirtualBlock::FreeStatsString(WCHAR* pStatsString) const if (pStatsString != NULL) { D3D12MA_DEBUG_GLOBAL_MUTEX_LOCK - D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString); + D3D12MA::Free(m_Pimpl->m_AllocationCallbacks, pStatsString); } } diff --git a/3rdparty/d3d12memalloc/src/D3D12Sample.cpp b/3rdparty/d3d12memalloc/src/D3D12Sample.cpp new file mode 100644 index 00000000000000..083a647c3c62c0 --- /dev/null +++ b/3rdparty/d3d12memalloc/src/D3D12Sample.cpp @@ -0,0 +1,1825 @@ +// +// Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#include "Common.h" +#include "D3D12MemAlloc.h" +#include "Tests.h" +#include +#include // For StrStrI + +namespace VS +{ + #include "Shaders\VS_Compiled.h" +} +namespace PS +{ + #include "Shaders\PS_Compiled.h" +} + +#if D3D12MA_USE_AGILITY_SDK + #if D3D12MA_USE_AGILITY_SDK_PREVIEW + extern "C" { __declspec(dllexport) extern const UINT D3D12SDKVersion = D3D12_PREVIEW_SDK_VERSION; } + #else + extern "C" { __declspec(dllexport) extern const UINT D3D12SDKVersion = D3D12_SDK_VERSION; } + #endif + extern "C" { __declspec(dllexport) extern const char* D3D12SDKPath = u8".\\D3D12\\"; } +#endif + +enum class ExitCode : int +{ + GPUList = 2, + Help = 1, + Success = 0, + RuntimeError = -1, + CommandLineError = -2, +}; + +static const wchar_t * const CLASS_NAME = L"D3D12MemAllocSample"; +static const wchar_t * const WINDOW_TITLE = L"D3D12 Memory Allocator Sample"; +static const int SIZE_X = 1024; +static const int SIZE_Y = 576; +static const bool FULLSCREEN = false; +static const UINT PRESENT_SYNC_INTERVAL = 1; +static const DXGI_FORMAT RENDER_TARGET_FORMAT = DXGI_FORMAT_R8G8B8A8_UNORM; +static const DXGI_FORMAT DEPTH_STENCIL_FORMAT = DXGI_FORMAT_D32_FLOAT; +static const size_t FRAME_BUFFER_COUNT = 3; // number of buffers we want, 2 for double buffering, 3 for tripple buffering +static const D3D_FEATURE_LEVEL MY_D3D_FEATURE_LEVEL = D3D_FEATURE_LEVEL_12_0; + +static const bool ENABLE_DEBUG_LAYER = true; +static const bool ENABLE_CPU_ALLOCATION_CALLBACKS = true; +static const bool ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT = false; +static constexpr D3D12MA::ALLOCATOR_FLAGS g_AllocatorFlags = D3D12MA::ALLOCATOR_FLAG_DEFAULT_POOLS_NOT_ZEROED; +static D3D12MA::ALLOCATION_CALLBACKS g_AllocationCallbacks = {}; // Used only when ENABLE_CPU_ALLOCATION_CALLBACKS + +static HINSTANCE g_Instance; +static HWND g_Wnd; + +struct GPUSelection +{ + UINT32 Index = UINT32_MAX; + std::wstring Substring; +}; + +class DXGIUsage +{ +public: + void Init(); + IDXGIFactory4* GetDXGIFactory() const { return m_DXGIFactory.Get(); } + void PrintAdapterList() const; + // If failed, returns null pointer. + ComPtr CreateAdapter(const GPUSelection& GPUSelection) const; + +private: + ComPtr m_DXGIFactory; +}; + +static UINT64 g_TimeOffset; // In ms. +static UINT64 g_TimeValue; // Time since g_TimeOffset, in ms. +static float g_Time; // g_TimeValue converted to float, in seconds. +static float g_TimeDelta; + +static DXGIUsage* g_DXGIUsage; +static ComPtr g_Device; +DXGI_ADAPTER_DESC1 g_AdapterDesc; +static ComPtr g_Allocator; + +static ComPtr g_SwapChain; // swapchain used to switch between render targets +static ComPtr g_CommandQueue; // container for command lists +static ComPtr g_RtvDescriptorHeap; // a descriptor heap to hold resources like the render targets +static ComPtr g_RenderTargets[FRAME_BUFFER_COUNT]; // number of render targets equal to buffer count +static ComPtr g_CommandAllocators[FRAME_BUFFER_COUNT]; // we want enough allocators for each buffer * number of threads (we only have one thread) +static ComPtr g_CommandList; // a command list we can record commands into, then execute them to render the frame +static ComPtr g_Fences[FRAME_BUFFER_COUNT]; // an object that is locked while our command list is being executed by the gpu. We need as many + //as we have allocators (more if we want to know when the gpu is finished with an asset) +static HANDLE g_FenceEvent; // a handle to an event when our g_Fences is unlocked by the gpu +static UINT64 g_FenceValues[FRAME_BUFFER_COUNT]; // this value is incremented each frame. each g_Fences will have its own value +static UINT g_FrameIndex; // current rtv we are on +static UINT g_RtvDescriptorSize; // size of the rtv descriptor on the g_Device (all front and back buffers will be the same size) + +static ComPtr g_PipelineStateObject; +static ComPtr g_RootSignature; +static ComPtr g_VertexBuffer; +static D3D12MA::Allocation* g_VertexBufferAllocation; +static ComPtr g_IndexBuffer; +static D3D12MA::Allocation* g_IndexBufferAllocation; +static D3D12_VERTEX_BUFFER_VIEW g_VertexBufferView; +static D3D12_INDEX_BUFFER_VIEW g_IndexBufferView; +static ComPtr g_DepthStencilBuffer; +static D3D12MA::Allocation* g_DepthStencilAllocation; +static ComPtr g_DepthStencilDescriptorHeap; + +struct Vertex { + vec3 pos; + vec2 texCoord; + + Vertex() { } + Vertex(float x, float y, float z, float tx, float ty) : + pos(x, y, z), + texCoord(tx, ty) + { + } +}; + +struct ConstantBuffer0_PS +{ + vec4 Color; +}; +struct ConstantBuffer1_VS +{ + mat4 WorldViewProj; +}; + +static const size_t ConstantBufferPerObjectAlignedSize = AlignUp(sizeof(ConstantBuffer1_VS), 256); +static D3D12MA::Allocation* g_CbPerObjectUploadHeapAllocations[FRAME_BUFFER_COUNT]; +static ComPtr g_CbPerObjectUploadHeaps[FRAME_BUFFER_COUNT]; +static void* g_CbPerObjectAddress[FRAME_BUFFER_COUNT]; +static uint32_t g_CubeIndexCount; + +static ComPtr g_MainDescriptorHeap[FRAME_BUFFER_COUNT]; +static ComPtr g_ConstantBufferUploadHeap[FRAME_BUFFER_COUNT]; +static D3D12MA::Allocation* g_ConstantBufferUploadAllocation[FRAME_BUFFER_COUNT]; +static void* g_ConstantBufferAddress[FRAME_BUFFER_COUNT]; + +static ComPtr g_Texture; +static D3D12MA::Allocation* g_TextureAllocation; + +static void* const CUSTOM_ALLOCATION_PRIVATE_DATA = (void*)(uintptr_t)0xDEADC0DE; + +static std::atomic g_CpuAllocationCount{0}; + +static void* CustomAllocate(size_t Size, size_t Alignment, void* pPrivateData) +{ + assert(pPrivateData == CUSTOM_ALLOCATION_PRIVATE_DATA); + void* memory = _aligned_malloc(Size, Alignment); + if(ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT) + { + wprintf(L"Allocate Size=%llu Alignment=%llu -> %p\n", Size, Alignment, memory); + } + ++g_CpuAllocationCount; + return memory; +} + +static void CustomFree(void* pMemory, void* pPrivateData) +{ + assert(pPrivateData == CUSTOM_ALLOCATION_PRIVATE_DATA); + if(pMemory) + { + --g_CpuAllocationCount; + if(ENABLE_CPU_ALLOCATION_CALLBACKS_PRINT) + { + wprintf(L"Free %p\n", pMemory); + } + _aligned_free(pMemory); + } +} + +struct CommandLineParameters +{ + bool m_Help = false; + bool m_List = false; + bool m_Test = false; + GPUSelection m_GPUSelection; + + bool Parse(int argc, wchar_t** argv) + { + for(int i = 1; i < argc; ++i) + { + if(_wcsicmp(argv[i], L"-h") == 0 || _wcsicmp(argv[i], L"--Help") == 0) + { + m_Help = true; + } + else if(_wcsicmp(argv[i], L"-l") == 0 || _wcsicmp(argv[i], L"--List") == 0) + { + m_List = true; + } + else if((_wcsicmp(argv[i], L"-g") == 0 || _wcsicmp(argv[i], L"--GPU") == 0) && i + 1 < argc) + { + m_GPUSelection.Substring = argv[i + 1]; + ++i; + } + else if((_wcsicmp(argv[i], L"-i") == 0 || _wcsicmp(argv[i], L"--GPUIndex") == 0) && i + 1 < argc) + { + m_GPUSelection.Index = _wtoi(argv[i + 1]); + ++i; + } + else if (_wcsicmp(argv[i], L"-t") == 0 || _wcsicmp(argv[i], L"--Test") == 0) + { + m_Test = true; + } + else + return false; + } + return true; + } +} g_CommandLineParameters; + +static void SetDefaultRasterizerDesc(D3D12_RASTERIZER_DESC& outDesc) +{ + outDesc.FillMode = D3D12_FILL_MODE_SOLID; + outDesc.CullMode = D3D12_CULL_MODE_BACK; + outDesc.FrontCounterClockwise = FALSE; + outDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + outDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + outDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + outDesc.DepthClipEnable = TRUE; + outDesc.MultisampleEnable = FALSE; + outDesc.AntialiasedLineEnable = FALSE; + outDesc.ForcedSampleCount = 0; + outDesc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; +} + +static void SetDefaultBlendDesc(D3D12_BLEND_DESC& outDesc) +{ + outDesc.AlphaToCoverageEnable = FALSE; + outDesc.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + FALSE,FALSE, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + outDesc.RenderTarget[i] = defaultRenderTargetBlendDesc; +} + +static void SetDefaultDepthStencilDesc(D3D12_DEPTH_STENCIL_DESC& outDesc) +{ + outDesc.DepthEnable = TRUE; + outDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + outDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS; + outDesc.StencilEnable = FALSE; + outDesc.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + outDesc.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = { + D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS }; + outDesc.FrontFace = defaultStencilOp; + outDesc.BackFace = defaultStencilOp; +} + +void WaitForFrame(size_t frameIndex) // wait until gpu is finished with command list +{ + // if the current g_Fences value is still less than "g_FenceValues", then we know the GPU has not finished executing + // the command queue since it has not reached the "g_CommandQueue->Signal(g_Fences, g_FenceValues)" command + if (g_Fences[frameIndex]->GetCompletedValue() < g_FenceValues[frameIndex]) + { + // we have the g_Fences create an event which is signaled once the g_Fences's current value is "g_FenceValues" + CHECK_HR( g_Fences[frameIndex]->SetEventOnCompletion(g_FenceValues[frameIndex], g_FenceEvent) ); + + // We will wait until the g_Fences has triggered the event that it's current value has reached "g_FenceValues". once it's value + // has reached "g_FenceValues", we know the command queue has finished executing + WaitForSingleObject(g_FenceEvent, INFINITE); + } +} + +void WaitGPUIdle(size_t frameIndex) +{ + g_FenceValues[frameIndex]++; + CHECK_HR( g_CommandQueue->Signal(g_Fences[frameIndex].Get(), g_FenceValues[frameIndex]) ); + WaitForFrame(frameIndex); +} + +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* +// Row-by-row memcpy +inline void MemcpySubresource( + _In_ const D3D12_MEMCPY_DEST* pDest, + _In_ const D3D12_SUBRESOURCE_DATA* pSrc, + SIZE_T RowSizeInBytes, + UINT NumRows, + UINT NumSlices) +{ + for (UINT z = 0; z < NumSlices; ++z) + { + BYTE* pDestSlice = reinterpret_cast(pDest->pData) + pDest->SlicePitch * z; + const BYTE* pSrcSlice = reinterpret_cast(pSrc->pData) + pSrc->SlicePitch * z; + for (UINT y = 0; y < NumRows; ++y) + { + memcpy(pDestSlice + pDest->RowPitch * y, + pSrcSlice + pSrc->RowPitch * y, + RowSizeInBytes); + } + } +} + +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, + UINT64 RequiredSize, + _In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts, + _In_reads_(NumSubresources) const UINT* pNumRows, + _In_reads_(NumSubresources) const UINT64* pRowSizesInBytes, + _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) +{ + // Minor validation + D3D12_RESOURCE_DESC IntermediateDesc = pIntermediate->GetDesc(); + D3D12_RESOURCE_DESC DestinationDesc = pDestinationResource->GetDesc(); + if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER || + IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset || + RequiredSize > (SIZE_T)-1 || + (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && + (FirstSubresource != 0 || NumSubresources != 1))) + { + return 0; + } + + BYTE* pData; + HRESULT hr = pIntermediate->Map(0, &EMPTY_RANGE, reinterpret_cast(&pData)); + if (FAILED(hr)) + { + return 0; + } + + for (UINT i = 0; i < NumSubresources; ++i) + { + if (pRowSizesInBytes[i] > (SIZE_T)-1) return 0; + D3D12_MEMCPY_DEST DestData = { pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, pLayouts[i].Footprint.RowPitch * pNumRows[i] }; + MemcpySubresource(&DestData, &pSrcData[i], (SIZE_T)pRowSizesInBytes[i], pNumRows[i], pLayouts[i].Footprint.Depth); + } + pIntermediate->Unmap(0, NULL); + + if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + { + D3D12_BOX SrcBox = { + UINT( pLayouts[0].Offset ), 0, 0, + UINT( pLayouts[0].Offset + pLayouts[0].Footprint.Width ), 0, 0 }; + pCmdList->CopyBufferRegion( + pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width); + } + else + { + for (UINT i = 0; i < NumSubresources; ++i) + { + D3D12_TEXTURE_COPY_LOCATION Dst = {}; + Dst.pResource = pDestinationResource; + Dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + Dst.SubresourceIndex = i + FirstSubresource; + D3D12_TEXTURE_COPY_LOCATION Src = {}; + Src.pResource = pIntermediate; + Src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + Src.PlacedFootprint = pLayouts[i]; + pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr); + } + } + return RequiredSize; +} + +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + UINT64 IntermediateOffset, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, + _In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData) +{ + UINT64 RequiredSize = 0; + UINT64 MemToAlloc = static_cast(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources; + if (MemToAlloc > SIZE_MAX) + { + return 0; + } + void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast(MemToAlloc)); + if (pMem == NULL) + { + return 0; + } + D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts = reinterpret_cast(pMem); + UINT64* pRowSizesInBytes = reinterpret_cast(pLayouts + NumSubresources); + UINT* pNumRows = reinterpret_cast(pRowSizesInBytes + NumSubresources); + + D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc(); + ID3D12Device* pDevice; + pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize); + pDevice->Release(); + + UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pSrcData); + HeapFree(GetProcessHeap(), 0, pMem); + return Result; +} + +void DXGIUsage::Init() +{ + g_Instance = (HINSTANCE)GetModuleHandle(NULL); + + CoInitialize(NULL); + + CHECK_HR( CreateDXGIFactory1(IID_PPV_ARGS(&m_DXGIFactory)) ); +} + +void DXGIUsage::PrintAdapterList() const +{ + UINT index = 0; + ComPtr adapter; + while (m_DXGIFactory->EnumAdapters1(index, &adapter) != DXGI_ERROR_NOT_FOUND) + { + DXGI_ADAPTER_DESC1 desc; + adapter->GetDesc1(&desc); + + const bool isSoftware = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0; + const wchar_t* const suffix = isSoftware ? L" (SOFTWARE)" : L""; + wprintf(L"Adapter %u: %s%s\n", index, desc.Description, suffix); + + adapter.Reset(); + ++index; + } +} + +ComPtr DXGIUsage::CreateAdapter(const GPUSelection& GPUSelection) const +{ + ComPtr adapter; + + if(GPUSelection.Index != UINT32_MAX) + { + // Cannot specify both index and name. + if(!GPUSelection.Substring.empty()) + { + return adapter; + } + + CHECK_HR(m_DXGIFactory->EnumAdapters1(GPUSelection.Index, &adapter)); + return adapter; + } + + if(!GPUSelection.Substring.empty()) + { + ComPtr tmpAdapter; + for(UINT i = 0; m_DXGIFactory->EnumAdapters1(i, &tmpAdapter) != DXGI_ERROR_NOT_FOUND; ++i) + { + DXGI_ADAPTER_DESC1 desc; + tmpAdapter->GetDesc1(&desc); + if(StrStrI(desc.Description, GPUSelection.Substring.c_str())) + { + // Second matching adapter found - error. + if(adapter) + { + adapter.Reset(); + return adapter; + } + // First matching adapter found. + adapter = std::move(tmpAdapter); + } + else + { + tmpAdapter.Reset(); + } + } + // Found or not, return it. + return adapter; + } + + // Select first one. + m_DXGIFactory->EnumAdapters1(0, &adapter); + return adapter; +} + +static const wchar_t* VendorIDToStr(uint32_t vendorID) +{ + switch(vendorID) + { + case 0x10001: return L"VIV"; + case 0x10002: return L"VSI"; + case 0x10003: return L"KAZAN"; + case 0x10004: return L"CODEPLAY"; + case 0x10005: return L"MESA"; + case 0x10006: return L"POCL"; + case VENDOR_ID_AMD: return L"AMD"; + case VENDOR_ID_NVIDIA: return L"NVIDIA"; + case VENDOR_ID_INTEL: return L"Intel"; + case 0x1010: return L"ImgTec"; + case 0x13B5: return L"ARM"; + case 0x5143: return L"Qualcomm"; + } + return L""; +} + +static std::wstring SizeToStr(size_t size) +{ + if(size == 0) + return L"0"; + wchar_t result[32]; + double size2 = (double)size; + if (size2 >= 1024.0*1024.0*1024.0*1024.0) + { + swprintf_s(result, L"%.2f TB", size2 / (1024.0*1024.0*1024.0*1024.0)); + } + else if (size2 >= 1024.0*1024.0*1024.0) + { + swprintf_s(result, L"%.2f GB", size2 / (1024.0*1024.0*1024.0)); + } + else if (size2 >= 1024.0*1024.0) + { + swprintf_s(result, L"%.2f MB", size2 / (1024.0*1024.0)); + } + else if (size2 >= 1024.0) + { + swprintf_s(result, L"%.2f KB", size2 / 1024.0); + } + else + swprintf_s(result, L"%llu B", size); + return result; +} + +static void PrintAdapterInformation(IDXGIAdapter1* adapter) +{ + wprintf(L"DXGI_ADAPTER_DESC1:\n"); + wprintf(L" Description = %s\n", g_AdapterDesc.Description); + wprintf(L" VendorId = 0x%X (%s)\n", g_AdapterDesc.VendorId, VendorIDToStr(g_AdapterDesc.VendorId)); + wprintf(L" DeviceId = 0x%X\n", g_AdapterDesc.DeviceId); + wprintf(L" SubSysId = 0x%X\n", g_AdapterDesc.SubSysId); + wprintf(L" Revision = 0x%X\n", g_AdapterDesc.Revision); + wprintf(L" DedicatedVideoMemory = %zu B (%s)\n", g_AdapterDesc.DedicatedVideoMemory, SizeToStr(g_AdapterDesc.DedicatedVideoMemory).c_str()); + wprintf(L" DedicatedSystemMemory = %zu B (%s)\n", g_AdapterDesc.DedicatedSystemMemory, SizeToStr(g_AdapterDesc.DedicatedSystemMemory).c_str()); + wprintf(L" SharedSystemMemory = %zu B (%s)\n", g_AdapterDesc.SharedSystemMemory, SizeToStr(g_AdapterDesc.SharedSystemMemory).c_str()); + + const D3D12_FEATURE_DATA_D3D12_OPTIONS& options = g_Allocator->GetD3D12Options(); + wprintf(L"D3D12_FEATURE_DATA_D3D12_OPTIONS:\n"); + wprintf(L" StandardSwizzle64KBSupported = %u\n", options.StandardSwizzle64KBSupported ? 1 : 0); + wprintf(L" CrossAdapterRowMajorTextureSupported = %u\n", options.CrossAdapterRowMajorTextureSupported ? 1 : 0); + switch(options.ResourceHeapTier) + { + case D3D12_RESOURCE_HEAP_TIER_1: + wprintf(L" ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_1\n"); + break; + case D3D12_RESOURCE_HEAP_TIER_2: + wprintf(L" ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_2\n"); + break; + default: + assert(0); + } + + ComPtr adapter3; + if(SUCCEEDED(adapter->QueryInterface(IID_PPV_ARGS(&adapter3)))) + { + wprintf(L"DXGI_QUERY_VIDEO_MEMORY_INFO:\n"); + for(UINT groupIndex = 0; groupIndex < 2; ++groupIndex) + { + const DXGI_MEMORY_SEGMENT_GROUP group = groupIndex == 0 ? DXGI_MEMORY_SEGMENT_GROUP_LOCAL : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL; + const wchar_t* const groupName = groupIndex == 0 ? L"DXGI_MEMORY_SEGMENT_GROUP_LOCAL" : L"DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL"; + DXGI_QUERY_VIDEO_MEMORY_INFO info = {}; + CHECK_HR(adapter3->QueryVideoMemoryInfo(0, group, &info)); + wprintf(L" %s:\n", groupName); + wprintf(L" Budget = %llu B (%s)\n", info.Budget, SizeToStr(info.Budget).c_str()); + wprintf(L" CurrentUsage = %llu B (%s)\n", info.CurrentUsage, SizeToStr(info.CurrentUsage).c_str()); + wprintf(L" AvailableForReservation = %llu B (%s)\n", info.AvailableForReservation, SizeToStr(info.AvailableForReservation).c_str()); + wprintf(L" CurrentReservation = %llu B (%s)\n", info.CurrentReservation, SizeToStr(info.CurrentReservation).c_str()); + } + } + + assert(g_Device); + D3D12_FEATURE_DATA_ARCHITECTURE1 architecture1 = {}; + if(SUCCEEDED(g_Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE1, &architecture1, sizeof architecture1))) + { + wprintf(L"D3D12_FEATURE_DATA_ARCHITECTURE1:\n"); + wprintf(L" UMA: %u\n", architecture1.UMA ? 1 : 0); + wprintf(L" CacheCoherentUMA: %u\n", architecture1.CacheCoherentUMA ? 1 : 0); + wprintf(L" IsolatedMMU: %u\n", architecture1.IsolatedMMU ? 1 : 0); + } +} + +static void InitD3D() // initializes direct3d 12 +{ + assert(g_DXGIUsage); + + ComPtr adapter = g_DXGIUsage->CreateAdapter(g_CommandLineParameters.m_GPUSelection); + CHECK_BOOL(adapter); + + CHECK_HR(adapter->GetDesc1(&g_AdapterDesc)); + + // Must be done before D3D12 device is created. + if(ENABLE_DEBUG_LAYER) + { + ComPtr debug; + if(SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(&debug)))) + debug->EnableDebugLayer(); + } + + // Create the g_Device + ID3D12Device* device = nullptr; + CHECK_HR( D3D12CreateDevice( + adapter.Get(), + MY_D3D_FEATURE_LEVEL, + IID_PPV_ARGS(&device)) ); + g_Device.Attach(device); + + // Create allocator + + { + D3D12MA::ALLOCATOR_DESC desc = {}; + desc.Flags = g_AllocatorFlags; + desc.pDevice = device; + desc.pAdapter = adapter.Get(); + + if(ENABLE_CPU_ALLOCATION_CALLBACKS) + { + g_AllocationCallbacks.pAllocate = &CustomAllocate; + g_AllocationCallbacks.pFree = &CustomFree; + g_AllocationCallbacks.pPrivateData = CUSTOM_ALLOCATION_PRIVATE_DATA; + desc.pAllocationCallbacks = &g_AllocationCallbacks; + } + + CHECK_HR( D3D12MA::CreateAllocator(&desc, &g_Allocator) ); + } + + PrintAdapterInformation(adapter.Get()); + wprintf(L"\n"); + + // -- Create the Command Queue -- // + + D3D12_COMMAND_QUEUE_DESC cqDesc = {}; // we will be using all the default values + + ID3D12CommandQueue* commandQueue = nullptr; + CHECK_HR( g_Device->CreateCommandQueue(&cqDesc, IID_PPV_ARGS(&commandQueue)) ); // create the command queue + g_CommandQueue.Attach(commandQueue); + + // -- Create the Swap Chain (double/tripple buffering) -- // + + DXGI_MODE_DESC backBufferDesc = {}; // this is to describe our display mode + backBufferDesc.Width = SIZE_X; // buffer width + backBufferDesc.Height = SIZE_Y; // buffer height + backBufferDesc.Format = RENDER_TARGET_FORMAT; // format of the buffer (rgba 32 bits, 8 bits for each chanel) + + // describe our multi-sampling. We are not multi-sampling, so we set the count to 1 (we need at least one sample of course) + DXGI_SAMPLE_DESC sampleDesc = {}; + sampleDesc.Count = 1; // multisample count (no multisampling, so we just put 1, since we still need 1 sample) + + // Describe and create the swap chain. + DXGI_SWAP_CHAIN_DESC swapChainDesc = {}; + swapChainDesc.BufferCount = FRAME_BUFFER_COUNT; // number of buffers we have + swapChainDesc.BufferDesc = backBufferDesc; // our back buffer description + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // this says the pipeline will render to this swap chain + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; // dxgi will discard the buffer (data) after we call present + swapChainDesc.OutputWindow = g_Wnd; // handle to our window + swapChainDesc.SampleDesc = sampleDesc; // our multi-sampling description + swapChainDesc.Windowed = !FULLSCREEN; // set to true, then if in fullscreen must call SetFullScreenState with true for full screen to get uncapped fps + + IDXGISwapChain* tempSwapChain; + + CHECK_HR( g_DXGIUsage->GetDXGIFactory()->CreateSwapChain( + g_CommandQueue.Get(), // the queue will be flushed once the swap chain is created + &swapChainDesc, // give it the swap chain description we created above + &tempSwapChain // store the created swap chain in a temp IDXGISwapChain interface + ) ); + + g_SwapChain.Attach(static_cast(tempSwapChain)); + + g_FrameIndex = g_SwapChain->GetCurrentBackBufferIndex(); + + // -- Create the Back Buffers (render target views) Descriptor Heap -- // + + // describe an rtv descriptor heap and create + D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {}; + rtvHeapDesc.NumDescriptors = FRAME_BUFFER_COUNT; // number of descriptors for this heap. + rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; // this heap is a render target view heap + + // This heap will not be directly referenced by the shaders (not shader visible), as this will store the output from the pipeline + // otherwise we would set the heap's flag to D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE + rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + ID3D12DescriptorHeap* rtvDescriptorHeap = nullptr; + CHECK_HR( g_Device->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvDescriptorHeap)) ); + g_RtvDescriptorHeap.Attach(rtvDescriptorHeap); + + // get the size of a descriptor in this heap (this is a rtv heap, so only rtv descriptors should be stored in it. + // descriptor sizes may vary from g_Device to g_Device, which is why there is no set size and we must ask the + // g_Device to give us the size. we will use this size to increment a descriptor handle offset + g_RtvDescriptorSize = g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + // get a handle to the first descriptor in the descriptor heap. a handle is basically a pointer, + // but we cannot literally use it like a c++ pointer. + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle { g_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart() }; + + // Create a RTV for each buffer (double buffering is two buffers, tripple buffering is 3). + for (int i = 0; i < FRAME_BUFFER_COUNT; i++) + { + // first we get the n'th buffer in the swap chain and store it in the n'th + // position of our ID3D12Resource array + ID3D12Resource* res = nullptr; + CHECK_HR( g_SwapChain->GetBuffer(i, IID_PPV_ARGS(&res)) ); + g_RenderTargets[i].Attach(res); + + // the we "create" a render target view which binds the swap chain buffer (ID3D12Resource[n]) to the rtv handle + g_Device->CreateRenderTargetView(g_RenderTargets[i].Get(), nullptr, rtvHandle); + + // we increment the rtv handle by the rtv descriptor size we got above + rtvHandle.ptr += g_RtvDescriptorSize; + } + + // -- Create the Command Allocators -- // + + for (int i = 0; i < FRAME_BUFFER_COUNT; i++) + { + ID3D12CommandAllocator* commandAllocator = nullptr; + CHECK_HR( g_Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator)) ); + g_CommandAllocators[i].Attach(commandAllocator); + } + + // create the command list with the first allocator + CHECK_HR( g_Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_CommandAllocators[0].Get(), NULL, IID_PPV_ARGS(&g_CommandList)) ); + + // command lists are created in the recording state. our main loop will set it up for recording again so close it now + g_CommandList->Close(); + + // create a depth stencil descriptor heap so we can get a pointer to the depth stencil buffer + D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {}; + dsvHeapDesc.NumDescriptors = 1; + dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; + dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + CHECK_HR( g_Device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&g_DepthStencilDescriptorHeap)) ); + + D3D12_CLEAR_VALUE depthOptimizedClearValue = {}; + depthOptimizedClearValue.Format = DEPTH_STENCIL_FORMAT; + depthOptimizedClearValue.DepthStencil.Depth = 1.0f; + depthOptimizedClearValue.DepthStencil.Stencil = 0; + + D3D12MA::ALLOCATION_DESC depthStencilAllocDesc = {}; + depthStencilAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + D3D12_RESOURCE_DESC depthStencilResourceDesc = {}; + depthStencilResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + depthStencilResourceDesc.Alignment = 0; + depthStencilResourceDesc.Width = SIZE_X; + depthStencilResourceDesc.Height = SIZE_Y; + depthStencilResourceDesc.DepthOrArraySize = 1; + depthStencilResourceDesc.MipLevels = 1; + depthStencilResourceDesc.Format = DEPTH_STENCIL_FORMAT; + depthStencilResourceDesc.SampleDesc.Count = 1; + depthStencilResourceDesc.SampleDesc.Quality = 0; + depthStencilResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + depthStencilResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + CHECK_HR( g_Allocator->CreateResource( + &depthStencilAllocDesc, + &depthStencilResourceDesc, + D3D12_RESOURCE_STATE_DEPTH_WRITE, + &depthOptimizedClearValue, + &g_DepthStencilAllocation, + IID_PPV_ARGS(&g_DepthStencilBuffer) + ) ); + CHECK_HR( g_DepthStencilBuffer->SetName(L"Depth/Stencil Resource Heap") ); + g_DepthStencilAllocation->SetName(L"Depth/Stencil Resource Heap"); + + D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilDesc = {}; + depthStencilDesc.Format = DEPTH_STENCIL_FORMAT; + depthStencilDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + depthStencilDesc.Flags = D3D12_DSV_FLAG_NONE; + g_Device->CreateDepthStencilView(g_DepthStencilBuffer.Get(), &depthStencilDesc, g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + + // -- Create a Fence & Fence Event -- // + + // create the fences + for (int i = 0; i < FRAME_BUFFER_COUNT; i++) + { + ID3D12Fence* fence = nullptr; + CHECK_HR( g_Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)) ); + g_Fences[i].Attach(fence); + g_FenceValues[i] = 0; // set the initial g_Fences value to 0 + } + + // create a handle to a g_Fences event + g_FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + assert(g_FenceEvent); + + D3D12_DESCRIPTOR_RANGE cbDescriptorRange; + cbDescriptorRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV; + cbDescriptorRange.NumDescriptors = 1; + cbDescriptorRange.BaseShaderRegister = 0; + cbDescriptorRange.RegisterSpace = 0; + cbDescriptorRange.OffsetInDescriptorsFromTableStart = 0; + + D3D12_DESCRIPTOR_RANGE textureDescRange; + textureDescRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + textureDescRange.NumDescriptors = 1; + textureDescRange.BaseShaderRegister = 0; + textureDescRange.RegisterSpace = 0; + textureDescRange.OffsetInDescriptorsFromTableStart = 1; + + D3D12_ROOT_PARAMETER rootParameters[3]; + + rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[0].DescriptorTable = {1, &cbDescriptorRange}; + rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + rootParameters[1].Descriptor = {1, 0}; + rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; + + rootParameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[2].DescriptorTable = {1, &textureDescRange}; + rootParameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + // create root signature + + // create a static sampler + D3D12_STATIC_SAMPLER_DESC sampler = {}; + sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER; + sampler.MipLODBias = 0; + sampler.MaxAnisotropy = 0; + sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; + sampler.MinLOD = 0.0f; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = 0; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {}; + rootSignatureDesc.NumParameters = _countof(rootParameters); + rootSignatureDesc.pParameters = rootParameters; + rootSignatureDesc.NumStaticSamplers = 1; + rootSignatureDesc.pStaticSamplers = &sampler; + rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | + D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | + D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS; + + ComPtr signatureBlob; + ID3DBlob* signatureBlobPtr; + CHECK_HR( D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signatureBlobPtr, nullptr) ); + signatureBlob.Attach(signatureBlobPtr); + + ID3D12RootSignature* rootSignature = nullptr; + CHECK_HR( device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&rootSignature)) ); + g_RootSignature.Attach(rootSignature); + + for (int i = 0; i < FRAME_BUFFER_COUNT; ++i) + { + D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.NumDescriptors = 2; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + CHECK_HR( g_Device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&g_MainDescriptorHeap[i])) ); + } + + // # CONSTANT BUFFER + + for (int i = 0; i < FRAME_BUFFER_COUNT; ++i) + { + D3D12MA::ALLOCATION_DESC constantBufferUploadAllocDesc = {}; + constantBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + D3D12_RESOURCE_DESC constantBufferResourceDesc = {}; + constantBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + constantBufferResourceDesc.Alignment = 0; + constantBufferResourceDesc.Width = 1024 * 64; + constantBufferResourceDesc.Height = 1; + constantBufferResourceDesc.DepthOrArraySize = 1; + constantBufferResourceDesc.MipLevels = 1; + constantBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + constantBufferResourceDesc.SampleDesc.Count = 1; + constantBufferResourceDesc.SampleDesc.Quality = 0; + constantBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + constantBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + CHECK_HR( g_Allocator->CreateResource( + &constantBufferUploadAllocDesc, + &constantBufferResourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + &g_ConstantBufferUploadAllocation[i], + IID_PPV_ARGS(&g_ConstantBufferUploadHeap[i])) ); + g_ConstantBufferUploadHeap[i]->SetName(L"Constant Buffer Upload Resource Heap"); + g_ConstantBufferUploadAllocation[i]->SetName(L"Constant Buffer Upload Resource Heap"); + + D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {}; + cbvDesc.BufferLocation = g_ConstantBufferUploadHeap[i]->GetGPUVirtualAddress(); + cbvDesc.SizeInBytes = AlignUp(sizeof(ConstantBuffer0_PS), 256); + g_Device->CreateConstantBufferView(&cbvDesc, g_MainDescriptorHeap[i]->GetCPUDescriptorHandleForHeapStart()); + + CHECK_HR( g_ConstantBufferUploadHeap[i]->Map(0, &EMPTY_RANGE, &g_ConstantBufferAddress[i]) ); + } + + // create input layout + + // The input layout is used by the Input Assembler so that it knows + // how to read the vertex data bound to it. + + const D3D12_INPUT_ELEMENT_DESC inputLayout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, + }; + + // create a pipeline state object (PSO) + + // In a real application, you will have many pso's. for each different shader + // or different combinations of shaders, different blend states or different rasterizer states, + // different topology types (point, line, triangle, patch), or a different number + // of render targets you will need a pso + + // VS is the only required shader for a pso. You might be wondering when a case would be where + // you only set the VS. It's possible that you have a pso that only outputs data with the stream + // output, and not on a render target, which means you would not need anything after the stream + // output. + + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; // a structure to define a pso + psoDesc.InputLayout.NumElements = _countof(inputLayout); + psoDesc.InputLayout.pInputElementDescs = inputLayout; + psoDesc.pRootSignature = g_RootSignature.Get(); // the root signature that describes the input data this pso needs + psoDesc.VS.BytecodeLength = sizeof(VS::g_main); + psoDesc.VS.pShaderBytecode = VS::g_main; + psoDesc.PS.BytecodeLength = sizeof(PS::g_main); + psoDesc.PS.pShaderBytecode = PS::g_main; + psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; // type of topology we are drawing + psoDesc.RTVFormats[0] = RENDER_TARGET_FORMAT; // format of the render target + psoDesc.DSVFormat = DEPTH_STENCIL_FORMAT; + psoDesc.SampleDesc = sampleDesc; // must be the same sample description as the swapchain and depth/stencil buffer + psoDesc.SampleMask = 0xffffffff; // sample mask has to do with multi-sampling. 0xffffffff means point sampling is done + SetDefaultRasterizerDesc(psoDesc.RasterizerState); + SetDefaultBlendDesc(psoDesc.BlendState); + psoDesc.NumRenderTargets = 1; // we are only binding one render target + SetDefaultDepthStencilDesc(psoDesc.DepthStencilState); + + // create the pso + ID3D12PipelineState* pipelineStateObject; + CHECK_HR( device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&pipelineStateObject)) ); + g_PipelineStateObject.Attach(pipelineStateObject); + + // Create vertex buffer + + // a triangle + Vertex vList[] = { + // front face + { -0.5f, 0.5f, -0.5f, 0.f, 0.f }, + { 0.5f, -0.5f, -0.5f, 1.f, 1.f }, + { -0.5f, -0.5f, -0.5f, 0.f, 1.f }, + { 0.5f, 0.5f, -0.5f, 1.f, 0.f }, + + // right side face + { 0.5f, -0.5f, -0.5f, 0.f, 1.f }, + { 0.5f, 0.5f, 0.5f, 1.f, 0.f }, + { 0.5f, -0.5f, 0.5f, 1.f, 1.f }, + { 0.5f, 0.5f, -0.5f, 0.f, 0.f }, + + // left side face + { -0.5f, 0.5f, 0.5f, 0.f, 0.f }, + { -0.5f, -0.5f, -0.5f, 1.f, 1.f }, + { -0.5f, -0.5f, 0.5f, 0.f, 1.f }, + { -0.5f, 0.5f, -0.5f, 1.f, 0.f }, + + // back face + { 0.5f, 0.5f, 0.5f, 0.f, 0.f }, + { -0.5f, -0.5f, 0.5f, 1.f, 1.f }, + { 0.5f, -0.5f, 0.5f, 0.f, 1.f }, + { -0.5f, 0.5f, 0.5f, 1.f, 0.f }, + + // top face + { -0.5f, 0.5f, -0.5f, 0.f, 0.f }, + { 0.5f, 0.5f, 0.5f, 1.f, 1.f }, + { 0.5f, 0.5f, -0.5f, 0.f, 1.f }, + { -0.5f, 0.5f, 0.5f, 1.f, 0.f }, + + // bottom face + { 0.5f, -0.5f, 0.5f, 0.f, 0.f }, + { -0.5f, -0.5f, -0.5f, 1.f, 1.f }, + { 0.5f, -0.5f, -0.5f, 0.f, 1.f }, + { -0.5f, -0.5f, 0.5f, 1.f, 0.f }, + }; + const uint32_t vBufferSize = sizeof(vList); + + // create default heap + // default heap is memory on the GPU. Only the GPU has access to this memory + // To get data into this heap, we will have to upload the data using + // an upload heap + D3D12MA::ALLOCATION_DESC vertexBufferAllocDesc = {}; + vertexBufferAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + D3D12_RESOURCE_DESC vertexBufferResourceDesc = {}; + vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + vertexBufferResourceDesc.Alignment = 0; + vertexBufferResourceDesc.Width = vBufferSize; + vertexBufferResourceDesc.Height = 1; + vertexBufferResourceDesc.DepthOrArraySize = 1; + vertexBufferResourceDesc.MipLevels = 1; + vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + vertexBufferResourceDesc.SampleDesc.Count = 1; + vertexBufferResourceDesc.SampleDesc.Quality = 0; + vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + ID3D12Resource* vertexBufferPtr; + CHECK_HR( g_Allocator->CreateResource( + &vertexBufferAllocDesc, + &vertexBufferResourceDesc, // resource description for a buffer + D3D12_RESOURCE_STATE_COPY_DEST, // we will start this heap in the copy destination state since we will copy data + // from the upload heap to this heap + nullptr, // optimized clear value must be null for this type of resource. used for render targets and depth/stencil buffers + &g_VertexBufferAllocation, + IID_PPV_ARGS(&vertexBufferPtr)) ); + g_VertexBuffer.Attach(vertexBufferPtr); + + // we can give resource heaps a name so when we debug with the graphics debugger we know what resource we are looking at + g_VertexBuffer->SetName(L"Vertex Buffer Resource Heap"); + g_VertexBufferAllocation->SetName(L"Vertex Buffer Resource Heap"); + + // create upload heap + // upload heaps are used to upload data to the GPU. CPU can write to it, GPU can read from it + // We will upload the vertex buffer using this heap to the default heap + D3D12MA::ALLOCATION_DESC vBufferUploadAllocDesc = {}; + vBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + D3D12_RESOURCE_DESC vertexBufferUploadResourceDesc = {}; + vertexBufferUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + vertexBufferUploadResourceDesc.Alignment = 0; + vertexBufferUploadResourceDesc.Width = vBufferSize; + vertexBufferUploadResourceDesc.Height = 1; + vertexBufferUploadResourceDesc.DepthOrArraySize = 1; + vertexBufferUploadResourceDesc.MipLevels = 1; + vertexBufferUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + vertexBufferUploadResourceDesc.SampleDesc.Count = 1; + vertexBufferUploadResourceDesc.SampleDesc.Quality = 0; + vertexBufferUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + vertexBufferUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + ComPtr vBufferUploadHeap; + D3D12MA::Allocation* vBufferUploadHeapAllocation = nullptr; + CHECK_HR( g_Allocator->CreateResource( + &vBufferUploadAllocDesc, + &vertexBufferUploadResourceDesc, // resource description for a buffer + D3D12_RESOURCE_STATE_GENERIC_READ, // GPU will read from this buffer and copy its contents to the default heap + nullptr, + &vBufferUploadHeapAllocation, + IID_PPV_ARGS(&vBufferUploadHeap)) ); + vBufferUploadHeap->SetName(L"Vertex Buffer Upload Resource Heap"); + vBufferUploadHeapAllocation->SetName(L"Vertex Buffer Upload Resource Heap"); + + // store vertex buffer in upload heap + D3D12_SUBRESOURCE_DATA vertexData = {}; + vertexData.pData = reinterpret_cast(vList); // pointer to our vertex array + vertexData.RowPitch = vBufferSize; // size of all our triangle vertex data + vertexData.SlicePitch = vBufferSize; // also the size of our triangle vertex data + + CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex].Get(), NULL) ); + + // we are now creating a command with the command list to copy the data from + // the upload heap to the default heap + UINT64 r = UpdateSubresources(g_CommandList.Get(), g_VertexBuffer.Get(), vBufferUploadHeap.Get(), 0, 0, 1, &vertexData); + assert(r); + + // transition the vertex buffer data from copy destination state to vertex buffer state + D3D12_RESOURCE_BARRIER vbBarrier = {}; + vbBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + vbBarrier.Transition.pResource = g_VertexBuffer.Get(); + vbBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + vbBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; + vbBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + g_CommandList->ResourceBarrier(1, &vbBarrier); + + // Create index buffer + + // a quad (2 triangles) + uint16_t iList[] = { + // ffront face + 0, 1, 2, // first triangle + 0, 3, 1, // second triangle + + // left face + 4, 5, 6, // first triangle + 4, 7, 5, // second triangle + + // right face + 8, 9, 10, // first triangle + 8, 11, 9, // second triangle + + // back face + 12, 13, 14, // first triangle + 12, 15, 13, // second triangle + + // top face + 16, 17, 18, // first triangle + 16, 19, 17, // second triangle + + // bottom face + 20, 21, 22, // first triangle + 20, 23, 21, // second triangle + }; + + g_CubeIndexCount = (uint32_t)_countof(iList); + + size_t iBufferSize = sizeof(iList); + + // create default heap to hold index buffer + D3D12MA::ALLOCATION_DESC indexBufferAllocDesc = {}; + indexBufferAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + D3D12_RESOURCE_DESC indexBufferResourceDesc = {}; + indexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + indexBufferResourceDesc.Alignment = 0; + indexBufferResourceDesc.Width = iBufferSize; + indexBufferResourceDesc.Height = 1; + indexBufferResourceDesc.DepthOrArraySize = 1; + indexBufferResourceDesc.MipLevels = 1; + indexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + indexBufferResourceDesc.SampleDesc.Count = 1; + indexBufferResourceDesc.SampleDesc.Quality = 0; + indexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + indexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + CHECK_HR( g_Allocator->CreateResource( + &indexBufferAllocDesc, + &indexBufferResourceDesc, // resource description for a buffer + D3D12_RESOURCE_STATE_COPY_DEST, // start in the copy destination state + nullptr, // optimized clear value must be null for this type of resource + &g_IndexBufferAllocation, + IID_PPV_ARGS(&g_IndexBuffer)) ); + + // we can give resource heaps a name so when we debug with the graphics debugger we know what resource we are looking at + g_IndexBuffer->SetName(L"Index Buffer Resource Heap"); + g_IndexBufferAllocation->SetName(L"Index Buffer Resource Heap"); + + // create upload heap to upload index buffer + D3D12MA::ALLOCATION_DESC iBufferUploadAllocDesc = {}; + iBufferUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + D3D12_RESOURCE_DESC indexBufferUploadResourceDesc = {}; + indexBufferUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + indexBufferUploadResourceDesc.Alignment = 0; + indexBufferUploadResourceDesc.Width = iBufferSize; + indexBufferUploadResourceDesc.Height = 1; + indexBufferUploadResourceDesc.DepthOrArraySize = 1; + indexBufferUploadResourceDesc.MipLevels = 1; + indexBufferUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + indexBufferUploadResourceDesc.SampleDesc.Count = 1; + indexBufferUploadResourceDesc.SampleDesc.Quality = 0; + indexBufferUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + indexBufferUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + ComPtr iBufferUploadHeap; + D3D12MA::Allocation* iBufferUploadHeapAllocation = nullptr; + CHECK_HR( g_Allocator->CreateResource( + &iBufferUploadAllocDesc, + &indexBufferUploadResourceDesc, // resource description for a buffer + D3D12_RESOURCE_STATE_GENERIC_READ, // GPU will read from this buffer and copy its contents to the default heap + nullptr, + &iBufferUploadHeapAllocation, + IID_PPV_ARGS(&iBufferUploadHeap)) ); + CHECK_HR( iBufferUploadHeap->SetName(L"Index Buffer Upload Resource Heap") ); + iBufferUploadHeapAllocation->SetName(L"Index Buffer Upload Resource Heap"); + + // store vertex buffer in upload heap + D3D12_SUBRESOURCE_DATA indexData = {}; + indexData.pData = iList; // pointer to our index array + indexData.RowPitch = iBufferSize; // size of all our index buffer + indexData.SlicePitch = iBufferSize; // also the size of our index buffer + + // we are now creating a command with the command list to copy the data from + // the upload heap to the default heap + r = UpdateSubresources(g_CommandList.Get(), g_IndexBuffer.Get(), iBufferUploadHeap.Get(), 0, 0, 1, &indexData); + assert(r); + + // transition the index buffer data from copy destination state to vertex buffer state + D3D12_RESOURCE_BARRIER ibBarrier = {}; + ibBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + ibBarrier.Transition.pResource = g_IndexBuffer.Get(); + ibBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + ibBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_INDEX_BUFFER; + ibBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + g_CommandList->ResourceBarrier(1, &ibBarrier); + + // create a vertex buffer view for the triangle. We get the GPU memory address to the vertex pointer using the GetGPUVirtualAddress() method + g_VertexBufferView.BufferLocation = g_VertexBuffer->GetGPUVirtualAddress(); + g_VertexBufferView.StrideInBytes = sizeof(Vertex); + g_VertexBufferView.SizeInBytes = vBufferSize; + + // create a index buffer view for the triangle. We get the GPU memory address to the vertex pointer using the GetGPUVirtualAddress() method + g_IndexBufferView.BufferLocation = g_IndexBuffer->GetGPUVirtualAddress(); + g_IndexBufferView.Format = DXGI_FORMAT_R16_UINT; + g_IndexBufferView.SizeInBytes = (UINT)iBufferSize; + + D3D12MA::ALLOCATION_DESC cbPerObjectUploadAllocDesc = {}; + cbPerObjectUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + D3D12_RESOURCE_DESC cbPerObjectUploadResourceDesc = {}; + cbPerObjectUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + cbPerObjectUploadResourceDesc.Alignment = 0; + cbPerObjectUploadResourceDesc.Width = 1024 * 64; + cbPerObjectUploadResourceDesc.Height = 1; + cbPerObjectUploadResourceDesc.DepthOrArraySize = 1; + cbPerObjectUploadResourceDesc.MipLevels = 1; + cbPerObjectUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + cbPerObjectUploadResourceDesc.SampleDesc.Count = 1; + cbPerObjectUploadResourceDesc.SampleDesc.Quality = 0; + cbPerObjectUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + cbPerObjectUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i) + { + // create resource for cube 1 + CHECK_HR( g_Allocator->CreateResource( + &cbPerObjectUploadAllocDesc, + &cbPerObjectUploadResourceDesc, // size of the resource heap. Must be a multiple of 64KB for single-textures and constant buffers + D3D12_RESOURCE_STATE_GENERIC_READ, // will be data that is read from so we keep it in the generic read state + nullptr, // we do not have use an optimized clear value for constant buffers + &g_CbPerObjectUploadHeapAllocations[i], + IID_PPV_ARGS(&g_CbPerObjectUploadHeaps[i])) ); + g_CbPerObjectUploadHeaps[i]->SetName(L"Constant Buffer Upload Resource Heap"); + g_CbPerObjectUploadHeapAllocations[i]->SetName(L"Constant Buffer Upload Resource Heap"); + + CHECK_HR( g_CbPerObjectUploadHeaps[i]->Map(0, &EMPTY_RANGE, &g_CbPerObjectAddress[i]) ); + } + + // # TEXTURE + + D3D12_RESOURCE_DESC textureDesc; + size_t imageBytesPerRow; + size_t imageSize = SIZE_MAX; + std::vector imageData; + { + const UINT sizeX = 256; + const UINT sizeY = 256; + const DXGI_FORMAT format = DXGI_FORMAT_R8G8B8A8_UNORM; + const UINT bytesPerPixel = 4; + + imageBytesPerRow = sizeX * bytesPerPixel; + imageSize = sizeY * imageBytesPerRow; + + imageData.resize(imageSize); + char* rowPtr = (char*)imageData.data(); + for(UINT y = 0; y < sizeY; ++y) + { + char* pixelPtr = rowPtr; + for(UINT x = 0; x < sizeX; ++x) + { + *(UINT8*)(pixelPtr ) = (UINT8)x; // R + *(UINT8*)(pixelPtr + 1) = (UINT8)y; // G + *(UINT8*)(pixelPtr + 2) = 0x00; // B + *(UINT8*)(pixelPtr + 3) = 0xFF; // A + + *(UINT8*)(pixelPtr ) = x > 128 ? 0xFF : 00; + *(UINT8*)(pixelPtr + 1) = y > 128 ? 0xFF : 00; + pixelPtr += bytesPerPixel; + } + rowPtr += imageBytesPerRow; + } + + textureDesc = {}; + textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + textureDesc.Alignment = 0; + textureDesc.Width = sizeX; + textureDesc.Height = sizeY; + textureDesc.DepthOrArraySize = 1; + textureDesc.MipLevels = 1; + textureDesc.Format = format; + textureDesc.SampleDesc.Count = 1; + textureDesc.SampleDesc.Quality = 0; + textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + } + + D3D12MA::ALLOCATION_DESC textureAllocDesc = {}; + textureAllocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT; + CHECK_HR( g_Allocator->CreateResource( + &textureAllocDesc, + &textureDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + nullptr, // pOptimizedClearValue + &g_TextureAllocation, + IID_PPV_ARGS(&g_Texture)) ); + g_Texture->SetName(L"g_Texture"); + g_TextureAllocation->SetName(L"g_Texture"); + + UINT64 textureUploadBufferSize; + device->GetCopyableFootprints( + &textureDesc, + 0, // FirstSubresource + 1, // NumSubresources + 0, // BaseOffset + nullptr, // pLayouts + nullptr, // pNumRows + nullptr, // pRowSizeInBytes + &textureUploadBufferSize); // pTotalBytes + + D3D12MA::ALLOCATION_DESC textureUploadAllocDesc = {}; + textureUploadAllocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD; + D3D12_RESOURCE_DESC textureUploadResourceDesc = {}; + textureUploadResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + textureUploadResourceDesc.Alignment = 0; + textureUploadResourceDesc.Width = textureUploadBufferSize; + textureUploadResourceDesc.Height = 1; + textureUploadResourceDesc.DepthOrArraySize = 1; + textureUploadResourceDesc.MipLevels = 1; + textureUploadResourceDesc.Format = DXGI_FORMAT_UNKNOWN; + textureUploadResourceDesc.SampleDesc.Count = 1; + textureUploadResourceDesc.SampleDesc.Quality = 0; + textureUploadResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + textureUploadResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + ComPtr textureUpload; + D3D12MA::Allocation* textureUploadAllocation; + CHECK_HR( g_Allocator->CreateResource( + &textureUploadAllocDesc, + &textureUploadResourceDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, // pOptimizedClearValue + &textureUploadAllocation, + IID_PPV_ARGS(&textureUpload)) ); + textureUpload->SetName(L"textureUpload"); + textureUploadAllocation->SetName(L"textureUpload"); + + D3D12_SUBRESOURCE_DATA textureSubresourceData = {}; + textureSubresourceData.pData = imageData.data(); + textureSubresourceData.RowPitch = imageBytesPerRow; + textureSubresourceData.SlicePitch = imageBytesPerRow * textureDesc.Height; + + UpdateSubresources(g_CommandList.Get(), g_Texture.Get(), textureUpload.Get(), 0, 0, 1, &textureSubresourceData); + + D3D12_RESOURCE_BARRIER textureBarrier = {}; + textureBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + textureBarrier.Transition.pResource = g_Texture.Get(); + textureBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + textureBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + textureBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + g_CommandList->ResourceBarrier(1, &textureBarrier); + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Format = textureDesc.Format; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = 1; + for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i) + { + D3D12_CPU_DESCRIPTOR_HANDLE descHandle = { + g_MainDescriptorHeap[i]->GetCPUDescriptorHandleForHeapStart().ptr + + g_Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)}; + g_Device->CreateShaderResourceView(g_Texture.Get(), &srvDesc, descHandle); + } + + // # END OF INITIAL COMMAND LIST + + // Now we execute the command list to upload the initial assets (triangle data) + g_CommandList->Close(); + ID3D12CommandList* ppCommandLists[] = { g_CommandList.Get() }; + commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); + + // increment the fence value now, otherwise the buffer might not be uploaded by the time we start drawing + WaitGPUIdle(g_FrameIndex); + + textureUploadAllocation->Release(); + iBufferUploadHeapAllocation->Release(); + vBufferUploadHeapAllocation->Release(); +} + +void Update() +{ + { + const float f = sin(g_Time * (PI * 2.f)) * 0.5f + 0.5f; + ConstantBuffer0_PS cb; + cb.Color = vec4(f, f, f, 1.f); + memcpy(g_ConstantBufferAddress[g_FrameIndex], &cb, sizeof(cb)); + } + + { + const mat4 projection = mat4::Perspective( + 45.f * (PI / 180.f), // fovY + (float)SIZE_X / (float)SIZE_Y, // aspectRatio + 0.1f, // zNear + 1000.0f); // zFar + const mat4 view = mat4::LookAt( + vec3(0.f, 0.f, 0.f), // at + vec3(-.4f, 1.7f, -3.5f), // eye + vec3(0.f, 1.f, 0.f)); // up + const mat4 viewProjection = view * projection; + + mat4 cube1World = mat4::RotationZ(g_Time); + + ConstantBuffer1_VS cb; + mat4 worldViewProjection = cube1World * viewProjection; + cb.WorldViewProj = worldViewProjection.Transposed(); + memcpy(g_CbPerObjectAddress[g_FrameIndex], &cb, sizeof(cb)); + + mat4 cube2World = mat4::Scaling(0.5f) * + mat4::RotationX(g_Time * 2.0f) * + mat4::Translation(vec3(-1.2f, 0.f, 0.f)) * + cube1World; + + worldViewProjection = cube2World * viewProjection; + cb.WorldViewProj = worldViewProjection.Transposed(); + memcpy((char*)g_CbPerObjectAddress[g_FrameIndex] + ConstantBufferPerObjectAlignedSize, &cb, sizeof(cb)); + } +} + +void Render() // execute the command list +{ + // # Here was UpdatePipeline function. + + // swap the current rtv buffer index so we draw on the correct buffer + g_FrameIndex = g_SwapChain->GetCurrentBackBufferIndex(); + // We have to wait for the gpu to finish with the command allocator before we reset it + WaitForFrame(g_FrameIndex); + // increment g_FenceValues for next frame + g_FenceValues[g_FrameIndex]++; + + // we can only reset an allocator once the gpu is done with it + // resetting an allocator frees the memory that the command list was stored in + CHECK_HR( g_CommandAllocators[g_FrameIndex]->Reset() ); + + // reset the command list. by resetting the command list we are putting it into + // a recording state so we can start recording commands into the command allocator. + // the command allocator that we reference here may have multiple command lists + // associated with it, but only one can be recording at any time. Make sure + // that any other command lists associated to this command allocator are in + // the closed state (not recording). + // Here you will pass an initial pipeline state object as the second parameter, + // but in this tutorial we are only clearing the rtv, and do not actually need + // anything but an initial default pipeline, which is what we get by setting + // the second parameter to NULL + CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex].Get(), NULL) ); + + // here we start recording commands into the g_CommandList (which all the commands will be stored in the g_CommandAllocators) + + // transition the "g_FrameIndex" render target from the present state to the render target state so the command list draws to it starting from here + D3D12_RESOURCE_BARRIER presentToRenderTargetBarrier = {}; + presentToRenderTargetBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + presentToRenderTargetBarrier.Transition.pResource = g_RenderTargets[g_FrameIndex].Get(); + presentToRenderTargetBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + presentToRenderTargetBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + presentToRenderTargetBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + g_CommandList->ResourceBarrier(1, &presentToRenderTargetBarrier); + + // here we again get the handle to our current render target view so we can set it as the render target in the output merger stage of the pipeline + D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = { + g_RtvDescriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + g_FrameIndex * g_RtvDescriptorSize}; + D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = + g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + + // set the render target for the output merger stage (the output of the pipeline) + g_CommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, &dsvHandle); + + g_CommandList->ClearDepthStencilView(g_DepthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); + + // Clear the render target by using the ClearRenderTargetView command + const float clearColor[] = { 0.0f, 0.2f, 0.4f, 1.0f }; + g_CommandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr); + + g_CommandList->SetPipelineState(g_PipelineStateObject.Get()); + + g_CommandList->SetGraphicsRootSignature(g_RootSignature.Get()); + + ID3D12DescriptorHeap* descriptorHeaps[] = { g_MainDescriptorHeap[g_FrameIndex].Get() }; + g_CommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps); + + g_CommandList->SetGraphicsRootDescriptorTable(0, g_MainDescriptorHeap[g_FrameIndex]->GetGPUDescriptorHandleForHeapStart()); + g_CommandList->SetGraphicsRootDescriptorTable(2, g_MainDescriptorHeap[g_FrameIndex]->GetGPUDescriptorHandleForHeapStart()); + + D3D12_VIEWPORT viewport{0.f, 0.f, (float)SIZE_X, (float)SIZE_Y, 0.f, 1.f}; + g_CommandList->RSSetViewports(1, &viewport); // set the viewports + + D3D12_RECT scissorRect{0, 0, SIZE_X, SIZE_Y}; + g_CommandList->RSSetScissorRects(1, &scissorRect); // set the scissor rects + + g_CommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // set the primitive topology + g_CommandList->IASetVertexBuffers(0, 1, &g_VertexBufferView); // set the vertex buffer (using the vertex buffer view) + g_CommandList->IASetIndexBuffer(&g_IndexBufferView); + + g_CommandList->SetGraphicsRootConstantBufferView(1, + g_CbPerObjectUploadHeaps[g_FrameIndex]->GetGPUVirtualAddress()); + g_CommandList->DrawIndexedInstanced(g_CubeIndexCount, 1, 0, 0, 0); + + g_CommandList->SetGraphicsRootConstantBufferView(1, + g_CbPerObjectUploadHeaps[g_FrameIndex]->GetGPUVirtualAddress() + ConstantBufferPerObjectAlignedSize); + g_CommandList->DrawIndexedInstanced(g_CubeIndexCount, 1, 0, 0, 0); + + // transition the "g_FrameIndex" render target from the render target state to the present state. If the debug layer is enabled, you will receive a + // warning if present is called on the render target when it's not in the present state + D3D12_RESOURCE_BARRIER renderTargetToPresentBarrier = {}; + renderTargetToPresentBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + renderTargetToPresentBarrier.Transition.pResource = g_RenderTargets[g_FrameIndex].Get(); + renderTargetToPresentBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + renderTargetToPresentBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + renderTargetToPresentBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + g_CommandList->ResourceBarrier(1, &renderTargetToPresentBarrier); + + CHECK_HR( g_CommandList->Close() ); + + // ================ + + // create an array of command lists (only one command list here) + ID3D12CommandList* ppCommandLists[] = { g_CommandList.Get() }; + + // execute the array of command lists + g_CommandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); + + // this command goes in at the end of our command queue. we will know when our command queue + // has finished because the g_Fences value will be set to "g_FenceValues" from the GPU since the command + // queue is being executed on the GPU + CHECK_HR( g_CommandQueue->Signal(g_Fences[g_FrameIndex].Get(), g_FenceValues[g_FrameIndex]) ); + + // present the current backbuffer + CHECK_HR( g_SwapChain->Present(PRESENT_SYNC_INTERVAL, 0) ); +} + +void Cleanup() // release com ojects and clean up memory +{ + // wait for the gpu to finish all frames + for (size_t i = 0; i < FRAME_BUFFER_COUNT; ++i) + { + WaitForFrame(i); + CHECK_HR( g_CommandQueue->Wait(g_Fences[i].Get(), g_FenceValues[i]) ); + } + + // get swapchain out of full screen before exiting + BOOL fs = false; + CHECK_HR( g_SwapChain->GetFullscreenState(&fs, NULL) ); + if (fs) + g_SwapChain->SetFullscreenState(false, NULL); + + WaitGPUIdle(0); + + g_Texture.Reset(); + g_TextureAllocation->Release(); g_TextureAllocation = nullptr; + g_IndexBuffer.Reset(); + g_IndexBufferAllocation->Release(); g_IndexBufferAllocation = nullptr; + g_VertexBuffer.Reset(); + g_VertexBufferAllocation->Release(); g_VertexBufferAllocation = nullptr; + g_PipelineStateObject.Reset(); + g_RootSignature.Reset(); + + CloseHandle(g_FenceEvent); + g_CommandList.Reset(); + g_CommandQueue.Reset(); + + for (size_t i = FRAME_BUFFER_COUNT; i--; ) + { + g_CbPerObjectUploadHeaps[i].Reset(); + g_CbPerObjectUploadHeapAllocations[i]->Release(); g_CbPerObjectUploadHeapAllocations[i] = nullptr; + g_MainDescriptorHeap[i].Reset(); + g_ConstantBufferUploadHeap[i].Reset(); + g_ConstantBufferUploadAllocation[i]->Release(); g_ConstantBufferUploadAllocation[i] = nullptr; + } + + g_DepthStencilDescriptorHeap.Reset(); + g_DepthStencilBuffer.Reset(); + g_DepthStencilAllocation->Release(); g_DepthStencilAllocation = nullptr; + g_RtvDescriptorHeap.Reset(); + for (size_t i = FRAME_BUFFER_COUNT; i--; ) + { + g_RenderTargets[i].Reset(); + g_CommandAllocators[i].Reset(); + g_Fences[i].Reset(); + } + + g_Allocator.Reset(); + if(ENABLE_CPU_ALLOCATION_CALLBACKS) + { + assert(g_CpuAllocationCount.load() == 0); + } + + g_Device.Reset(); + g_SwapChain.Reset(); +} + +static void ExecuteTests() +{ + try + { + TestContext ctx = {}; + ctx.allocationCallbacks = &g_AllocationCallbacks; + ctx.device = g_Device.Get(); + ctx.allocator = g_Allocator.Get(); + ctx.allocatorFlags = g_AllocatorFlags; + Test(ctx); + } + catch(const std::exception& ex) + { + wprintf(L"ERROR: %hs\n", ex.what()); + } +} + +static void OnKeyDown(WPARAM key) +{ + switch (key) + { + case 'T': + ExecuteTests(); + break; + + case 'J': + { + WCHAR* statsString = NULL; + g_Allocator->BuildStatsString(&statsString, TRUE); + wprintf(L"%s\n", statsString); + g_Allocator->FreeStatsString(statsString); + } + break; + + case VK_ESCAPE: + PostMessage(g_Wnd, WM_CLOSE, 0, 0); + break; + } +} + +#define CATCH_PRINT_ERROR(extraCatchCode) \ + catch(const std::exception& ex) \ + { \ + fwprintf(stderr, L"ERROR: %hs\n", ex.what()); \ + extraCatchCode \ + } \ + catch(...) \ + { \ + fwprintf(stderr, L"UNKNOWN ERROR.\n"); \ + extraCatchCode \ + } + +static LRESULT WINAPI WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_DESTROY: + try + { + Cleanup(); + } + CATCH_PRINT_ERROR(;) + PostQuitMessage(0); + return 0; + + case WM_KEYDOWN: + try + { + OnKeyDown(wParam); + } + CATCH_PRINT_ERROR(DestroyWindow(wnd);) + return 0; + } + + return DefWindowProc(wnd, msg, wParam, lParam); +} + +ID3D12GraphicsCommandList* BeginCommandList() +{ + CHECK_HR( g_CommandList->Reset(g_CommandAllocators[g_FrameIndex].Get(), NULL) ); + + return g_CommandList.Get(); +} + +void EndCommandList(ID3D12GraphicsCommandList* cmdList) +{ + cmdList->Close(); + + ID3D12CommandList* genericCmdList = cmdList; + g_CommandQueue->ExecuteCommandLists(1, &genericCmdList); + + WaitGPUIdle(g_FrameIndex); +} + +static void PrintLogo() +{ + wprintf(L"%s\n", WINDOW_TITLE); +} + +static void PrintHelp() +{ + wprintf( + L"Command line syntax:\n" + L"-h, --Help Print this information\n" + L"-l, --List Print list of GPUs\n" + L"-g S, --GPU S Select GPU with name containing S\n" + L"-i N, --GPUIndex N Select GPU index N\n" + L"-t, --Test Run tests and exit\n" + ); +} + +int MainWindow() +{ + WNDCLASSEX wndClass; + ZeroMemory(&wndClass, sizeof(wndClass)); + wndClass.cbSize = sizeof(wndClass); + wndClass.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS; + wndClass.hbrBackground = NULL; + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndClass.hInstance = g_Instance; + wndClass.lpfnWndProc = &WndProc; + wndClass.lpszClassName = CLASS_NAME; + + ATOM classR = RegisterClassEx(&wndClass); + assert(classR); + + DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE; + DWORD exStyle = 0; + + RECT rect = { 0, 0, SIZE_X, SIZE_Y }; + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + g_Wnd = CreateWindowEx( + exStyle, + CLASS_NAME, + WINDOW_TITLE, + style, + CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + NULL, + NULL, + g_Instance, + 0); + assert(g_Wnd); + + InitD3D(); + g_TimeOffset = GetTickCount64(); + + // Execute tests and close program + if (g_CommandLineParameters.m_Test) + { + ExecuteTests(); + PostMessage(g_Wnd, WM_CLOSE, 0, 0); + } + + MSG msg; + for (;;) + { + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + break; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + else + { + const UINT64 newTimeValue = GetTickCount64() - g_TimeOffset; + g_TimeDelta = (float)(newTimeValue - g_TimeValue) * 0.001f; + g_TimeValue = newTimeValue; + g_Time = (float)newTimeValue * 0.001f; + + Update(); + Render(); + } + } + return (int)msg.wParam; +} + +int Main2(int argc, wchar_t** argv) +{ + PrintLogo(); + + if(!g_CommandLineParameters.Parse(argc, argv)) + { + wprintf(L"ERROR: Invalid command line syntax.\n"); + PrintHelp(); + return (int)ExitCode::CommandLineError; + } + + if(g_CommandLineParameters.m_Help) + { + PrintHelp(); + return (int)ExitCode::Help; + } + + // variable name should not same as class name + std::unique_ptr dxgiUsage = std::make_unique(); + dxgiUsage->Init(); + g_DXGIUsage = dxgiUsage.get(); + + if(g_CommandLineParameters.m_List) + { + dxgiUsage->PrintAdapterList(); + return (int)ExitCode::GPUList; + } + + return MainWindow(); +} + +int wmain(int argc, wchar_t** argv) +{ + try + { + return Main2(argc, argv); + } + CATCH_PRINT_ERROR(return (int)ExitCode::RuntimeError;) +} diff --git a/3rdparty/d3d12memalloc/src/Doxyfile b/3rdparty/d3d12memalloc/src/Doxyfile new file mode 100644 index 00000000000000..a93bdbbcb00484 --- /dev/null +++ b/3rdparty/d3d12memalloc/src/Doxyfile @@ -0,0 +1,2778 @@ +# Doxyfile 1.9.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Direct3D 12 Memory Allocator" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @CMAKE_SOURCE_DIR@/docs + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = "@CMAKE_SOURCE_DIR@/include/D3D12MemAlloc.h" + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /