diff --git a/OptiScaler/hudfix/Hudfix_Dx12.cpp b/OptiScaler/hudfix/Hudfix_Dx12.cpp index 4aeb8f96..0f2f05af 100644 --- a/OptiScaler/hudfix/Hudfix_Dx12.cpp +++ b/OptiScaler/hudfix/Hudfix_Dx12.cpp @@ -92,6 +92,140 @@ inline static bool CompareResourceFormats(DXGI_FORMAT sc, DXGI_FORMAT hudless) return scGroup >= 0 && scGroup == hudlessGroup; } +void Hudfix_Dx12::RemoveCaptureBuffer(ID3D12Resource** resource) +{ + if (resource == nullptr || *resource == nullptr) + return; + + auto* removedResource = *resource; + *resource = nullptr; + + { + std::lock_guard lock(_captureRemoveMutex); + _pendingCaptureRemovals.push_back(removedResource); + _captureRemovalActive.store(true, std::memory_order_release); + } + + LOG_DEBUG("Capture buffer removed: {:X}", (size_t) removedResource); +} + +void Hudfix_Dx12::ProcessPendingCaptureRemovals() +{ + if (!_captureRemovalActive.load(std::memory_order_acquire)) + return; + + std::vector completedResources; + std::vector completedFences; + + { + std::lock_guard lock(_captureRemoveMutex); + + for (auto it = _captureRemoveInfos.begin(); it != _captureRemoveInfos.end();) + { + if (it->fence != nullptr && it->fence->GetCompletedValue() >= it->fenceValue) + { + completedResources.insert(completedResources.end(), it->resources.begin(), it->resources.end()); + completedFences.push_back(it->fence); + it = _captureRemoveInfos.erase(it); + } + else + { + ++it; + } + } + + if (_captureRemoveInfos.empty() && _pendingCaptureRemovals.empty()) + _captureRemovalActive.store(false, std::memory_order_release); + } + + // Release outside _captureRemoveMutex + for (auto* resource : completedResources) + { + if (resource != nullptr) + resource->Release(); + } + + for (auto* fence : completedFences) + { + if (fence != nullptr) + fence->Release(); + } + + if (!_captureRemovalActive.load(std::memory_order_acquire)) + return; + + if (State::Instance().isShuttingDown) + return; + + std::vector pendingResources; + { + std::lock_guard lock(_captureRemoveMutex); + if (_pendingCaptureRemovals.empty()) + return; + + pendingResources.swap(_pendingCaptureRemovals); + } + + auto* fg = State::Instance().currentFG; + ID3D12CommandQueue* queue = fg != nullptr ? fg->GetCommandQueue() : nullptr; + if (queue == nullptr) + queue = State::Instance().currentCommandQueue; + + auto requeuePending = [&pendingResources]() + { + std::lock_guard lock(_captureRemoveMutex); + _pendingCaptureRemovals.insert(_pendingCaptureRemovals.end(), pendingResources.begin(), pendingResources.end()); + _captureRemovalActive.store(true, std::memory_order_release); + }; + + if (queue == nullptr) + { + requeuePending(); + return; + } + + ID3D12Device* device = nullptr; + auto result = queue->GetDevice(IID_PPV_ARGS(&device)); + if (FAILED(result) || device == nullptr) + { + LOG_WARN("Can't get capture remove queue device: {:X}", (UINT) result); + requeuePending(); + return; + } + + ID3D12Fence* fence = nullptr; + result = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); + device->Release(); + + if (FAILED(result) || fence == nullptr) + { + LOG_WARN("Can't create capture remove fence: {:X}", (UINT) result); + requeuePending(); + return; + } + + constexpr UINT64 fenceValue = 1; + result = queue->Signal(fence, fenceValue); + if (FAILED(result)) + { + LOG_WARN("Can't signal capture remove fence: {:X}", (UINT) result); + fence->Release(); + requeuePending(); + return; + } + + CaptureRemoveInfo batch {}; + batch.fence = fence; + batch.fenceValue = fenceValue; + batch.resources = std::move(pendingResources); + + { + std::lock_guard lock(_captureRemoveMutex); + _captureRemoveInfos.push_back(std::move(batch)); + _captureRemovalActive.store(true, std::memory_order_release); + } +} + bool Hudfix_Dx12::CreateBufferResource(ID3D12Device* InDevice, ResourceInfo* InSource, D3D12_RESOURCE_STATES InState, ID3D12Resource** OutResource) { @@ -105,11 +239,8 @@ bool Hudfix_Dx12::CreateBufferResource(ID3D12Device* InDevice, ResourceInfo* InS if (bufDesc.Width != (UINT64) (InSource->width) || bufDesc.Height != (UINT) (InSource->height) || bufDesc.Format != InSource->format) { - // Maybe need to add a fence here - // To be sure it's not used anymore - (*OutResource)->Release(); - (*OutResource) = nullptr; - LOG_WARN("Release {}x{}, new one: {}x{}", bufDesc.Width, bufDesc.Height, InSource->width, InSource->height); + RemoveCaptureBuffer(OutResource); + LOG_WARN("Remove {}x{}, new one: {}x{}", bufDesc.Width, bufDesc.Height, InSource->width, InSource->height); } else { @@ -156,9 +287,8 @@ bool Hudfix_Dx12::CreateBufferResourceWithSize(ID3D12Device* InDevice, ResourceI if (bufDesc.Width != (UINT64) InWidth || bufDesc.Height != InHeight || bufDesc.Format != InSource->format) { - (*OutResource)->Release(); - (*OutResource) = nullptr; - LOG_WARN("Release {}x{}, new one: {}x{}", bufDesc.Width, bufDesc.Height, InWidth, InHeight); + RemoveCaptureBuffer(OutResource); + LOG_WARN("Remove {}x{}, new one: {}x{}", bufDesc.Width, bufDesc.Height, InWidth, InHeight); } else { @@ -443,7 +573,11 @@ void Hudfix_Dx12::PresentStart() _fgCounter.store(_upscaleCounter.load(std::memory_order_acquire), std::memory_order_release); } -void Hudfix_Dx12::PresentEnd() { LOG_DEBUG(""); } +void Hudfix_Dx12::PresentEnd() +{ + ProcessPendingCaptureRemovals(); + LOG_DEBUG(""); +} UINT64 Hudfix_Dx12::ActiveUpscaleFrame() { return _upscaleCounter.load(std::memory_order_acquire); } diff --git a/OptiScaler/hudfix/Hudfix_Dx12.h b/OptiScaler/hudfix/Hudfix_Dx12.h index 48a27dda..df22b678 100644 --- a/OptiScaler/hudfix/Hudfix_Dx12.h +++ b/OptiScaler/hudfix/Hudfix_Dx12.h @@ -9,6 +9,7 @@ #include #include #include +#include enum ResourceType { @@ -78,6 +79,18 @@ class Hudfix_Dx12 // Buffer for Format Transfer inline static ID3D12Resource* _captureBuffer[BUFFER_COUNT] = { nullptr, nullptr, nullptr, nullptr }; + struct CaptureRemoveInfo + { + ID3D12Fence* fence = nullptr; + UINT64 fenceValue = 0; + std::vector resources; + }; + + inline static std::atomic _captureRemovalActive { false }; + inline static std::mutex _captureRemoveMutex; + inline static std::vector _pendingCaptureRemovals; + inline static std::vector _captureRemoveInfos; + // used hudless list inline static ankerl::unordered_dense::map _hudlessList; @@ -93,6 +106,9 @@ class Hudfix_Dx12 inline static std::atomic _skipHudlessChecks { false }; + static void RemoveCaptureBuffer(ID3D12Resource** resource); + static void ProcessPendingCaptureRemovals(); + static bool CreateBufferResource(ID3D12Device* InDevice, ResourceInfo* InSource, D3D12_RESOURCE_STATES InState, ID3D12Resource** OutResource); static bool CreateBufferResourceWithSize(ID3D12Device* InDevice, ResourceInfo* InSource,