diff --git a/renderdoc/core/resource_manager.cpp b/renderdoc/core/resource_manager.cpp index e14056658..883d48e3b 100644 --- a/renderdoc/core/resource_manager.cpp +++ b/renderdoc/core/resource_manager.cpp @@ -63,6 +63,8 @@ rdcstr DoStringise(const FrameRefType &el) STRINGISE_ENUM_CLASS_NAMED(eFrameRef_CompleteWrite, "Complete Write"); STRINGISE_ENUM_CLASS_NAMED(eFrameRef_Read, "Read"); STRINGISE_ENUM_CLASS_NAMED(eFrameRef_ReadBeforeWrite, "Read Before Write"); + STRINGISE_ENUM_CLASS_NAMED(eFrameRef_CompleteWriteAndDiscard, "Complete Write and Discard"); + STRINGISE_ENUM_CLASS_NAMED(eFrameRef_Unknown, "Unknown"); } END_ENUM_STRINGISE() } @@ -95,38 +97,59 @@ FrameRefType ComposeFrameRefs(FrameRefType first, FrameRefType second) return first; case eFrameRef_CompleteWrite: + case eFrameRef_CompleteWriteAndDiscard: case eFrameRef_ReadBeforeWrite: // These reference types are both locked in, and cannot be affected by // later references. return first; - default: RDCERR("Unknown FrameRefType: %d", first); return eFrameRef_Maximum; + default: RDCERR("Unknown FrameRefType: %d", first); return eFrameRef_ReadBeforeWrite; } } FrameRefType ComposeFrameRefsUnordered(FrameRefType first, FrameRefType second) { if((IncludesRead(first) && IncludesWrite(second)) || (IncludesRead(second) && IncludesWrite(first))) + { // There is a way to order these references so that the resource is read // and then written. Since read-before-write is the worst case in terms of // reset requirements, we conservatively assume this is the case. return eFrameRef_ReadBeforeWrite; + } else + { + // first patch CompleteWriteAndDiscard to CompleteWrite so the values are as expected. + if(first == eFrameRef_CompleteWriteAndDiscard) + first = eFrameRef_CompleteWrite; + if(second == eFrameRef_CompleteWriteAndDiscard) + second = eFrameRef_CompleteWrite; + // Otherwise either: // - first and second are each Read or None, or // - first and second are each CompleteWrite, PartialWrite or None. // In either case, Compose(first,second) = Compose(second,first) = max(first,second). return RDCMAX(first, second); + } } FrameRefType ComposeFrameRefsDisjoint(FrameRefType x, FrameRefType y) { if(x == eFrameRef_ReadBeforeWrite || y == eFrameRef_ReadBeforeWrite) + { // If any subresource is `ReadBeforeWrite`, then the whole resource is. return eFrameRef_ReadBeforeWrite; + } else + { + // first patch CompleteWriteAndDiscard to CompleteWrite so the values are as expected. + if(x == eFrameRef_CompleteWriteAndDiscard) + x = eFrameRef_CompleteWrite; + if(y == eFrameRef_CompleteWriteAndDiscard) + y = eFrameRef_CompleteWrite; + // For all other cases, just return the larger value. return RDCMAX(x, y); + } } FrameRefType ComposeFrameRefsFirstKnown(FrameRefType first, FrameRefType second) @@ -159,6 +182,7 @@ bool IncludesWrite(FrameRefType refType) { case eFrameRef_PartialWrite: case eFrameRef_CompleteWrite: + case eFrameRef_CompleteWriteAndDiscard: case eFrameRef_WriteBeforeRead: case eFrameRef_ReadBeforeWrite: return true; default: return false; @@ -172,7 +196,7 @@ bool IsDirtyFrameRef(FrameRefType refType) bool IsCompleteWriteFrameRef(FrameRefType refType) { - return refType == eFrameRef_CompleteWrite; + return refType == eFrameRef_CompleteWrite || refType == eFrameRef_CompleteWriteAndDiscard; } void ResourceRecord::AddResourceReferences(ResourceRecordHandler *mgr) diff --git a/renderdoc/core/resource_manager.h b/renderdoc/core/resource_manager.h index cebca2e85..f95cb4f1a 100644 --- a/renderdoc/core/resource_manager.h +++ b/renderdoc/core/resource_manager.h @@ -108,6 +108,12 @@ enum FrameRefType // are not reset, so the write from the previous replay may still be present. eFrameRef_WriteBeforeRead = 5, + // Similar to a CompleteWrite, but this is effectively an atomic "completely written, used, and + // discarded". This encodes more information than CompleteWrite because it tells us that the + // resource contents won't ever be used. For most purposes we treat this the same as + // CompleteWrite, but below we use it to identify skippable resources. + eFrameRef_CompleteWriteAndDiscard = 6, + // No reference info is available; // This should only appear durring replay, and any (sub)resource with `Unknown` // reference type should be conservatively reset before each replay. @@ -119,13 +125,14 @@ bool IncludesRead(FrameRefType refType); bool IncludesWrite(FrameRefType refType); const FrameRefType eFrameRef_Minimum = eFrameRef_None; -const FrameRefType eFrameRef_Maximum = eFrameRef_WriteBeforeRead; +const FrameRefType eFrameRef_Maximum = eFrameRef_CompleteWriteAndDiscard; // Threshold value for resource "age", i.e. how long it wasn't // referred with the any write reference. const double PERSISTENT_RESOURCE_AGE = 3000; -// how long it wasn't referred with any read reference. -const double IRRELEVANT_RESOURCE_AGE = 3000; +// how long since the first time it was skipped. If the skip state hasn't been invalidated in this +// long it can be skipped +const double SKIP_RESOURCE_AGE = 3000; DECLARE_REFLECTION_ENUM(FrameRefType); @@ -665,10 +672,9 @@ public: void ResetLastWriteTimes(); void ResetCaptureStartTime(); - void ResetLastPartialUseTimes(); bool HasPersistentAge(ResourceId id); - bool HasIrrelevantAge(ResourceId id); + bool HasSkippableAge(ResourceId id); bool IsResourcePostponed(ResourceId id); bool IsResourceSkipped(ResourceId id); bool ShouldPostpone(ResourceId id); @@ -694,7 +700,7 @@ protected: virtual void Apply_InitialState(WrappedResourceType live, const InitialContentData &initial) = 0; virtual rdcarray InitialContentResources(); - void UpdateLastWriteAndPartialUseTime(ResourceId id, FrameRefType refType); + void UpdateLastWriteTime(ResourceId id, FrameRefType refType); void Prepare_InitialStateIfPostponed(ResourceId id, bool midframe); void SkipOrPostponeOrPrepare_InitialState(ResourceId id, FrameRefType refType); @@ -776,12 +782,12 @@ protected: // 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; + // firstSkipTime is referring to the first time we saw a resource get marked as skippable. + // Any write will reset this time to 0.0 (not skippable), so if the firstSkipTime is longer than + // `SKIP_RESOURCE_AGE` ago then we know the resource has only ever been skipped (or not written) + // for that long. If the time is 0.0 then it's been written in a more visible way recently or + // has never been marked skippable. + double firstSkipTime; bool operator<(const ResourceId &o) const { return id < o; } }; @@ -850,7 +856,7 @@ void ResourceManager::MarkBackgroundFrameReferenced( if(refs.size() <= m_ResourceRefTimes.size()) { for(auto it = refs.begin(); it != refs.end(); ++it) - UpdateLastWriteAndPartialUseTime(it->first, it->second); + UpdateLastWriteTime(it->first, it->second); } else { @@ -859,7 +865,7 @@ void ResourceManager::MarkBackgroundFrameReferenced( auto it = refs.find(res.id); if(it != refs.end()) - UpdateLastWriteAndPartialUseTime(it->first, it->second); + UpdateLastWriteTime(it->first, it->second); } } } @@ -874,7 +880,9 @@ void ResourceManager::CleanBackgroundFrameReferences() { double now = m_ResourcesUpdateTimer.GetMilliseconds(); - // retire any old entries, if they were written once they shouldn't be tracked forever. + // retire any old entries, if they were written once they shouldn't be tracked forever. This + // means the list only keeps track of recently written resources, not all resources that have + // ever been written. // // 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 @@ -884,7 +892,11 @@ void ResourceManager::CleanBackgroundFrameReferences() for(dst = 0, src = 0; src < m_ResourceRefTimes.size();) { ResourceRefTimes &check = m_ResourceRefTimes[src]; - if(now - check.writeTime > IRRELEVANT_RESOURCE_AGE) + + // if this isn't skippable, and the write time was a long time ago then we can delete it. + // Resources not in the list are treated as if they were written an infinite time ago and so + // are postponable. + if(now - check.writeTime > PERSISTENT_RESOURCE_AGE && check.firstSkipTime == 0.0) { // skip src, check the next one src++; @@ -925,7 +937,7 @@ void ResourceManager::MarkResourceFrameReferenced(ResourceId id, } } - UpdateLastWriteAndPartialUseTime(id, refType); + UpdateLastWriteTime(id, refType); if(IsBackgroundCapturing(m_State)) return; @@ -1137,7 +1149,7 @@ void ResourceManager::SkipOrPostponeOrPrepare_InitialState(Resour // skip this forever if the first encounter is a complete write if(IsCompleteWriteFrameRef(refType)) { - RDCDEBUG("Resource %s skipped forever on refType of %s)", ToStr(id).c_str(), + RDCDEBUG("Resource %s skipped forever on refType of %s", ToStr(id).c_str(), ToStr(refType).c_str()); return; } @@ -1183,20 +1195,20 @@ inline void ResourceManager::ResetLastWriteTimes() } template -inline void ResourceManager::UpdateLastWriteAndPartialUseTime(ResourceId id, - FrameRefType refType) +inline void ResourceManager::UpdateLastWriteTime(ResourceId id, FrameRefType refType) { // parent must hold m_Lock for us + // only care about write refs. A read ref would invalidate skippable state, however a skippable + // resource is left in an undefined state where reads are not valid so we don't. We need to see + // another write first before a read could be a problem, so we just pay attention for that write. + if(!IsDirtyFrameRef(refType)) + return; + 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}); @@ -1205,21 +1217,18 @@ inline void ResourceManager::UpdateLastWriteAndPartialUseTime(Res double now = m_ResourcesUpdateTimer.GetMilliseconds(); - if(IsDirtyFrameRef(refType)) - it->writeTime = now; + it->writeTime = now; - if(!IsCompleteWriteFrameRef(refType)) - it->partialUseTime = now; -} - -template -inline void ResourceManager::ResetLastPartialUseTimes() -{ - SCOPED_LOCK_OPTIONAL(m_Lock, m_Capturing); - for(auto it = m_ResourceRefTimes.begin(); it != m_ResourceRefTimes.end(); ++it) + if(refType == eFrameRef_CompleteWriteAndDiscard) { - if(m_captureStartTime - it->partialUseTime <= IRRELEVANT_RESOURCE_AGE) - it->partialUseTime = m_ResourcesUpdateTimer.GetMilliseconds(); + // don't continually update it. We want to know that this resource *was* completely written and + // discarded, and hasn't been written in any other way since then. + if(it->firstSkipTime == 0.0) + it->firstSkipTime = now; + } + else + { + it->firstSkipTime = 0.0; } } @@ -1237,16 +1246,21 @@ inline bool ResourceManager::HasPersistentAge(ResourceId id) } template -inline bool ResourceManager::HasIrrelevantAge(ResourceId id) +inline bool ResourceManager::HasSkippableAge(ResourceId id) { SCOPED_LOCK_OPTIONAL(m_Lock, m_Capturing); ResourceRefTimes *it = std::lower_bound(m_ResourceRefTimes.begin(), m_ResourceRefTimes.end(), id); + // if it doesn't have a write time, it can't be skippable if(it == m_ResourceRefTimes.end() || it->id != id) return false; - return m_ResourcesUpdateTimer.GetMilliseconds() - it->partialUseTime >= IRRELEVANT_RESOURCE_AGE; + // if it's never been skipped or it was reset, it's also not skippable + if(it->firstSkipTime == 0.0) + return false; + + return m_ResourcesUpdateTimer.GetMilliseconds() - it->firstSkipTime >= SKIP_RESOURCE_AGE; } template @@ -1286,7 +1300,7 @@ inline bool ResourceManager::ShouldSkip(ResourceId id) if(!IsResourceTrackedForPersistency(res)) return false; - return HasIrrelevantAge(id); + return HasSkippableAge(id); } template diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 8e463a104..c39a0701b 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -1057,90 +1057,54 @@ void DescriptorSetSlotImageInfo::SetFrom(const VkDescriptorImageInfo &imInfo, bo imageLayout = imInfo.imageLayout; } -void DescriptorSetSlot::RemoveBindRefs(rdcarray &ids, VulkanResourceManager *rm, - VkResourceRecord *record) +void AddBindFrameRef(DescriptorBindRefs &refs, ResourceId id, FrameRefType ref) { - SCOPED_LOCK(record->descInfo->refLock); - - if(texelBufferView != ResourceId()) + if(id == ResourceId()) { - record->RemoveBindFrameRef(ids, texelBufferView); - - VkResourceRecord *viewRecord = rm->GetResourceRecord(texelBufferView); - if(viewRecord && viewRecord->baseResource != ResourceId()) - record->RemoveBindFrameRef(ids, viewRecord->baseResource); - if(viewRecord && viewRecord->baseResourceMem != ResourceId()) - record->RemoveBindFrameRef(ids, viewRecord->baseResourceMem); + RDCERR("Unexpected NULL resource ID being added as a bind frame ref"); + return; } - if(imageInfo.imageView != ResourceId()) - { - record->RemoveBindFrameRef(ids, imageInfo.imageView); - - VkResourceRecord *viewRecord = rm->GetResourceRecord(imageInfo.imageView); - if(viewRecord) - { - record->RemoveBindFrameRef(ids, viewRecord->baseResource); - if(viewRecord->baseResourceMem != ResourceId()) - record->RemoveBindFrameRef(ids, viewRecord->baseResourceMem); - } - } - if(imageInfo.sampler != ResourceId()) - { - record->RemoveBindFrameRef(ids, imageInfo.sampler); - } - if(bufferInfo.buffer != ResourceId()) - { - record->RemoveBindFrameRef(ids, bufferInfo.buffer); - - VkResourceRecord *bufRecord = rm->GetResourceRecord(bufferInfo.buffer); - if(bufRecord && bufRecord->baseResource != ResourceId()) - record->RemoveBindFrameRef(ids, bufRecord->baseResource); - } - - // NULL everything out now so that we don't accidentally reference an object - // that was removed already - texelBufferView = ResourceId(); - bufferInfo.buffer = ResourceId(); - imageInfo.imageView = ResourceId(); - imageInfo.sampler = ResourceId(); + FrameRefType &p = refs.bindFrameRefs[id]; + // be conservative - mark refs as read before write if we see a write and a read ref on it + p = ComposeFrameRefsUnordered(p, ref); } -void DescriptorSetSlot::AddBindRefs(rdcarray &ids, VkResourceRecord *bufView, - VkResourceRecord *imgView, VkResourceRecord *buffer, - VkResourceRecord *descSetRecord, FrameRefType ref) +void AddImgFrameRef(DescriptorBindRefs &refs, VkResourceRecord *view, FrameRefType refType) { - SCOPED_LOCK(descSetRecord->descInfo->refLock); + AddBindFrameRef(refs, view->GetResourceID(), eFrameRef_Read); + if(view->resInfo && view->resInfo->IsSparse()) + refs.sparseRefs.insert(view); + if(view->baseResourceMem != ResourceId()) + AddBindFrameRef(refs, view->baseResourceMem, eFrameRef_Read); - if(bufView) - { - descSetRecord->AddBindFrameRef(ids, bufView->GetResourceID(), eFrameRef_Read, - bufView->resInfo && bufView->resInfo->IsSparse()); - if(bufView->baseResource != ResourceId()) - descSetRecord->AddBindFrameRef(ids, bufView->baseResource, eFrameRef_Read); - if(bufView->baseResourceMem != ResourceId()) - descSetRecord->AddMemFrameRef(ids, bufView->baseResourceMem, bufView->memOffset, - bufView->memSize, ref); - } - if(imgView) - { - descSetRecord->AddImgFrameRef(ids, imgView, ref); - } - if(imageInfo.sampler != ResourceId()) - { - descSetRecord->AddBindFrameRef(ids, imageInfo.sampler, eFrameRef_Read); - } - if(buffer) - { - descSetRecord->AddBindFrameRef(ids, bufferInfo.buffer, eFrameRef_Read, - buffer->resInfo && buffer->resInfo->IsSparse()); - if(buffer->baseResource != ResourceId()) - descSetRecord->AddMemFrameRef(ids, buffer->baseResource, buffer->memOffset, buffer->memSize, - ref); - } + FrameRefType &p = refs.bindFrameRefs[view->baseResource]; + + ImageRange imgRange = ImageRange((VkImageSubresourceRange)view->viewRange); + imgRange.viewType = view->viewRange.viewType(); + + FrameRefType maxRef = + MarkImageReferenced(refs.bindImageStates, view->baseResource, view->resInfo->imageInfo, + ImageSubresourceRange(imgRange), VK_QUEUE_FAMILY_IGNORED, refType); + + p = ComposeFrameRefsDisjoint(p, maxRef); } -void DescriptorSetSlot::AddBindRefs(rdcarray &ids, VulkanResourceManager *rm, - VkResourceRecord *descSetRecord, FrameRefType ref) +void AddMemFrameRef(DescriptorBindRefs &refs, ResourceId mem, VkDeviceSize offset, + VkDeviceSize size, FrameRefType refType) +{ + if(mem == ResourceId()) + { + RDCERR("Unexpected NULL resource ID being added as a bind frame ref"); + return; + } + FrameRefType &p = refs.bindFrameRefs[mem]; + FrameRefType maxRef = + MarkMemoryReferenced(refs.bindMemRefs, mem, offset, size, refType, ComposeFrameRefsUnordered); + p = ComposeFrameRefsDisjoint(p, maxRef); +} + +void DescriptorSetSlot::AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResourceManager *rm, + FrameRefType ref) const { VkResourceRecord *bufView = NULL, *imgView = NULL, *buffer = NULL; @@ -1151,59 +1115,31 @@ void DescriptorSetSlot::AddBindRefs(rdcarray &ids, VulkanResourceMan if(bufferInfo.buffer != ResourceId()) buffer = rm->GetResourceRecord(bufferInfo.buffer); - AddBindRefs(ids, bufView, imgView, buffer, descSetRecord, ref); -} - -void DescriptorSetData::UpdateBackgroundRefCache(const rdcarray &ids) -{ - SCOPED_LOCK(refLock); - - if(backgroundFrameRefs.empty()) + if(bufView) { - for(auto refit = bindFrameRefs.begin(); refit != bindFrameRefs.end(); ++refit) - backgroundFrameRefs.insert(make_rdcpair(refit->first, refit->second.second)); - return; + AddBindFrameRef(refs, bufView->GetResourceID(), eFrameRef_Read); + if(bufView->resInfo && bufView->resInfo->IsSparse()) + refs.sparseRefs.insert(bufView); + if(bufView->baseResource != ResourceId()) + AddBindFrameRef(refs, bufView->baseResource, eFrameRef_Read); + if(bufView->baseResourceMem != ResourceId()) + AddMemFrameRef(refs, bufView->baseResourceMem, bufView->memOffset, bufView->memSize, ref); } - - rdcpair *cacheit = backgroundFrameRefs.begin(); - for(auto refit = ids.begin(); refit != ids.end(); ++refit) + if(imgView) { - ResourceId id = *refit; - - // find the Id we're looking for in the remainder of the cache. This won't skip over any one - // that we care about because we're iterating in ascending Id order - cacheit = std::lower_bound(cacheit, backgroundFrameRefs.end(), id, - [](const rdcpair &a, - const ResourceId &id) { return a.first < id; }); - - auto bindit = bindFrameRefs.find(id); - - // this id is no longer referenced, remove from the cache - if(bindit == bindFrameRefs.end()) - { - if(cacheit != backgroundFrameRefs.end()) - backgroundFrameRefs.erase(cacheit); - continue; - } - - FrameRefType refType = bindit->second.second; - - // if we didn't find a match, insert the desired entry here - if(cacheit == backgroundFrameRefs.end() || cacheit->first != id) - { - // calculate the index - size_t idx = cacheit - backgroundFrameRefs.begin(); - // insert the entry - backgroundFrameRefs.insert(cacheit, {id, refType}); - // re-initialise our iterator to point here, as the above insert might have invalidated it due - // to a resize - cacheit = backgroundFrameRefs.begin() + idx; - } - else - { - // update the frameref - cacheit->second = refType; - } + AddImgFrameRef(refs, imgView, ref); + } + if(imageInfo.sampler != ResourceId()) + { + AddBindFrameRef(refs, imageInfo.sampler, eFrameRef_Read); + } + if(buffer) + { + AddBindFrameRef(refs, bufferInfo.buffer, eFrameRef_Read); + if(buffer->resInfo && buffer->resInfo->IsSparse()) + refs.sparseRefs.insert(buffer); + if(buffer->baseResource != ResourceId()) + AddMemFrameRef(refs, buffer->baseResource, buffer->memOffset, buffer->memSize, ref); } } diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index 820c38fee..6a65caa6b 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -432,13 +432,11 @@ struct DescriptorSetSlotImageInfo VkImageLayout imageLayout; }; +struct DescriptorBindRefs; + struct DescriptorSetSlot { - void RemoveBindRefs(rdcarray &ids, VulkanResourceManager *rm, VkResourceRecord *record); - void AddBindRefs(rdcarray &ids, VulkanResourceManager *rm, VkResourceRecord *record, - FrameRefType ref); - void AddBindRefs(rdcarray &ids, VkResourceRecord *bufView, VkResourceRecord *imgView, - VkResourceRecord *buffer, VkResourceRecord *descSetRecord, FrameRefType ref); + void AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResourceManager *rm, FrameRefType ref) const; // VkDescriptorBufferInfo DescriptorSetSlotBufferInfo bufferInfo; diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index d749c4cb2..e30481407 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -1646,6 +1646,11 @@ void WrappedVulkan::StartFrameCapture(void *dev, void *wnd) FlushQ(); SubmitAndFlushImageStateBarriers(m_cleanupImageBarriers); + { + SCOPED_LOCK(m_CapDescriptorsLock); + m_CapDescriptors.clear(); + } + RDCDEBUG("Attempting capture"); m_FrameCaptureRecord->DeleteChunks(); { @@ -2102,8 +2107,6 @@ bool WrappedVulkan::EndFrameCapture(void *dev, void *wnd) GetResourceManager()->ResetLastWriteTimes(); - GetResourceManager()->ResetLastPartialUseTimes(); - GetResourceManager()->MarkUnwrittenResources(); GetResourceManager()->ClearReferencedMemory(); diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 3b3eb3e2b..f64fc480a 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -326,6 +326,9 @@ private: std::set m_StringDB; + Threading::CriticalSection m_CapDescriptorsLock; + std::set m_CapDescriptors; + VkResourceRecord *m_FrameCaptureRecord; Chunk *m_HeaderChunk; diff --git a/renderdoc/driver/vulkan/vk_manager.cpp b/renderdoc/driver/vulkan/vk_manager.cpp index f7fbdfdc7..7b10b1745 100644 --- a/renderdoc/driver/vulkan/vk_manager.cpp +++ b/renderdoc/driver/vulkan/vk_manager.cpp @@ -423,7 +423,7 @@ bool VulkanResourceManager::Serialise_DeviceMemoryRefs(SerialiserType &ser, { ResourceId mem = it_data->memory; - auto res = m_MemFrameRefs.insert(rdcpair(mem, MemRefs())); + auto res = m_MemFrameRefs.insert(std::pair(mem, MemRefs())); RDCASSERTMSG("MemRefIntervals for each memory resource must be contiguous", res.second); Intervals &rangeRefs = res.first->second.rangeRefs; @@ -851,7 +851,7 @@ void VulkanResourceManager::MarkMemoryFrameReferenced(ResourceId mem, VkDeviceSi SCOPED_LOCK_OPTIONAL(m_Lock, m_Capturing); FrameRefType maxRef = MarkMemoryReferenced(m_MemFrameRefs, mem, offset, size, refType); - if(maxRef == eFrameRef_CompleteWrite) + if(IsCompleteWriteFrameRef(maxRef)) { // check and make sure this is really a CompleteWrite VkResourceRecord *record = GetResourceRecord(mem); @@ -881,7 +881,7 @@ void VulkanResourceManager::RemoveDeviceMemory(ResourceId mem) m_DeviceMemories.erase(mem); } -void VulkanResourceManager::MergeReferencedMemory(rdcflatmap &memRefs) +void VulkanResourceManager::MergeReferencedMemory(std::unordered_map &memRefs) { SCOPED_LOCK_OPTIONAL(m_Lock, m_Capturing); diff --git a/renderdoc/driver/vulkan/vk_manager.h b/renderdoc/driver/vulkan/vk_manager.h index cc0d33519..51a62bd23 100644 --- a/renderdoc/driver/vulkan/vk_manager.h +++ b/renderdoc/driver/vulkan/vk_manager.h @@ -449,7 +449,7 @@ public: void AddDeviceMemory(ResourceId mem); void RemoveDeviceMemory(ResourceId mem); - void MergeReferencedMemory(rdcflatmap &memRefs); + void MergeReferencedMemory(std::unordered_map &memRefs); void ClearReferencedMemory(); MemRefs *FindMemRefs(ResourceId mem); ImgRefs *FindImgRefs(ResourceId img); @@ -484,7 +484,7 @@ private: rdcarray InitialContentResources(); WrappedVulkan *m_Core; - rdcflatmap m_MemFrameRefs; + std::unordered_map m_MemFrameRefs; std::set m_DeviceMemories; InitPolicy m_InitPolicy = eInitPolicy_CopyAll; }; diff --git a/renderdoc/driver/vulkan/vk_resources.cpp b/renderdoc/driver/vulkan/vk_resources.cpp index 3e3215f30..dd3b35828 100644 --- a/renderdoc/driver/vulkan/vk_resources.cpp +++ b/renderdoc/driver/vulkan/vk_resources.cpp @@ -69,7 +69,16 @@ bool IsDispatchableRes(WrappedVkRes *ptr) bool IsPostponableRes(const WrappedVkRes *ptr) { - return (WrappedVkDeviceMemory::IsAlloc(ptr) || WrappedVkImage::IsAlloc(ptr)); + // only memory and images are postponed + if(WrappedVkDeviceMemory::IsAlloc(ptr) || WrappedVkImage::IsAlloc(ptr)) + { + // and only if they're not storable. If they are storable they may have been written recently in + // a descriptor set binding and we didn't track it (since descriptor updates can be too + // high-frequency to be worth tracking), so pessimistically we don't postpone. + return !((WrappedVkNonDispRes *)ptr)->record->storable; + } + + return false; } VkResourceType IdentifyTypeByPtr(WrappedVkRes *ptr) @@ -3260,11 +3269,16 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo &ci) } loadOpTable = new VkAttachmentLoadOp[arrayCount]; + storeOpTable = new VkAttachmentStoreOp[arrayCount]; // we only care about which attachment doesn't have LOAD specificed, so we - // assume all attachments have VK_ATTACHMENT_LOAD_OP_LOAD until proven otherwise + // assume all attachments have VK_ATTACHMENT_LOAD_OP_LOAD until proven otherwise. + // similarly for store for(uint32_t a = 0; a < arrayCount; ++a) + { loadOpTable[a] = VK_ATTACHMENT_LOAD_OP_LOAD; + storeOpTable[a] = VK_ATTACHMENT_STORE_OP_STORE; + } for(uint32_t s = 0; s < ci.subpassCount; ++s) { @@ -3283,6 +3297,7 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo &ci) if(index < ci.attachmentCount) { loadOpTable[index] = ci.pAttachments[index].loadOp; + storeOpTable[index] = ci.pAttachments[index].storeOp; if(multiviewViewMaskTable) { @@ -3304,6 +3319,7 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo &ci) if(index < ci.attachmentCount) { loadOpTable[index] = ci.pAttachments[index].loadOp; + storeOpTable[index] = ci.pAttachments[index].storeOp; if(multiviewViewMaskTable) { @@ -3320,6 +3336,7 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo &ci) if(index < ci.attachmentCount) { VkAttachmentLoadOp depthStencilLoadOp = ci.pAttachments[index].loadOp; + VkAttachmentStoreOp depthStencilStoreOp = ci.pAttachments[index].storeOp; // make depthstencil VK_ATTACHMENT_LOAD_OP_LOAD if either depth or stencil is // VK_ATTACHMENT_LOAD_OP_LOAD @@ -3328,8 +3345,15 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo &ci) { depthStencilLoadOp = ci.pAttachments[index].stencilLoadOp; } + // similarly for store + if(depthStencilStoreOp != VK_ATTACHMENT_STORE_OP_STORE && + IsStencilFormat(ci.pAttachments[index].format)) + { + depthStencilStoreOp = ci.pAttachments[index].stencilStoreOp; + } loadOpTable[index] = depthStencilLoadOp; + storeOpTable[index] = depthStencilStoreOp; if(multiviewViewMaskTable) { @@ -3394,11 +3418,16 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo2 &ci) } loadOpTable = new VkAttachmentLoadOp[arrayCount]; + storeOpTable = new VkAttachmentStoreOp[arrayCount]; // we only care about which attachment doesn't have LOAD specificed, so we - // assume all attachments have VK_ATTACHMENT_LOAD_OP_LOAD until proven otherwise + // assume all attachments have VK_ATTACHMENT_LOAD_OP_LOAD until proven otherwise. + // similarly for store for(uint32_t a = 0; a < arrayCount; ++a) + { loadOpTable[a] = VK_ATTACHMENT_LOAD_OP_LOAD; + storeOpTable[a] = VK_ATTACHMENT_STORE_OP_STORE; + } for(uint32_t s = 0; s < ci.subpassCount; ++s) { @@ -3420,6 +3449,7 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo2 &ci) RDCASSERT(remappedIndex < arrayCount); loadOpTable[remappedIndex] = ci.pAttachments[index].loadOp; + storeOpTable[remappedIndex] = ci.pAttachments[index].storeOp; if(multiviewViewMaskTable) { @@ -3444,6 +3474,7 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo2 &ci) RDCASSERT(remappedIndex < arrayCount); loadOpTable[remappedIndex] = ci.pAttachments[index].loadOp; + storeOpTable[remappedIndex] = ci.pAttachments[index].storeOp; if(multiviewViewMaskTable) { @@ -3460,7 +3491,9 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo2 &ci) if(index < ci.attachmentCount) { VkAttachmentLoadOp depthStencilLoadOp = ci.pAttachments[index].loadOp; + VkAttachmentStoreOp depthStencilStoreOp = ci.pAttachments[index].storeOp; VkAttachmentLoadOp stencilLoadOp = ci.pAttachments[index].stencilLoadOp; + VkAttachmentStoreOp stencilStoreOp = ci.pAttachments[index].stencilStoreOp; uint32_t remappedIndex = indexRemapTable[index]; RDCASSERT(remappedIndex < arrayCount); @@ -3476,6 +3509,7 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo2 &ci) if(separateStencil) { loadOpTable[remappedIndex + 1] = stencilLoadOp; + storeOpTable[remappedIndex + 1] = stencilStoreOp; } else { @@ -3485,10 +3519,15 @@ RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo2 &ci) { depthStencilLoadOp = stencilLoadOp; } + if(depthStencilStoreOp != VK_ATTACHMENT_STORE_OP_STORE) + { + depthStencilStoreOp = stencilStoreOp; + } } } loadOpTable[remappedIndex] = depthStencilLoadOp; + storeOpTable[remappedIndex] = depthStencilStoreOp; if(multiviewViewMaskTable) { @@ -3503,6 +3542,7 @@ RenderPassInfo::~RenderPassInfo() { delete[] imageAttachments; delete[] loadOpTable; + delete[] storeOpTable; delete[] multiviewViewMaskTable; } @@ -3782,10 +3822,11 @@ VkResourceRecord::~VkResourceRecord() void VkResourceRecord::MarkImageFrameReferenced(VkResourceRecord *img, const ImageRange &range, FrameRefType refType) { + ResourceId id = img->GetResourceID(); + // mark backing memory MarkResourceFrameReferenced(img->baseResource, refType); - ResourceId id = img->GetResourceID(); if(img->resInfo && img->resInfo->IsSparse()) cmdInfo->sparse.insert(img->resInfo); diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index d73456849..edde7bbd4 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -985,7 +985,28 @@ struct ResourceInfo void Update(uint32_t numBindings, const VkSparseImageMemoryBind *pBindings); }; -struct MemRefs; +struct MemRefs +{ + Intervals rangeRefs; + WrappedVkRes *initializedLiveRes; + inline MemRefs() : initializedLiveRes(NULL) {} + inline MemRefs(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) + : initializedLiveRes(NULL) + { + size = RDCMIN(size, UINT64_MAX - offset); + rangeRefs.update(offset, offset + size, refType, ComposeFrameRefs); + } + template + FrameRefType Update(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType, Compose comp); + inline FrameRefType Update(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) + { + return Update(offset, size, refType, ComposeFrameRefs); + } + template + FrameRefType Merge(MemRefs &other, Compose comp); + inline FrameRefType Merge(MemRefs &other) { return Merge(other, ComposeFrameRefs); } +}; + struct ImgRefs; struct ImageState; @@ -1040,7 +1061,7 @@ struct CmdBufferRecordingInfo rdcflatmap imageStates; - rdcflatmap memFrameRefs; + std::unordered_map memFrameRefs; // AdvanceFrame/Present should be called after this buffer is submitted bool present; @@ -1063,23 +1084,18 @@ struct DescriptorSetData // descriptor set bindings for this descriptor set. Filled out on // create from the layout. BindingStorage data; +}; - // lock protecting bindFrameRefs and bindMemRefs - Threading::CriticalSection refLock; - - // contains the framerefs (ref counted) for the bound resources - // in the binding slots. Updated when updating descriptor sets - // and then applied in a block on descriptor set bind. - // the refcount has the high-bit set if this resource has sparse - // mapping information - static const uint32_t SPARSE_REF_BIT = 0x80000000; - rdcflatmap> bindFrameRefs; - rdcflatmap bindMemRefs; +// we used to cache these bindrefs at update time, but unfortunately many applications have +// extremely high numbers of descriptors and update them almost a 1:1 rate with their use (or +// sometimes higher). It's not feasible to do any per-descriptor tracking in the background, so we +// gather the data we need from the descriptor contents at capture time only into this struct. +struct DescriptorBindRefs +{ + std::unordered_map bindFrameRefs; + std::unordered_map bindMemRefs; rdcflatmap bindImageStates; - - void UpdateBackgroundRefCache(const rdcarray &ids); - - rdcflatmap backgroundFrameRefs; + std::unordered_set sparseRefs; }; struct PipelineLayoutData @@ -1131,8 +1147,9 @@ struct RenderPassInfo AttachmentInfo *imageAttachments; - // table of loadOps for each attachment + // table of loadOps/storeOps for each attachment VkAttachmentLoadOp *loadOpTable; + VkAttachmentStoreOp *storeOpTable; // table of multiview viewMasks for each attachment uint32_t *multiviewViewMaskTable; @@ -1911,7 +1928,7 @@ FrameRefType ImgRefs::Update(ImageRange range, FrameRefType refType, Compose com if(range.levelCount == VK_REMAINING_MIP_LEVELS) range.levelCount = imageInfo.levelCount - range.baseMipLevel; - if(refType == eFrameRef_CompleteWrite && + if(IsCompleteWriteFrameRef(refType) && (range.offset.x != 0 || range.offset.y != 0 || range.extent.width != imageInfo.extent.width || range.extent.height != imageInfo.extent.height)) // Complete write, but only to part of the image. @@ -2012,28 +2029,6 @@ FrameRefType ImgRefs::Merge(const ImgRefs &other, Compose comp) return maxRefType; } -struct MemRefs -{ - Intervals rangeRefs; - WrappedVkRes *initializedLiveRes; - inline MemRefs() : initializedLiveRes(NULL) {} - inline MemRefs(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) - : initializedLiveRes(NULL) - { - size = RDCMIN(size, UINT64_MAX - offset); - rangeRefs.update(offset, offset + size, refType, ComposeFrameRefs); - } - template - FrameRefType Update(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType, Compose comp); - inline FrameRefType Update(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) - { - return Update(offset, size, refType, ComposeFrameRefs); - } - template - FrameRefType Merge(MemRefs &other, Compose comp); - inline FrameRefType Merge(MemRefs &other) { return Merge(other, ComposeFrameRefs); } -}; - template FrameRefType MemRefs::Update(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType, Compose comp) @@ -2081,7 +2076,7 @@ FrameRefType MarkImageReferenced(rdcflatmap &imageStates uint32_t queueFamilyIndex, FrameRefType refType); template -FrameRefType MarkMemoryReferenced(rdcflatmap &memRefs, ResourceId mem, +FrameRefType MarkMemoryReferenced(std::unordered_map &memRefs, ResourceId mem, VkDeviceSize offset, VkDeviceSize size, FrameRefType refType, Compose comp) { @@ -2099,8 +2094,9 @@ FrameRefType MarkMemoryReferenced(rdcflatmap &memRefs, Reso } } -inline FrameRefType MarkMemoryReferenced(rdcflatmap &memRefs, ResourceId mem, - VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) +inline FrameRefType MarkMemoryReferenced(std::unordered_map &memRefs, + ResourceId mem, VkDeviceSize offset, VkDeviceSize size, + FrameRefType refType) { return MarkMemoryReferenced(memRefs, mem, offset, size, refType, ComposeFrameRefs); } @@ -2143,114 +2139,6 @@ public: cmdInfo->memFrameRefs.swap(bakedCommands->cmdInfo->memFrameRefs); } - void AddBindFrameRef(rdcarray &ids, ResourceId id, FrameRefType ref, - bool hasSparse = false) - { - if(id == ResourceId()) - { - RDCERR("Unexpected NULL resource ID being added as a bind frame ref"); - return; - } - if(!ids.contains(id)) - ids.push_back(id); - rdcpair &p = descInfo->bindFrameRefs[id]; - if((p.first & ~DescriptorSetData::SPARSE_REF_BIT) == 0) - { - p.second = ref; - p.first = 1 | (hasSparse ? DescriptorSetData::SPARSE_REF_BIT : 0); - } - else - { - // be conservative - mark refs as read before write if we see a write and a read ref on it - p.second = ComposeFrameRefsUnordered(descInfo->bindFrameRefs[id].second, ref); - p.first++; - p.first |= (hasSparse ? DescriptorSetData::SPARSE_REF_BIT : 0); - } - } - - void AddImgFrameRef(rdcarray &ids, VkResourceRecord *view, FrameRefType refType) - { - if(!ids.contains(view->baseResource)) - ids.push_back(view->baseResource); - AddBindFrameRef(ids, view->GetResourceID(), eFrameRef_Read, - view->resInfo && view->resInfo->IsSparse()); - if(view->baseResourceMem != ResourceId()) - AddBindFrameRef(ids, view->baseResourceMem, eFrameRef_Read, false); - - rdcpair &p = descInfo->bindFrameRefs[view->baseResource]; - if((p.first & ~DescriptorSetData::SPARSE_REF_BIT) == 0) - { - descInfo->bindImageStates.erase(view->baseResource); - p.first = 1; - p.second = eFrameRef_None; - } - else - { - p.first++; - } - - ImageRange imgRange = ImageRange((VkImageSubresourceRange)view->viewRange); - imgRange.viewType = view->viewRange.viewType(); - - FrameRefType maxRef = - MarkImageReferenced(descInfo->bindImageStates, view->baseResource, view->resInfo->imageInfo, - ImageSubresourceRange(imgRange), VK_QUEUE_FAMILY_IGNORED, refType); - - p.second = ComposeFrameRefsDisjoint(p.second, maxRef); - } - - void AddMemFrameRef(rdcarray &ids, ResourceId mem, VkDeviceSize offset, - VkDeviceSize size, FrameRefType refType) - { - if(mem == ResourceId()) - { - RDCERR("Unexpected NULL resource ID being added as a bind frame ref"); - return; - } - if(!ids.contains(mem)) - ids.push_back(mem); - rdcpair &p = descInfo->bindFrameRefs[mem]; - if((p.first & ~DescriptorSetData::SPARSE_REF_BIT) == 0) - { - descInfo->bindMemRefs.erase(mem); - p.first = 1; - p.second = eFrameRef_None; - } - else - { - p.first++; - } - FrameRefType maxRef = MarkMemoryReferenced(descInfo->bindMemRefs, mem, offset, size, refType, - ComposeFrameRefsUnordered); - p.second = ComposeFrameRefsDisjoint(p.second, maxRef); - } - - void RemoveBindFrameRef(rdcarray &ids, ResourceId id) - { - // ignore any NULL IDs - probably an object that was - // deleted since it was bound. - if(id == ResourceId()) - return; - - auto it = descInfo->bindFrameRefs.find(id); - - // in the case of re-used handles bound to descriptor sets, - // it's possible to try and remove a frameref on something we - // don't have (which means we'll have a corresponding stale ref) - // but this is harmless so we can ignore it. - if(it == descInfo->bindFrameRefs.end()) - return; - - it->second.first--; - - if((it->second.first & ~DescriptorSetData::SPARSE_REF_BIT) == 0) - { - if(!ids.contains(id)) - ids.push_back(id); - descInfo->bindFrameRefs.erase(it); - } - } - // we have a lot of 'cold' data in the resource record, as it can be accessed // through the wrapped objects without locking any lookup structures. // To save on object size, the data is union'd as much as possible where only @@ -2271,6 +2159,7 @@ public: VkDeviceSize memOffset; VkDeviceSize memSize; VkResourceType resType; + bool storable = false; void MarkMemoryFrameReferenced(ResourceId mem, VkDeviceSize offset, VkDeviceSize size, FrameRefType refType); diff --git a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp index 8bd130e7b..a6def6354 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp @@ -1605,62 +1605,60 @@ void WrappedVulkan::vkCmdBeginRenderPass(VkCommandBuffer commandBuffer, FramebufferInfo *fbInfo = fb->framebufferInfo; RenderPassInfo *rpInfo = rp->renderPassInfo; - if(fbInfo->imageAttachments[0].barrier.sType && fbInfo->imageAttachments[0].record) + bool renderArea_covers_entire_framebuffer = + pRenderPassBegin->renderArea.offset.x == 0 && pRenderPassBegin->renderArea.offset.y == 0 && + pRenderPassBegin->renderArea.extent.width >= fbInfo->width && + pRenderPassBegin->renderArea.extent.height >= fbInfo->height; + + const VkRenderPassAttachmentBeginInfo *attachmentsInfo = + (const VkRenderPassAttachmentBeginInfo *)FindNextStruct( + pRenderPassBegin, VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO); + + for(size_t i = 0; fbInfo->imageAttachments[i].barrier.sType; i++) { - bool renderArea_covers_entire_framebuffer = - pRenderPassBegin->renderArea.offset.x == 0 && pRenderPassBegin->renderArea.offset.y == 0 && - pRenderPassBegin->renderArea.extent.width >= fbInfo->width && - pRenderPassBegin->renderArea.extent.height >= fbInfo->height; + VkResourceRecord *att = fbInfo->imageAttachments[i].record; - for(size_t i = 0; fbInfo->imageAttachments[i].barrier.sType; i++) + if(attachmentsInfo && !att) + att = GetRecord(attachmentsInfo->pAttachments[i]); + + if(att == NULL) + break; + + bool framebuffer_reference_entire_attachment = fbInfo->AttachmentFullyReferenced(i, rpInfo); + + FrameRefType refType = eFrameRef_ReadBeforeWrite; + + if(renderArea_covers_entire_framebuffer && framebuffer_reference_entire_attachment) { - VkResourceRecord *att = fbInfo->imageAttachments[i].record; - if(att == NULL) - break; - - bool framebuffer_reference_entire_attachment = fbInfo->AttachmentFullyReferenced(i, rpInfo); - - FrameRefType refType = eFrameRef_ReadBeforeWrite; - - if(renderArea_covers_entire_framebuffer && framebuffer_reference_entire_attachment) + if(rpInfo->loadOpTable[i] != VK_ATTACHMENT_LOAD_OP_LOAD) { - if(rpInfo->loadOpTable[i] != VK_ATTACHMENT_LOAD_OP_LOAD) - { - refType = eFrameRef_CompleteWrite; - } + refType = eFrameRef_CompleteWrite; } - - record->MarkImageViewFrameReferenced(att, ImageRange(), refType); - - if(fbInfo->imageAttachments[i].barrier.oldLayout != - fbInfo->imageAttachments[i].barrier.newLayout) - barriers.push_back(fbInfo->imageAttachments[i].barrier); } - } - else if(fbInfo->imageAttachments[0].barrier.sType) - { - // if we have attachments but the framebuffer doesn't have images, then it's imageless. Look - // for the image records now - const VkRenderPassAttachmentBeginInfo *attachmentsInfo = - (const VkRenderPassAttachmentBeginInfo *)FindNextStruct( - pRenderPassBegin, VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO); - - for(uint32_t i = 0; i < attachmentsInfo->attachmentCount; i++) + // if we're completely writing this resource (i.e. nothing from previous data is visible) and + // it's also DONT_CARE storage (so nothing from this render pass will be visible after) then + // it's completely written and discarded in one go. + if(refType == eFrameRef_CompleteWrite && + rpInfo->storeOpTable[i] == VK_ATTACHMENT_STORE_OP_DONT_CARE) { - VkResourceRecord *att = GetRecord(attachmentsInfo->pAttachments[i]); - record->MarkImageViewFrameReferenced(att, ImageRange(), eFrameRef_ReadBeforeWrite); + refType = eFrameRef_CompleteWriteAndDiscard; + } - if(fbInfo->imageAttachments[i].barrier.oldLayout != - fbInfo->imageAttachments[i].barrier.newLayout) + record->MarkImageViewFrameReferenced(att, ImageRange(), refType); + + if(fbInfo->imageAttachments[i].barrier.oldLayout != + fbInfo->imageAttachments[i].barrier.newLayout) + { + VkImageMemoryBarrier barrier = fbInfo->imageAttachments[i].barrier; + + if(attachmentsInfo) { - VkImageMemoryBarrier barrier = fbInfo->imageAttachments[i].barrier; - barrier.image = GetResourceManager()->GetCurrentHandle(att->baseResource); barrier.subresourceRange = att->viewRange; - - barriers.push_back(barrier); } + + barriers.push_back(barrier); } } @@ -2215,63 +2213,60 @@ void WrappedVulkan::vkCmdBeginRenderPass2(VkCommandBuffer commandBuffer, FramebufferInfo *fbInfo = fb->framebufferInfo; RenderPassInfo *rpInfo = rp->renderPassInfo; + bool renderArea_covers_entire_framebuffer = + pRenderPassBegin->renderArea.offset.x == 0 && pRenderPassBegin->renderArea.offset.y == 0 && + pRenderPassBegin->renderArea.extent.width >= fbInfo->width && + pRenderPassBegin->renderArea.extent.height >= fbInfo->height; - if(fbInfo->imageAttachments[0].barrier.sType && fbInfo->imageAttachments[0].record) + const VkRenderPassAttachmentBeginInfo *attachmentsInfo = + (const VkRenderPassAttachmentBeginInfo *)FindNextStruct( + pRenderPassBegin, VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO); + + for(size_t i = 0; fbInfo->imageAttachments[i].barrier.sType; i++) { - bool renderArea_covers_entire_framebuffer = - pRenderPassBegin->renderArea.offset.x == 0 && pRenderPassBegin->renderArea.offset.y == 0 && - pRenderPassBegin->renderArea.extent.width >= fbInfo->width && - pRenderPassBegin->renderArea.extent.height >= fbInfo->height; + VkResourceRecord *att = fbInfo->imageAttachments[i].record; - for(size_t i = 0; fbInfo->imageAttachments[i].barrier.sType; i++) + if(attachmentsInfo && !att) + att = GetRecord(attachmentsInfo->pAttachments[i]); + + if(att == NULL) + break; + + bool framebuffer_reference_entire_attachment = fbInfo->AttachmentFullyReferenced(i, rpInfo); + + FrameRefType refType = eFrameRef_ReadBeforeWrite; + + if(renderArea_covers_entire_framebuffer && framebuffer_reference_entire_attachment) { - VkResourceRecord *att = fbInfo->imageAttachments[i].record; - if(att == NULL) - break; - - bool framebuffer_reference_entire_attachment = fbInfo->AttachmentFullyReferenced(i, rpInfo); - - FrameRefType refType = eFrameRef_ReadBeforeWrite; - - if(renderArea_covers_entire_framebuffer && framebuffer_reference_entire_attachment) + if(rpInfo->loadOpTable[i] != VK_ATTACHMENT_LOAD_OP_LOAD) { - if(rpInfo->loadOpTable[i] != VK_ATTACHMENT_LOAD_OP_LOAD) - { - refType = eFrameRef_CompleteWrite; - } + refType = eFrameRef_CompleteWrite; } - - record->MarkImageViewFrameReferenced(att, ImageRange(), refType); - - if(fbInfo->imageAttachments[i].barrier.oldLayout != - fbInfo->imageAttachments[i].barrier.newLayout) - barriers.push_back(fbInfo->imageAttachments[i].barrier); } - } - else - { - // if we have attachments but the framebuffer doesn't have images, then it's imageless. Look - // for the image records now - const VkRenderPassAttachmentBeginInfo *attachmentsInfo = - (const VkRenderPassAttachmentBeginInfo *)FindNextStruct( - pRenderPassBegin, VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO); - - for(uint32_t i = 0; i < attachmentsInfo->attachmentCount; i++) + // if we're completely writing this resource (i.e. nothing from previous data is visible) and + // it's also DONT_CARE storage (so nothing from this render pass will be visible after) then + // it's completely written and discarded in one go. + if(refType == eFrameRef_CompleteWrite && + rpInfo->storeOpTable[i] == VK_ATTACHMENT_STORE_OP_DONT_CARE) { - VkResourceRecord *att = GetRecord(attachmentsInfo->pAttachments[i]); - record->MarkImageViewFrameReferenced(att, ImageRange(), eFrameRef_ReadBeforeWrite); + refType = eFrameRef_CompleteWriteAndDiscard; + } - if(fbInfo->imageAttachments[i].barrier.oldLayout != - fbInfo->imageAttachments[i].barrier.newLayout) + record->MarkImageViewFrameReferenced(att, ImageRange(), refType); + + if(fbInfo->imageAttachments[i].barrier.oldLayout != + fbInfo->imageAttachments[i].barrier.newLayout) + { + VkImageMemoryBarrier barrier = fbInfo->imageAttachments[i].barrier; + + if(attachmentsInfo) { - VkImageMemoryBarrier barrier = fbInfo->imageAttachments[i].barrier; - barrier.image = GetResourceManager()->GetCurrentHandle(att->baseResource); barrier.subresourceRange = att->viewRange; - - barriers.push_back(barrier); } + + barriers.push_back(barrier); } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp index fdac4362b..ea689d989 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_descriptor_funcs.cpp @@ -689,10 +689,6 @@ VkResult WrappedVulkan::vkResetDescriptorPool(VkDevice device, VkDescriptorPool { ((WrappedVkNonDispRes *)(*it)->Resource)->real = RealVkRes(0x123456); (*it)->descInfo->data.reset(); - (*it)->descInfo->bindFrameRefs.clear(); - (*it)->descInfo->bindMemRefs.clear(); - (*it)->descInfo->bindImageStates.clear(); - (*it)->descInfo->backgroundFrameRefs.clear(); } record->descPoolInfo->freelist.assign(record->pooledChildren); @@ -1167,12 +1163,10 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, for(uint32_t i = 0; i < copyCount; i++) { - // At the same time as ref'ing the source set, we must ref all of its resources (via the - // bindFrameRefs). This is because they must be valid even if the source set is not ever - // bound - // (and so its bindings aren't pulled in). + // At the same time as ref'ing the source set, add it to a special list of descriptor sets + // to pull in at the next queue submit. This is because it must be referenced even if the + // source set is never bound to a command buffer, so that the source set's data is valid. // - // We just ref all rather than looking at only the copied sets to keep things simple. // This does mean a slightly conservative ref'ing if the dest set doesn't end up getting // bound, but we only do this during frame capture so it's not too bad. @@ -1181,21 +1175,9 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, GetResourceManager()->MarkResourceFrameReferenced(GetResID(pDescriptorCopies[i].srcSet), eFrameRef_Read); - VkResourceRecord *setrecord = GetRecord(pDescriptorCopies[i].srcSet); - - SCOPED_LOCK(setrecord->descInfo->refLock); - - for(auto refit = setrecord->descInfo->bindFrameRefs.begin(); - refit != setrecord->descInfo->bindFrameRefs.end(); ++refit) { - GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second); - - if(refit->second.first & DescriptorSetData::SPARSE_REF_BIT) - { - VkResourceRecord *record = GetResourceManager()->GetResourceRecord(refit->first); - - GetResourceManager()->MarkSparseMapReferenced(record->resInfo); - } + SCOPED_LOCK(m_CapDescriptorsLock); + m_CapDescriptors.insert(pDescriptorCopies[i].srcSet); } } } @@ -1204,11 +1186,6 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, // need to track descriptor set contents whether capframing or idle if(IsCaptureMode(m_State)) { - rdcarray ids; - VkResourceRecord *setrecord = NULL; - - ids.reserve(128); - for(uint32_t i = 0; i < writeCount; i++) { const VkWriteDescriptorSet &descWrite = pDescriptorWrites[i]; @@ -1217,17 +1194,6 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, RDCASSERT(record->descInfo && record->descInfo->layout); const DescSetLayout &layout = *record->descInfo->layout; - if(setrecord && setrecord != record) - { - std::sort(ids.begin(), ids.end()); - - setrecord->descInfo->UpdateBackgroundRefCache(ids); - - ids.clear(); - } - - setrecord = record; - RDCASSERT(descWrite.dstBinding < record->descInfo->data.binds.size()); DescriptorSetSlot **binding = &record->descInfo->data.binds[descWrite.dstBinding]; @@ -1282,15 +1248,10 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, DescriptorSetSlot &bind = (*binding)[curIdx]; - bind.RemoveBindRefs(ids, GetResourceManager(), record); - - VkResourceRecord *bufView = NULL, *imgView = NULL, *buffer = NULL; - if(descWrite.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || descWrite.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { bind.texelBufferView = GetResID(descWrite.pTexelBufferView[d]); - bufView = GetRecord(descWrite.pTexelBufferView[d]); } else if(descWrite.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || descWrite.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || @@ -1311,9 +1272,6 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, imageView = false; bind.imageInfo.SetFrom(descWrite.pImageInfo[d], sampler, imageView); - - if(imageView) - imgView = GetRecord(descWrite.pImageInfo[d].imageView); } else if(descWrite.descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) { @@ -1329,21 +1287,10 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, else { bind.bufferInfo.SetFrom(descWrite.pBufferInfo[d]); - buffer = GetRecord(descWrite.pBufferInfo[d].buffer); } - - bind.AddBindRefs(ids, bufView, imgView, buffer, record, ref); } } - if(setrecord) - { - std::sort(ids.begin(), ids.end()); - - setrecord->descInfo->UpdateBackgroundRefCache(ids); - } - setrecord = NULL; - // this is almost identical to the above loop, except that instead of sourcing the descriptors // from the writedescriptor struct, we source it from our stored bindings on the source // descrpitor set @@ -1378,17 +1325,6 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, uint32_t curSrcIdx = pDescriptorCopies[i].srcArrayElement; uint32_t curDstIdx = pDescriptorCopies[i].dstArrayElement; - if(setrecord && setrecord != dstrecord) - { - std::sort(ids.begin(), ids.end()); - - setrecord->descInfo->UpdateBackgroundRefCache(ids); - - ids.clear(); - } - - setrecord = dstrecord; - for(uint32_t d = 0; d < pDescriptorCopies[i].descriptorCount; d++, curSrcIdx++, curDstIdx++) { if(srclayoutBinding->descriptorType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) @@ -1427,20 +1363,9 @@ void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, DescriptorSetSlot &bind = (*dstbinding)[curDstIdx]; - bind.RemoveBindRefs(ids, GetResourceManager(), dstrecord); bind = (*srcbinding)[curSrcIdx]; - bind.AddBindRefs(ids, GetResourceManager(), dstrecord, ref); } } - - if(setrecord) - { - std::sort(ids.begin(), ids.end()); - - setrecord->descInfo->UpdateBackgroundRefCache(ids); - setrecord = NULL; - ids.clear(); - } } } @@ -1683,26 +1608,10 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate( // need to track descriptor set contents whether capframing or idle if(IsCaptureMode(m_State)) { - rdcarray ids; - VkResourceRecord *setrecord = NULL; - - ids.reserve(128); - for(const VkDescriptorUpdateTemplateEntry &entry : tempInfo->updates) { VkResourceRecord *record = GetRecord(descriptorSet); - if(setrecord && setrecord != record) - { - std::sort(ids.begin(), ids.end()); - - setrecord->descInfo->UpdateBackgroundRefCache(ids); - - ids.clear(); - } - - setrecord = record; - RDCASSERT(record->descInfo && record->descInfo->layout); const DescSetLayout &layout = *record->descInfo->layout; @@ -1747,8 +1656,6 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate( DescriptorSetSlot &bind = (*binding)[curIdx]; - bind.RemoveBindRefs(ids, GetResourceManager(), record); - if(entry.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || entry.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { @@ -1788,17 +1695,8 @@ void WrappedVulkan::vkUpdateDescriptorSetWithTemplate( { bind.bufferInfo.SetFrom(*(const VkDescriptorBufferInfo *)src); } - - bind.AddBindRefs(ids, GetResourceManager(), record, ref); } } - - if(setrecord) - { - std::sort(ids.begin(), ids.end()); - - setrecord->descInfo->UpdateBackgroundRefCache(ids); - } } } diff --git a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp index dcbe8eea9..88cdfca53 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp @@ -873,6 +873,15 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, std::set refdIDs; + std::set descriptorSets; + + // pull in any copy sources, conservatively + if(capframe) + { + SCOPED_LOCK(m_CapDescriptorsLock); + descriptorSets.swap(m_CapDescriptors); + } + for(uint32_t s = 0; s < submitCount; s++) { for(uint32_t i = 0; i < pSubmits[s].commandBufferCount; i++) @@ -885,33 +894,11 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, if(capframe) { - // for each bound descriptor set, mark it referenced as well as all resources currently - // bound to it + // add the bound descriptor sets for(auto it = record->bakedCommands->cmdInfo->boundDescSets.begin(); it != record->bakedCommands->cmdInfo->boundDescSets.end(); ++it) { - GetResourceManager()->MarkResourceFrameReferenced(GetResID(*it), eFrameRef_Read); - - VkResourceRecord *setrecord = GetRecord(*it); - - SCOPED_LOCK(setrecord->descInfo->refLock); - - for(auto refit = setrecord->descInfo->bindFrameRefs.begin(); - refit != setrecord->descInfo->bindFrameRefs.end(); ++refit) - { - refdIDs.insert(refit->first); - GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second); - - if(refit->second.first & DescriptorSetData::SPARSE_REF_BIT) - { - VkResourceRecord *sparserecord = - GetResourceManager()->GetResourceRecord(refit->first); - - GetResourceManager()->MarkSparseMapReferenced(sparserecord->resInfo); - } - } - UpdateImageStates(setrecord->descInfo->bindImageStates); - GetResourceManager()->MergeReferencedMemory(setrecord->descInfo->bindMemRefs); + descriptorSets.insert(*it); } for(auto it = record->bakedCommands->cmdInfo->sparse.begin(); @@ -977,17 +964,6 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, { VkResourceRecord *record = GetRecord(pSubmits[s].pCommandBuffers[i]); - for(auto it = record->bakedCommands->cmdInfo->boundDescSets.begin(); - it != record->bakedCommands->cmdInfo->boundDescSets.end(); ++it) - { - VkResourceRecord *setrecord = GetRecord(*it); - - SCOPED_LOCK(setrecord->descInfo->refLock); - - GetResourceManager()->MarkBackgroundFrameReferenced( - setrecord->descInfo->backgroundFrameRefs); - } - record->bakedCommands->AddResourceReferences(GetResourceManager()); for(VkResourceRecord *sub : record->bakedCommands->cmdInfo->subcmds) @@ -1004,6 +980,52 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, if(capframe) { + VulkanResourceManager *rm = GetResourceManager(); + + // for each descriptor set, mark it referenced as well as all resources currently bound to it + for(auto it = descriptorSets.begin(); it != descriptorSets.end(); ++it) + { + rm->MarkResourceFrameReferenced(GetResID(*it), eFrameRef_Read); + + VkResourceRecord *setrecord = GetRecord(*it); + + DescriptorBindRefs refs; + + DescSetLayout *layout = setrecord->descInfo->layout; + + for(size_t b = 0, num = layout->bindings.size(); b < num; b++) + { + const DescSetLayout::Binding &bind = layout->bindings[b]; + + // skip empty bindings + if(bind.descriptorType == VK_DESCRIPTOR_TYPE_MAX_ENUM) + continue; + + uint32_t count = bind.descriptorCount; + if(bind.variableSize) + count = setrecord->descInfo->data.variableDescriptorCount; + + FrameRefType ref = GetRefType(bind.descriptorType); + + for(uint32_t a = 0; a < count; a++) + setrecord->descInfo->data.binds[b][a].AccumulateBindRefs(refs, rm, ref); + } + + for(auto refit = refs.bindFrameRefs.begin(); refit != refs.bindFrameRefs.end(); ++refit) + { + refdIDs.insert(refit->first); + GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second); + } + + for(auto refit = refs.sparseRefs.begin(); refit != refs.sparseRefs.end(); ++refit) + { + GetResourceManager()->MarkSparseMapReferenced((*refit)->resInfo); + } + + UpdateImageStates(refs.bindImageStates); + GetResourceManager()->MergeReferencedMemory(refs.bindMemRefs); + } + GetResourceManager()->MarkResourceFrameReferenced(GetResID(queue), eFrameRef_Read); if(fence != VK_NULL_HANDLE) diff --git a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp index 5143e1e55..e18870fe3 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_resource_funcs.cpp @@ -1283,10 +1283,14 @@ VkResult WrappedVulkan::vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkD // to memory mid-frame record->AddChunk(chunk); - record->AddParent(GetRecord(memory)); + VkResourceRecord *memrecord = GetRecord(memory); + + record->AddParent(memrecord); record->baseResource = id; record->memOffset = memoryOffset; + memrecord->storable |= record->storable; + // if the buffer was force-referenced, do the same with the memory if(IsForcedReference(record)) { @@ -1612,6 +1616,9 @@ VkResult WrappedVulkan::vkCreateBuffer(VkDevice device, const VkBufferCreateInfo record->AddChunk(chunk); + record->storable = (pCreateInfo->usage & (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) != 0; + bool isSparse = (pCreateInfo->flags & (VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT)) != 0; @@ -2067,6 +2074,8 @@ VkResult WrappedVulkan::vkCreateImage(VkDevice device, const VkImageCreateInfo * ResourceInfo &resInfo = *record->resInfo; resInfo.imageInfo = ImageInfo(*pCreateInfo); + record->storable = (pCreateInfo->usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0; + // pre-populate memory requirements ObjDisp(device)->GetImageMemoryRequirements(Unwrap(device), Unwrap(*pImage), &resInfo.memreqs); @@ -2450,6 +2459,8 @@ VkResult WrappedVulkan::vkBindBufferMemory2(VkDevice device, uint32_t bindInfoCo bufrecord->baseResource = memrecord->GetResourceID(); bufrecord->memOffset = pBindInfos[i].memoryOffset; + memrecord->storable |= bufrecord->storable; + // if the buffer was force-referenced, do the same with the memory if(IsForcedReference(bufrecord)) {