From abe72399872303e781a63c7d99c4b05c96985abd Mon Sep 17 00:00:00 2001 From: Brandon Jones Date: Fri, 29 Jul 2022 13:38:27 -0700 Subject: [PATCH] Remove Usage of std::set in ResidencySet Removes usage of std::set in ResidencySet and replaces it with a vector. This does allow duplicat heaps to be inserted into the ResidencySet, however insertion will be O(1) instead of O(log(n)) and instead we can easily identify duplicate heaps by checking their last used fence value when when they are iterated. --- README.md | 4 +- src/gpgmm/BUILD.gn | 4 +- src/gpgmm/CMakeLists.txt | 4 +- src/gpgmm/d3d12/HeapD3D12.h | 2 +- src/gpgmm/d3d12/JSONSerializerD3D12.cpp | 16 +++--- src/gpgmm/d3d12/JSONSerializerD3D12.h | 4 +- ...ncySetD3D12.cpp => ResidencyListD3D12.cpp} | 36 ++++++++++++- ...sidencySetD3D12.h => ResidencyListD3D12.h} | 54 ++++++++++++++++++- src/gpgmm/d3d12/ResidencyManagerD3D12.cpp | 40 ++++++++++++-- src/gpgmm/d3d12/ResidencyManagerD3D12.h | 21 +++++++- src/gpgmm/d3d12/ResourceAllocationD3D12.cpp | 2 +- src/gpgmm/d3d12/ResourceAllocationD3D12.h | 2 +- src/include/gpgmm_d3d12.h | 2 +- .../D3D12EventTraceReplay.cpp | 22 ++++---- .../end2end/D3D12ResidencyManagerTests.cpp | 22 ++++---- 15 files changed, 182 insertions(+), 53 deletions(-) rename src/gpgmm/d3d12/{ResidencySetD3D12.cpp => ResidencyListD3D12.cpp} (64%) rename src/gpgmm/d3d12/{ResidencySetD3D12.h => ResidencyListD3D12.h} (57%) diff --git a/README.md b/README.md index 9985d82b1..b810ba90b 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,10 @@ GPUs do not support page-faulting, so it's up the GPU application to avoid using physical GPU memory. GPGMM integrates residency into the resource allocators to simplify and optimize allocation: ```cpp -gpgmm::d3d12::ResidencySet set; +gpgmm::d3d12::ResidencyList list; set.Insert(allocation->GetMemory()); -residency->ExecuteCommandList(&queue, &commandList, &set, 1); +residency->ExecuteCommandList(&queue, &commandList, &list, 1); // Prepare for next frame. set.Reset(); diff --git a/src/gpgmm/BUILD.gn b/src/gpgmm/BUILD.gn index 03280e0ec..7a4029115 100644 --- a/src/gpgmm/BUILD.gn +++ b/src/gpgmm/BUILD.gn @@ -131,10 +131,10 @@ source_set("gpgmm_sources") { "d3d12/IUnknownImplD3D12.h", "d3d12/JSONSerializerD3D12.cpp", "d3d12/JSONSerializerD3D12.h", + "d3d12/ResidencyListD3D12.cpp", + "d3d12/ResidencyListD3D12.h", "d3d12/ResidencyManagerD3D12.cpp", "d3d12/ResidencyManagerD3D12.h", - "d3d12/ResidencySetD3D12.cpp", - "d3d12/ResidencySetD3D12.h", "d3d12/ResourceAllocationD3D12.cpp", "d3d12/ResourceAllocationD3D12.h", "d3d12/ResourceAllocatorD3D12.cpp", diff --git a/src/gpgmm/CMakeLists.txt b/src/gpgmm/CMakeLists.txt index 2e3a220e9..7da62b0ca 100644 --- a/src/gpgmm/CMakeLists.txt +++ b/src/gpgmm/CMakeLists.txt @@ -73,10 +73,10 @@ if (GPGMM_ENABLE_D3D12) "d3d12/IUnknownImplD3D12.h" "d3d12/JSONSerializerD3D12.cpp" "d3d12/JSONSerializerD3D12.h" + "d3d12/ResidencyListD3D12.cpp" + "d3d12/ResidencyListD3D12.h" "d3d12/ResidencyManagerD3D12.cpp" "d3d12/ResidencyManagerD3D12.h" - "d3d12/ResidencySetD3D12.cpp" - "d3d12/ResidencySetD3D12.h" "d3d12/ResourceAllocationD3D12.cpp" "d3d12/ResourceAllocationD3D12.h" "d3d12/ResourceAllocatorD3D12.cpp" diff --git a/src/gpgmm/d3d12/HeapD3D12.h b/src/gpgmm/d3d12/HeapD3D12.h index 5bc057712..1943182ca 100644 --- a/src/gpgmm/d3d12/HeapD3D12.h +++ b/src/gpgmm/d3d12/HeapD3D12.h @@ -29,7 +29,7 @@ namespace gpgmm::d3d12 { - class ResidencySet; + class ResidencyList; class ResidencyManager; class ResourceAllocator; diff --git a/src/gpgmm/d3d12/JSONSerializerD3D12.cpp b/src/gpgmm/d3d12/JSONSerializerD3D12.cpp index 0761889ea..08d8566fa 100644 --- a/src/gpgmm/d3d12/JSONSerializerD3D12.cpp +++ b/src/gpgmm/d3d12/JSONSerializerD3D12.cpp @@ -16,8 +16,8 @@ #include "gpgmm/common/TraceEvent.h" #include "gpgmm/d3d12/HeapD3D12.h" +#include "gpgmm/d3d12/ResidencyListD3D12.h" #include "gpgmm/d3d12/ResidencyManagerD3D12.h" -#include "gpgmm/d3d12/ResidencySetD3D12.h" #include "gpgmm/d3d12/ResourceAllocationD3D12.h" #include "gpgmm/d3d12/ResourceAllocatorD3D12.h" #include "gpgmm/d3d12/UtilsD3D12.h" @@ -230,22 +230,22 @@ namespace gpgmm::d3d12 { // static JSONDict JSONSerializer::Serialize(const EXECUTE_COMMAND_LISTS_DESC& desc) { JSONDict dict; - JSONArray residencySets; + JSONArray residencyLists; for (uint64_t i = 0; i < desc.Count; i++) { - JSONDict residencySetDict; + JSONDict residencyListDict; JSONArray heapArray; - for (const auto& heap : *desc.ResidencySets[i]) { + for (const auto& heap : *desc.ResidencyLists[i]) { heapArray.AddItem(gpgmm::JSONSerializer::Serialize(heap)); } if (!heapArray.IsEmpty()) { - residencySetDict.AddItem("Heaps", heapArray); + residencyListDict.AddItem("Heaps", heapArray); } - residencySets.AddItem(residencySetDict); + residencyLists.AddItem(residencyListDict); } - if (!residencySets.IsEmpty()) { - dict.AddItem("ResidencySets", residencySets); + if (!residencyLists.IsEmpty()) { + dict.AddItem("ResidencyLists", residencyLists); } return dict; diff --git a/src/gpgmm/d3d12/JSONSerializerD3D12.h b/src/gpgmm/d3d12/JSONSerializerD3D12.h index 99fa5d1e8..2c4676a00 100644 --- a/src/gpgmm/d3d12/JSONSerializerD3D12.h +++ b/src/gpgmm/d3d12/JSONSerializerD3D12.h @@ -28,7 +28,7 @@ namespace gpgmm::d3d12 { struct HEAP_INFO; struct RESOURCE_ALLOCATION_DESC; struct RESOURCE_ALLOCATION_INFO; - class ResidencySet; + class ResidencyList; struct RESIDENCY_DESC; // Declare backend aliases. @@ -47,7 +47,7 @@ namespace gpgmm::d3d12 { }; struct EXECUTE_COMMAND_LISTS_DESC { - ResidencySet* const* ResidencySets; + ResidencyList* const* ResidencyLists; uint32_t Count; }; diff --git a/src/gpgmm/d3d12/ResidencySetD3D12.cpp b/src/gpgmm/d3d12/ResidencyListD3D12.cpp similarity index 64% rename from src/gpgmm/d3d12/ResidencySetD3D12.cpp rename to src/gpgmm/d3d12/ResidencyListD3D12.cpp index 34fb39728..c2ca8f74a 100644 --- a/src/gpgmm/d3d12/ResidencySetD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyListD3D12.cpp @@ -12,12 +12,46 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "gpgmm/d3d12/ResidencySetD3D12.h" +#include "gpgmm/d3d12/ResidencyListD3D12.h" #include "gpgmm/common/TraceEvent.h" namespace gpgmm::d3d12 { + ResidencyList::ResidencyList() { + GPGMM_TRACE_EVENT_OBJECT_NEW(this); + } + + ResidencyList::~ResidencyList() { + GPGMM_TRACE_EVENT_OBJECT_DESTROY(this); + } + + HRESULT ResidencyList::Add(Heap* pHeap) { + if (pHeap == nullptr) { + return E_INVALIDARG; + } + + mList.push_back(pHeap); + return S_OK; + } + + HRESULT ResidencyList::Reset() { + mList.clear(); + return S_OK; + } + + ResidencyList::UnderlyingType::const_iterator ResidencyList::begin() const { + return mList.begin(); + } + + ResidencyList::UnderlyingType::const_iterator ResidencyList::end() const { + return mList.end(); + } + + const char* ResidencyList::GetTypename() const { + return "ResidencyList"; + } + ResidencySet::ResidencySet() { GPGMM_TRACE_EVENT_OBJECT_NEW(this); } diff --git a/src/gpgmm/d3d12/ResidencySetD3D12.h b/src/gpgmm/d3d12/ResidencyListD3D12.h similarity index 57% rename from src/gpgmm/d3d12/ResidencySetD3D12.h rename to src/gpgmm/d3d12/ResidencyListD3D12.h index b487a83e3..f41bffa64 100644 --- a/src/gpgmm/d3d12/ResidencySetD3D12.h +++ b/src/gpgmm/d3d12/ResidencyListD3D12.h @@ -12,18 +12,66 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef GPGMM_D3D12_RESIDENCYSETD3D12_H_ -#define GPGMM_D3D12_RESIDENCYSETD3D12_H_ +#ifndef GPGMM_D3D12_RESIDENCYLISTD3D12_H_ +#define GPGMM_D3D12_RESIDENCYLISTD3D12_H_ #include "gpgmm/d3d12/d3d12_platform.h" #include "include/gpgmm_export.h" #include +#include namespace gpgmm::d3d12 { class Heap; + /** \brief Represents a list of heaps which will be "made resident" when executing a + command-list. + + A residency set helps track heaps for residency which will be referenced together by a + command-list. The application uses a ResidencyList by inserting heaps, by calling + ResourceAllocation::GetMemory, into the list. Once ResidencyManager::ExecuteCommandLists is + called, the list can be reset or cleared for the next frame. + + Without a ResidencyList, the application would need to manually ResidencyManager::LockHeap and + ResidencyManager::UnlockHeap each heap before and after ResidencyManager::ExecuteCommandLists, + respectively. + */ + class GPGMM_EXPORT ResidencyList final { + public: + /** \brief Create a residency list or collection of heaps to manage together for residency. + */ + ResidencyList(); + ~ResidencyList(); + + ResidencyList(const ResidencyList&) = default; + ResidencyList& operator=(const ResidencyList&) = default; + + /** \brief Adds a heap to the residency list. + + @param pHeap A pointer to Heap about to be added. + + \return S_OK if heap was added, else error. + */ + HRESULT Add(Heap* pHeap); + + /** \brief Reset this residency set. + + Removes all heaps from the set so the set can be re-used. + */ + HRESULT Reset(); + + using UnderlyingType = std::vector; + + UnderlyingType::const_iterator begin() const; + UnderlyingType::const_iterator end() const; + + private: + const char* GetTypename() const; + + UnderlyingType mList; + }; + /** \brief Represents a set of heaps which will be "made resident" when executing a command-list. @@ -35,6 +83,8 @@ namespace gpgmm::d3d12 { Without a ResidencySet, the application would need to manually ResidencyManager::LockHeap and ResidencyManager::UnlockHeap each heap before and after ResidencyManager::ExecuteCommandLists, respectively. + + \deprecated Use ResidencyList instead of ResidencySet. */ class GPGMM_EXPORT ResidencySet final { public: diff --git a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp index bb8ac2f2a..2c4ea47e8 100644 --- a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp @@ -23,7 +23,7 @@ #include "gpgmm/d3d12/FenceD3D12.h" #include "gpgmm/d3d12/HeapD3D12.h" #include "gpgmm/d3d12/JSONSerializerD3D12.h" -#include "gpgmm/d3d12/ResidencySetD3D12.h" +#include "gpgmm/d3d12/ResidencyListD3D12.h" #include "gpgmm/d3d12/UtilsD3D12.h" #include "gpgmm/utils/Math.h" @@ -565,12 +565,36 @@ namespace gpgmm::d3d12 { return S_OK; } + /** \brief Execute command lists using residency managed heaps. + * + /deprecated use ResidencyList instead of ResidencySet + */ + HRESULT ResidencyManager::ExecuteCommandLists(ID3D12CommandQueue* pQueue, + ID3D12CommandList* const* ppCommandLists, + ResidencySet* const* ppResidencySets, + uint32_t count) { + ResidencyList residencyList; + + // TODO: support multiple command lists. + if (count > 1) { + return E_NOTIMPL; + } + + for (Heap* heap : *ppResidencySets[0]) { + residencyList.Add(heap); + } + + ResidencyList* residencyListPtr = &residencyList; + + return ExecuteCommandLists(pQueue, ppCommandLists, &residencyListPtr, count); + } + // Given a list of heaps that are pending usage, this function will estimate memory needed, // evict resources until enough space is available, then make resident any heaps scheduled for // usage. HRESULT ResidencyManager::ExecuteCommandLists(ID3D12CommandQueue* pQueue, ID3D12CommandList* const* ppCommandLists, - ResidencySet* const* ppResidencySets, + ResidencyList* const* ppResidencyLists, uint32_t count) { TRACE_EVENT0(TraceEventCategory::Default, "ResidencyManager.ExecuteCommandLists"); @@ -585,19 +609,25 @@ namespace gpgmm::d3d12 { return E_NOTIMPL; } - ResidencySet* residencySet = ppResidencySets[0]; + ResidencyList* residencyList = ppResidencyLists[0]; std::vector localHeapsToMakeResident; std::vector nonLocalHeapsToMakeResident; uint64_t localSizeToMakeResident = 0; uint64_t nonLocalSizeToMakeResident = 0; - for (Heap* heap : *residencySet) { + for (Heap* heap : *residencyList) { // Heaps that are locked resident are not tracked in the LRU cache. if (heap->IsResidencyLocked()) { continue; } + // ResidencyList can contain duplicates. We can skip them by checking if the heap's last + // used fence is the same as the current one. + if (heap->GetLastUsedFenceValue() == mFence->GetCurrentFence()) { + continue; + } + if (heap->IsInResidencyLRUCache()) { // If the heap is already in the LRU, we must remove it and append again below to // update its position in the LRU. @@ -657,7 +687,7 @@ namespace gpgmm::d3d12 { } GPGMM_TRACE_EVENT_OBJECT_CALL("ResidencyManager.ExecuteCommandLists", - (EXECUTE_COMMAND_LISTS_DESC{ppResidencySets, count})); + (EXECUTE_COMMAND_LISTS_DESC{ppResidencyLists, count})); return S_OK; } diff --git a/src/gpgmm/d3d12/ResidencyManagerD3D12.h b/src/gpgmm/d3d12/ResidencyManagerD3D12.h index 9d09eef3a..cdd766049 100644 --- a/src/gpgmm/d3d12/ResidencyManagerD3D12.h +++ b/src/gpgmm/d3d12/ResidencyManagerD3D12.h @@ -32,6 +32,7 @@ namespace gpgmm::d3d12 { class Fence; class Heap; + class ResidencyList; class ResidencySet; class ResourceAllocator; @@ -140,7 +141,7 @@ namespace gpgmm::d3d12 { A Heap is considered "resident" when it is accessible by the GPU. A Heap can be made explicitly resident by calling ResidencyManager::LockHeap or implicitly resident by using the Heap with a - ResidencySet upon calling ResidencyManager::ExecuteCommandLists or through a + ResidencyList upon calling ResidencyManager::ExecuteCommandLists or through a operation that always requires the Heap to be resident (eg. Map, Unmap). Internally, the ResidencyManager keeps the application in-budget by calling ID3D12Device::Evict @@ -180,12 +181,28 @@ namespace gpgmm::d3d12 { /** \brief Execute command lists using residency managed heaps. + Submits an array of command lists and residency lists for the specified command queue. + + @param pQueue The command queue to submit to. + @param ppCommandLists The array of ID3D12CommandList command lists to be executed. + @param ppResidencyLists The array of ResidencyList residency lists to make resident. + @param count The size of commandLists and residencyLists arrays. + */ + HRESULT ExecuteCommandLists(ID3D12CommandQueue* pQueue, + ID3D12CommandList* const* ppCommandLists, + ResidencyList* const* ppResidencyLists, + uint32_t count); + + /** \brief Execute command lists using residency managed heaps. + Submits an array of command lists and residency sets for the specified command queue. @param pQueue The command queue to submit to. @param ppCommandLists The array of ID3D12CommandList command lists to be executed. @param ppResidencySets The array of ResidencySet residency sets to make resident. - @param count The size of commandLists and residencySets arrays. + @param count The size of commandLists and residencyLists arrays. + + /deprecated Use ResidencyList instead of ResidencySet. */ HRESULT ExecuteCommandLists(ID3D12CommandQueue* pQueue, ID3D12CommandList* const* ppCommandLists, diff --git a/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp b/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp index dda76ac09..096eca4c7 100644 --- a/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp +++ b/src/gpgmm/d3d12/ResourceAllocationD3D12.cpp @@ -21,8 +21,8 @@ #include "gpgmm/d3d12/ErrorD3D12.h" #include "gpgmm/d3d12/HeapD3D12.h" #include "gpgmm/d3d12/JSONSerializerD3D12.h" +#include "gpgmm/d3d12/ResidencyListD3D12.h" #include "gpgmm/d3d12/ResidencyManagerD3D12.h" -#include "gpgmm/d3d12/ResidencySetD3D12.h" #include "gpgmm/d3d12/UtilsD3D12.h" #include diff --git a/src/gpgmm/d3d12/ResourceAllocationD3D12.h b/src/gpgmm/d3d12/ResourceAllocationD3D12.h index 611a3818a..b7f17ff97 100644 --- a/src/gpgmm/d3d12/ResourceAllocationD3D12.h +++ b/src/gpgmm/d3d12/ResourceAllocationD3D12.h @@ -28,7 +28,7 @@ namespace gpgmm::d3d12 { class DebugResourceAllocator; class Heap; class ResidencyManager; - class ResidencySet; + class ResidencyList; /** \struct RESOURCE_ALLOCATION_DESC Describes a resource allocation. diff --git a/src/include/gpgmm_d3d12.h b/src/include/gpgmm_d3d12.h index b4a8b33d2..b66e801ae 100644 --- a/src/include/gpgmm_d3d12.h +++ b/src/include/gpgmm_d3d12.h @@ -20,7 +20,7 @@ // clang-format off #include "gpgmm/d3d12/HeapD3D12.h" -#include "gpgmm/d3d12/ResidencySetD3D12.h" +#include "gpgmm/d3d12/ResidencyListD3D12.h" #include "gpgmm/d3d12/ResidencyManagerD3D12.h" #include "gpgmm/d3d12/ResourceAllocationD3D12.h" #include "gpgmm/d3d12/ResourceAllocatorD3D12.h" diff --git a/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp b/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp index ef242ba79..389acaeb5 100644 --- a/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp +++ b/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp @@ -125,7 +125,7 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith std::string currentAllocatorID; std::string currentResidencyID; - std::vector currentResidencySets; + std::vector currentResidencyLists; const Json::Value& traceEvents = root["traceEvents"]; ASSERT_TRUE(!traceEvents.empty()); @@ -145,19 +145,19 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith continue; } - // Create ResidencySets. - std::vector residencySetPtrs; - for (auto& setJson : args["ResidencySets"]) { - ResidencySet set = {}; + // Create ResidencyLists. + std::vector residencyListPtrs; + for (auto& setJson : args["ResidencyLists"]) { + ResidencyList list = {}; for (auto heap : setJson["Heaps"]) { const std::string heapId = heap["id_ref"].asString(); if (createdHeapToID.find(heapId) == createdHeapToID.end()) { break; } - set.Insert(createdHeapToID[heapId].get()); + list.Add(createdHeapToID[heapId].get()); } - residencySetPtrs.push_back(&set); - currentResidencySets.push_back(std::move(set)); + residencyListPtrs.push_back(&list); + currentResidencyLists.push_back(std::move(list)); } ResidencyManager* residencyManager = @@ -165,11 +165,11 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith ASSERT_NE(residencyManager, nullptr); ASSERT_SUCCEEDED(residencyManager->ExecuteCommandLists( - nullptr, nullptr, residencySetPtrs.data(), - static_cast(residencySetPtrs.size()))); + nullptr, nullptr, residencyListPtrs.data(), + static_cast(residencyListPtrs.size()))); // Prepare for the next frame. - for (auto& set : currentResidencySets) { + for (auto& set : currentResidencyLists) { set.Reset(); } diff --git a/src/tests/end2end/D3D12ResidencyManagerTests.cpp b/src/tests/end2end/D3D12ResidencyManagerTests.cpp index 52b0abaf3..e2eb43838 100644 --- a/src/tests/end2end/D3D12ResidencyManagerTests.cpp +++ b/src/tests/end2end/D3D12ResidencyManagerTests.cpp @@ -144,7 +144,7 @@ TEST_F(D3D12ResidencyManagerTests, CreateResourceHeap) { ASSERT_FAILED(residencyManager->UnlockHeap(resourceHeap.Get())); // Not locked } -TEST_F(D3D12ResidencyManagerTests, CreateResidencySet) { +TEST_F(D3D12ResidencyManagerTests, CreateResidencyList) { ComPtr resourceAllocator; ASSERT_SUCCEEDED(ResourceAllocator::CreateAllocator(CreateBasicAllocatorDesc(), &resourceAllocator, nullptr)); @@ -155,26 +155,24 @@ TEST_F(D3D12ResidencyManagerTests, CreateResidencySet) { // Inserting a non-existant heap should always fail { - ResidencySet set; + ResidencyList list; Heap* invalid = nullptr; - ASSERT_FAILED(set.Insert(invalid)); + ASSERT_FAILED(list.Add(invalid)); } // Inserting from a valid allocation should always succeed. { - ResidencySet set; - ASSERT_SUCCEEDED(set.Insert(allocation->GetMemory())); - ASSERT_SUCCEEDED(set.Insert(allocation->GetMemory())); + ResidencyList list; + ASSERT_SUCCEEDED(list.Add(allocation->GetMemory())); + ASSERT_SUCCEEDED(list.Add(allocation->GetMemory())); } // Re-inserting allocation between two sets should always succeed. { - ResidencySet setA; - ASSERT_SUCCEEDED(setA.Insert(allocation->GetMemory())); - ResidencySet setB(setA); - EXPECT_EQ(setA.Insert(allocation->GetMemory()), S_FALSE); - ResidencySet setC; - EXPECT_EQ(setC.Insert(allocation->GetMemory()), S_OK); + ResidencyList listA; + ASSERT_SUCCEEDED(listA.Add(allocation->GetMemory())); + ResidencyList listB(listA); + EXPECT_EQ(listB.Add(allocation->GetMemory()), S_OK); } }