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); } }