diff --git a/src/gpgmm/BUILD.gn b/src/gpgmm/BUILD.gn index 03459f30..3d956a1b 100644 --- a/src/gpgmm/BUILD.gn +++ b/src/gpgmm/BUILD.gn @@ -113,6 +113,8 @@ source_set("gpgmm_sources") { libs += [ "dxguid.lib" ] sources += [ + "d3d12/BudgetUpdateD3D12.cpp", + "d3d12/BudgetUpdateD3D12.h", "d3d12/BufferAllocatorD3D12.cpp", "d3d12/BufferAllocatorD3D12.h", "d3d12/CapsD3D12.cpp", diff --git a/src/gpgmm/CMakeLists.txt b/src/gpgmm/CMakeLists.txt index 6ade789c..ab8ad459 100644 --- a/src/gpgmm/CMakeLists.txt +++ b/src/gpgmm/CMakeLists.txt @@ -55,6 +55,8 @@ endif() if (GPGMM_ENABLE_D3D12) target_sources(gpgmm PRIVATE + "d3d12/BudgetUpdateD3D12.cpp" + "d3d12/BudgetUpdateD3D12.h" "d3d12/BufferAllocatorD3D12.cpp" "d3d12/BufferAllocatorD3D12.h" "d3d12/DebugObjectD3D12.cpp" diff --git a/src/gpgmm/d3d12/BudgetUpdateD3D12.cpp b/src/gpgmm/d3d12/BudgetUpdateD3D12.cpp new file mode 100644 index 00000000..3c078b53 --- /dev/null +++ b/src/gpgmm/d3d12/BudgetUpdateD3D12.cpp @@ -0,0 +1,128 @@ +// Copyright 2022 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gpgmm/d3d12/BudgetUpdateD3D12.h" + +#include "gpgmm/common/Message.h" +#include "gpgmm/common/TraceEvent.h" +#include "gpgmm/d3d12/ErrorD3D12.h" +#include "gpgmm/d3d12/LogD3D12.h" +#include "gpgmm/d3d12/ResidencyManagerD3D12.h" + +namespace gpgmm::d3d12 { + + // BudgetUpdateTask + + BudgetUpdateTask::BudgetUpdateTask(ResidencyManager* const residencyManager, + IDXGIAdapter3* adapter) + : mResidencyManager(residencyManager), + mAdapter(std::move(adapter)), + mBudgetNotificationUpdateEvent(CreateEventW(NULL, FALSE, FALSE, NULL)), + mUnregisterAndExitEvent(CreateEventW(NULL, FALSE, FALSE, NULL)) { + ASSERT(mResidencyManager != nullptr); + ASSERT(mAdapter != nullptr); + mLastError = mAdapter->RegisterVideoMemoryBudgetChangeNotificationEvent( + mBudgetNotificationUpdateEvent, &mCookie); + } + + BudgetUpdateTask::~BudgetUpdateTask() { + CloseHandle(mUnregisterAndExitEvent); + CloseHandle(mBudgetNotificationUpdateEvent); + } + + void BudgetUpdateTask::operator()() { + HRESULT hr = GetLastError(); + bool isExiting = false; + while (!isExiting && SUCCEEDED(hr)) { + // Wait on two events: one to unblock for OS budget changes, and another to unblock + // for shutdown. + HANDLE hWaitEvents[2] = {mBudgetNotificationUpdateEvent, mUnregisterAndExitEvent}; + const DWORD waitedEvent = + WaitForMultipleObjects(2, hWaitEvents, /*bWaitAll*/ false, INFINITE); + switch (waitedEvent) { + // mBudgetNotificationUpdateEvent + case (WAIT_OBJECT_0 + 0): { + hr = mResidencyManager->UpdateMemorySegments(); + if (FAILED(hr)) { + break; + } + + DebugLog(mResidencyManager, MessageId::kBudgetUpdated) + << "Updated budget from OS notification."; + break; + } + // mUnregisterAndExitEvent + case (WAIT_OBJECT_0 + 1): { + isExiting = true; + break; + } + default: { + UNREACHABLE(); + break; + } + } + } + + if (FAILED(hr)) { + ErrorLog(mResidencyManager, MessageId::kBudgetInvalid) + << "Unable to update budget: " + + GetDeviceErrorMessage(mResidencyManager->mDevice, hr); + } + + SetLastError(hr); + } + + HRESULT BudgetUpdateTask::GetLastError() const { + std::lock_guard lock(mMutex); + return mLastError; + } + + bool BudgetUpdateTask::UnregisterAndExit() { + mAdapter->UnregisterVideoMemoryBudgetChangeNotification(mCookie); + return SetEvent(mUnregisterAndExitEvent); + } + + void BudgetUpdateTask::SetLastError(HRESULT hr) { + std::lock_guard lock(mMutex); + mLastError = hr; + } + + // BudgetUpdateEvent + + BudgetUpdateEvent::BudgetUpdateEvent(std::shared_ptr event, + std::shared_ptr task) + : mTask(task), mEvent(event) { + } + + void BudgetUpdateEvent::Wait() { + mEvent->Wait(); + } + + bool BudgetUpdateEvent::IsSignaled() { + return mEvent->IsSignaled(); + } + + void BudgetUpdateEvent::Signal() { + return mEvent->Signal(); + } + + bool BudgetUpdateEvent::UnregisterAndExit() { + return mTask->UnregisterAndExit(); + } + + bool BudgetUpdateEvent::GetLastError() const { + return mTask->GetLastError(); + } + +} // namespace gpgmm::d3d12 diff --git a/src/gpgmm/d3d12/BudgetUpdateD3D12.h b/src/gpgmm/d3d12/BudgetUpdateD3D12.h new file mode 100644 index 00000000..671c64c3 --- /dev/null +++ b/src/gpgmm/d3d12/BudgetUpdateD3D12.h @@ -0,0 +1,76 @@ +// Copyright 2022 The GPGMM Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef GPGMM_D3D12_BUDGETUPDATED3D12_H_ +#define GPGMM_D3D12_BUDGETUPDATED3D12_H_ + +#include "gpgmm/common/ThreadPool.h" +#include "gpgmm/d3d12/D3D12Platform.h" + +#include +#include + +namespace gpgmm::d3d12 { + + class ResidencyManager; + + // Creates a long-lived task to recieve and process OS budget change events. + class BudgetUpdateTask : public VoidCallback { + public: + BudgetUpdateTask(ResidencyManager* const residencyManager, IDXGIAdapter3* adapter); + ~BudgetUpdateTask() override; + + void operator()() override; + + HRESULT GetLastError() const; + + // Shutdown the event loop. + bool UnregisterAndExit(); + + private: + void SetLastError(HRESULT hr); + + ResidencyManager* const mResidencyManager; + IDXGIAdapter3* mAdapter = nullptr; + + HANDLE mBudgetNotificationUpdateEvent = INVALID_HANDLE_VALUE; + HANDLE mUnregisterAndExitEvent = INVALID_HANDLE_VALUE; + + DWORD mCookie = 0; // Used to unregister from notifications. + + mutable std::mutex mMutex; // Protect access between threads for members below. + HRESULT mLastError = S_OK; + }; + + class BudgetUpdateEvent final : public Event { + public: + BudgetUpdateEvent(std::shared_ptr event, std::shared_ptr task); + + // Event overrides + void Wait() override; + bool IsSignaled() override; + void Signal() override; + + bool UnregisterAndExit(); + + bool GetLastError() const; + + private: + std::shared_ptr mTask; + std::shared_ptr mEvent; + }; + +} // namespace gpgmm::d3d12 + +#endif // GPGMM_D3D12_BUDGETUPDATED3D12_H_ diff --git a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp index 2882f111..bede14ea 100644 --- a/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp +++ b/src/gpgmm/d3d12/ResidencyManagerD3D12.cpp @@ -17,8 +17,7 @@ #include "gpgmm/common/EventMessage.h" #include "gpgmm/common/SizeClass.h" -#include "gpgmm/common/ThreadPool.h" -#include "gpgmm/common/TraceEvent.h" +#include "gpgmm/d3d12/BudgetUpdateD3D12.h" #include "gpgmm/d3d12/CapsD3D12.h" #include "gpgmm/d3d12/ErrorD3D12.h" #include "gpgmm/d3d12/FenceD3D12.h" @@ -39,128 +38,6 @@ namespace gpgmm::d3d12 { static constexpr float kDefaultMinPctOfBudgetToReserve = 0.50f; // 50% static constexpr float kMinCurrentUsageOfBudgetReportingThreshold = 0.90f; - // Creates a long-lived task to recieve and process OS budget change events. - class BudgetUpdateTask : public VoidCallback { - public: - BudgetUpdateTask(ResidencyManager* const residencyManager, IDXGIAdapter3* adapter) - : mResidencyManager(residencyManager), - mAdapter(std::move(adapter)), - mBudgetNotificationUpdateEvent(CreateEventW(NULL, FALSE, FALSE, NULL)), - mUnregisterAndExitEvent(CreateEventW(NULL, FALSE, FALSE, NULL)) { - ASSERT(mResidencyManager != nullptr); - ASSERT(mAdapter != nullptr); - mLastError = mAdapter->RegisterVideoMemoryBudgetChangeNotificationEvent( - mBudgetNotificationUpdateEvent, &mCookie); - } - - ~BudgetUpdateTask() override { - CloseHandle(mUnregisterAndExitEvent); - CloseHandle(mBudgetNotificationUpdateEvent); - } - - void operator()() override { - HRESULT hr = GetLastError(); - bool isExiting = false; - while (!isExiting && SUCCEEDED(hr)) { - // Wait on two events: one to unblock for OS budget changes, and another to unblock - // for shutdown. - HANDLE hWaitEvents[2] = {mBudgetNotificationUpdateEvent, mUnregisterAndExitEvent}; - const DWORD waitedEvent = - WaitForMultipleObjects(2, hWaitEvents, /*bWaitAll*/ false, INFINITE); - switch (waitedEvent) { - // mBudgetNotificationUpdateEvent - case (WAIT_OBJECT_0 + 0): { - hr = mResidencyManager->UpdateMemorySegments(); - if (FAILED(hr)) { - break; - } - - DebugLog(mResidencyManager, MessageId::kBudgetUpdated) - << "Updated budget from OS notification."; - break; - } - // mUnregisterAndExitEvent - case (WAIT_OBJECT_0 + 1): { - isExiting = true; - break; - } - default: { - UNREACHABLE(); - break; - } - } - } - - if (FAILED(hr)) { - ErrorLog(mResidencyManager, MessageId::kBudgetInvalid) - << "Unable to update budget: " + - GetDeviceErrorMessage(mResidencyManager->mDevice, hr); - } - - SetLastError(hr); - } - - HRESULT GetLastError() const { - std::lock_guard lock(mMutex); - return mLastError; - } - - // Shutdown the event loop. - bool UnregisterAndExit() { - mAdapter->UnregisterVideoMemoryBudgetChangeNotification(mCookie); - return SetEvent(mUnregisterAndExitEvent); - } - - private: - void SetLastError(HRESULT hr) { - std::lock_guard lock(mMutex); - mLastError = hr; - } - - ResidencyManager* const mResidencyManager; - IDXGIAdapter3* mAdapter = nullptr; - - HANDLE mBudgetNotificationUpdateEvent = INVALID_HANDLE_VALUE; - HANDLE mUnregisterAndExitEvent = INVALID_HANDLE_VALUE; - - DWORD mCookie = 0; // Used to unregister from notifications. - - mutable std::mutex mMutex; // Protect access between threads for members below. - HRESULT mLastError = S_OK; - }; - - class BudgetUpdateEvent final : public Event { - public: - BudgetUpdateEvent(std::shared_ptr event, std::shared_ptr task) - : mTask(task), mEvent(event) { - } - - // Event overrides - void Wait() override { - mEvent->Wait(); - } - - bool IsSignaled() override { - return mEvent->IsSignaled(); - } - - void Signal() override { - return mEvent->Signal(); - } - - bool UnregisterAndExit() { - return mTask->UnregisterAndExit(); - } - - bool GetLastError() const { - return mTask->GetLastError(); - } - - private: - std::shared_ptr mTask; - std::shared_ptr mEvent; - }; - HRESULT CreateResidencyManager(const RESIDENCY_MANAGER_DESC& descriptor, ID3D12Device* pDevice, IDXGIAdapter3* pAdapter,