From 2828232ae8edd24d10c3d6a76bc6bd3a8fe9e57c Mon Sep 17 00:00:00 2001 From: Benson Joeris Date: Fri, 1 Feb 2019 12:50:51 -0500 Subject: [PATCH] Track frame references to ranges of Vulkan `VkDeviceMemory`s The current frame reference tracking handles `VkDeviceMemory`s as single resource--e.g. if one region of the memory is read, and a disjoint region of the memory is written, this is treated exactly the same way as if the read and write were on the same region of the memory. The new behaviour tracks the frame references for ranges of device memory; this will be used to improve the detection of device memory that does not require initialization or resetting. The `FrameRefType` associated with the entire memory resource (e.g. through `MarkResourceFrameReferenced`), should now keep an up-to-date maximum of the `FrameRefType`s of all of the intervals within that memory. Maximum is used here because `FrameRefType`s with stronger init/reset requirements have larger values. --- renderdoc/core/resource_manager.cpp | 22 ---- renderdoc/core/resource_manager.h | 53 ++++++++- renderdoc/driver/vulkan/vk_common.cpp | 18 ++-- renderdoc/driver/vulkan/vk_core.cpp | 3 + renderdoc/driver/vulkan/vk_manager.cpp | 21 +++- renderdoc/driver/vulkan/vk_manager.h | 4 + renderdoc/driver/vulkan/vk_resources.cpp | 6 +- renderdoc/driver/vulkan/vk_resources.h | 101 ++++++++++++++++++ .../driver/vulkan/wrappers/vk_queue_funcs.cpp | 5 + 9 files changed, 197 insertions(+), 36 deletions(-) diff --git a/renderdoc/core/resource_manager.cpp b/renderdoc/core/resource_manager.cpp index 730c08b2f..558a20f88 100644 --- a/renderdoc/core/resource_manager.cpp +++ b/renderdoc/core/resource_manager.cpp @@ -132,28 +132,6 @@ bool IsDirtyFrameRef(FrameRefType refType) return (refType != eFrameRef_None && refType != eFrameRef_Read); } -bool MarkReferenced(std::map &refs, ResourceId id, FrameRefType refType) -{ - auto refit = refs.find(id); - if(refit == refs.end()) - { - refs[id] = refType; - return true; - } - else - { - refit->second = ComposeFrameRefs(refit->second, refType); - } - return false; -} - -bool ResourceRecord::MarkResourceFrameReferenced(ResourceId id, FrameRefType refType) -{ - if(id == ResourceId()) - return false; - return MarkReferenced(m_FrameRefs, id, refType); -} - void ResourceRecord::AddResourceReferences(ResourceRecordHandler *mgr) { for(auto it = m_FrameRefs.begin(); it != m_FrameRefs.end(); ++it) diff --git a/renderdoc/core/resource_manager.h b/renderdoc/core/resource_manager.h index ecdc48eba..48d6c1261 100644 --- a/renderdoc/core/resource_manager.h +++ b/renderdoc/core/resource_manager.h @@ -158,7 +158,28 @@ inline InitReqType InitReq(FrameRefType refType) } // handle marking a resource referenced for read or write and storing RAW access etc. -bool MarkReferenced(std::map &refs, ResourceId id, FrameRefType refType); +template +bool MarkReferenced(std::map &refs, ResourceId id, FrameRefType refType, + Compose comp) +{ + auto refit = refs.find(id); + if(refit == refs.end()) + { + refs[id] = refType; + return true; + } + else + { + refit->second = comp(refit->second, refType); + } + return false; +} + +inline bool MarkReferenced(std::map &refs, ResourceId id, + FrameRefType refType) +{ + return MarkReferenced(refs, id, refType, ComposeFrameRefs); +} // verbose prints with IDs of each dirty resource and whether it was prepared, // and whether it was serialised. @@ -353,7 +374,12 @@ struct ResourceRecord bool HasDataPtr() { return DataPtr != NULL; } void SetDataOffset(uint64_t offs) { DataOffset = offs; } void SetDataPtr(byte *ptr) { DataPtr = ptr; } - bool MarkResourceFrameReferenced(ResourceId id, FrameRefType refType); + template + bool MarkResourceFrameReferenced(ResourceId id, FrameRefType refType, Compose comp); + inline bool MarkResourceFrameReferenced(ResourceId id, FrameRefType refType) + { + return MarkResourceFrameReferenced(id, refType, ComposeFrameRefs); + } void AddResourceReferences(ResourceRecordHandler *mgr); void AddReferencedIDs(std::set &ids) { @@ -400,6 +426,14 @@ protected: map m_FrameRefs; }; +template +bool ResourceRecord::MarkResourceFrameReferenced(ResourceId id, FrameRefType refType, Compose comp) +{ + if(id == ResourceId()) + return false; + return MarkReferenced(m_FrameRefs, id, refType, comp); +} + // the resource manager is a utility class that's not required but is likely wanted by any API // implementation. // It keeps track of resource records, which resources are alive and allows you to query for them by @@ -488,6 +522,9 @@ public: // mark resource referenced somewhere in the main frame-affecting calls. // That means this resource should be included in the final serialise out + template + void MarkResourceFrameReferenced(ResourceId id, FrameRefType refType, Compose comp); + inline void MarkResourceFrameReferenced(ResourceId id, FrameRefType refType); /////////////////////////////////////////// @@ -626,14 +663,16 @@ ResourceManager::~ResourceManager() } template -void ResourceManager::MarkResourceFrameReferenced(ResourceId id, FrameRefType refType) +template +void ResourceManager::MarkResourceFrameReferenced(ResourceId id, + FrameRefType refType, Compose comp) { SCOPED_LOCK(m_Lock); if(id == ResourceId()) return; - bool newRef = MarkReferenced(m_FrameReferencedResources, id, refType); + bool newRef = MarkReferenced(m_FrameReferencedResources, id, refType, comp); if(newRef) { @@ -644,6 +683,12 @@ void ResourceManager::MarkResourceFrameReferenced(ResourceId id, } } +template +void ResourceManager::MarkResourceFrameReferenced(ResourceId id, FrameRefType refType) +{ + return MarkResourceFrameReferenced(id, refType, ComposeFrameRefs); +} + template void ResourceManager::MarkDirtyResource(ResourceId res) { diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp index 62eec3734..fa8132e96 100644 --- a/renderdoc/driver/vulkan/vk_common.cpp +++ b/renderdoc/driver/vulkan/vk_common.cpp @@ -837,10 +837,12 @@ void DescriptorSetSlot::AddBindRefs(VkResourceRecord *record, FrameRefType ref) { if(texelBufferView != VK_NULL_HANDLE) { - record->AddBindFrameRef(GetResID(texelBufferView), eFrameRef_Read, - GetRecord(texelBufferView)->resInfo != NULL); - if(GetRecord(texelBufferView)->baseResource != ResourceId()) - record->AddBindFrameRef(GetRecord(texelBufferView)->baseResource, ref); + VkResourceRecord *bufView = GetRecord(texelBufferView); + record->AddBindFrameRef(bufView->GetResourceID(), eFrameRef_Read, bufView->resInfo != NULL); + if(bufView->baseResource != ResourceId()) + record->AddBindFrameRef(bufView->baseResource, eFrameRef_Read); + if(bufView->baseResourceMem != ResourceId()) + record->AddMemFrameRef(bufView->baseResourceMem, bufView->memOffset, bufView->memSize, ref); } if(imageInfo.imageView != VK_NULL_HANDLE) { @@ -856,9 +858,9 @@ void DescriptorSetSlot::AddBindRefs(VkResourceRecord *record, FrameRefType ref) } if(bufferInfo.buffer != VK_NULL_HANDLE) { - record->AddBindFrameRef(GetResID(bufferInfo.buffer), eFrameRef_Read, - GetRecord(bufferInfo.buffer)->resInfo != NULL); - if(GetRecord(bufferInfo.buffer)->baseResource != ResourceId()) - record->AddBindFrameRef(GetRecord(bufferInfo.buffer)->baseResource, ref); + VkResourceRecord *buf = GetRecord(bufferInfo.buffer); + record->AddBindFrameRef(GetResID(bufferInfo.buffer), eFrameRef_Read, buf->resInfo != NULL); + if(buf->baseResource != ResourceId()) + record->AddMemFrameRef(buf->baseResource, buf->memOffset, buf->memSize, ref); } } diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 2a9895ef0..9065133ca 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -1301,6 +1301,7 @@ void WrappedVulkan::StartFrameCapture(void *dev, void *wnd) m_CapturedFrames.push_back(frame); GetResourceManager()->ClearReferencedResources(); + GetResourceManager()->ClearReferencedMemory(); GetResourceManager()->MarkResourceFrameReferenced(GetResID(m_Instance), eFrameRef_Read); GetResourceManager()->MarkResourceFrameReferenced(GetResID(m_Device), eFrameRef_Read); @@ -1751,6 +1752,8 @@ bool WrappedVulkan::EndFrameCapture(void *dev, void *wnd) GetResourceManager()->MarkUnwrittenResources(); + GetResourceManager()->ClearReferencedMemory(); + GetResourceManager()->ClearReferencedResources(); GetResourceManager()->FreeInitialContents(); diff --git a/renderdoc/driver/vulkan/vk_manager.cpp b/renderdoc/driver/vulkan/vk_manager.cpp index 704c13813..30369fc2b 100644 --- a/renderdoc/driver/vulkan/vk_manager.cpp +++ b/renderdoc/driver/vulkan/vk_manager.cpp @@ -610,7 +610,26 @@ ResourceId VulkanResourceManager::GetFirstIDForHandle(uint64_t handle) void VulkanResourceManager::MarkMemoryFrameReferenced(ResourceId mem, VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) { - MarkResourceFrameReferenced(mem, refType); + FrameRefType maxRef = MarkMemoryReferenced(m_MemFrameRefs, mem, offset, size, refType); + MarkResourceFrameReferenced( + mem, maxRef, [](FrameRefType x, FrameRefType y) -> FrameRefType { return std::max(x, y); }); +} + +void VulkanResourceManager::MergeReferencedMemory(std::map &memRefs) +{ + for(auto j = memRefs.begin(); j != memRefs.end(); j++) + { + auto i = m_MemFrameRefs.find(j->first); + if(i == m_MemFrameRefs.end()) + m_MemFrameRefs.insert(*j); + else + i->second.Merge(j->second); + } +} + +void VulkanResourceManager::ClearReferencedMemory() +{ + m_MemFrameRefs.clear(); } bool VulkanResourceManager::Force_InitialState(WrappedVkRes *res, bool prepare) diff --git a/renderdoc/driver/vulkan/vk_manager.h b/renderdoc/driver/vulkan/vk_manager.h index b5ec9ee3a..81166e107 100644 --- a/renderdoc/driver/vulkan/vk_manager.h +++ b/renderdoc/driver/vulkan/vk_manager.h @@ -413,6 +413,9 @@ public: void MarkMemoryFrameReferenced(ResourceId mem, VkDeviceSize start, VkDeviceSize end, FrameRefType refType); + void MergeReferencedMemory(std::map &memRefs); + void ClearReferencedMemory(); + private: bool ResourceTypeRelease(WrappedVkRes *res); @@ -428,4 +431,5 @@ private: CaptureState m_State; WrappedVulkan *m_Core; + std::map m_MemFrameRefs; }; diff --git a/renderdoc/driver/vulkan/vk_resources.cpp b/renderdoc/driver/vulkan/vk_resources.cpp index 58dcccdda..de965edee 100644 --- a/renderdoc/driver/vulkan/vk_resources.cpp +++ b/renderdoc/driver/vulkan/vk_resources.cpp @@ -3033,7 +3033,11 @@ VkResourceRecord::~VkResourceRecord() void VkResourceRecord::MarkMemoryFrameReferenced(ResourceId mem, VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) { - MarkResourceFrameReferenced(mem, refType); + if(refType != eFrameRef_Read && refType != eFrameRef_None) + cmdInfo->dirtied.insert(mem); + FrameRefType maxRef = MarkMemoryReferenced(cmdInfo->memFrameRefs, mem, offset, size, refType); + MarkResourceFrameReferenced( + mem, maxRef, [](FrameRefType x, FrameRefType y) -> FrameRefType { return std::max(x, y); }); } void VkResourceRecord::MarkBufferFrameReferenced(VkResourceRecord *buf, VkDeviceSize offset, diff --git a/renderdoc/driver/vulkan/vk_resources.h b/renderdoc/driver/vulkan/vk_resources.h index a6c3ebae6..08151d882 100644 --- a/renderdoc/driver/vulkan/vk_resources.h +++ b/renderdoc/driver/vulkan/vk_resources.h @@ -25,6 +25,7 @@ #pragma once #include "common/wrapped_pool.h" +#include "core/intervals.h" #include "core/resource_manager.h" #include "vk_common.h" #include "vk_hookset_defs.h" @@ -906,6 +907,8 @@ struct ResourceInfo void Update(uint32_t numBindings, const VkSparseImageMemoryBind *pBindings); }; +struct MemRefs; + struct CmdBufferRecordingInfo { VkDevice device; @@ -930,6 +933,8 @@ struct CmdBufferRecordingInfo vector subcmds; + std::map memFrameRefs; + // AdvanceFrame/Present should be called after this buffer is submitted bool present; }; @@ -959,6 +964,7 @@ struct DescriptorSetData // mapping information static const uint32_t SPARSE_REF_BIT = 0x80000000; map > bindFrameRefs; + map bindMemRefs; }; struct PipelineLayoutData @@ -997,6 +1003,77 @@ struct AttachmentInfo VkImageMemoryBarrier barrier; }; +struct MemRefs +{ + Intervals rangeRefs; + inline MemRefs() {} + inline MemRefs(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) + { + 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) +{ + FrameRefType maxRefType = eFrameRef_None; + rangeRefs.update(offset, offset + size, refType, + [&maxRefType, comp](FrameRefType oldRef, FrameRefType newRef) -> FrameRefType { + FrameRefType ref = comp(oldRef, newRef); + maxRefType = std::max(maxRefType, ref); + return ref; + }); + return maxRefType; +} + +template +FrameRefType MemRefs::Merge(MemRefs &other, Compose comp) +{ + FrameRefType maxRefType = eFrameRef_None; + rangeRefs.merge(other.rangeRefs, + [&maxRefType, comp](FrameRefType oldRef, FrameRefType newRef) -> FrameRefType { + FrameRefType ref = comp(oldRef, newRef); + maxRefType = std::max(maxRefType, ref); + return ref; + }); + return maxRefType; +} + +template +FrameRefType MarkMemoryReferenced(std::map &memRefs, ResourceId mem, + VkDeviceSize offset, VkDeviceSize size, FrameRefType refType, + Compose comp) +{ + if(refType == eFrameRef_None) + return refType; + auto refs = memRefs.find(mem); + if(refs == memRefs.end()) + { + memRefs.insert(std::make_pair(mem, MemRefs(offset, size, refType))); + return refType; + } + else + { + return refs->second.Update(offset, size, refType, comp); + } +} + +inline FrameRefType MarkMemoryReferenced(std::map &memRefs, ResourceId mem, + VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) +{ + return MarkMemoryReferenced(memRefs, mem, offset, size, refType, ComposeFrameRefs); +} + struct DescUpdateTemplate; struct VkResourceRecord : public ResourceRecord @@ -1030,6 +1107,7 @@ public: cmdInfo->imgbarriers.swap(bakedCommands->cmdInfo->imgbarriers); cmdInfo->subcmds.swap(bakedCommands->cmdInfo->subcmds); cmdInfo->sparse.swap(bakedCommands->cmdInfo->sparse); + cmdInfo->memFrameRefs.swap(bakedCommands->cmdInfo->memFrameRefs); } void AddBindFrameRef(ResourceId id, FrameRefType ref, bool hasSparse = false) @@ -1054,6 +1132,29 @@ public: } } + void AddMemFrameRef(ResourceId mem, VkDeviceSize offset, VkDeviceSize size, FrameRefType refType) + { + if(mem == ResourceId()) + { + RDCERR("Unexpected NULL resource ID being added as a bind frame ref"); + return; + } + pair &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 = std::max(p.second, maxRef); + } + void RemoveBindFrameRef(ResourceId id) { // ignore any NULL IDs - probably an object that was diff --git a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp index 9f14328c0..6b235a1f3 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp @@ -857,6 +857,7 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, GetResourceManager()->MarkSparseMapReferenced(sparserecord->resInfo); } } + GetResourceManager()->MergeReferencedMemory(setrecord->descInfo->bindMemRefs); } for(auto it = record->bakedCommands->cmdInfo->sparse.begin(); @@ -867,6 +868,8 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, record->bakedCommands->AddResourceReferences(GetResourceManager()); record->bakedCommands->AddReferencedIDs(refdIDs); + GetResourceManager()->MergeReferencedMemory(record->bakedCommands->cmdInfo->memFrameRefs); + // ref the parent command buffer's alloc record, this will pull in the cmd buffer pool GetResourceManager()->MarkResourceFrameReferenced( record->cmdInfo->allocRecord->GetResourceID(), eFrameRef_Read); @@ -876,6 +879,8 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddResourceReferences( GetResourceManager()); record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddReferencedIDs(refdIDs); + GetResourceManager()->MergeReferencedMemory( + record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->cmdInfo->memFrameRefs); GetResourceManager()->MarkResourceFrameReferenced( record->bakedCommands->cmdInfo->subcmds[sub]->cmdInfo->allocRecord->GetResourceID(), eFrameRef_Read);