mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-25 16:06:31 +00:00
Make sure going between WRITING_IDLE and WRITING_CAPFRAME is atomic
* This fixes a bug we had before, where we'd go into CAPFRAME then do a bunch of work before clearing the frame record. * The threading around this probably needs more careful thought - e.g. the snapshotting and chunk insertion on end capture.
This commit is contained in:
@@ -535,8 +535,6 @@ void WrappedVulkan::EndCaptureFrame(VkImage presentImage)
|
||||
|
||||
void WrappedVulkan::AttemptCapture()
|
||||
{
|
||||
m_State = WRITING_CAPFRAME;
|
||||
|
||||
{
|
||||
RDCDEBUG("Attempting capture");
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@ private:
|
||||
vector<VkResourceRecord *> m_CmdBufferRecords;
|
||||
|
||||
VulkanResourceManager *m_ResourceManager;
|
||||
|
||||
Threading::CriticalSection m_CapTransitionLock;
|
||||
|
||||
uint32_t m_FrameCounter;
|
||||
|
||||
|
||||
@@ -273,10 +273,13 @@ VkResult WrappedVulkan::vkAllocDescriptorSets(
|
||||
record->AddParent(GetResourceManager()->GetResourceRecord(layoutID));
|
||||
|
||||
// just always treat descriptor sets as dirty
|
||||
if(m_State != WRITING_CAPFRAME)
|
||||
GetResourceManager()->MarkDirtyResource(id);
|
||||
else
|
||||
GetResourceManager()->MarkPendingDirty(id);
|
||||
{
|
||||
SCOPED_LOCK(m_CapTransitionLock);
|
||||
if(m_State != WRITING_CAPFRAME)
|
||||
GetResourceManager()->MarkDirtyResource(id);
|
||||
else
|
||||
GetResourceManager()->MarkPendingDirty(id);
|
||||
}
|
||||
|
||||
record->layout = layoutID;
|
||||
m_CreationInfo.m_DescSetLayout[layoutID].CreateBindingsArray(record->descBindings);
|
||||
@@ -473,8 +476,14 @@ void WrappedVulkan::vkUpdateDescriptorSets(
|
||||
|
||||
ObjDisp(device)->UpdateDescriptorSets(Unwrap(device), writeCount, unwrappedWrites, copyCount, unwrappedCopies);
|
||||
}
|
||||
|
||||
bool capframe = false;
|
||||
{
|
||||
SCOPED_LOCK(m_CapTransitionLock);
|
||||
capframe = (m_State == WRITING_CAPFRAME);
|
||||
}
|
||||
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
if(capframe)
|
||||
{
|
||||
// don't have to mark referenced any of the resources pointed to by the descriptor set - that's handled
|
||||
// on queue submission by marking ref'd all the current bindings of the sets referenced by the cmd buffer
|
||||
|
||||
@@ -330,10 +330,72 @@ VkResult WrappedVulkan::vkQueueSubmit(
|
||||
|
||||
VkResult ret = ObjDisp(queue)->QueueSubmit(Unwrap(queue), cmdBufferCount, unwrapped, Unwrap(fence));
|
||||
|
||||
// VKTODOHIGH when maps are intercepted with local buffers, this will have to be
|
||||
// done when not in capframe :(.
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
bool capframe = false;
|
||||
|
||||
for(uint32_t i=0; i < cmdBufferCount; i++)
|
||||
{
|
||||
ResourceId cmd = GetResID(pCmdBuffers[i]);
|
||||
GetResourceManager()->ApplyTransitions(m_CmdBufferInfo[cmd].imgtransitions, m_ImageInfo);
|
||||
|
||||
VkResourceRecord *record = GetRecord(pCmdBuffers[i]);
|
||||
|
||||
// need to lock the whole section of code, not just the check on
|
||||
// m_State, as we also need to make sure we don't check the state,
|
||||
// start marking dirty resources then while we're doing so the
|
||||
// state becomes capframe.
|
||||
// the next sections where we mark resources referenced and add
|
||||
// the submit chunk to the frame record don't have to be protected.
|
||||
// Only the decision of whether we're inframe or not, and marking
|
||||
// dirty.
|
||||
{
|
||||
SCOPED_LOCK(m_CapTransitionLock);
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
{
|
||||
for(auto it = record->bakedCommands->dirtied.begin(); it != record->bakedCommands->dirtied.end(); ++it)
|
||||
GetResourceManager()->MarkPendingDirty(*it);
|
||||
|
||||
capframe = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto it = record->bakedCommands->dirtied.begin(); it != record->bakedCommands->dirtied.end(); ++it)
|
||||
GetResourceManager()->MarkDirtyResource(*it);
|
||||
}
|
||||
}
|
||||
|
||||
if(capframe)
|
||||
{
|
||||
// for each bound descriptor set, mark it referenced as well as all resources currently bound to it
|
||||
for(auto it = record->bakedCommands->boundDescSets.begin(); it != record->bakedCommands->boundDescSets.end(); ++it)
|
||||
{
|
||||
GetResourceManager()->MarkResourceFrameReferenced(GetResID(*it), eFrameRef_Read);
|
||||
|
||||
VkResourceRecord *setrecord = GetRecord(*it);
|
||||
|
||||
for(auto refit = setrecord->bindFrameRefs.begin(); refit != setrecord->bindFrameRefs.end(); ++refit)
|
||||
GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second);
|
||||
}
|
||||
|
||||
// pull in frame refs from this baked command buffer
|
||||
record->bakedCommands->AddResourceReferences(GetResourceManager());
|
||||
|
||||
// ref the parent command buffer by itself, this will pull in the cmd buffer pool
|
||||
GetResourceManager()->MarkResourceFrameReferenced(record->GetResourceID(), eFrameRef_Read);
|
||||
|
||||
if(fence != VK_NULL_HANDLE)
|
||||
GetResourceManager()->MarkResourceFrameReferenced(GetResID(fence), eFrameRef_Read);
|
||||
|
||||
m_CmdBufferRecords.push_back(record->bakedCommands);
|
||||
record->bakedCommands->AddRef();
|
||||
}
|
||||
|
||||
record->dirtied.clear();
|
||||
}
|
||||
|
||||
if(capframe)
|
||||
{
|
||||
// VKTODOHIGH when maps are intercepted with local buffers, this will have to be
|
||||
// done when not in capframe :(.
|
||||
for(auto it = m_MemoryInfo.begin(); it != m_MemoryInfo.end(); ++it)
|
||||
{
|
||||
// potential persistent map, force a full flush
|
||||
@@ -364,65 +426,17 @@ VkResult WrappedVulkan::vkQueueSubmit(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
{
|
||||
CACHE_THREAD_SERIALISER();
|
||||
|
||||
SCOPED_SERIALISE_CONTEXT(QUEUE_SUBMIT);
|
||||
Serialise_vkQueueSubmit(localSerialiser, queue, cmdBufferCount, pCmdBuffers, fence);
|
||||
|
||||
m_FrameCaptureRecord->AddChunk(scope.Get());
|
||||
}
|
||||
|
||||
for(uint32_t i=0; i < cmdBufferCount; i++)
|
||||
{
|
||||
ResourceId cmd = GetResID(pCmdBuffers[i]);
|
||||
GetResourceManager()->ApplyTransitions(m_CmdBufferInfo[cmd].imgtransitions, m_ImageInfo);
|
||||
|
||||
VkResourceRecord *record = GetRecord(pCmdBuffers[i]);
|
||||
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
{
|
||||
for(auto it = record->bakedCommands->dirtied.begin(); it != record->bakedCommands->dirtied.end(); ++it)
|
||||
GetResourceManager()->MarkPendingDirty(*it);
|
||||
CACHE_THREAD_SERIALISER();
|
||||
|
||||
SCOPED_SERIALISE_CONTEXT(QUEUE_SUBMIT);
|
||||
Serialise_vkQueueSubmit(localSerialiser, queue, cmdBufferCount, pCmdBuffers, fence);
|
||||
|
||||
m_FrameCaptureRecord->AddChunk(scope.Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto it = record->bakedCommands->dirtied.begin(); it != record->bakedCommands->dirtied.end(); ++it)
|
||||
GetResourceManager()->MarkDirtyResource(*it);
|
||||
}
|
||||
|
||||
if(m_State == WRITING_CAPFRAME)
|
||||
{
|
||||
// for each bound descriptor set, mark it referenced as well as all resources currently bound to it
|
||||
for(auto it = record->bakedCommands->boundDescSets.begin(); it != record->bakedCommands->boundDescSets.end(); ++it)
|
||||
{
|
||||
GetResourceManager()->MarkResourceFrameReferenced(GetResID(*it), eFrameRef_Read);
|
||||
|
||||
VkResourceRecord *setrecord = GetRecord(*it);
|
||||
|
||||
for(auto refit = setrecord->bindFrameRefs.begin(); refit != setrecord->bindFrameRefs.end(); ++refit)
|
||||
GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second);
|
||||
}
|
||||
|
||||
// pull in frame refs from this baked command buffer
|
||||
record->bakedCommands->AddResourceReferences(GetResourceManager());
|
||||
|
||||
// ref the parent command buffer by itself, this will pull in the cmd buffer pool
|
||||
GetResourceManager()->MarkResourceFrameReferenced(record->GetResourceID(), eFrameRef_Read);
|
||||
|
||||
if(fence != VK_NULL_HANDLE)
|
||||
GetResourceManager()->MarkResourceFrameReferenced(GetResID(fence), eFrameRef_Read);
|
||||
|
||||
m_CmdBufferRecords.push_back(record->bakedCommands);
|
||||
record->bakedCommands->AddRef();
|
||||
}
|
||||
|
||||
record->dirtied.clear();
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,10 +94,13 @@ VkResult WrappedVulkan::vkAllocMemory(
|
||||
|
||||
// VKTODOMED always treat memory as dirty for now, so its initial state
|
||||
// is guaranteed to be prepared
|
||||
if(m_State != WRITING_CAPFRAME)
|
||||
GetResourceManager()->MarkDirtyResource(id);
|
||||
else
|
||||
GetResourceManager()->MarkPendingDirty(id);
|
||||
{
|
||||
SCOPED_LOCK(m_CapTransitionLock);
|
||||
if(m_State != WRITING_CAPFRAME)
|
||||
GetResourceManager()->MarkDirtyResource(id);
|
||||
else
|
||||
GetResourceManager()->MarkPendingDirty(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -235,7 +238,20 @@ void WrappedVulkan::vkUnmapMemory(
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_State >= WRITING_CAPFRAME)
|
||||
// decide atomically if this chunk should be in-frame or not
|
||||
// so that we're not in the else branch but haven't marked
|
||||
// dirty when capframe starts, then we mark dirty while in-frame
|
||||
|
||||
bool capframe = false;
|
||||
{
|
||||
SCOPED_LOCK(m_CapTransitionLock);
|
||||
capframe = (m_State == WRITING_CAPFRAME);
|
||||
|
||||
if(!capframe)
|
||||
GetResourceManager()->MarkDirtyResource(GetResID(mem));
|
||||
}
|
||||
|
||||
if(capframe)
|
||||
{
|
||||
if(!it->second.mapFlushed)
|
||||
{
|
||||
@@ -262,10 +278,6 @@ void WrappedVulkan::vkUnmapMemory(
|
||||
// this is true for all non-coherent memory types.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GetResourceManager()->MarkDirtyResource(GetResID(mem));
|
||||
}
|
||||
|
||||
it->second.mappedPtr = NULL;
|
||||
SAFE_DELETE_ARRAY(it->second.refData);
|
||||
|
||||
@@ -619,8 +619,12 @@ VkResult WrappedVulkan::vkQueuePresentKHR(
|
||||
|
||||
GetResourceManager()->MarkResourceFrameReferenced(swapid, eFrameRef_Read);
|
||||
|
||||
EndCaptureFrame(backbuffer);
|
||||
FinishCapture();
|
||||
// transition back to IDLE atomically
|
||||
{
|
||||
SCOPED_LOCK(m_CapTransitionLock);
|
||||
EndCaptureFrame(backbuffer);
|
||||
FinishCapture();
|
||||
}
|
||||
|
||||
byte *thpixels = NULL;
|
||||
uint32_t thwidth = 0;
|
||||
@@ -943,8 +947,6 @@ VkResult WrappedVulkan::vkQueuePresentKHR(
|
||||
|
||||
if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE && m_FrameRecord.empty())
|
||||
{
|
||||
m_State = WRITING_CAPFRAME;
|
||||
|
||||
FetchFrameRecord record;
|
||||
record.frameInfo.frameNumber = m_FrameCounter+1;
|
||||
record.frameInfo.captureTime = Timing::GetUnixTimestamp();
|
||||
@@ -953,10 +955,19 @@ VkResult WrappedVulkan::vkQueuePresentKHR(
|
||||
GetResourceManager()->ClearReferencedResources();
|
||||
|
||||
GetResourceManager()->MarkResourceFrameReferenced(m_InstanceRecord->GetResourceID(), eFrameRef_Read);
|
||||
GetResourceManager()->PrepareInitialContents();
|
||||
|
||||
AttemptCapture();
|
||||
BeginCaptureFrame();
|
||||
|
||||
// need to do all this atomically so that no other commands
|
||||
// will check to see if they need to markdirty or markpendingdirty
|
||||
// and go into the frame record.
|
||||
{
|
||||
SCOPED_LOCK(m_CapTransitionLock);
|
||||
GetResourceManager()->PrepareInitialContents();
|
||||
|
||||
AttemptCapture();
|
||||
BeginCaptureFrame();
|
||||
|
||||
m_State = WRITING_CAPFRAME;
|
||||
}
|
||||
|
||||
RDCLOG("Starting capture, frame %u", m_FrameCounter);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user