diff --git a/renderdoc/core/resource_manager.h b/renderdoc/core/resource_manager.h index 5ded3f7ec..c34e10348 100644 --- a/renderdoc/core/resource_manager.h +++ b/renderdoc/core/resource_manager.h @@ -25,6 +25,7 @@ #pragma once +#include #include #include #include "api/replay/resourceid.h" @@ -610,6 +611,7 @@ public: inline void MarkResourceFrameReferenced(ResourceId id, FrameRefType refType); void MarkBackgroundFrameReferenced(const rdcarray> &refs); + void CleanBackgroundFrameReferences(); /////////////////////////////////////////// // Replay-side methods @@ -652,10 +654,8 @@ public: void Prepare_InitialStateIfPostponed(ResourceId id); void SkipOrPostponeOrPrepare_InitialState(ResourceId id, FrameRefType refType); - void UpdateLastWriteTime(ResourceId id, FrameRefType refType); void ResetLastWriteTimes(); void ResetCaptureStartTime(); - void UpdateLastPartialUseTime(ResourceId id, FrameRefType refType); void ResetLastPartialUseTimes(); bool HasPersistentAge(ResourceId id); @@ -685,6 +685,8 @@ protected: virtual void Apply_InitialState(WrappedResourceType live, const InitialContentData &initial) = 0; virtual rdcarray InitialContentResources(); + void UpdateLastWriteAndPartialUseTime(ResourceId id, FrameRefType refType); + // very coarse lock, protects EVERYTHING. This could certainly be improved and it may be a // bottleneck // for performance. Given that the main use cases are write-rarely read-often the lock should be @@ -754,17 +756,31 @@ protected: // over are skipped std::set m_SkippedResourceIDs; - // On marking resource write-referenced in frame, its last write - // time is reset. The time is used to determine persistent resources, - // and is checked against the `PERSISTENT_RESOURCE_AGE`. - std::map m_LastWriteTime; - // m_LastPartialUseTime is referring to: eFrameRef_PartialWrite, eFrameRef_Read, - // eFrameRef_ReadBeforeWrite, eFrameRef_WriteBeforeRead. The goal is to predict - // whether a resource will have a eFrameRef_CompleteWrite reference or not - std::map m_LastPartialUseTime; + struct ResourceRefTimes + { + ResourceId id; + + // On marking resource write-referenced in frame, its last write time is reset. The time is used + // to determine persistent resources, and is checked against the `PERSISTENT_RESOURCE_AGE`. + double writeTime; + + // partialUseTime is referring to: eFrameRef_PartialWrite, eFrameRef_Read, + // eFrameRef_ReadBeforeWrite, eFrameRef_WriteBeforeRead. The goal is to predict whether a + // resource will only ever have a eFrameRef_CompleteWrite reference. If it does, then we don't + // need to serialise the initial contents because we know it will always be fully initialised + // within the frame. + double partialUseTime; + + bool operator<(const ResourceId &o) const { return id < o; } + }; + + // all resources that are written in some way end up in this list. We then check the last time + // they were written, and the last time they were ever partially used (not completely overwritten + // in one atomic chunk). + rdcarray m_ResourceRefTimes; // Timestamp at the beginning of the frame capture. Used to determine which - // resources to refresh for their last write time (see `m_LastWriteTime`). + // resources to refresh for their last write or partial use time (see `ResourceRefTimes`). double m_captureStartTime; PerformanceTimer m_ResourcesUpdateTimer; @@ -822,8 +838,7 @@ void ResourceManager::MarkBackgroundFrameReferenced( { for(const rdcpair &ref : refs) { - UpdateLastPartialUseTime(ref.first, ref.second); - UpdateLastWriteTime(ref.first, ref.second); + UpdateLastWriteAndPartialUseTime(ref.first, ref.second); } } else @@ -837,14 +852,53 @@ void ResourceManager::MarkBackgroundFrameReferenced( if(it != refs.end() && it->first == res.id) { - UpdateLastPartialUseTime(it->first, it->second); - UpdateLastWriteTime(it->first, it->second); + UpdateLastWriteAndPartialUseTime(it->first, it->second); } } } } } +template +void ResourceManager::CleanBackgroundFrameReferences() +{ + SCOPED_LOCK(m_Lock); + + if(IsBackgroundCapturing(m_State)) + { + double now = m_ResourcesUpdateTimer.GetMilliseconds(); + + // retire any old entries, if they were written once they shouldn't be tracked forever. + // + // walk the array. If we find an item we want to remove we increment src otherwise we copy src + // to dst (if they are different). Thus next time dst will point at the item we want to skip, at + // each stage copying src to dst (if they are different). If we find an item we want to remove + // we increment src and continue (thus it will get copied ove + size_t dst = 0, src = 0; + for(dst = 0, src = 0; src < m_ResourceRefTimes.size();) + { + ResourceRefTimes &check = m_ResourceRefTimes[src]; + if(now - check.writeTime > IRRELEVANT_RESOURCE_AGE) + { + // skip src, check the next one + src++; + continue; + } + + // we want to keep src. If dst == src we can just continue on to the next iteration, if not + // then we need to copy src into dst (where dst is the leftovers from previously remove + // entries) + if(dst != src) + m_ResourceRefTimes[dst] = m_ResourceRefTimes[src]; + + dst++; + src++; + } + + m_ResourceRefTimes.resize(dst); + } +} + template template void ResourceManager::MarkResourceFrameReferenced(ResourceId id, @@ -865,8 +919,7 @@ void ResourceManager::MarkResourceFrameReferenced(ResourceId id, } } - UpdateLastPartialUseTime(id, refType); - UpdateLastWriteTime(id, refType); + UpdateLastWriteAndPartialUseTime(id, refType); if(IsBackgroundCapturing(m_State)) return; @@ -1089,15 +1142,6 @@ void ResourceManager::SkipOrPostponeOrPrepare_InitialState(Resour } } -template -inline void ResourceManager::UpdateLastWriteTime(ResourceId id, FrameRefType refType) -{ - if(!IsDirtyFrameRef(refType)) - return; - SCOPED_LOCK(m_Lock); - m_LastWriteTime[id] = m_ResourcesUpdateTimer.GetMilliseconds(); -} - template inline void ResourceManager::ResetCaptureStartTime() { @@ -1111,33 +1155,53 @@ template inline void ResourceManager::ResetLastWriteTimes() { SCOPED_LOCK(m_Lock); - for(auto it = m_LastWriteTime.begin(); it != m_LastWriteTime.end(); ++it) + for(auto it = m_ResourceRefTimes.begin(); it != m_ResourceRefTimes.end(); ++it) { // Reset only those resources which were below the threshold on // capture start. Other resource are already above the threshold. - if(m_captureStartTime - it->second <= PERSISTENT_RESOURCE_AGE) - it->second = m_ResourcesUpdateTimer.GetMilliseconds(); + if(m_captureStartTime - it->writeTime <= PERSISTENT_RESOURCE_AGE) + it->writeTime = m_ResourcesUpdateTimer.GetMilliseconds(); } } template -inline void ResourceManager::UpdateLastPartialUseTime(ResourceId id, - FrameRefType refType) +inline void ResourceManager::UpdateLastWriteAndPartialUseTime(ResourceId id, + FrameRefType refType) { - if(IsCompleteWriteFrameRef(refType)) - return; - SCOPED_LOCK(m_Lock); - m_LastPartialUseTime[id] = m_ResourcesUpdateTimer.GetMilliseconds(); + // parent must hold m_Lock for us + + ResourceRefTimes *it = std::lower_bound(m_ResourceRefTimes.begin(), m_ResourceRefTimes.end(), id); + + if(it == m_ResourceRefTimes.end() || it->id != id) + { + // don't add resources unless it's a dirty ref. After that, we'll keep updating for read and + // write refs to get partialUseTime + if(!IsDirtyFrameRef(refType)) + return; + + // if it's not pointing to the end, figure out where we need to insert it there + size_t idx = it - m_ResourceRefTimes.begin(); + m_ResourceRefTimes.insert(idx, {id, 0.0, 0.0}); + it = m_ResourceRefTimes.begin() + idx; + } + + double now = m_ResourcesUpdateTimer.GetMilliseconds(); + + if(IsDirtyFrameRef(refType)) + it->writeTime = now; + + if(!IsCompleteWriteFrameRef(refType)) + it->partialUseTime = now; } template inline void ResourceManager::ResetLastPartialUseTimes() { SCOPED_LOCK(m_Lock); - for(auto it = m_LastPartialUseTime.begin(); it != m_LastPartialUseTime.end(); ++it) + for(auto it = m_ResourceRefTimes.begin(); it != m_ResourceRefTimes.end(); ++it) { - if(m_captureStartTime - it->second <= IRRELEVANT_RESOURCE_AGE) - it->second = m_ResourcesUpdateTimer.GetMilliseconds(); + if(m_captureStartTime - it->partialUseTime <= IRRELEVANT_RESOURCE_AGE) + it->partialUseTime = m_ResourcesUpdateTimer.GetMilliseconds(); } } @@ -1146,12 +1210,12 @@ inline bool ResourceManager::HasPersistentAge(ResourceId id) { SCOPED_LOCK(m_Lock); - auto it = m_LastWriteTime.find(id); + ResourceRefTimes *it = std::lower_bound(m_ResourceRefTimes.begin(), m_ResourceRefTimes.end(), id); - if(it == m_LastWriteTime.end()) + if(it == m_ResourceRefTimes.end() || it->id != id) return true; - return m_ResourcesUpdateTimer.GetMilliseconds() - it->second >= PERSISTENT_RESOURCE_AGE; + return m_ResourcesUpdateTimer.GetMilliseconds() - it->writeTime >= PERSISTENT_RESOURCE_AGE; } template @@ -1159,12 +1223,12 @@ inline bool ResourceManager::HasIrrelevantAge(ResourceId id) { SCOPED_LOCK(m_Lock); - auto it = m_LastPartialUseTime.find(id); + ResourceRefTimes *it = std::lower_bound(m_ResourceRefTimes.begin(), m_ResourceRefTimes.end(), id); - if(it == m_LastPartialUseTime.end()) - return true; + if(it == m_ResourceRefTimes.end() || it->id != id) + return false; - return m_ResourcesUpdateTimer.GetMilliseconds() - it->second >= IRRELEVANT_RESOURCE_AGE; + return m_ResourcesUpdateTimer.GetMilliseconds() - it->partialUseTime >= IRRELEVANT_RESOURCE_AGE; } template @@ -1796,8 +1860,10 @@ void ResourceManager::ReleaseCurrentResource(ResourceId id) m_CurrentResourceMap.erase(id); m_DirtyResources.erase(id); - m_LastWriteTime.erase(id); - m_LastPartialUseTime.erase(id); + + auto it = std::lower_bound(m_ResourceRefTimes.begin(), m_ResourceRefTimes.end(), id); + if(it != m_ResourceRefTimes.end()) + m_ResourceRefTimes.erase(it - m_ResourceRefTimes.begin()); } template diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index a32bdd115..3bcb0b24f 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -2105,6 +2105,8 @@ void WrappedOpenGL::SwapBuffers(WindowingSystem winSystem, void *windowHandle) RenderDoc::Inst().AddActiveDriver(GetDriverType(), true); + GetResourceManager()->CleanBackgroundFrameReferences(); + if(!activeWindow) return; diff --git a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp index dfacc2bfe..c6a2e2b1a 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp @@ -861,6 +861,8 @@ VkResult WrappedVulkan::vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR FlushQ(); } + + GetResourceManager()->CleanBackgroundFrameReferences(); } VkResult vkr;