Completely remove idle background tracking of descriptors on vulkan

* In principle descriptors should be updated less often than they are
  referenced, so we used to cache tracking of them at update time to improve the
  speed that references can be processed at submit time during capture.
* Some applications though update a *lot* of descriptors *very* often,
  effectively writing all their descriptors for a frame every frame. That means
  that this background tracking is wasteful and has a big performance impact, so
  instead it's a better balance to do more work at submit time during capture.
This commit is contained in:
baldurk committed 2021-01-05 18:26:27 +00:00
1 parent b01c84f051
commit f5f9e78dde
14 files changed
+399 -565

No files matched your search

+26 -2
View File
@@ -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)
+55 -41
View File
@@ -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<ResourceId> 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<Configuration>::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<Configuration>::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<Configuration>::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<Configuration>::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<Configuration>::MarkResourceFrameReferenced(ResourceId id,
}
}
UpdateLastWriteAndPartialUseTime(id, refType);
UpdateLastWriteTime(id, refType);
if(IsBackgroundCapturing(m_State))
return;
@@ -1137,7 +1149,7 @@ void ResourceManager<Configuration>::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<Configuration>::ResetLastWriteTimes()
}
template <typename Configuration>
inline void ResourceManager<Configuration>::UpdateLastWriteAndPartialUseTime(ResourceId id,
FrameRefType refType)
inline void ResourceManager<Configuration>::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<Configuration>::UpdateLastWriteAndPartialUseTime(Res
double now = m_ResourcesUpdateTimer.GetMilliseconds();
if(IsDirtyFrameRef(refType))
it->writeTime = now;
it->writeTime = now;
if(!IsCompleteWriteFrameRef(refType))
it->partialUseTime = now;
}
template <typename Configuration>
inline void ResourceManager<Configuration>::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<Configuration>::HasPersistentAge(ResourceId id)
}
template <typename Configuration>
inline bool ResourceManager<Configuration>::HasIrrelevantAge(ResourceId id)
inline bool ResourceManager<Configuration>::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 <typename Configuration>
@@ -1286,7 +1300,7 @@ inline bool ResourceManager<Configuration>::ShouldSkip(ResourceId id)
if(!IsResourceTrackedForPersistency(res))
return false;
return HasIrrelevantAge(id);
return HasSkippableAge(id);
}
template <typename Configuration>
+61 -125
View File
@@ -1057,90 +1057,54 @@ void DescriptorSetSlotImageInfo::SetFrom(const VkDescriptorImageInfo &imInfo, bo
imageLayout = imInfo.imageLayout;
}
void DescriptorSetSlot::RemoveBindRefs(rdcarray<ResourceId> &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<ResourceId> &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<ResourceId> &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<ResourceId> &ids, VulkanResourceMan
if(bufferInfo.buffer != ResourceId())
buffer = rm->GetResourceRecord(bufferInfo.buffer);
AddBindRefs(ids, bufView, imgView, buffer, descSetRecord, ref);
}
void DescriptorSetData::UpdateBackgroundRefCache(const rdcarray<ResourceId> &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<ResourceId, FrameRefType> *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<ResourceId, FrameRefType> &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);
}
}
+3 -5
View File
@@ -432,13 +432,11 @@ struct DescriptorSetSlotImageInfo
VkImageLayout imageLayout;
};
struct DescriptorBindRefs;
struct DescriptorSetSlot
{
void RemoveBindRefs(rdcarray<ResourceId> &ids, VulkanResourceManager *rm, VkResourceRecord *record);
void AddBindRefs(rdcarray<ResourceId> &ids, VulkanResourceManager *rm, VkResourceRecord *record,
FrameRefType ref);
void AddBindRefs(rdcarray<ResourceId> &ids, VkResourceRecord *bufView, VkResourceRecord *imgView,
VkResourceRecord *buffer, VkResourceRecord *descSetRecord, FrameRefType ref);
void AccumulateBindRefs(DescriptorBindRefs &refs, VulkanResourceManager *rm, FrameRefType ref) const;
// VkDescriptorBufferInfo
DescriptorSetSlotBufferInfo bufferInfo;
+5 -2
View File
@@ -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();
+3
View File
@@ -326,6 +326,9 @@ private:
std::set<rdcstr> m_StringDB;
Threading::CriticalSection m_CapDescriptorsLock;
std::set<VkDescriptorSet> m_CapDescriptors;
VkResourceRecord *m_FrameCaptureRecord;
Chunk *m_HeaderChunk;
+3 -3
View File
@@ -423,7 +423,7 @@ bool VulkanResourceManager::Serialise_DeviceMemoryRefs(SerialiserType &ser,
{
ResourceId mem = it_data->memory;
auto res = m_MemFrameRefs.insert(rdcpair<ResourceId, MemRefs>(mem, MemRefs()));
auto res = m_MemFrameRefs.insert(std::pair<ResourceId, MemRefs>(mem, MemRefs()));
RDCASSERTMSG("MemRefIntervals for each memory resource must be contiguous", res.second);
Intervals<FrameRefType> &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<ResourceId, MemRefs> &memRefs)
void VulkanResourceManager::MergeReferencedMemory(std::unordered_map<ResourceId, MemRefs> &memRefs)
{
SCOPED_LOCK_OPTIONAL(m_Lock, m_Capturing);
+2 -2
View File
@@ -449,7 +449,7 @@ public:
void AddDeviceMemory(ResourceId mem);
void RemoveDeviceMemory(ResourceId mem);
void MergeReferencedMemory(rdcflatmap<ResourceId, MemRefs> &memRefs);
void MergeReferencedMemory(std::unordered_map<ResourceId, MemRefs> &memRefs);
void ClearReferencedMemory();
MemRefs *FindMemRefs(ResourceId mem);
ImgRefs *FindImgRefs(ResourceId img);
@@ -484,7 +484,7 @@ private:
rdcarray<ResourceId> InitialContentResources();
WrappedVulkan *m_Core;
rdcflatmap<ResourceId, MemRefs> m_MemFrameRefs;
std::unordered_map<ResourceId, MemRefs> m_MemFrameRefs;
std::set<ResourceId> m_DeviceMemories;
InitPolicy m_InitPolicy = eInitPolicy_CopyAll;
};
+45 -4
View File
@@ -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);
+41 -152
View File
@@ -985,7 +985,28 @@ struct ResourceInfo
void Update(uint32_t numBindings, const VkSparseImageMemoryBind *pBindings);
};
struct MemRefs;
struct MemRefs
{
Intervals<FrameRefType> 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 <typename Compose>
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 <typename Compose>
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<ResourceId, ImageState> imageStates;
rdcflatmap<ResourceId, MemRefs> memFrameRefs;
std::unordered_map<ResourceId, MemRefs> 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<ResourceId, rdcpair<uint32_t, FrameRefType>> bindFrameRefs;
rdcflatmap<ResourceId, MemRefs> 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<ResourceId, FrameRefType> bindFrameRefs;
std::unordered_map<ResourceId, MemRefs> bindMemRefs;
rdcflatmap<ResourceId, ImageState> bindImageStates;
void UpdateBackgroundRefCache(const rdcarray<ResourceId> &ids);
rdcflatmap<ResourceId, FrameRefType> backgroundFrameRefs;
std::unordered_set<VkResourceRecord *> 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<FrameRefType> 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 <typename Compose>
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 <typename Compose>
FrameRefType Merge(MemRefs &other, Compose comp);
inline FrameRefType Merge(MemRefs &other) { return Merge(other, ComposeFrameRefs); }
};
template <typename Compose>
FrameRefType MemRefs::Update(VkDeviceSize offset, VkDeviceSize size, FrameRefType refType,
Compose comp)
@@ -2081,7 +2076,7 @@ FrameRefType MarkImageReferenced(rdcflatmap<ResourceId, ImageState> &imageStates
uint32_t queueFamilyIndex, FrameRefType refType);
template <typename Compose>
FrameRefType MarkMemoryReferenced(rdcflatmap<ResourceId, MemRefs> &memRefs, ResourceId mem,
FrameRefType MarkMemoryReferenced(std::unordered_map<ResourceId, MemRefs> &memRefs, ResourceId mem,
VkDeviceSize offset, VkDeviceSize size, FrameRefType refType,
Compose comp)
{
@@ -2099,8 +2094,9 @@ FrameRefType MarkMemoryReferenced(rdcflatmap<ResourceId, MemRefs> &memRefs, Reso
}
}
inline FrameRefType MarkMemoryReferenced(rdcflatmap<ResourceId, MemRefs> &memRefs, ResourceId mem,
VkDeviceSize offset, VkDeviceSize size, FrameRefType refType)
inline FrameRefType MarkMemoryReferenced(std::unordered_map<ResourceId, MemRefs> &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<ResourceId> &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<uint32_t, FrameRefType> &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<ResourceId> &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<uint32_t, FrameRefType> &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<ResourceId> &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<uint32_t, FrameRefType> &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<ResourceId> &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);
@@ -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<VkImage>(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<VkImage>(att->baseResource);
barrier.subresourceRange = att->viewRange;
barriers.push_back(barrier);
}
barriers.push_back(barrier);
}
}
@@ -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<ResourceId> 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<ResourceId> 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);
}
}
}
@@ -873,6 +873,15 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount,
std::set<ResourceId> refdIDs;
std::set<VkDescriptorSet> 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)
@@ -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))
{