skip initial states for cleared renderpass

also refactored the postpone logic
This commit is contained in:
thisisjimmyfb
2020-06-01 10:15:02 -07:00
committed by Baldur Karlsson
parent aa16eb0ed0
commit 58a6d7eb76
8 changed files with 604 additions and 120 deletions
+5
View File
@@ -170,6 +170,11 @@ bool IsDirtyFrameRef(FrameRefType refType)
return (refType != eFrameRef_None && refType != eFrameRef_Read);
}
bool IsCompleteWriteFrameRef(FrameRefType refType)
{
return refType == eFrameRef_CompleteWrite;
}
void ResourceRecord::AddResourceReferences(ResourceRecordHandler *mgr)
{
for(auto it = m_FrameRefs.begin(); it != m_FrameRefs.end(); ++it)
+140 -21
View File
@@ -120,6 +120,8 @@ const FrameRefType eFrameRef_Maximum = eFrameRef_WriteBeforeRead;
// 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;
DECLARE_REFLECTION_ENUM(FrameRefType);
@@ -149,6 +151,7 @@ FrameRefType ComposeFrameRefsFirstKnown(FrameRefType first, FrameRefType second)
FrameRefType KeepOldFrameRef(FrameRefType first, FrameRefType second);
bool IsDirtyFrameRef(FrameRefType refType);
bool IsCompleteWriteFrameRef(FrameRefType refType);
// Captures the possible initialization/reset requirements for resources.
// These requirements are entirely determined by the resource's FrameRefType,
@@ -645,16 +648,21 @@ public:
WrappedResourceType GetWrapper(RealResourceType real);
void RemoveWrapper(RealResourceType real);
void Prepare_ResourceInitialStateIfNeeded(ResourceId id);
void Prepare_ResourceIfActivePostponed(ResourceId id);
void Prepare_InitialStateIfPostponed(ResourceId id);
void SkipOrPostponeOrPrepare_InitialState(ResourceId id, FrameRefType refType);
void UpdateLastWriteTime(ResourceId id);
void UpdateLastWriteTime(ResourceId id, FrameRefType refType);
void ResetLastWriteTimes();
void ResetCaptureStartTime();
void UpdateLastPartialUseTime(ResourceId id, FrameRefType refType);
void ResetLastPartialUseTimes();
bool HasPersistentAge(ResourceId id);
bool HasIrrelevantAge(ResourceId id);
bool IsResourcePostponed(ResourceId id);
bool IsResourcePersistent(ResourceId id);
bool IsResourceSkipped(ResourceId id);
bool ShouldPostpone(ResourceId id);
bool ShouldSkip(ResourceId id);
virtual bool IsResourceTrackedForPersistency(const WrappedResourceType &res) { return false; }
protected:
@@ -741,11 +749,18 @@ protected:
// During initial resources preparation, persistent resources are
// postponed until serializing to RDC file.
std::set<ResourceId> m_PostponedResourceIDs;
// During initial resources preparation, resources that are completely written
// over are skipped
std::set<ResourceId> m_SkippedResourceIDs;
// On marking resource write-referenced in frame, its last write
// time is reset. The time is used to determine persistent resources,
// and is checked against the `PERSISTENT_RESOURCE_AGE`.
std::map<ResourceId, double> m_LastWriteTime;
// m_LastPartialUseTime is referring to: eFrameRef_PartialWrite, eFrameRef_Read,
// eFrameRef_ReadBeforeWrite, eFrameRef_WriteBeforeRead. The goal is to predict
// whether a resource will have a eFrameRef_CompleteWrite reference or not
std::map<ResourceId, double> m_LastPartialUseTime;
// Timestamp at the beginning of the frame capture. Used to determine which
// resources to refresh for their last write time (see `m_LastWriteTime`).
@@ -804,12 +819,19 @@ void ResourceManager<Configuration>::MarkResourceFrameReferenced(ResourceId id,
if(id == ResourceId())
return;
if(IsDirtyFrameRef(refType))
if(IsActiveCapturing(m_State))
{
Prepare_ResourceIfActivePostponed(id);
UpdateLastWriteTime(id);
SkipOrPostponeOrPrepare_InitialState(id, refType);
if(IsDirtyFrameRef(refType))
{
Prepare_InitialStateIfPostponed(id);
}
}
UpdateLastPartialUseTime(id, refType);
UpdateLastWriteTime(id, refType);
if(IsBackgroundCapturing(m_State))
return;
@@ -972,10 +994,11 @@ void ResourceManager<Configuration>::FreeInitialContents()
m_InitialContents.erase(m_InitialContents.begin());
}
m_PostponedResourceIDs.clear();
m_SkippedResourceIDs.clear();
}
template <typename Configuration>
void ResourceManager<Configuration>::Prepare_ResourceInitialStateIfNeeded(ResourceId id)
void ResourceManager<Configuration>::Prepare_InitialStateIfPostponed(ResourceId id)
{
SCOPED_LOCK(m_Lock);
@@ -983,28 +1006,58 @@ void ResourceManager<Configuration>::Prepare_ResourceInitialStateIfNeeded(Resour
return;
WrappedResourceType res = GetCurrentResource(id);
RDCDEBUG("Preparing resource %s after it has been postponed.", ToStr(id).c_str());
Prepare_InitialState(res);
m_PostponedResourceIDs.erase(id);
}
template <typename Configuration>
void ResourceManager<Configuration>::Prepare_ResourceIfActivePostponed(ResourceId id)
void ResourceManager<Configuration>::SkipOrPostponeOrPrepare_InitialState(ResourceId id,
FrameRefType refType)
{
SCOPED_LOCK(m_Lock);
// If the resource was postponed during Active Capture, we need to prepare it
// right away, since next Read might be invalid.
if(!IsActiveCapturing(m_State) || !IsResourcePostponed(id))
if(!IsResourceSkipped(id))
return;
RDCDEBUG("Preparing resource %s after it has been postponed.", ToStr(id).c_str());
Prepare_ResourceInitialStateIfNeeded(id);
// the first time we encounter a skipped resource, we can choose to
// skip this resource forever, convert it to be postponed, or prepare
// it immediately. We can't retrieve its initial state once it has
// been written over.
m_SkippedResourceIDs.erase(id);
// 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(),
ToStr(refType).c_str());
return;
}
// If this resource is only being read, we might as well try to
// postpone it to conserve memory consumption.
if(!IsDirtyFrameRef(refType) && IsResourceTrackedForPersistency(GetCurrentResource(id)))
{
m_PostponedResourceIDs.insert(id);
RDCDEBUG("Resource %s converted from skipped to postponed on refType of %s", ToStr(id).c_str(),
ToStr(refType).c_str());
SetInitialContents(id, InitialContentData());
}
else
{
WrappedResourceType res = GetCurrentResource(id);
RDCDEBUG("Preparing resource %s after it has been skipped on refType of %s", ToStr(id).c_str(),
ToStr(refType).c_str());
Prepare_InitialState(res);
}
}
template <typename Configuration>
inline void ResourceManager<Configuration>::UpdateLastWriteTime(ResourceId id)
inline void ResourceManager<Configuration>::UpdateLastWriteTime(ResourceId id, FrameRefType refType)
{
if(!IsDirtyFrameRef(refType))
return;
SCOPED_LOCK(m_Lock);
m_LastWriteTime[id] = m_ResourcesUpdateTimer.GetMilliseconds();
}
@@ -1031,6 +1084,27 @@ inline void ResourceManager<Configuration>::ResetLastWriteTimes()
}
}
template <typename Configuration>
inline void ResourceManager<Configuration>::UpdateLastPartialUseTime(ResourceId id,
FrameRefType refType)
{
if(IsCompleteWriteFrameRef(refType))
return;
SCOPED_LOCK(m_Lock);
m_LastPartialUseTime[id] = m_ResourcesUpdateTimer.GetMilliseconds();
}
template <typename Configuration>
inline void ResourceManager<Configuration>::ResetLastPartialUseTimes()
{
SCOPED_LOCK(m_Lock);
for(auto it = m_LastPartialUseTime.begin(); it != m_LastPartialUseTime.end(); ++it)
{
if(m_captureStartTime - it->second <= IRRELEVANT_RESOURCE_AGE)
it->second = m_ResourcesUpdateTimer.GetMilliseconds();
}
}
template <typename Configuration>
inline bool ResourceManager<Configuration>::HasPersistentAge(ResourceId id)
{
@@ -1044,6 +1118,19 @@ inline bool ResourceManager<Configuration>::HasPersistentAge(ResourceId id)
return m_ResourcesUpdateTimer.GetMilliseconds() - it->second >= PERSISTENT_RESOURCE_AGE;
}
template <typename Configuration>
inline bool ResourceManager<Configuration>::HasIrrelevantAge(ResourceId id)
{
SCOPED_LOCK(m_Lock);
auto it = m_LastPartialUseTime.find(id);
if(it == m_LastPartialUseTime.end())
return true;
return m_ResourcesUpdateTimer.GetMilliseconds() - it->second >= IRRELEVANT_RESOURCE_AGE;
}
template <typename Configuration>
inline bool ResourceManager<Configuration>::IsResourcePostponed(ResourceId id)
{
@@ -1052,7 +1139,14 @@ inline bool ResourceManager<Configuration>::IsResourcePostponed(ResourceId id)
}
template <typename Configuration>
inline bool ResourceManager<Configuration>::IsResourcePersistent(ResourceId id)
inline bool ResourceManager<Configuration>::IsResourceSkipped(ResourceId id)
{
SCOPED_LOCK(m_Lock);
return m_SkippedResourceIDs.find(id) != m_SkippedResourceIDs.end();
}
template <typename Configuration>
inline bool ResourceManager<Configuration>::ShouldPostpone(ResourceId id)
{
SCOPED_LOCK(m_Lock);
@@ -1064,6 +1158,19 @@ inline bool ResourceManager<Configuration>::IsResourcePersistent(ResourceId id)
return HasPersistentAge(id);
}
template <typename Configuration>
inline bool ResourceManager<Configuration>::ShouldSkip(ResourceId id)
{
SCOPED_LOCK(m_Lock);
WrappedResourceType res = GetCurrentResource(id);
if(!IsResourceTrackedForPersistency(res))
return false;
return HasIrrelevantAge(id);
}
template <typename Configuration>
void ResourceManager<Configuration>::CreateInitialContents(ReadSerialiser &ser)
{
@@ -1201,6 +1308,7 @@ void ResourceManager<Configuration>::PrepareInitialContents()
RDCDEBUG("Preparing up to %u potentially dirty resources", (uint32_t)m_DirtyResources.size());
uint32_t prepared = 0;
uint32_t postponed = 0;
uint32_t skipped = 0;
float num = float(m_DirtyResources.size());
float idx = 0.0f;
@@ -1225,7 +1333,14 @@ void ResourceManager<Configuration>::PrepareInitialContents()
if(record == NULL || record->InternalResource)
continue;
if(IsResourcePersistent(id))
if(ShouldSkip(id))
{
m_SkippedResourceIDs.insert(id);
skipped++;
continue;
}
if(ShouldPostpone(id))
{
m_PostponedResourceIDs.insert(id);
// Set empty contents here, it'll be prepared on serialization.
@@ -1243,7 +1358,7 @@ void ResourceManager<Configuration>::PrepareInitialContents()
Prepare_InitialState(res);
}
RDCDEBUG("Prepared %u dirty resources, postponed %u", prepared, postponed);
RDCDEBUG("Prepared %u dirty resources, postponed %u, skipped %u", prepared, postponed, skipped);
}
template <typename Configuration>
@@ -1270,7 +1385,7 @@ void ResourceManager<Configuration>::InsertInitialContentsChunks(WriteSerialiser
!RenderDoc::Inst().GetCaptureOptions().refAllResources)
{
#if ENABLED(VERBOSE_DIRTY_RESOURCES)
RDCDEBUG("Dirty tesource %s is GPU dirty but not referenced - skipping", ToStr(id).c_str());
RDCDEBUG("Dirty resource %s is GPU dirty but not referenced - skipping", ToStr(id).c_str());
#endif
skipped++;
continue;
@@ -1299,7 +1414,7 @@ void ResourceManager<Configuration>::InsertInitialContentsChunks(WriteSerialiser
#endif
// Load postponed resource if needed.
Prepare_ResourceInitialStateIfNeeded(id);
Prepare_InitialStateIfPostponed(id);
dirty++;
@@ -1638,11 +1753,15 @@ void ResourceManager<Configuration>::ReleaseCurrentResource(ResourceId id)
// We potentially need to prepare this resource on Active Capture,
// if it was postponed, but is about to go away.
Prepare_ResourceIfActivePostponed(id);
if(IsActiveCapturing(m_State))
{
Prepare_InitialStateIfPostponed(id);
}
m_CurrentResourceMap.erase(id);
m_DirtyResources.erase(id);
m_LastWriteTime.erase(id);
m_LastPartialUseTime.erase(id);
}
template <typename Configuration>
+2
View File
@@ -2054,6 +2054,8 @@ bool WrappedVulkan::EndFrameCapture(void *dev, void *wnd)
GetResourceManager()->ResetLastWriteTimes();
GetResourceManager()->ResetLastPartialUseTimes();
GetResourceManager()->MarkUnwrittenResources();
GetResourceManager()->ClearReferencedMemory();
+329 -6
View File
@@ -3216,6 +3216,326 @@ VkImageAspectFlags FormatImageAspects(VkFormat fmt)
return VK_IMAGE_ASPECT_COLOR_BIT;
}
RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo &ci)
{
// *2 in case we need separate barriers for depth and stencil, +1 for the terminating null
// attachment info (though separate depth/stencil buffers aren't needed here, we keep the
// array size the same)
uint32_t arrayCount = ci.attachmentCount * 2 + 1;
imageAttachments = new AttachmentInfo[arrayCount];
RDCEraseMem(imageAttachments, arrayCount * sizeof(imageAttachments[0]));
for(uint32_t i = 0; i < ci.attachmentCount; ++i)
{
imageAttachments[i].record = NULL;
imageAttachments[i].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageAttachments[i].barrier.oldLayout = ci.pAttachments[i].initialLayout;
imageAttachments[i].barrier.newLayout = ci.pAttachments[i].finalLayout;
}
// VK_KHR_multiview
const VkRenderPassMultiviewCreateInfo *multiview =
(const VkRenderPassMultiviewCreateInfo *)FindNextStruct(
&ci, VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO);
if(multiview && multiview->subpassCount > 0)
{
multiviewViewMaskTable = new uint32_t[arrayCount];
RDCEraseMem(multiviewViewMaskTable, arrayCount * sizeof(multiviewViewMaskTable[0]));
}
else
{
multiviewViewMaskTable = NULL;
}
loadOpTable = new VkAttachmentLoadOp[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
for(uint32_t a = 0; a < arrayCount; ++a)
loadOpTable[a] = VK_ATTACHMENT_LOAD_OP_LOAD;
for(uint32_t s = 0; s < ci.subpassCount; ++s)
{
const VkAttachmentReference *pColorAttachments = ci.pSubpasses[s].pColorAttachments;
const VkAttachmentReference *pResolveAttachments = ci.pSubpasses[s].pResolveAttachments;
const VkAttachmentReference *pDepthStencilAttachment = ci.pSubpasses[s].pDepthStencilAttachment;
if(pColorAttachments)
{
const VkAttachmentReference *pColorRunner = pColorAttachments;
const VkAttachmentReference *pColorEnd = pColorRunner + ci.pSubpasses[s].colorAttachmentCount;
while(pColorRunner != pColorEnd)
{
uint32_t index = pColorRunner->attachment;
if(index < ci.attachmentCount)
{
loadOpTable[index] = ci.pAttachments[index].loadOp;
if(multiviewViewMaskTable)
{
multiviewViewMaskTable[index] |= multiview->pViewMasks[s];
}
}
++pColorRunner;
}
}
if(pResolveAttachments)
{
const VkAttachmentReference *pResolveRunner = pResolveAttachments;
const VkAttachmentReference *pResolveEnd =
pResolveRunner + ci.pSubpasses[s].colorAttachmentCount;
while(pResolveRunner != pResolveEnd)
{
uint32_t index = pResolveRunner->attachment;
if(index < ci.attachmentCount)
{
loadOpTable[index] = ci.pAttachments[index].loadOp;
if(multiviewViewMaskTable)
{
multiviewViewMaskTable[index] |= multiview->pViewMasks[s];
}
}
++pResolveRunner;
}
}
if(pDepthStencilAttachment)
{
uint32_t index = pDepthStencilAttachment->attachment;
if(index < ci.attachmentCount)
{
VkAttachmentLoadOp depthStencilLoadOp = ci.pAttachments[index].loadOp;
// make depthstencil VK_ATTACHMENT_LOAD_OP_LOAD if either depth or stencil is
// VK_ATTACHMENT_LOAD_OP_LOAD
if(depthStencilLoadOp != VK_ATTACHMENT_LOAD_OP_LOAD &&
IsStencilFormat(ci.pAttachments[index].format))
{
depthStencilLoadOp = ci.pAttachments[index].stencilLoadOp;
}
loadOpTable[index] = depthStencilLoadOp;
if(multiviewViewMaskTable)
{
multiviewViewMaskTable[index] |= multiview->pViewMasks[s];
}
}
}
}
}
RenderPassInfo::RenderPassInfo(const VkRenderPassCreateInfo2 &ci)
{
// *2 in case we need separate barriers for depth and stencil, +1 for the terminating null
// attachment info
uint32_t arrayCount = ci.attachmentCount * 2 + 1;
imageAttachments = new AttachmentInfo[arrayCount];
RDCEraseMem(imageAttachments, arrayCount * sizeof(imageAttachments[0]));
// need to keep a table for the index remap, because imageAttachments won't have the same
// order as ci.pAttachments
rdcarray<uint32_t> indexRemapTable;
indexRemapTable.fill(ci.attachmentCount, 0xFFFFFFFF);
for(uint32_t i = 0, a = 0; i < ci.attachmentCount; i++, a++)
{
imageAttachments[a].record = NULL;
imageAttachments[a].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageAttachments[a].barrier.oldLayout = ci.pAttachments[i].initialLayout;
imageAttachments[a].barrier.newLayout = ci.pAttachments[i].finalLayout;
indexRemapTable[i] = a;
// VK_KHR_separate_depth_stencil_layouts
VkAttachmentDescriptionStencilLayoutKHR *separateStencil =
(VkAttachmentDescriptionStencilLayoutKHR *)FindNextStruct(
&ci.pAttachments[i], VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT_KHR);
if(separateStencil)
{
imageAttachments[a].barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
// add a separate barrier for stencil
a++;
imageAttachments[a].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageAttachments[a].barrier.oldLayout = separateStencil->stencilInitialLayout;
imageAttachments[a].barrier.newLayout = separateStencil->stencilFinalLayout;
imageAttachments[a].barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
}
}
// if any subpass' viewMask is non-zero, then multiview is enabled
multiviewViewMaskTable = NULL;
for(uint32_t s = 0; s < ci.subpassCount; ++s)
{
if(ci.pSubpasses[s].viewMask)
{
multiviewViewMaskTable = new uint32_t[arrayCount];
RDCEraseMem(multiviewViewMaskTable, arrayCount * sizeof(multiviewViewMaskTable[0]));
break;
}
}
loadOpTable = new VkAttachmentLoadOp[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
for(uint32_t a = 0; a < arrayCount; ++a)
loadOpTable[a] = VK_ATTACHMENT_LOAD_OP_LOAD;
for(uint32_t s = 0; s < ci.subpassCount; ++s)
{
const VkAttachmentReference2 *pColorAttachments = ci.pSubpasses[s].pColorAttachments;
const VkAttachmentReference2 *pResolveAttachments = ci.pSubpasses[s].pResolveAttachments;
const VkAttachmentReference2 *pDepthStencilAttachment = ci.pSubpasses[s].pDepthStencilAttachment;
if(pColorAttachments)
{
const VkAttachmentReference2 *pColorRunner = pColorAttachments;
const VkAttachmentReference2 *pColorEnd = pColorRunner + ci.pSubpasses[s].colorAttachmentCount;
while(pColorRunner != pColorEnd)
{
uint32_t index = pColorRunner->attachment;
if(index < ci.attachmentCount)
{
uint32_t remappedIndex = indexRemapTable[index];
RDCASSERT(remappedIndex < arrayCount);
loadOpTable[remappedIndex] = ci.pAttachments[index].loadOp;
if(multiviewViewMaskTable)
{
multiviewViewMaskTable[remappedIndex] |= ci.pSubpasses[s].viewMask;
}
}
++pColorRunner;
}
}
if(pResolveAttachments)
{
const VkAttachmentReference2 *pResolveRunner = pResolveAttachments;
const VkAttachmentReference2 *pResolveEnd =
pResolveRunner + ci.pSubpasses[s].colorAttachmentCount;
while(pResolveRunner != pResolveEnd)
{
uint32_t index = pResolveRunner->attachment;
if(index < ci.attachmentCount)
{
uint32_t remappedIndex = indexRemapTable[index];
RDCASSERT(remappedIndex < arrayCount);
loadOpTable[remappedIndex] = ci.pAttachments[index].loadOp;
if(multiviewViewMaskTable)
{
multiviewViewMaskTable[remappedIndex] |= ci.pSubpasses[s].viewMask;
}
}
++pResolveRunner;
}
}
if(pDepthStencilAttachment)
{
uint32_t index = pDepthStencilAttachment->attachment;
if(index < ci.attachmentCount)
{
VkAttachmentLoadOp depthStencilLoadOp = ci.pAttachments[index].loadOp;
VkAttachmentLoadOp stencilLoadOp = ci.pAttachments[index].stencilLoadOp;
uint32_t remappedIndex = indexRemapTable[index];
RDCASSERT(remappedIndex < arrayCount);
if(IsStencilFormat(ci.pAttachments[index].format))
{
// VK_KHR_separate_depth_stencil_layouts
VkAttachmentDescriptionStencilLayoutKHR *separateStencil =
(VkAttachmentDescriptionStencilLayoutKHR *)FindNextStruct(
&ci.pAttachments[index],
VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT_KHR);
if(separateStencil)
{
loadOpTable[remappedIndex + 1] = stencilLoadOp;
}
else
{
// make depthstencil VK_ATTACHMENT_LOAD_OP_LOAD if either depth or stencil is
// VK_ATTACHMENT_LOAD_OP_LOAD
if(depthStencilLoadOp != VK_ATTACHMENT_LOAD_OP_LOAD)
{
depthStencilLoadOp = stencilLoadOp;
}
}
}
loadOpTable[remappedIndex] = depthStencilLoadOp;
if(multiviewViewMaskTable)
{
multiviewViewMaskTable[remappedIndex] |= ci.pSubpasses[s].viewMask;
}
}
}
}
}
RenderPassInfo::~RenderPassInfo()
{
delete[] imageAttachments;
delete[] loadOpTable;
delete[] multiviewViewMaskTable;
}
FramebufferInfo::FramebufferInfo(const VkFramebufferCreateInfo &ci)
{
// *2 in case we need separate barriers for depth and stencil, +1 for the terminating null
// attachment info
uint32_t arrayCount = ci.attachmentCount * 2 + 1;
imageAttachments = new AttachmentInfo[arrayCount];
RDCEraseMem(imageAttachments, arrayCount * sizeof(imageAttachments[0]));
width = ci.width;
height = ci.height;
layers = ci.layers;
}
FramebufferInfo::~FramebufferInfo()
{
delete[] imageAttachments;
}
bool FramebufferInfo::AttachmentFullyReferenced(size_t attachmentIndex, const RenderPassInfo *rpi)
{
VkResourceRecord *att = imageAttachments[attachmentIndex].record;
// if framebuffer doesn't reference the entire image
if(att->resInfo->imageInfo.extent.width != width || att->resInfo->imageInfo.extent.height != height)
{
return false;
}
// if view doesn't reference the entire image
if(att->viewRange.baseArrayLayer != 0 ||
att->viewRange.layerCount() != (uint32_t)att->resInfo->imageInfo.layerCount)
{
return false;
}
if(rpi->multiviewViewMaskTable)
{
// check and make sure all views are referenced by the renderpass
uint32_t renderpass_viewmask = rpi->multiviewViewMaskTable[attachmentIndex];
return (int)Bits::CountOnes(renderpass_viewmask) == att->resInfo->imageInfo.layerCount;
}
return imageAttachments[attachmentIndex].barrier.subresourceRange.layerCount == layers;
}
int ImgRefs::GetAspectCount() const
{
int aspectCount = 0;
@@ -3421,8 +3741,11 @@ VkResourceRecord::~VkResourceRecord()
if(resType == eResCommandBuffer)
SAFE_DELETE(cmdInfo);
if(resType == eResFramebuffer || resType == eResRenderPass)
SAFE_DELETE_ARRAY(imageAttachments);
if(resType == eResFramebuffer)
SAFE_DELETE(framebufferInfo);
if(resType == eResRenderPass)
SAFE_DELETE(renderPassInfo);
// only the descriptor set layout actually owns this pointer, descriptor sets
// have a pointer to it but don't own it
@@ -3442,8 +3765,8 @@ VkResourceRecord::~VkResourceRecord()
void VkResourceRecord::MarkImageFrameReferenced(VkResourceRecord *img, const ImageRange &range,
FrameRefType refType)
{
// mark backing memory as read
MarkResourceFrameReferenced(img->baseResource, eFrameRef_Read);
// mark backing memory
MarkResourceFrameReferenced(img->baseResource, refType);
ResourceId id = img->GetResourceID();
if(refType != eFrameRef_Read && refType != eFrameRef_None)
@@ -3470,8 +3793,8 @@ void VkResourceRecord::MarkImageViewFrameReferenced(VkResourceRecord *view, cons
// mark image view as read
MarkResourceFrameReferenced(view->GetResourceID(), eFrameRef_Read);
// mark memory backing image as read
MarkResourceFrameReferenced(mem, eFrameRef_Read);
// mark memory backing image
MarkResourceFrameReferenced(mem, refType);
if(refType != eFrameRef_Read && refType != eFrameRef_None)
cmdInfo->dirtied.insert(img);
+32 -1
View File
@@ -1108,6 +1108,36 @@ struct AttachmentInfo
VkImageMemoryBarrier barrier;
};
struct RenderPassInfo
{
RenderPassInfo(const VkRenderPassCreateInfo &ci);
RenderPassInfo(const VkRenderPassCreateInfo2 &ci);
~RenderPassInfo();
AttachmentInfo *imageAttachments;
// table of loadOps for each attachment
VkAttachmentLoadOp *loadOpTable;
// table of multiview viewMasks for each attachment
uint32_t *multiviewViewMaskTable;
};
struct FramebufferInfo
{
FramebufferInfo(const VkFramebufferCreateInfo &ci);
~FramebufferInfo();
bool AttachmentFullyReferenced(size_t attachmentIndex, const RenderPassInfo *rpi);
AttachmentInfo *imageAttachments;
uint32_t width;
uint32_t height;
uint32_t layers;
};
struct ImageRange
{
ImageRange() {}
@@ -2217,7 +2247,8 @@ public:
SwapchainInfo *swapInfo; // only for swapchains
MemMapState *memMapState; // only for device memory
CmdBufferRecordingInfo *cmdInfo; // only for command buffers
AttachmentInfo *imageAttachments; // only for framebuffers and render passes
FramebufferInfo *framebufferInfo; // only for framebuffers
RenderPassInfo *renderPassInfo; // only for render passes
PipelineLayoutData *pipeLayoutInfo; // only for pipeline layouts
DescriptorSetData *descInfo; // only for descriptor sets and descriptor set layouts
DescUpdateTemplate *descTemplateInfo; // only for descriptor update templates
@@ -1493,6 +1493,7 @@ void WrappedVulkan::vkCmdBeginRenderPass(VkCommandBuffer commandBuffer,
record->MarkResourceFrameReferenced(GetResID(pRenderPassBegin->renderPass), eFrameRef_Read);
VkResourceRecord *fb = GetRecord(pRenderPassBegin->framebuffer);
VkResourceRecord *rp = GetRecord(pRenderPassBegin->renderPass);
record->MarkResourceFrameReferenced(fb->GetResourceID(), eFrameRef_Read);
@@ -1500,21 +1501,42 @@ void WrappedVulkan::vkCmdBeginRenderPass(VkCommandBuffer commandBuffer,
barriers.clear();
if(fb->imageAttachments[0].barrier.sType && fb->imageAttachments[0].record)
FramebufferInfo *fbInfo = fb->framebufferInfo;
RenderPassInfo *rpInfo = rp->renderPassInfo;
if(fbInfo->imageAttachments[0].barrier.sType && fbInfo->imageAttachments[0].record)
{
for(size_t i = 0; fb->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;
for(size_t i = 0; fbInfo->imageAttachments[i].barrier.sType; i++)
{
VkResourceRecord *att = fb->imageAttachments[i].record;
VkResourceRecord *att = fbInfo->imageAttachments[i].record;
if(att == NULL)
break;
record->MarkImageViewFrameReferenced(att, ImageRange(), eFrameRef_ReadBeforeWrite);
bool framebuffer_reference_entire_attachment = fbInfo->AttachmentFullyReferenced(i, rpInfo);
if(fb->imageAttachments[i].barrier.oldLayout != fb->imageAttachments[i].barrier.newLayout)
barriers.push_back(fb->imageAttachments[i].barrier);
FrameRefType refType = eFrameRef_ReadBeforeWrite;
if(renderArea_covers_entire_framebuffer && framebuffer_reference_entire_attachment)
{
if(rpInfo->loadOpTable[i] != VK_ATTACHMENT_LOAD_OP_LOAD)
{
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(fb->imageAttachments[0].barrier.sType)
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
@@ -1528,9 +1550,10 @@ void WrappedVulkan::vkCmdBeginRenderPass(VkCommandBuffer commandBuffer,
VkResourceRecord *att = GetRecord(attachmentsInfo->pAttachments[i]);
record->MarkImageViewFrameReferenced(att, ImageRange(), eFrameRef_ReadBeforeWrite);
if(fb->imageAttachments[i].barrier.oldLayout != fb->imageAttachments[i].barrier.newLayout)
if(fbInfo->imageAttachments[i].barrier.oldLayout !=
fbInfo->imageAttachments[i].barrier.newLayout)
{
VkImageMemoryBarrier barrier = fb->imageAttachments[i].barrier;
VkImageMemoryBarrier barrier = fbInfo->imageAttachments[i].barrier;
barrier.image = GetResourceManager()->GetCurrentHandle<VkImage>(att->baseResource);
barrier.subresourceRange = att->viewRange;
@@ -1966,6 +1989,7 @@ void WrappedVulkan::vkCmdBeginRenderPass2(VkCommandBuffer commandBuffer,
record->MarkResourceFrameReferenced(GetResID(pRenderPassBegin->renderPass), eFrameRef_Read);
VkResourceRecord *fb = GetRecord(pRenderPassBegin->framebuffer);
VkResourceRecord *rp = GetRecord(pRenderPassBegin->renderPass);
record->MarkResourceFrameReferenced(fb->GetResourceID(), eFrameRef_Read);
@@ -1973,18 +1997,39 @@ void WrappedVulkan::vkCmdBeginRenderPass2(VkCommandBuffer commandBuffer,
barriers.clear();
if(fb->imageAttachments[0].barrier.sType && fb->imageAttachments[0].record)
FramebufferInfo *fbInfo = fb->framebufferInfo;
RenderPassInfo *rpInfo = rp->renderPassInfo;
if(fbInfo->imageAttachments[0].barrier.sType && fbInfo->imageAttachments[0].record)
{
for(size_t i = 0; fb->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;
for(size_t i = 0; fbInfo->imageAttachments[i].barrier.sType; i++)
{
VkResourceRecord *att = fb->imageAttachments[i].record;
VkResourceRecord *att = fbInfo->imageAttachments[i].record;
if(att == NULL)
break;
record->MarkImageViewFrameReferenced(att, ImageRange(), eFrameRef_ReadBeforeWrite);
bool framebuffer_reference_entire_attachment = fbInfo->AttachmentFullyReferenced(i, rpInfo);
if(fb->imageAttachments[i].barrier.oldLayout != fb->imageAttachments[i].barrier.newLayout)
barriers.push_back(fb->imageAttachments[i].barrier);
FrameRefType refType = eFrameRef_ReadBeforeWrite;
if(renderArea_covers_entire_framebuffer && framebuffer_reference_entire_attachment)
{
if(rpInfo->loadOpTable[i] != VK_ATTACHMENT_LOAD_OP_LOAD)
{
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
@@ -2001,9 +2046,10 @@ void WrappedVulkan::vkCmdBeginRenderPass2(VkCommandBuffer commandBuffer,
VkResourceRecord *att = GetRecord(attachmentsInfo->pAttachments[i]);
record->MarkImageViewFrameReferenced(att, ImageRange(), eFrameRef_ReadBeforeWrite);
if(fb->imageAttachments[i].barrier.oldLayout != fb->imageAttachments[i].barrier.newLayout)
if(fbInfo->imageAttachments[i].barrier.oldLayout !=
fbInfo->imageAttachments[i].barrier.newLayout)
{
VkImageMemoryBarrier barrier = fb->imageAttachments[i].barrier;
VkImageMemoryBarrier barrier = fbInfo->imageAttachments[i].barrier;
barrier.image = GetResourceManager()->GetCurrentHandle<VkImage>(att->baseResource);
barrier.subresourceRange = att->viewRange;
@@ -752,55 +752,52 @@ VkResult WrappedVulkan::vkCreateFramebuffer(VkDevice device,
VkResourceRecord *rpRecord = GetRecord(pCreateInfo->renderPass);
record->AddParent(rpRecord);
// *2 in case we need separate barriers for depth and stencil, +1 for the terminating null
// attachment info
uint32_t arrayCount = pCreateInfo->attachmentCount * 2 + 1;
record->imageAttachments = new AttachmentInfo[arrayCount];
RDCEraseMem(record->imageAttachments, sizeof(AttachmentInfo) * arrayCount);
RenderPassInfo *rpInfo = rpRecord->renderPassInfo;
FramebufferInfo *fbInfo = record->framebufferInfo = new FramebufferInfo(*pCreateInfo);
for(uint32_t i = 0, a = 0; i < pCreateInfo->attachmentCount; i++, a++)
{
record->imageAttachments[a].barrier = rpRecord->imageAttachments[a].barrier;
fbInfo->imageAttachments[a].barrier = rpInfo->imageAttachments[a].barrier;
if((pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) == 0)
{
VkResourceRecord *attRecord = GetRecord(pCreateInfo->pAttachments[i]);
record->AddParent(attRecord);
record->imageAttachments[a].record = attRecord;
record->imageAttachments[a].barrier.image =
fbInfo->imageAttachments[a].record = attRecord;
fbInfo->imageAttachments[a].barrier.image =
GetResourceManager()->GetCurrentHandle<VkImage>(attRecord->baseResource);
record->imageAttachments[a].barrier.subresourceRange = attRecord->viewRange;
fbInfo->imageAttachments[a].barrier.subresourceRange = attRecord->viewRange;
{
auto state = FindImageState(attRecord->GetResourceID());
if(state && state->GetImageInfo().extent.depth > 1)
{
record->imageAttachments[a].barrier.subresourceRange.baseArrayLayer = 0;
record->imageAttachments[a].barrier.subresourceRange.layerCount = 1;
fbInfo->imageAttachments[a].barrier.subresourceRange.baseArrayLayer = 0;
fbInfo->imageAttachments[a].barrier.subresourceRange.layerCount = 1;
}
}
// if the renderpass specifies an aspect mask that mean split depth/stencil handling, so
// respect the aspect mask and the next one will be stencil
if(rpRecord->imageAttachments[a].barrier.subresourceRange.aspectMask != 0)
if(rpInfo->imageAttachments[a].barrier.subresourceRange.aspectMask != 0)
{
record->imageAttachments[a].barrier.subresourceRange.aspectMask =
rpRecord->imageAttachments[a].barrier.subresourceRange.aspectMask;
// restore the aspectMask that might have been trampled by attRecord->viewRange
fbInfo->imageAttachments[a].barrier.subresourceRange.aspectMask =
rpInfo->imageAttachments[a].barrier.subresourceRange.aspectMask;
a++;
// copy most properties from previous barrier
record->imageAttachments[a].record = record->imageAttachments[a - 1].record;
record->imageAttachments[a].barrier = record->imageAttachments[a - 1].barrier;
fbInfo->imageAttachments[a].record = fbInfo->imageAttachments[a - 1].record;
fbInfo->imageAttachments[a].barrier = fbInfo->imageAttachments[a - 1].barrier;
// copy aspect mask and layouts from the next barrier in the RP
record->imageAttachments[a].barrier.subresourceRange.aspectMask =
rpRecord->imageAttachments[a].barrier.subresourceRange.aspectMask;
record->imageAttachments[a].barrier.oldLayout =
rpRecord->imageAttachments[a].barrier.oldLayout;
record->imageAttachments[a].barrier.newLayout =
rpRecord->imageAttachments[a].barrier.newLayout;
fbInfo->imageAttachments[a].barrier.subresourceRange.aspectMask =
rpInfo->imageAttachments[a].barrier.subresourceRange.aspectMask;
fbInfo->imageAttachments[a].barrier.oldLayout =
rpInfo->imageAttachments[a].barrier.oldLayout;
fbInfo->imageAttachments[a].barrier.newLayout =
rpInfo->imageAttachments[a].barrier.newLayout;
}
}
}
@@ -1007,22 +1004,7 @@ VkResult WrappedVulkan::vkCreateRenderPass(VkDevice device, const VkRenderPassCr
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pRenderPass);
record->AddChunk(chunk);
// *2 in case we need separate barriers for depth and stencil, +1 for the terminating null
// attachment info (though separate depth/stencil buffers aren't needed here, we keep the
// array size the same)
uint32_t arrayCount = pCreateInfo->attachmentCount * 2 + 1;
record->imageAttachments = new AttachmentInfo[arrayCount];
RDCEraseMem(record->imageAttachments, sizeof(AttachmentInfo) * arrayCount);
for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++)
{
record->imageAttachments[i].record = NULL;
record->imageAttachments[i].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
record->imageAttachments[i].barrier.oldLayout = pCreateInfo->pAttachments[i].initialLayout;
record->imageAttachments[i].barrier.newLayout = pCreateInfo->pAttachments[i].finalLayout;
}
record->renderPassInfo = new RenderPassInfo(*pCreateInfo);
}
else
{
@@ -1243,42 +1225,7 @@ VkResult WrappedVulkan::vkCreateRenderPass2(VkDevice device,
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pRenderPass);
record->AddChunk(chunk);
// *2 in case we need separate barriers for depth and stencil, +1 for the terminating null
// attachment info
uint32_t arrayCount = pCreateInfo->attachmentCount * 2 + 1;
record->imageAttachments = new AttachmentInfo[arrayCount];
RDCEraseMem(record->imageAttachments, sizeof(AttachmentInfo) * arrayCount);
for(uint32_t i = 0, a = 0; i < pCreateInfo->attachmentCount; i++, a++)
{
record->imageAttachments[a].record = NULL;
record->imageAttachments[a].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
record->imageAttachments[a].barrier.oldLayout = pCreateInfo->pAttachments[i].initialLayout;
record->imageAttachments[a].barrier.newLayout = pCreateInfo->pAttachments[i].finalLayout;
// VK_KHR_separate_depth_stencil_layouts
VkAttachmentDescriptionStencilLayoutKHR *separateStencil =
(VkAttachmentDescriptionStencilLayoutKHR *)FindNextStruct(
&pCreateInfo->pAttachments[i],
VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_STENCIL_LAYOUT_KHR);
if(separateStencil)
{
record->imageAttachments[a].barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
// add a separate barrier for stencil
a++;
record->imageAttachments[a].record = NULL;
record->imageAttachments[a].barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
record->imageAttachments[a].barrier.oldLayout = separateStencil->stencilInitialLayout;
record->imageAttachments[a].barrier.newLayout = separateStencil->stencilFinalLayout;
record->imageAttachments[a].barrier.subresourceRange.aspectMask =
VK_IMAGE_ASPECT_STENCIL_BIT;
}
}
record->renderPassInfo = new RenderPassInfo(*pCreateInfo);
}
else
{
@@ -1024,6 +1024,17 @@ VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount,
GetResourceManager()->MarkResourceFrameReferenced(record->GetResourceID(),
eFrameRef_ReadBeforeWrite);
}
// pull in frame refs while background capturing too
for(uint32_t s = 0; s < submitCount; s++)
{
for(uint32_t i = 0; i < pSubmits[s].commandBufferCount; i++)
{
VkResourceRecord *record = GetRecord(pSubmits[s].pCommandBuffers[i]);
record->bakedCommands->AddResourceReferences(GetResourceManager());
}
}
}
if(capframe)