From cf549e3be8ab8db60c73d644dc96039ae0cef4ab Mon Sep 17 00:00:00 2001 From: cdozdil Date: Sun, 11 Jan 2026 14:19:20 +0300 Subject: [PATCH] Made resource tracking optional --- OptiScaler.ini | 5 + OptiScaler/Config.cpp | 2 + OptiScaler/Config.h | 1 + .../resource_tracking/ResTrack_dx12.cpp | 281 ++++++++++++++++-- OptiScaler/resource_tracking/ResTrack_dx12.h | 10 +- 5 files changed, 259 insertions(+), 40 deletions(-) diff --git a/OptiScaler.ini b/OptiScaler.ini index 58516495..e1a28ecd 100644 --- a/OptiScaler.ini +++ b/OptiScaler.ini @@ -306,6 +306,11 @@ HUDFixImmediate=auto ; true or false - Default (auto) is false AlwaysTrackHeaps=auto +; Instead of single mutex use shards for tracking resources +; Might improve performance on heavy multithreaded games +; true or false - Default (auto) is false +UseShards=auto + ; Block rarely used resources from using as hudless ; to prevent flickers and other issues ; true or false - Default (auto) is false diff --git a/OptiScaler/Config.cpp b/OptiScaler/Config.cpp index b129c32c..7c6c4f35 100644 --- a/OptiScaler/Config.cpp +++ b/OptiScaler/Config.cpp @@ -142,6 +142,7 @@ bool Config::Reload(std::filesystem::path iniPath) FGHUDLimit.set_from_config(readInt("OptiFG", "HUDLimit")); FGHUDFixExtended.set_from_config(readBool("OptiFG", "HUDFixExtended")); FGImmediateCapture.set_from_config(readBool("OptiFG", "HUDFixImmediate")); + FGUseShards.set_from_config(readBool("OptiFG", "UseShards")); FGAlwaysTrackHeaps.set_from_config(readBool("OptiFG", "AlwaysTrackHeaps")); FGResourceBlocking.set_from_config(readBool("OptiFG", "ResourceBlocking")); FGMakeDepthCopy.set_from_config(readBool("OptiFG", "MakeDepthCopy")); @@ -768,6 +769,7 @@ bool Config::SaveIni() ini.SetValue("OptiFG", "HUDFixExtended", GetBoolValue(Instance()->FGHUDFixExtended.value_for_config()).c_str()); ini.SetValue("OptiFG", "HUDFixImmediate", GetBoolValue(Instance()->FGImmediateCapture.value_for_config()).c_str()); + ini.SetValue("OptiFG", "UseShards", GetBoolValue(Instance()->FGUseShards.value_for_config()).c_str()); ini.SetValue("OptiFG", "AlwaysTrackHeaps", GetBoolValue(Instance()->FGAlwaysTrackHeaps.value_for_config()).c_str()); ini.SetValue("OptiFG", "ResourceBlocking", diff --git a/OptiScaler/Config.h b/OptiScaler/Config.h index 4df44bc1..0c3b1e0b 100644 --- a/OptiScaler/Config.h +++ b/OptiScaler/Config.h @@ -438,6 +438,7 @@ class Config // OptiFG - Resource Tracking CustomOptional FGAlwaysTrackHeaps { false }; CustomOptional FGResourceBlocking { false }; + CustomOptional FGUseShards { false }; // OptiFG - DLSS-D Depth scale CustomOptional FGEnableDepthScale { false }; diff --git a/OptiScaler/resource_tracking/ResTrack_dx12.cpp b/OptiScaler/resource_tracking/ResTrack_dx12.cpp index d028b3f4..d6e9f814 100644 --- a/OptiScaler/resource_tracking/ResTrack_dx12.cpp +++ b/OptiScaler/resource_tracking/ResTrack_dx12.cpp @@ -109,7 +109,12 @@ static PFN_OMSetRenderTargets o_OMSetRenderTargets = nullptr; static PFN_SetGraphicsRootDescriptorTable o_SetGraphicsRootDescriptorTable = nullptr; static PFN_SetComputeRootDescriptorTable o_SetComputeRootDescriptorTable = nullptr; -// heaps +static std::mutex _hudlessTrackMutex; +static ankerl::unordered_dense::map> + fgPossibleHudless[BUFFER_COUNT]; + +// heaps section // #define USE_SPINLOCK_MUTEX_FOR_HEAP_CREATION @@ -1190,10 +1195,26 @@ void ResTrack_Dx12::hkSetGraphicsRootDescriptorTable(ID3D12GraphicsCommandList* } auto fIndex = Hudfix_Dx12::ActivePresentFrame() % BUFFER_COUNT; - size_t shardIdx = GetShardIndex(This); - auto& shard = _hudlessShards[fIndex][shardIdx]; + if (!_useShards) { + std::lock_guard lock(_hudlessTrackMutex); + + if (!fgPossibleHudless[fIndex].contains(This)) + { + ankerl::unordered_dense::map newMap; + newMap.reserve(32); + fgPossibleHudless[fIndex].insert_or_assign(This, newMap); + } + + LOG_TRACK("AddRef Resource: {:X}, Desc: {:X}", (size_t) capturedBuffer->buffer, BaseDescriptor.ptr); + fgPossibleHudless[fIndex][This].insert_or_assign(capturedBuffer->buffer, *capturedBuffer); + } + else + { + size_t shardIdx = GetShardIndex(This); + auto& shard = _hudlessShards[fIndex][shardIdx]; + #ifdef USE_SPINLOCK_MUTEX std::lock_guard lock(shard.mutex); #else @@ -1293,10 +1314,27 @@ void ResTrack_Dx12::hkOMSetRenderTargets(ID3D12GraphicsCommandList* This, UINT N } auto fIndex = Hudfix_Dx12::ActivePresentFrame() % BUFFER_COUNT; - size_t shardIdx = GetShardIndex(This); - auto& shard = _hudlessShards[fIndex][shardIdx]; + if (!_useShards) { + std::lock_guard lock(_hudlessTrackMutex); + + if (!fgPossibleHudless[fIndex].contains(This)) + { + ankerl::unordered_dense::map newMap; + newMap.reserve(32); + fgPossibleHudless[fIndex].insert_or_assign(This, newMap); + } + + // add found resource + LOG_TRACK("AddRef Resource: {:X}, Desc: {:X}", (size_t) capturedBuffer->buffer, handle.ptr); + fgPossibleHudless[fIndex][This].insert_or_assign(capturedBuffer->buffer, *capturedBuffer); + } + else + { + size_t shardIdx = GetShardIndex(This); + auto& shard = _hudlessShards[fIndex][shardIdx]; + #ifdef USE_SPINLOCK_MUTEX std::lock_guard lock(shard.mutex); #else @@ -1382,10 +1420,27 @@ void ResTrack_Dx12::hkSetComputeRootDescriptorTable(ID3D12GraphicsCommandList* T } auto fIndex = Hudfix_Dx12::ActivePresentFrame() % BUFFER_COUNT; - size_t shardIdx = GetShardIndex(This); - auto& shard = _hudlessShards[fIndex][shardIdx]; + if (!_useShards) { + std::lock_guard lock(_hudlessTrackMutex); + + if (!fgPossibleHudless[fIndex].contains(This)) + { + ankerl::unordered_dense::map newMap; + newMap.reserve(32); + fgPossibleHudless[fIndex].insert_or_assign(This, newMap); + } + + // add found resource + LOG_TRACK("AddRef Resource: {:X}, Desc: {:X}", (size_t) capturedBuffer->buffer, handle.ptr); + fgPossibleHudless[fIndex][This].insert_or_assign(capturedBuffer->buffer, *capturedBuffer); + } + else + { + size_t shardIdx = GetShardIndex(This); + auto& shard = _hudlessShards[fIndex][shardIdx]; + #ifdef USE_SPINLOCK_MUTEX std::lock_guard lock(shard.mutex); #else @@ -1429,18 +1484,61 @@ void ResTrack_Dx12::hkDrawInstanced(ID3D12GraphicsCommandList* This, UINT Vertex LOG_TRACK("CmdList: {:X}", (size_t) This); auto fIndex = Hudfix_Dx12::ActivePresentFrame() % BUFFER_COUNT; - size_t shardIdx = GetShardIndex(This); - auto& shard = _hudlessShards[fIndex][shardIdx]; + if (!_useShards) { -#ifdef USE_SPINLOCK_MUTEX - std::lock_guard lock(shard.mutex); -#else - std::lock_guard lock(shard.mutex); -#endif + if (This == MenuOverlayDx::MenuCommandList() && fgPossibleHudless[fIndex].contains(This)) + { + std::lock_guard lock(_hudlessTrackMutex); + fgPossibleHudless[fIndex][This].clear(); + return; + } + + if (fgPossibleHudless[fIndex].size() == 0 || !fgPossibleHudless[fIndex].contains(This)) + { + LOG_DEBUG_ONLY("Early exit"); + return; + } + + std::lock_guard lock(_hudlessTrackMutex); + auto val0 = fgPossibleHudless[fIndex][This]; + + do + { + // if this command list does not have entries skip + if (val0.size() == 0) + break; + + if (Config::Instance()->FGHudfixDisableDI.value_or_default()) + break; + + for (auto& [key, val] : val0) + { + std::lock_guard lock(_drawMutex); + + val.captureInfo |= CaptureInfo::DrawInstanced; + + if (Hudfix_Dx12::CheckForHudless(This, &val, val.state)) + break; + } + + } while (false); + + fgPossibleHudless[fIndex][This].clear(); + } + else + { + size_t shardIdx = GetShardIndex(This); + auto& shard = _hudlessShards[fIndex][shardIdx]; if (This == MenuOverlayDx::MenuCommandList() && shard.map.contains(This)) { +#ifdef USE_SPINLOCK_MUTEX + std::lock_guard lock(shard.mutex); +#else + std::lock_guard lock(shard.mutex); +#endif + shard.map[This].clear(); return; } @@ -1452,6 +1550,12 @@ void ResTrack_Dx12::hkDrawInstanced(ID3D12GraphicsCommandList* This, UINT Vertex return; } +#ifdef USE_SPINLOCK_MUTEX + std::lock_guard lock(shard.mutex); +#else + std::lock_guard lock(shard.mutex); +#endif + auto val0 = shard.map[This]; do @@ -1495,18 +1599,63 @@ void ResTrack_Dx12::hkDrawIndexedInstanced(ID3D12GraphicsCommandList* This, UINT LOG_TRACK("CmdList: {:X}", (size_t) This); auto fIndex = Hudfix_Dx12::ActivePresentFrame() % BUFFER_COUNT; - size_t shardIdx = GetShardIndex(This); - auto& shard = _hudlessShards[fIndex][shardIdx]; + if (!_useShards) { -#ifdef USE_SPINLOCK_MUTEX - std::lock_guard lock(shard.mutex); -#else - std::lock_guard lock(shard.mutex); -#endif + if (This == MenuOverlayDx::MenuCommandList() && fgPossibleHudless[fIndex].contains(This)) + { + std::lock_guard lock(_hudlessTrackMutex); + fgPossibleHudless[fIndex][This].clear(); + return; + } + + // if can't find output skip + if (fgPossibleHudless[fIndex].size() == 0 || !fgPossibleHudless[fIndex].contains(This)) + { + LOG_DEBUG_ONLY("Early exit"); + return; + } + + std::lock_guard lock(_hudlessTrackMutex); + auto val0 = fgPossibleHudless[fIndex][This]; + + do + { + // if this command list does not have entries skip + if (val0.size() == 0) + break; + + if (Config::Instance()->FGHudfixDisableDII.value_or_default()) + break; + + for (auto& [key, val] : val0) + { + // LOG_DEBUG("Waiting _drawMutex {:X}", (size_t)val.buffer); + std::lock_guard lock(_drawMutex); + + val.captureInfo |= CaptureInfo::DrawIndexedInstanced; + + if (Hudfix_Dx12::CheckForHudless(This, &val, val.state)) + break; + } + + } while (false); + + fgPossibleHudless[fIndex][This].clear(); + } + else + { + size_t shardIdx = GetShardIndex(This); + auto& shard = _hudlessShards[fIndex][shardIdx]; if (This == MenuOverlayDx::MenuCommandList() && shard.map.contains(This)) { +#ifdef USE_SPINLOCK_MUTEX + std::lock_guard lock(shard.mutex); +#else + std::lock_guard lock(shard.mutex); +#endif + shard.map[This].clear(); return; } @@ -1518,6 +1667,12 @@ void ResTrack_Dx12::hkDrawIndexedInstanced(ID3D12GraphicsCommandList* This, UINT return; } +#ifdef USE_SPINLOCK_MUTEX + std::lock_guard lock(shard.mutex); +#else + std::lock_guard lock(shard.mutex); +#endif + auto val0 = shard.map[This]; do @@ -1633,18 +1788,63 @@ void ResTrack_Dx12::hkDispatch(ID3D12GraphicsCommandList* This, UINT ThreadGroup LOG_TRACK("CmdList: {:X}", (size_t) This); auto fIndex = Hudfix_Dx12::ActivePresentFrame() % BUFFER_COUNT; - size_t shardIdx = GetShardIndex(This); - auto& shard = _hudlessShards[fIndex][shardIdx]; + if (!_useShards) { -#ifdef USE_SPINLOCK_MUTEX - std::lock_guard lock(shard.mutex); -#else - std::lock_guard lock(shard.mutex); -#endif + if (This == MenuOverlayDx::MenuCommandList() && fgPossibleHudless[fIndex].contains(This)) + { + std::lock_guard lock(_hudlessTrackMutex); + fgPossibleHudless[fIndex][This].clear(); + return; + } + + // if can't find output skip + if (fgPossibleHudless[fIndex].size() == 0 || !fgPossibleHudless[fIndex].contains(This)) + { + LOG_DEBUG_ONLY("Early exit"); + return; + } + + std::lock_guard lock(_hudlessTrackMutex); + auto val0 = fgPossibleHudless[fIndex][This]; + + do + { + // if this command list does not have entries skip + if (val0.size() == 0) + break; + + if (Config::Instance()->FGHudfixDisableDispatch.value_or_default()) + break; + + for (auto& [key, val] : val0) + { + // LOG_DEBUG("Waiting _drawMutex {:X}", (size_t)val.buffer); + std::lock_guard lock(_drawMutex); + + val.captureInfo |= CaptureInfo::Dispatch; + if (Hudfix_Dx12::CheckForHudless(This, &val, val.state)) + { + break; + } + } + } while (false); + + fgPossibleHudless[fIndex][This].clear(); + } + else + { + size_t shardIdx = GetShardIndex(This); + auto& shard = _hudlessShards[fIndex][shardIdx]; if (This == MenuOverlayDx::MenuCommandList() && shard.map.contains(This)) { +#ifdef USE_SPINLOCK_MUTEX + std::lock_guard lock(shard.mutex); +#else + std::lock_guard lock(shard.mutex); +#endif + shard.map[This].clear(); return; } @@ -1656,6 +1856,12 @@ void ResTrack_Dx12::hkDispatch(ID3D12GraphicsCommandList* This, UINT ThreadGroup return; } +#ifdef USE_SPINLOCK_MUTEX + std::lock_guard lock(shard.mutex); +#else + std::lock_guard lock(shard.mutex); +#endif + auto val0 = shard.map[This]; do @@ -1844,6 +2050,7 @@ void ResTrack_Dx12::HookDevice(ID3D12Device* device) if (device == nullptr) return; + _useShards = Config::Instance()->FGUseShards.value_or_default(); _trackedResources.reserve(1024); fgHeaps.reserve(65536); @@ -1984,17 +2191,25 @@ void ResTrack_Dx12::ClearPossibleHudless() auto hfIndex = Hudfix_Dx12::ActivePresentFrame() % BUFFER_COUNT; - for (size_t i = 0; i < SHARD_COUNT; i++) + if (!_useShards) { - auto& shard = _hudlessShards[hfIndex][i]; + std::lock_guard lock(_hudlessTrackMutex); + fgPossibleHudless[hfIndex].clear(); + } + else + { + for (size_t i = 0; i < SHARD_COUNT; i++) + { + auto& shard = _hudlessShards[hfIndex][i]; #ifdef USE_SPINLOCK_MUTEX - std::lock_guard lock(shard.mutex); + std::lock_guard lock(shard.mutex); #else - std::lock_guard lock(shard.mutex); + std::lock_guard lock(shard.mutex); #endif - shard.map.clear(); + shard.map.clear(); + } } std::lock_guard lock2(_resourceCommandListMutex); diff --git a/OptiScaler/resource_tracking/ResTrack_dx12.h b/OptiScaler/resource_tracking/ResTrack_dx12.h index b1510244..e2eaee01 100644 --- a/OptiScaler/resource_tracking/ResTrack_dx12.h +++ b/OptiScaler/resource_tracking/ResTrack_dx12.h @@ -36,7 +36,7 @@ static void TestResource(ResourceInfo* info) #ifdef USE_SPINLOCK_MUTEX -#define USE_PERF_SPINLOCK +// #define USE_PERF_SPINLOCK #ifdef __cpp_lib_hardware_interference_size constexpr size_t CACHE_LINE_SIZE = std::hardware_destructive_interference_size; @@ -337,6 +337,7 @@ class ResTrack_Dx12 private: inline static bool _presentDone = true; inline static std::mutex _drawMutex; + inline static bool _useShards = false; inline static std::mutex _resourceCommandListMutex; inline static std::unordered_map _resourceCommandList[BUFFER_COUNT]; @@ -424,13 +425,8 @@ class ResTrack_Dx12 static void FillResourceInfo(ID3D12Resource* resource, ResourceInfo* info); -// Sharding -#ifdef USE_SPINLOCK_MUTEX + // Sharding inline static constexpr size_t SHARD_COUNT = 16; -#else - inline static constexpr size_t SHARD_COUNT = 16; -#endif - inline static CommandListShard _hudlessShards[BUFFER_COUNT][SHARD_COUNT]; inline static size_t GetShardIndex(ID3D12GraphicsCommandList* ptr)