Files
renderdoc/renderdoc/driver/vulkan/wrappers/vk_cmd_funcs.cpp
T
baldurk f442543ab5 Make sure not to offset secondary cmd buf submissions multiple times
* When a primary command buffer is submitted that contained some
  secondary executes, we offset their executes to be 'absolute'
  root-relative events so they know when they're being partially
  replayed.
* If the same secondary is executed in multiple primaries that are
  submitted separately, we don't want to apply the same offset over and
  over, instead we mark an execute as updated and only offset 'new'
  executions.
2017-08-17 18:09:04 +01:00

2873 lines
100 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2015-2017 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include "../vk_core.h"
std::vector<VkImageMemoryBarrier> WrappedVulkan::GetImplicitRenderPassBarriers(uint32_t subpass)
{
ResourceId rp, fb;
if(m_State == EXECUTING)
{
rp = m_RenderState.renderPass;
fb = m_RenderState.framebuffer;
}
else
{
rp = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.renderPass;
fb = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer;
}
std::vector<VkImageMemoryBarrier> ret;
VulkanCreationInfo::Framebuffer fbinfo = m_CreationInfo.m_Framebuffer[fb];
VulkanCreationInfo::RenderPass rpinfo = m_CreationInfo.m_RenderPass[rp];
std::vector<VkAttachmentReference> atts;
// a bit of dancing to get a subpass index. Because we don't increment
// the subpass counter on EndRenderPass the value is the same for the last
// NextSubpass. Instead we pass in the subpass index of ~0U for End
if(subpass == ~0U)
{
// we transition all attachments to finalLayout from whichever they
// were in previously
atts.resize(rpinfo.attachments.size());
for(size_t i = 0; i < rpinfo.attachments.size(); i++)
{
atts[i].attachment = (uint32_t)i;
atts[i].layout = rpinfo.attachments[i].finalLayout;
}
}
else
{
if(m_State == EXECUTING)
subpass = m_RenderState.subpass;
else
subpass = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass;
// transition the attachments in this subpass
for(size_t i = 0; i < rpinfo.subpasses[subpass].colorAttachments.size(); i++)
{
uint32_t attIdx = rpinfo.subpasses[subpass].colorAttachments[i];
if(attIdx == VK_ATTACHMENT_UNUSED)
continue;
atts.push_back(VkAttachmentReference());
atts.back().attachment = attIdx;
atts.back().layout = rpinfo.subpasses[subpass].colorLayouts[i];
}
for(size_t i = 0; i < rpinfo.subpasses[subpass].inputAttachments.size(); i++)
{
uint32_t attIdx = rpinfo.subpasses[subpass].inputAttachments[i];
if(attIdx == VK_ATTACHMENT_UNUSED)
continue;
atts.push_back(VkAttachmentReference());
atts.back().attachment = attIdx;
atts.back().layout = rpinfo.subpasses[subpass].inputLayouts[i];
}
int32_t ds = rpinfo.subpasses[subpass].depthstencilAttachment;
if(ds != -1)
{
atts.push_back(VkAttachmentReference());
atts.back().attachment = (uint32_t)rpinfo.subpasses[subpass].depthstencilAttachment;
atts.back().layout = rpinfo.subpasses[subpass].depthstencilLayout;
}
}
for(size_t i = 0; i < atts.size(); i++)
{
uint32_t idx = atts[i].attachment;
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
ResourceId view = fbinfo.attachments[idx].view;
barrier.subresourceRange = m_CreationInfo.m_ImageView[view].range;
barrier.image = Unwrap(
GetResourceManager()->GetCurrentHandle<VkImage>(m_CreationInfo.m_ImageView[view].image));
barrier.newLayout = atts[i].layout;
// search back from this subpass to see which layout it was in before. If it's
// not been used in a previous subpass, then default to initialLayout
barrier.oldLayout = rpinfo.attachments[idx].initialLayout;
if(subpass == ~0U)
subpass = (uint32_t)rpinfo.subpasses.size();
// subpass is at this point a 1-indexed value essentially, as it's the index
// of the subpass we just finished (or 0 if we're in BeginRenderPass in which
// case the loop just skips completely and we use initialLayout, which is
// correct).
for(uint32_t s = subpass; s > 0; s--)
{
bool found = false;
for(size_t a = 0; !found && a < rpinfo.subpasses[s - 1].colorAttachments.size(); a++)
{
if(rpinfo.subpasses[s - 1].colorAttachments[a] == idx)
{
barrier.oldLayout = rpinfo.subpasses[s - 1].colorLayouts[a];
found = true;
break;
}
}
if(found)
break;
for(size_t a = 0; !found && a < rpinfo.subpasses[s - 1].inputAttachments.size(); a++)
{
if(rpinfo.subpasses[s - 1].inputAttachments[a] == idx)
{
barrier.oldLayout = rpinfo.subpasses[s - 1].inputLayouts[a];
found = true;
break;
}
}
if(found)
break;
if((uint32_t)rpinfo.subpasses[s - 1].depthstencilAttachment == idx)
{
barrier.oldLayout = rpinfo.subpasses[s - 1].depthstencilLayout;
break;
}
}
ReplacePresentableImageLayout(barrier.oldLayout);
ReplacePresentableImageLayout(barrier.newLayout);
ret.push_back(barrier);
}
// erase any do-nothing barriers
for(auto it = ret.begin(); it != ret.end();)
{
if(it->oldLayout == it->newLayout)
it = ret.erase(it);
else
++it;
}
return ret;
}
string WrappedVulkan::MakeRenderPassOpString(bool store)
{
string opDesc = "";
const VulkanCreationInfo::RenderPass &info =
m_CreationInfo.m_RenderPass[m_BakedCmdBufferInfo[m_LastCmdBufferID].state.renderPass];
const VulkanCreationInfo::Framebuffer &fbinfo =
m_CreationInfo.m_Framebuffer[m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer];
const vector<VkAttachmentDescription> &atts = info.attachments;
if(atts.empty())
{
opDesc = "-";
}
else
{
bool colsame = true;
uint32_t subpass = m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass;
// find which attachment is the depth-stencil one
int32_t dsAttach = info.subpasses[subpass].depthstencilAttachment;
bool hasStencil = false;
bool depthonly = false;
// if there is a depth-stencil attachment, see if it has a stencil
// component and if the subpass is depth only (no other attachments)
if(dsAttach >= 0)
{
hasStencil = !IsDepthOnlyFormat(fbinfo.attachments[dsAttach].format);
depthonly = info.subpasses[subpass].colorAttachments.size() == 0;
}
const std::vector<uint32_t> &cols = info.subpasses[subpass].colorAttachments;
// we check all non-UNUSED attachments to see if they're all the same.
// To begin with we point to an invalid attachment index
uint32_t col0 = VK_ATTACHMENT_UNUSED;
// look through all other color attachments to see if they're identical
for(size_t i = 0; i < cols.size(); i++)
{
const uint32_t col = cols[i];
// skip unused attachments
if(col == VK_ATTACHMENT_UNUSED)
continue;
// the first valid attachment we find, use that as our reference point
if(col0 == VK_ATTACHMENT_UNUSED)
{
col0 = col;
continue;
}
// for any other attachments, compare them to the reference
if(store)
{
if(atts[col].storeOp != atts[col0].storeOp)
colsame = false;
}
else
{
if(atts[col].loadOp != atts[col0].loadOp)
colsame = false;
}
}
// handle depth only passes
if(depthonly)
{
opDesc = "";
}
else if(!colsame)
{
// if we have different storage for the colour, don't display
// the full details
opDesc = store ? "Different store ops" : "Different load ops";
}
else if(col0 == VK_ATTACHMENT_UNUSED)
{
// we're here if we didn't find any non-UNUSED color attachments at all
opDesc = "Unused";
}
else
{
// all colour ops are the same, print it
opDesc = store ? ToStr::Get(atts[col0].storeOp) : ToStr::Get(atts[col0].loadOp);
}
// do we have depth?
if(dsAttach != -1)
{
// could be empty if this is a depth-only pass
if(!opDesc.empty())
opDesc = "C=" + opDesc + ", ";
// if there's no stencil, just print depth op
if(!hasStencil)
{
opDesc +=
"D=" + (store ? ToStr::Get(atts[dsAttach].storeOp) : ToStr::Get(atts[dsAttach].loadOp));
}
else
{
if(store)
{
// if depth and stencil have same op, print together, otherwise separately
if(atts[dsAttach].storeOp == atts[dsAttach].stencilStoreOp)
opDesc += "DS=" + ToStr::Get(atts[dsAttach].storeOp);
else
opDesc += "D=" + ToStr::Get(atts[dsAttach].storeOp) + ", S=" +
ToStr::Get(atts[dsAttach].stencilStoreOp);
}
else
{
// if depth and stencil have same op, print together, otherwise separately
if(atts[dsAttach].loadOp == atts[dsAttach].stencilLoadOp)
opDesc += "DS=" + ToStr::Get(atts[dsAttach].loadOp);
else
opDesc += "D=" + ToStr::Get(atts[dsAttach].loadOp) + ", S=" +
ToStr::Get(atts[dsAttach].stencilLoadOp);
}
}
}
}
return opDesc;
}
// Command pool functions
bool WrappedVulkan::Serialise_vkCreateCommandPool(Serialiser *localSerialiser, VkDevice device,
const VkCommandPoolCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkCommandPool *pCmdPool)
{
SERIALISE_ELEMENT(ResourceId, devId, GetResID(device));
SERIALISE_ELEMENT(VkCommandPoolCreateInfo, info, *pCreateInfo);
SERIALISE_ELEMENT(ResourceId, id, GetResID(*pCmdPool));
if(m_State == READING)
{
device = GetResourceManager()->GetLiveHandle<VkDevice>(devId);
VkCommandPool pool = VK_NULL_HANDLE;
VkResult ret = ObjDisp(device)->CreateCommandPool(Unwrap(device), &info, NULL, &pool);
if(ret != VK_SUCCESS)
{
RDCERR("Failed on resource serialise-creation, VkResult: 0x%08x", ret);
}
else
{
ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), pool);
GetResourceManager()->AddLiveResource(id, pool);
}
}
return true;
}
VkResult WrappedVulkan::vkCreateCommandPool(VkDevice device,
const VkCommandPoolCreateInfo *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkCommandPool *pCmdPool)
{
VkResult ret =
ObjDisp(device)->CreateCommandPool(Unwrap(device), pCreateInfo, pAllocator, pCmdPool);
if(ret == VK_SUCCESS)
{
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pCmdPool);
if(m_State >= WRITING)
{
Chunk *chunk = NULL;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(CREATE_CMD_POOL);
Serialise_vkCreateCommandPool(localSerialiser, device, pCreateInfo, NULL, pCmdPool);
chunk = scope.Get();
}
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pCmdPool);
record->AddChunk(chunk);
}
else
{
GetResourceManager()->AddLiveResource(id, *pCmdPool);
}
}
return ret;
}
VkResult WrappedVulkan::vkResetCommandPool(VkDevice device, VkCommandPool cmdPool,
VkCommandPoolResetFlags flags)
{
return ObjDisp(device)->ResetCommandPool(Unwrap(device), Unwrap(cmdPool), flags);
}
void WrappedVulkan::vkTrimCommandPoolKHR(VkDevice device, VkCommandPool commandPool,
VkCommandPoolTrimFlagsKHR flags)
{
return ObjDisp(device)->TrimCommandPoolKHR(Unwrap(device), Unwrap(commandPool), flags);
}
// Command buffer functions
VkResult WrappedVulkan::vkAllocateCommandBuffers(VkDevice device,
const VkCommandBufferAllocateInfo *pAllocateInfo,
VkCommandBuffer *pCommandBuffers)
{
VkCommandBufferAllocateInfo unwrappedInfo = *pAllocateInfo;
unwrappedInfo.commandPool = Unwrap(unwrappedInfo.commandPool);
VkResult ret =
ObjDisp(device)->AllocateCommandBuffers(Unwrap(device), &unwrappedInfo, pCommandBuffers);
if(ret == VK_SUCCESS)
{
for(uint32_t i = 0; i < unwrappedInfo.commandBufferCount; i++)
{
VkCommandBuffer unwrappedReal = pCommandBuffers[i];
ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), pCommandBuffers[i]);
// we set this *after* wrapping, so that the wrapped resource copies the 'uninitialised'
// loader table, since the loader expects to set the dispatch table onto an existing magic
// number in the trampoline function at the start of the chain.
if(m_SetDeviceLoaderData)
m_SetDeviceLoaderData(device, unwrappedReal);
else
SetDispatchTableOverMagicNumber(device, unwrappedReal);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetResourceManager()->AddResourceRecord(pCommandBuffers[i]);
record->bakedCommands = NULL;
record->pool = GetRecord(pAllocateInfo->commandPool);
record->AddParent(record->pool);
{
record->pool->LockChunks();
record->pool->pooledChildren.push_back(record);
record->pool->UnlockChunks();
}
// we don't serialise this as we never create this command buffer directly.
// Instead we create a command buffer for each baked list that we find.
// if pNext is non-NULL, need to do a deep copy
// we don't support any extensions on VkCommandBufferCreateInfo anyway
RDCASSERT(pAllocateInfo->pNext == NULL);
record->cmdInfo = new CmdBufferRecordingInfo();
record->cmdInfo->device = device;
record->cmdInfo->allocInfo = *pAllocateInfo;
record->cmdInfo->allocInfo.commandBufferCount = 1;
}
else
{
GetResourceManager()->AddLiveResource(id, pCommandBuffers[i]);
}
}
}
return ret;
}
bool WrappedVulkan::Serialise_vkBeginCommandBuffer(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
const VkCommandBufferBeginInfo *pBeginInfo)
{
SERIALISE_ELEMENT(ResourceId, cmdId, GetResID(commandBuffer));
ResourceId bakedCmdId;
VkCommandBufferAllocateInfo allocInfo;
VkDevice device = VK_NULL_HANDLE;
if(m_State >= WRITING)
{
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(cmdId);
RDCASSERT(record->bakedCommands);
if(record->bakedCommands)
bakedCmdId = record->bakedCommands->GetResourceID();
RDCASSERT(record->cmdInfo);
device = record->cmdInfo->device;
allocInfo = record->cmdInfo->allocInfo;
}
SERIALISE_ELEMENT(VkCommandBufferBeginInfo, info, *pBeginInfo);
SERIALISE_ELEMENT(ResourceId, bakeId, bakedCmdId);
if(m_State < WRITING)
{
m_LastCmdBufferID = cmdId;
}
SERIALISE_ELEMENT(ResourceId, devId, GetResID(device));
localSerialiser->Serialise("allocInfo", allocInfo);
if(m_State < WRITING)
{
device = GetResourceManager()->GetLiveHandle<VkDevice>(devId);
m_BakedCmdBufferInfo[cmdId].level = m_BakedCmdBufferInfo[bakeId].level = allocInfo.level;
m_BakedCmdBufferInfo[cmdId].beginFlags = m_BakedCmdBufferInfo[bakeId].beginFlags = info.flags;
m_BakedCmdBufferInfo[cmdId].markerCount = 0;
}
if(m_State == EXECUTING)
{
const uint32_t length = m_BakedCmdBufferInfo[bakeId].eventCount;
bool partial = false;
int partialType = ePartialNum;
// check for partial execution of this command buffer
for(int p = 0; p < ePartialNum; p++)
{
const vector<Submission> &submissions = m_Partial[p].cmdBufferSubmits[bakeId];
for(auto it = submissions.begin(); it != submissions.end(); ++it)
{
if(it->baseEvent <= m_LastEventID && m_LastEventID < (it->baseEvent + length))
{
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("vkBegin - partial detected %u < %u < %u, %llu -> %llu", it->baseEvent,
m_LastEventID, it->baseEvent + length, cmdId, bakeId);
#endif
m_Partial[p].partialParent = cmdId;
m_Partial[p].baseEvent = it->baseEvent;
m_Partial[p].renderPassActive = false;
m_Partial[p].partialDevice = device;
m_Partial[p].resultPartialCmdPool =
(VkCommandPool)(uint64_t)GetResourceManager()->GetNonDispWrapper(allocInfo.commandPool);
partial = true;
partialType = p;
}
}
}
if(partial || (m_DrawcallCallback && m_DrawcallCallback->RecordAllCmds()))
{
// pull all re-recorded commands from our own device and command pool for easier cleanup
if(!partial)
{
device = GetDev();
allocInfo.commandPool = Unwrap(m_InternalCmds.cmdpool);
}
VkCommandBuffer cmd = VK_NULL_HANDLE;
VkResult ret = ObjDisp(device)->AllocateCommandBuffers(Unwrap(device), &allocInfo, &cmd);
if(ret != VK_SUCCESS)
{
RDCERR("Failed on resource serialise-creation, VkResult: 0x%08x", ret);
}
else
{
GetResourceManager()->WrapResource(Unwrap(device), cmd);
}
if(partial)
{
m_Partial[partialType].resultPartialCmdBuffer = cmd;
}
else
{
// we store under both baked and non baked ID.
// The baked ID is the 'real' entry, the non baked is simply so it
// can be found in the subsequent serialised commands that ref the
// non-baked ID. The baked ID is referenced by the submit itself.
//
// In vkEndCommandBuffer we erase the non-baked reference, and since
// we know you can only be recording a command buffer once at a time
// (even if it's baked to several command buffers in the frame)
// there's no issue with clashes here.
m_RerecordCmds[bakeId] = cmd;
m_RerecordCmds[cmdId] = cmd;
}
// add one-time submit flag as this partial cmd buffer will only be submitted once
info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
ObjDisp(cmd)->BeginCommandBuffer(Unwrap(cmd), &info);
}
m_BakedCmdBufferInfo[cmdId].curEventID = 0;
}
else if(m_State == READING)
{
// remove one-time submit flag as we will want to submit many
info.flags &= ~VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VkCommandBuffer cmd = VK_NULL_HANDLE;
if(!GetResourceManager()->HasLiveResource(bakeId))
{
VkResult ret = ObjDisp(device)->AllocateCommandBuffers(Unwrap(device), &allocInfo, &cmd);
if(ret != VK_SUCCESS)
{
RDCERR("Failed on resource serialise-creation, VkResult: 0x%08x", ret);
}
else
{
ResourceId live = GetResourceManager()->WrapResource(Unwrap(device), cmd);
GetResourceManager()->AddLiveResource(bakeId, cmd);
}
// whenever a vkCmd command-building chunk asks for the command buffer, it
// will get our baked version.
GetResourceManager()->ReplaceResource(cmdId, bakeId);
}
else
{
cmd = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(bakeId);
}
// propagate any name there might be
if(m_CreationInfo.m_Names.find(cmdId) != m_CreationInfo.m_Names.end())
m_CreationInfo.m_Names[GetResourceManager()->GetLiveID(bakeId)] = m_CreationInfo.m_Names[cmdId];
{
VulkanDrawcallTreeNode *draw = new VulkanDrawcallTreeNode;
m_BakedCmdBufferInfo[cmdId].draw = draw;
// On queue submit we increment all child events/drawcalls by
// m_RootEventID and insert them into the tree.
m_BakedCmdBufferInfo[cmdId].curEventID = 0;
m_BakedCmdBufferInfo[cmdId].eventCount = 0;
m_BakedCmdBufferInfo[cmdId].drawCount = 0;
m_BakedCmdBufferInfo[cmdId].drawStack.push_back(draw);
}
ObjDisp(device)->BeginCommandBuffer(Unwrap(cmd), &info);
}
return true;
}
VkResult WrappedVulkan::vkBeginCommandBuffer(VkCommandBuffer commandBuffer,
const VkCommandBufferBeginInfo *pBeginInfo)
{
VkResourceRecord *record = GetRecord(commandBuffer);
RDCASSERT(record);
if(record)
{
// If a command bfufer was already recorded (ie we have some baked commands),
// then begin is spec'd to implicitly reset. That means we need to tidy up
// any existing baked commands before creating a new set.
if(record->bakedCommands)
record->bakedCommands->Delete(GetResourceManager());
record->bakedCommands = GetResourceManager()->AddResourceRecord(ResourceIDGen::GetNewUniqueID());
record->bakedCommands->SpecialResource = true;
record->bakedCommands->Resource = (WrappedVkRes *)commandBuffer;
record->bakedCommands->cmdInfo = new CmdBufferRecordingInfo();
record->bakedCommands->cmdInfo->device = record->cmdInfo->device;
record->bakedCommands->cmdInfo->allocInfo = record->cmdInfo->allocInfo;
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BEGIN_CMD_BUFFER);
Serialise_vkBeginCommandBuffer(localSerialiser, commandBuffer, pBeginInfo);
record->AddChunk(scope.Get());
}
if(pBeginInfo->pInheritanceInfo)
{
record->MarkResourceFrameReferenced(GetResID(pBeginInfo->pInheritanceInfo->renderPass),
eFrameRef_Read);
record->MarkResourceFrameReferenced(GetResID(pBeginInfo->pInheritanceInfo->framebuffer),
eFrameRef_Read);
}
}
VkCommandBufferInheritanceInfo unwrappedInfo;
if(pBeginInfo->pInheritanceInfo)
{
unwrappedInfo = *pBeginInfo->pInheritanceInfo;
unwrappedInfo.framebuffer = Unwrap(unwrappedInfo.framebuffer);
unwrappedInfo.renderPass = Unwrap(unwrappedInfo.renderPass);
VkCommandBufferBeginInfo beginInfo = *pBeginInfo;
beginInfo.pInheritanceInfo = &unwrappedInfo;
return ObjDisp(commandBuffer)->BeginCommandBuffer(Unwrap(commandBuffer), &beginInfo);
}
return ObjDisp(commandBuffer)->BeginCommandBuffer(Unwrap(commandBuffer), pBeginInfo);
}
bool WrappedVulkan::Serialise_vkEndCommandBuffer(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
ResourceId bakedCmdId;
if(m_State >= WRITING)
{
VkResourceRecord *record = GetResourceManager()->GetResourceRecord(cmdid);
RDCASSERT(record->bakedCommands);
if(record->bakedCommands)
bakedCmdId = record->bakedCommands->GetResourceID();
}
SERIALISE_ELEMENT(ResourceId, bakeId, bakedCmdId);
if(m_State < WRITING)
{
m_LastCmdBufferID = cmdid;
}
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("Ending partial command buffer for %llu baked to %llu", cmdid, bakeId);
#endif
bool recordAll = m_DrawcallCallback && m_DrawcallCallback->RecordAllCmds();
if(!recordAll && m_Partial[Primary].partialParent == cmdid &&
m_Partial[Primary].renderPassActive)
{
uint32_t numSubpasses =
(uint32_t)m_CreationInfo.m_RenderPass[m_RenderState.renderPass].subpasses.size();
for(uint32_t sub = m_RenderState.subpass; sub < numSubpasses - 1; sub++)
ObjDisp(commandBuffer)->CmdNextSubpass(Unwrap(commandBuffer), VK_SUBPASS_CONTENTS_INLINE);
ObjDisp(commandBuffer)->CmdEndRenderPass(Unwrap(commandBuffer));
}
if(ObjDisp(commandBuffer)->CmdDebugMarkerEndEXT)
for(int i = 0; i < m_BakedCmdBufferInfo[cmdid].markerCount; i++)
ObjDisp(commandBuffer)->CmdDebugMarkerEndEXT(Unwrap(commandBuffer));
ObjDisp(commandBuffer)->EndCommandBuffer(Unwrap(commandBuffer));
// erase the non-baked reference to this command buffer so that we don't have
// duplicates when it comes time to clean up. See above in vkBeginCommandBuffer
m_RerecordCmds.erase(cmdid);
if(m_Partial[Primary].partialParent == cmdid)
m_Partial[Primary].partialParent = ResourceId();
}
m_BakedCmdBufferInfo[cmdid].curEventID = 0;
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(bakeId);
GetResourceManager()->RemoveReplacement(cmdid);
ObjDisp(commandBuffer)->EndCommandBuffer(Unwrap(commandBuffer));
if(m_State == READING && !m_BakedCmdBufferInfo[m_LastCmdBufferID].curEvents.empty())
{
DrawcallDescription draw;
draw.name = "API Calls";
draw.flags |= DrawFlags::SetMarker | DrawFlags::APICalls;
AddDrawcall(draw, true);
m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID++;
}
{
if(GetDrawcallStack().size() > 1)
GetDrawcallStack().pop_back();
}
{
m_BakedCmdBufferInfo[bakeId].draw = m_BakedCmdBufferInfo[m_LastCmdBufferID].draw;
m_BakedCmdBufferInfo[bakeId].curEvents = m_BakedCmdBufferInfo[m_LastCmdBufferID].curEvents;
m_BakedCmdBufferInfo[bakeId].debugMessages =
m_BakedCmdBufferInfo[m_LastCmdBufferID].debugMessages;
m_BakedCmdBufferInfo[bakeId].curEventID = 0;
m_BakedCmdBufferInfo[bakeId].eventCount = m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID;
m_BakedCmdBufferInfo[bakeId].drawCount = m_BakedCmdBufferInfo[m_LastCmdBufferID].drawCount;
m_BakedCmdBufferInfo[m_LastCmdBufferID].draw = NULL;
m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID = 0;
m_BakedCmdBufferInfo[m_LastCmdBufferID].eventCount = 0;
m_BakedCmdBufferInfo[m_LastCmdBufferID].drawCount = 0;
}
}
return true;
}
VkResult WrappedVulkan::vkEndCommandBuffer(VkCommandBuffer commandBuffer)
{
VkResourceRecord *record = GetRecord(commandBuffer);
RDCASSERT(record);
if(record)
{
// ensure that we have a matching begin
RDCASSERT(record->bakedCommands);
{
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(END_CMD_BUFFER);
Serialise_vkEndCommandBuffer(localSerialiser, commandBuffer);
record->AddChunk(scope.Get());
}
record->Bake();
}
return ObjDisp(commandBuffer)->EndCommandBuffer(Unwrap(commandBuffer));
}
VkResult WrappedVulkan::vkResetCommandBuffer(VkCommandBuffer commandBuffer,
VkCommandBufferResetFlags flags)
{
VkResourceRecord *record = GetRecord(commandBuffer);
RDCASSERT(record);
if(record)
{
// all we need to do is remove the existing baked commands.
// The application will still need to call begin command buffer itself.
// this function is essentially a driver hint as it cleans up implicitly
// on begin.
//
// Because it's totally legal for an application to record, submit, reset,
// record, submit again, and we need some way of referencing the two different
// sets of commands on replay, our command buffers are given new unique IDs
// each time they are begun, so on replay it looks like they were all unique
// (albeit with the same properties for those that share a 'parent'). Hence,
// we don't need to record or replay when a ResetCommandBuffer happens
if(record->bakedCommands)
record->bakedCommands->Delete(GetResourceManager());
record->bakedCommands = NULL;
}
return ObjDisp(commandBuffer)->ResetCommandBuffer(Unwrap(commandBuffer), flags);
}
// Command buffer building functions
bool WrappedVulkan::Serialise_vkCmdBeginRenderPass(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
const VkRenderPassBeginInfo *pRenderPassBegin,
VkSubpassContents contents)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(VkRenderPassBeginInfo, beginInfo, *pRenderPassBegin);
SERIALISE_ELEMENT(VkSubpassContents, cont, contents);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
m_Partial[Primary].renderPassActive = true;
ObjDisp(commandBuffer)->CmdBeginRenderPass(Unwrap(commandBuffer), &beginInfo, cont);
m_RenderState.subpass = 0;
m_RenderState.renderPass = GetResourceManager()->GetNonDispWrapper(beginInfo.renderPass)->id;
m_RenderState.framebuffer = GetResourceManager()->GetNonDispWrapper(beginInfo.framebuffer)->id;
m_RenderState.renderArea = beginInfo.renderArea;
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers();
ResourceId cmd = GetResID(commandBuffer);
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
ObjDisp(commandBuffer)->CmdBeginRenderPass(Unwrap(commandBuffer), &beginInfo, cont);
// track while reading, for fetching the right set of outputs in AddDrawcall
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass = 0;
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.renderPass =
GetResourceManager()->GetNonDispWrapper(beginInfo.renderPass)->id;
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer =
GetResourceManager()->GetNonDispWrapper(beginInfo.framebuffer)->id;
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers();
ResourceId cmd = GetResID(commandBuffer);
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
const string desc = localSerialiser->GetDebugStr();
string opDesc = MakeRenderPassOpString(false);
AddEvent(desc);
DrawcallDescription draw;
draw.name = StringFormat::Fmt("vkCmdBeginRenderPass(%s)", opDesc.c_str());
draw.flags |= DrawFlags::PassBoundary | DrawFlags::BeginPass;
AddDrawcall(draw, true);
}
return true;
}
void WrappedVulkan::vkCmdBeginRenderPass(VkCommandBuffer commandBuffer,
const VkRenderPassBeginInfo *pRenderPassBegin,
VkSubpassContents contents)
{
SCOPED_DBG_SINK();
VkRenderPassBeginInfo unwrappedInfo = *pRenderPassBegin;
unwrappedInfo.renderPass = Unwrap(unwrappedInfo.renderPass);
unwrappedInfo.framebuffer = Unwrap(unwrappedInfo.framebuffer);
ObjDisp(commandBuffer)->CmdBeginRenderPass(Unwrap(commandBuffer), &unwrappedInfo, contents);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BEGIN_RENDERPASS);
Serialise_vkCmdBeginRenderPass(localSerialiser, commandBuffer, pRenderPassBegin, contents);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(pRenderPassBegin->renderPass), eFrameRef_Read);
VkResourceRecord *fb = GetRecord(pRenderPassBegin->framebuffer);
record->MarkResourceFrameReferenced(fb->GetResourceID(), eFrameRef_Read);
for(size_t i = 0; i < VkResourceRecord::MaxImageAttachments; i++)
{
VkResourceRecord *att = fb->imageAttachments[i].record;
if(att == NULL)
break;
record->MarkResourceFrameReferenced(att->baseResource, eFrameRef_Write);
if(att->baseResourceMem != ResourceId())
record->MarkResourceFrameReferenced(att->baseResourceMem, eFrameRef_Read);
if(att->sparseInfo)
record->cmdInfo->sparse.insert(att->sparseInfo);
record->cmdInfo->dirtied.insert(att->baseResource);
}
record->cmdInfo->framebuffer = fb;
}
}
bool WrappedVulkan::Serialise_vkCmdNextSubpass(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
VkSubpassContents contents)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(VkSubpassContents, cont, contents);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
// don't do anything if we're executing a single draw, NextSubpass is meaningless (and invalid
// on a partial render pass)
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid) && m_FirstEventID != m_LastEventID)
{
commandBuffer = RerecordCmdBuf(cmdid);
m_RenderState.subpass++;
ObjDisp(commandBuffer)->CmdNextSubpass(Unwrap(commandBuffer), cont);
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers();
ResourceId cmd = GetResID(commandBuffer);
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
ObjDisp(commandBuffer)->CmdNextSubpass(Unwrap(commandBuffer), cont);
// track while reading, for fetching the right set of outputs in AddDrawcall
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass++;
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers();
ResourceId cmd = GetResID(commandBuffer);
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
const string desc = localSerialiser->GetDebugStr();
AddEvent(desc);
DrawcallDescription draw;
draw.name = StringFormat::Fmt("vkCmdNextSubpass() => %u",
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.subpass);
draw.flags |= DrawFlags::PassBoundary | DrawFlags::BeginPass | DrawFlags::EndPass;
AddDrawcall(draw, true);
}
return true;
}
void WrappedVulkan::vkCmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)->CmdNextSubpass(Unwrap(commandBuffer), contents);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(NEXT_SUBPASS);
Serialise_vkCmdNextSubpass(localSerialiser, commandBuffer, contents);
record->AddChunk(scope.Get());
}
}
bool WrappedVulkan::Serialise_vkCmdEndRenderPass(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
m_Partial[Primary].renderPassActive = false;
ObjDisp(commandBuffer)->CmdEndRenderPass(Unwrap(commandBuffer));
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers(~0U);
ResourceId cmd = GetResID(commandBuffer);
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
ObjDisp(commandBuffer)->CmdEndRenderPass(Unwrap(commandBuffer));
std::vector<VkImageMemoryBarrier> imgBarriers = GetImplicitRenderPassBarriers(~0U);
ResourceId cmd = GetResID(commandBuffer);
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
const string desc = localSerialiser->GetDebugStr();
string opDesc = MakeRenderPassOpString(true);
AddEvent(desc);
DrawcallDescription draw;
draw.name = StringFormat::Fmt("vkCmdEndRenderPass(%s)", opDesc.c_str());
draw.flags |= DrawFlags::PassBoundary | DrawFlags::EndPass;
AddDrawcall(draw, true);
// track while reading, reset this to empty so AddDrawcall sets no outputs,
// but only AFTER the above AddDrawcall (we want it grouped together)
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.renderPass = ResourceId();
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.framebuffer = ResourceId();
}
return true;
}
void WrappedVulkan::vkCmdEndRenderPass(VkCommandBuffer commandBuffer)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)->CmdEndRenderPass(Unwrap(commandBuffer));
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(END_RENDERPASS);
Serialise_vkCmdEndRenderPass(localSerialiser, commandBuffer);
record->AddChunk(scope.Get());
VkResourceRecord *fb = record->cmdInfo->framebuffer;
std::vector<VkImageMemoryBarrier> barriers;
for(size_t i = 0; i < VkResourceRecord::MaxImageAttachments; i++)
{
if(fb->imageAttachments[i].barrier.oldLayout == fb->imageAttachments[i].barrier.newLayout)
continue;
barriers.push_back(fb->imageAttachments[i].barrier);
}
// apply the implicit layout transitions here
{
SCOPED_LOCK(m_ImageLayoutsLock);
GetResourceManager()->RecordBarriers(GetRecord(commandBuffer)->cmdInfo->imgbarriers,
m_ImageLayouts, (uint32_t)barriers.size(), &barriers[0]);
}
}
}
bool WrappedVulkan::Serialise_vkCmdBindPipeline(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint,
VkPipeline pipeline)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(VkPipelineBindPoint, bind, pipelineBindPoint);
SERIALISE_ELEMENT(ResourceId, pipeid, GetResID(pipeline));
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
pipeline = GetResourceManager()->GetLiveHandle<VkPipeline>(pipeid);
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)->CmdBindPipeline(Unwrap(commandBuffer), bind, Unwrap(pipeline));
ResourceId liveid = GetResID(pipeline);
if(bind == VK_PIPELINE_BIND_POINT_GRAPHICS)
m_RenderState.graphics.pipeline = liveid;
else
m_RenderState.compute.pipeline = liveid;
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_VIEWPORT])
{
m_RenderState.views = m_CreationInfo.m_Pipeline[liveid].viewports;
}
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_SCISSOR])
{
m_RenderState.scissors = m_CreationInfo.m_Pipeline[liveid].scissors;
}
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_LINE_WIDTH])
{
m_RenderState.lineWidth = m_CreationInfo.m_Pipeline[liveid].lineWidth;
}
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_DEPTH_BIAS])
{
m_RenderState.bias.depth = m_CreationInfo.m_Pipeline[liveid].depthBiasConstantFactor;
m_RenderState.bias.biasclamp = m_CreationInfo.m_Pipeline[liveid].depthBiasClamp;
m_RenderState.bias.slope = m_CreationInfo.m_Pipeline[liveid].depthBiasSlopeFactor;
}
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_BLEND_CONSTANTS])
{
memcpy(m_RenderState.blendConst, m_CreationInfo.m_Pipeline[liveid].blendConst,
sizeof(float) * 4);
}
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_DEPTH_BOUNDS])
{
m_RenderState.mindepth = m_CreationInfo.m_Pipeline[liveid].minDepthBounds;
m_RenderState.maxdepth = m_CreationInfo.m_Pipeline[liveid].maxDepthBounds;
}
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK])
{
m_RenderState.front.compare = m_CreationInfo.m_Pipeline[liveid].front.compareMask;
m_RenderState.back.compare = m_CreationInfo.m_Pipeline[liveid].back.compareMask;
}
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_STENCIL_WRITE_MASK])
{
m_RenderState.front.write = m_CreationInfo.m_Pipeline[liveid].front.writeMask;
m_RenderState.back.write = m_CreationInfo.m_Pipeline[liveid].back.writeMask;
}
if(!m_CreationInfo.m_Pipeline[liveid].dynamicStates[VK_DYNAMIC_STATE_STENCIL_REFERENCE])
{
m_RenderState.front.ref = m_CreationInfo.m_Pipeline[liveid].front.reference;
m_RenderState.back.ref = m_CreationInfo.m_Pipeline[liveid].back.reference;
}
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
pipeline = GetResourceManager()->GetLiveHandle<VkPipeline>(pipeid);
// track while reading, as we need to bind current topology & index byte width in AddDrawcall
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.pipeline = GetResID(pipeline);
ObjDisp(commandBuffer)->CmdBindPipeline(Unwrap(commandBuffer), bind, Unwrap(pipeline));
}
return true;
}
void WrappedVulkan::vkCmdBindPipeline(VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)->CmdBindPipeline(Unwrap(commandBuffer), pipelineBindPoint, Unwrap(pipeline));
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BIND_PIPELINE);
Serialise_vkCmdBindPipeline(localSerialiser, commandBuffer, pipelineBindPoint, pipeline);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(pipeline), eFrameRef_Read);
}
}
bool WrappedVulkan::Serialise_vkCmdBindDescriptorSets(
Serialiser *localSerialiser, VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet,
uint32_t setCount, const VkDescriptorSet *pDescriptorSets, uint32_t dynamicOffsetCount,
const uint32_t *pDynamicOffsets)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, layoutid, GetResID(layout));
SERIALISE_ELEMENT(VkPipelineBindPoint, bind, pipelineBindPoint);
SERIALISE_ELEMENT(uint32_t, first, firstSet);
SERIALISE_ELEMENT(uint32_t, numSets, setCount);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
ResourceId *descriptorIDs = NULL;
VkDescriptorSet *sets = (VkDescriptorSet *)pDescriptorSets;
if(m_State < WRITING)
{
descriptorIDs = new ResourceId[numSets];
sets = new VkDescriptorSet[numSets];
}
for(uint32_t i = 0; i < numSets; i++)
{
ResourceId id;
if(m_State >= WRITING)
id = GetResID(sets[i]);
localSerialiser->Serialise("DescriptorSet", id);
if(m_State < WRITING)
{
descriptorIDs[i] = id;
sets[i] = GetResourceManager()->GetLiveHandle<VkDescriptorSet>(descriptorIDs[i]);
descriptorIDs[i] = GetResID(sets[i]);
sets[i] = Unwrap(sets[i]);
}
}
SERIALISE_ELEMENT(uint32_t, offsCount, dynamicOffsetCount);
SERIALISE_ELEMENT_ARR_OPT(uint32_t, offs, pDynamicOffsets, offsCount, offsCount > 0);
if(m_State == EXECUTING)
{
layout = GetResourceManager()->GetLiveHandle<VkPipelineLayout>(layoutid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)
->CmdBindDescriptorSets(Unwrap(commandBuffer), bind, Unwrap(layout), first, numSets, sets,
offsCount, offs);
vector<VulkanRenderState::Pipeline::DescriptorAndOffsets> &descsets =
(bind == VK_PIPELINE_BIND_POINT_GRAPHICS) ? m_RenderState.graphics.descSets
: m_RenderState.compute.descSets;
// expand as necessary
if(descsets.size() < first + numSets)
descsets.resize(first + numSets);
const vector<ResourceId> &descSetLayouts =
m_CreationInfo.m_PipelineLayout[GetResID(layout)].descSetLayouts;
uint32_t *offsIter = offs;
uint32_t dynConsumed = 0;
// consume the offsets linearly along the descriptor set layouts
for(uint32_t i = 0; i < numSets; i++)
{
descsets[first + i].descSet = descriptorIDs[i];
uint32_t dynCount = m_CreationInfo.m_DescSetLayout[descSetLayouts[first + i]].dynamicCount;
descsets[first + i].offsets.assign(offsIter, offsIter + dynCount);
dynConsumed += dynCount;
RDCASSERT(dynConsumed <= offsCount);
}
// if there are dynamic offsets, bake them into the current bindings by alias'ing
// the image layout member (which is never used for buffer views).
// This lets us look it up easily when we want to show the current pipeline state
RDCCOMPILE_ASSERT(sizeof(VkImageLayout) >= sizeof(uint32_t),
"Can't alias image layout for dynamic offset!");
if(offsCount > 0)
{
uint32_t o = 0;
// spec states that dynamic offsets precisely match all the offsets needed for these
// sets, in order of set N before set N+1, binding X before binding X+1 within a set,
// and in array element order within a binding
for(uint32_t i = 0; i < numSets; i++)
{
const DescSetLayout &layoutinfo = m_CreationInfo.m_DescSetLayout[descSetLayouts[first + i]];
for(size_t b = 0; b < layoutinfo.bindings.size(); b++)
{
// not dynamic, doesn't need an offset
if(layoutinfo.bindings[b].descriptorType != VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC &&
layoutinfo.bindings[b].descriptorType != VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
continue;
// assign every array element an offset according to array size
for(uint32_t a = 0; a < layoutinfo.bindings[b].descriptorCount; a++)
{
RDCASSERT(o < offsCount);
uint32_t *alias = (uint32_t *)&m_DescriptorSetState[descriptorIDs[i]]
.currentBindings[b][a]
.imageInfo.imageLayout;
*alias = offs[o++];
}
}
}
}
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
layout = GetResourceManager()->GetLiveHandle<VkPipelineLayout>(layoutid);
// track while reading, as we need to track resource usage
vector<BakedCmdBufferInfo::CmdBufferState::DescriptorAndOffsets> &descsets =
(bind == VK_PIPELINE_BIND_POINT_GRAPHICS)
? m_BakedCmdBufferInfo[m_LastCmdBufferID].state.graphicsDescSets
: m_BakedCmdBufferInfo[m_LastCmdBufferID].state.computeDescSets;
// expand as necessary
if(descsets.size() < first + numSets)
descsets.resize(first + numSets);
for(uint32_t i = 0; i < numSets; i++)
descsets[first + i].descSet = descriptorIDs[i];
ObjDisp(commandBuffer)
->CmdBindDescriptorSets(Unwrap(commandBuffer), bind, Unwrap(layout), first, numSets, sets,
offsCount, offs);
}
if(m_State < WRITING)
{
SAFE_DELETE_ARRAY(sets);
SAFE_DELETE_ARRAY(descriptorIDs);
}
SAFE_DELETE_ARRAY(offs);
return true;
}
void WrappedVulkan::vkCmdBindDescriptorSets(VkCommandBuffer commandBuffer,
VkPipelineBindPoint pipelineBindPoint,
VkPipelineLayout layout, uint32_t firstSet,
uint32_t setCount, const VkDescriptorSet *pDescriptorSets,
uint32_t dynamicOffsetCount,
const uint32_t *pDynamicOffsets)
{
SCOPED_DBG_SINK();
VkDescriptorSet *unwrapped = GetTempArray<VkDescriptorSet>(setCount);
for(uint32_t i = 0; i < setCount; i++)
unwrapped[i] = Unwrap(pDescriptorSets[i]);
ObjDisp(commandBuffer)
->CmdBindDescriptorSets(Unwrap(commandBuffer), pipelineBindPoint, Unwrap(layout), firstSet,
setCount, unwrapped, dynamicOffsetCount, pDynamicOffsets);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BIND_DESCRIPTOR_SET);
Serialise_vkCmdBindDescriptorSets(localSerialiser, commandBuffer, pipelineBindPoint, layout,
firstSet, setCount, pDescriptorSets, dynamicOffsetCount,
pDynamicOffsets);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(layout), eFrameRef_Read);
record->cmdInfo->boundDescSets.insert(pDescriptorSets, pDescriptorSets + setCount);
// conservatively mark all writeable objects in the descriptor set as dirty here.
// Technically not all might be written although that required verifying what the
// shader does and is a large problem space. The binding could be overridden though
// but per Vulkan ethos we consider that the application's problem to solve. Plus,
// it would mean we'd need to dirty every drawcall instead of just every bind at
// lower frequency.
for(uint32_t i = 0; i < setCount; i++)
{
VkResourceRecord *descSet = GetRecord(pDescriptorSets[i]);
map<ResourceId, pair<uint32_t, FrameRefType> > &frameRefs = descSet->descInfo->bindFrameRefs;
for(auto it = frameRefs.begin(); it != frameRefs.end(); ++it)
{
if(it->second.second == eFrameRef_Write || it->second.second == eFrameRef_ReadBeforeWrite)
record->cmdInfo->dirtied.insert(it->first);
}
}
}
}
bool WrappedVulkan::Serialise_vkCmdBindVertexBuffers(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
uint32_t firstBinding, uint32_t bindingCount,
const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(uint32_t, first, firstBinding);
SERIALISE_ELEMENT(uint32_t, count, bindingCount);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
vector<ResourceId> bufids;
vector<VkBuffer> bufs;
vector<VkDeviceSize> offs;
for(uint32_t i = 0; i < count; i++)
{
ResourceId id;
VkDeviceSize o;
if(m_State >= WRITING)
{
id = GetResID(pBuffers[i]);
o = pOffsets[i];
}
localSerialiser->Serialise("pBuffers[]", id);
localSerialiser->Serialise("pOffsets[]", o);
if(m_State < WRITING)
{
VkBuffer buf = GetResourceManager()->GetLiveHandle<VkBuffer>(id);
bufids.push_back(GetResID(buf));
bufs.push_back(Unwrap(buf));
offs.push_back(o);
}
}
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)
->CmdBindVertexBuffers(Unwrap(commandBuffer), first, count, &bufs[0], &offs[0]);
if(m_RenderState.vbuffers.size() < first + count)
m_RenderState.vbuffers.resize(first + count);
for(uint32_t i = 0; i < count; i++)
{
m_RenderState.vbuffers[first + i].buf = bufids[i];
m_RenderState.vbuffers[first + i].offs = offs[i];
}
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
// track while reading, as we need to track resource usage
if(m_BakedCmdBufferInfo[m_LastCmdBufferID].state.vbuffers.size() < first + count)
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.vbuffers.resize(first + count);
for(uint32_t i = 0; i < count; i++)
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.vbuffers[first + i] = bufids[i];
ObjDisp(commandBuffer)
->CmdBindVertexBuffers(Unwrap(commandBuffer), first, count, &bufs[0], &offs[0]);
}
return true;
}
void WrappedVulkan::vkCmdBindVertexBuffers(VkCommandBuffer commandBuffer, uint32_t firstBinding,
uint32_t bindingCount, const VkBuffer *pBuffers,
const VkDeviceSize *pOffsets)
{
SCOPED_DBG_SINK();
VkBuffer *unwrapped = GetTempArray<VkBuffer>(bindingCount);
for(uint32_t i = 0; i < bindingCount; i++)
unwrapped[i] = Unwrap(pBuffers[i]);
ObjDisp(commandBuffer)
->CmdBindVertexBuffers(Unwrap(commandBuffer), firstBinding, bindingCount, unwrapped, pOffsets);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BIND_VERTEX_BUFFERS);
Serialise_vkCmdBindVertexBuffers(localSerialiser, commandBuffer, firstBinding, bindingCount,
pBuffers, pOffsets);
record->AddChunk(scope.Get());
for(uint32_t i = 0; i < bindingCount; i++)
{
record->MarkResourceFrameReferenced(GetResID(pBuffers[i]), eFrameRef_Read);
record->MarkResourceFrameReferenced(GetRecord(pBuffers[i])->baseResource, eFrameRef_Read);
if(GetRecord(pBuffers[i])->sparseInfo)
record->cmdInfo->sparse.insert(GetRecord(pBuffers[i])->sparseInfo);
}
}
}
bool WrappedVulkan::Serialise_vkCmdBindIndexBuffer(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer, VkBuffer buffer,
VkDeviceSize offset, VkIndexType indexType)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, bufid, GetResID(buffer));
SERIALISE_ELEMENT(uint64_t, offs, offset);
SERIALISE_ELEMENT(VkIndexType, idxType, indexType);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
buffer = GetResourceManager()->GetLiveHandle<VkBuffer>(bufid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)->CmdBindIndexBuffer(Unwrap(commandBuffer), Unwrap(buffer), offs, idxType);
m_RenderState.ibuffer.buf = GetResID(buffer);
m_RenderState.ibuffer.offs = offs;
m_RenderState.ibuffer.bytewidth = idxType == VK_INDEX_TYPE_UINT32 ? 4 : 2;
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
buffer = GetResourceManager()->GetLiveHandle<VkBuffer>(bufid);
// track while reading, as we need to bind current topology & index byte width in AddDrawcall
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.idxWidth =
(idxType == VK_INDEX_TYPE_UINT32 ? 4 : 2);
// track while reading, as we need to track resource usage
m_BakedCmdBufferInfo[m_LastCmdBufferID].state.ibuffer = GetResID(buffer);
ObjDisp(commandBuffer)->CmdBindIndexBuffer(Unwrap(commandBuffer), Unwrap(buffer), offs, idxType);
}
return true;
}
void WrappedVulkan::vkCmdBindIndexBuffer(VkCommandBuffer commandBuffer, VkBuffer buffer,
VkDeviceSize offset, VkIndexType indexType)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)->CmdBindIndexBuffer(Unwrap(commandBuffer), Unwrap(buffer), offset, indexType);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BIND_INDEX_BUFFER);
Serialise_vkCmdBindIndexBuffer(localSerialiser, commandBuffer, buffer, offset, indexType);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(buffer), eFrameRef_Read);
record->MarkResourceFrameReferenced(GetRecord(buffer)->baseResource, eFrameRef_Read);
if(GetRecord(buffer)->sparseInfo)
record->cmdInfo->sparse.insert(GetRecord(buffer)->sparseInfo);
}
}
bool WrappedVulkan::Serialise_vkCmdUpdateBuffer(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer, VkBuffer destBuffer,
VkDeviceSize destOffset, VkDeviceSize dataSize,
const uint32_t *pData)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, bufid, GetResID(destBuffer));
SERIALISE_ELEMENT(VkDeviceSize, offs, destOffset);
SERIALISE_ELEMENT(VkDeviceSize, sz, dataSize);
SERIALISE_ELEMENT_BUF(byte *, bufdata, (byte *)pData, (size_t)dataSize);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
destBuffer = GetResourceManager()->GetLiveHandle<VkBuffer>(bufid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)
->CmdUpdateBuffer(Unwrap(commandBuffer), Unwrap(destBuffer), offs, sz, (uint32_t *)bufdata);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
destBuffer = GetResourceManager()->GetLiveHandle<VkBuffer>(bufid);
ObjDisp(commandBuffer)
->CmdUpdateBuffer(Unwrap(commandBuffer), Unwrap(destBuffer), offs, sz, (uint32_t *)bufdata);
}
if(m_State < WRITING)
SAFE_DELETE_ARRAY(bufdata);
return true;
}
void WrappedVulkan::vkCmdUpdateBuffer(VkCommandBuffer commandBuffer, VkBuffer destBuffer,
VkDeviceSize destOffset, VkDeviceSize dataSize,
const uint32_t *pData)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)
->CmdUpdateBuffer(Unwrap(commandBuffer), Unwrap(destBuffer), destOffset, dataSize, pData);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(UPDATE_BUF);
Serialise_vkCmdUpdateBuffer(localSerialiser, commandBuffer, destBuffer, destOffset, dataSize,
pData);
record->AddChunk(scope.Get());
VkResourceRecord *buf = GetRecord(destBuffer);
// mark buffer just as read, and memory behind as write & dirtied
record->MarkResourceFrameReferenced(buf->GetResourceID(), eFrameRef_Read);
record->MarkResourceFrameReferenced(buf->baseResource, eFrameRef_Write);
if(buf->baseResource != ResourceId())
record->cmdInfo->dirtied.insert(buf->baseResource);
if(buf->sparseInfo)
record->cmdInfo->sparse.insert(buf->sparseInfo);
}
}
bool WrappedVulkan::Serialise_vkCmdFillBuffer(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer, VkBuffer destBuffer,
VkDeviceSize destOffset, VkDeviceSize fillSize,
uint32_t data)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, bufid, GetResID(destBuffer));
SERIALISE_ELEMENT(VkDeviceSize, offs, destOffset);
SERIALISE_ELEMENT(VkDeviceSize, sz, fillSize);
SERIALISE_ELEMENT(uint32_t, d, data);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
destBuffer = GetResourceManager()->GetLiveHandle<VkBuffer>(bufid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)->CmdFillBuffer(Unwrap(commandBuffer), Unwrap(destBuffer), offs, sz, d);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
destBuffer = GetResourceManager()->GetLiveHandle<VkBuffer>(bufid);
ObjDisp(commandBuffer)->CmdFillBuffer(Unwrap(commandBuffer), Unwrap(destBuffer), offs, sz, d);
}
return true;
}
void WrappedVulkan::vkCmdFillBuffer(VkCommandBuffer commandBuffer, VkBuffer destBuffer,
VkDeviceSize destOffset, VkDeviceSize fillSize, uint32_t data)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)
->CmdFillBuffer(Unwrap(commandBuffer), Unwrap(destBuffer), destOffset, fillSize, data);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(FILL_BUF);
Serialise_vkCmdFillBuffer(localSerialiser, commandBuffer, destBuffer, destOffset, fillSize, data);
record->AddChunk(scope.Get());
VkResourceRecord *buf = GetRecord(destBuffer);
// mark buffer just as read, and memory behind as write & dirtied
record->MarkResourceFrameReferenced(buf->GetResourceID(), eFrameRef_Read);
record->MarkResourceFrameReferenced(buf->baseResource, eFrameRef_Write);
if(buf->baseResource != ResourceId())
record->cmdInfo->dirtied.insert(buf->baseResource);
if(buf->sparseInfo)
record->cmdInfo->sparse.insert(buf->sparseInfo);
}
}
bool WrappedVulkan::Serialise_vkCmdPushConstants(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
VkPipelineLayout layout,
VkShaderStageFlags stageFlags, uint32_t start,
uint32_t length, const void *values)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, layid, GetResID(layout));
SERIALISE_ELEMENT(VkShaderStageFlagBits, flags, (VkShaderStageFlagBits)stageFlags);
SERIALISE_ELEMENT(uint32_t, s, start);
SERIALISE_ELEMENT(uint32_t, len, length);
SERIALISE_ELEMENT_BUF(byte *, vals, (byte *)values, (size_t)len);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
layout = GetResourceManager()->GetLiveHandle<VkPipelineLayout>(layid);
ObjDisp(commandBuffer)->CmdPushConstants(Unwrap(commandBuffer), Unwrap(layout), flags, s, len, vals);
RDCASSERT(s + len < (uint32_t)ARRAY_COUNT(m_RenderState.pushconsts));
memcpy(m_RenderState.pushconsts + s, vals, len);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
layout = GetResourceManager()->GetLiveHandle<VkPipelineLayout>(layid);
ObjDisp(commandBuffer)->CmdPushConstants(Unwrap(commandBuffer), Unwrap(layout), flags, s, len, vals);
}
if(m_State < WRITING)
SAFE_DELETE_ARRAY(vals);
return true;
}
void WrappedVulkan::vkCmdPushConstants(VkCommandBuffer commandBuffer, VkPipelineLayout layout,
VkShaderStageFlags stageFlags, uint32_t start,
uint32_t length, const void *values)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)
->CmdPushConstants(Unwrap(commandBuffer), Unwrap(layout), stageFlags, start, length, values);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(PUSH_CONST);
Serialise_vkCmdPushConstants(localSerialiser, commandBuffer, layout, stageFlags, start, length,
values);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(layout), eFrameRef_Read);
}
}
bool WrappedVulkan::Serialise_vkCmdPipelineBarrier(
Serialiser *localSerialiser, VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags destStageMask, VkDependencyFlags dependencyFlags,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers,
uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(VkPipelineStageFlagBits, srcStages, (VkPipelineStageFlagBits)srcStageMask);
SERIALISE_ELEMENT(VkPipelineStageFlagBits, destStages, (VkPipelineStageFlagBits)destStageMask);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
SERIALISE_ELEMENT(VkDependencyFlags, flags, dependencyFlags);
SERIALISE_ELEMENT(uint32_t, memCount, memoryBarrierCount);
SERIALISE_ELEMENT(uint32_t, bufCount, bufferMemoryBarrierCount);
SERIALISE_ELEMENT(uint32_t, imgCount, imageMemoryBarrierCount);
SERIALISE_ELEMENT_ARR(VkMemoryBarrier, memBarriers, pMemoryBarriers, memCount);
SERIALISE_ELEMENT_ARR(VkBufferMemoryBarrier, bufMemBarriers, pBufferMemoryBarriers, bufCount);
SERIALISE_ELEMENT_ARR(VkImageMemoryBarrier, imgMemBarriers, pImageMemoryBarriers, imgCount);
Serialise_DebugMessages(localSerialiser, false);
vector<VkImageMemoryBarrier> imgBarriers;
vector<VkBufferMemoryBarrier> bufBarriers;
// it's possible for buffer or image to be NULL if it refers to a resource that is otherwise
// not in the log (barriers do not mark resources referenced). If the resource in question does
// not exist, then it's safe to skip this barrier.
if(m_State < WRITING)
{
for(uint32_t i = 0; i < bufCount; i++)
if(bufMemBarriers[i].buffer != VK_NULL_HANDLE)
bufBarriers.push_back(bufMemBarriers[i]);
for(uint32_t i = 0; i < imgCount; i++)
{
if(imgMemBarriers[i].image != VK_NULL_HANDLE)
{
imgBarriers.push_back(imgMemBarriers[i]);
ReplacePresentableImageLayout(imgBarriers.back().oldLayout);
ReplacePresentableImageLayout(imgBarriers.back().newLayout);
ReplaceExternalQueueFamily(imgBarriers.back().srcQueueFamilyIndex,
imgBarriers.back().dstQueueFamilyIndex);
}
}
}
SAFE_DELETE_ARRAY(bufMemBarriers);
SAFE_DELETE_ARRAY(imgMemBarriers);
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)
->CmdPipelineBarrier(Unwrap(commandBuffer), (VkPipelineStageFlags)srcStages,
(VkPipelineStageFlags)destStages, flags, memCount, memBarriers,
(uint32_t)bufBarriers.size(), &bufBarriers[0],
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
ResourceId cmd = GetResID(commandBuffer);
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
ObjDisp(commandBuffer)
->CmdPipelineBarrier(Unwrap(commandBuffer), (VkPipelineStageFlags)srcStages,
(VkPipelineStageFlags)destStages, flags, memCount, memBarriers,
(uint32_t)bufBarriers.size(), &bufBarriers[0],
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
ResourceId cmd = GetResID(commandBuffer);
GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts,
(uint32_t)imgBarriers.size(), &imgBarriers[0]);
for(size_t i = 0; i < imgBarriers.size(); i++)
{
m_BakedCmdBufferInfo[cmdid].resourceUsage.push_back(std::make_pair(
GetResourceManager()->GetNonDispWrapper(imgBarriers[i].image)->id,
EventUsage(m_BakedCmdBufferInfo[cmdid].curEventID, ResourceUsage::Barrier)));
}
}
SAFE_DELETE_ARRAY(memBarriers);
return true;
}
void WrappedVulkan::vkCmdPipelineBarrier(
VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags destStageMask, VkDependencyFlags dependencyFlags,
uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers,
uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers,
uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers)
{
SCOPED_DBG_SINK();
{
byte *memory = GetTempMemory(sizeof(VkBufferMemoryBarrier) * bufferMemoryBarrierCount +
sizeof(VkImageMemoryBarrier) * imageMemoryBarrierCount);
VkImageMemoryBarrier *im = (VkImageMemoryBarrier *)memory;
VkBufferMemoryBarrier *buf = (VkBufferMemoryBarrier *)(im + imageMemoryBarrierCount);
for(uint32_t i = 0; i < bufferMemoryBarrierCount; i++)
{
buf[i] = pBufferMemoryBarriers[i];
buf[i].buffer = Unwrap(buf[i].buffer);
}
for(uint32_t i = 0; i < imageMemoryBarrierCount; i++)
{
im[i] = pImageMemoryBarriers[i];
im[i].image = Unwrap(im[i].image);
}
ObjDisp(commandBuffer)
->CmdPipelineBarrier(Unwrap(commandBuffer), srcStageMask, destStageMask, dependencyFlags,
memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, buf,
imageMemoryBarrierCount, im);
}
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(PIPELINE_BARRIER);
Serialise_vkCmdPipelineBarrier(localSerialiser, commandBuffer, srcStageMask, destStageMask,
dependencyFlags, memoryBarrierCount, pMemoryBarriers,
bufferMemoryBarrierCount, pBufferMemoryBarriers,
imageMemoryBarrierCount, pImageMemoryBarriers);
record->AddChunk(scope.Get());
if(imageMemoryBarrierCount > 0)
{
SCOPED_LOCK(m_ImageLayoutsLock);
GetResourceManager()->RecordBarriers(GetRecord(commandBuffer)->cmdInfo->imgbarriers,
m_ImageLayouts, imageMemoryBarrierCount,
pImageMemoryBarriers);
}
}
}
bool WrappedVulkan::Serialise_vkCmdWriteTimestamp(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
VkPipelineStageFlagBits pipelineStage,
VkQueryPool queryPool, uint32_t query)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(VkPipelineStageFlagBits, stage, pipelineStage);
SERIALISE_ELEMENT(ResourceId, poolid, GetResID(queryPool));
SERIALISE_ELEMENT(uint32_t, q, query);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(poolid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)->CmdWriteTimestamp(Unwrap(commandBuffer), stage, Unwrap(queryPool), q);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(poolid);
ObjDisp(commandBuffer)->CmdWriteTimestamp(Unwrap(commandBuffer), stage, Unwrap(queryPool), q);
}
return true;
}
void WrappedVulkan::vkCmdWriteTimestamp(VkCommandBuffer commandBuffer,
VkPipelineStageFlagBits pipelineStage,
VkQueryPool queryPool, uint32_t query)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)
->CmdWriteTimestamp(Unwrap(commandBuffer), pipelineStage, Unwrap(queryPool), query);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(WRITE_TIMESTAMP);
Serialise_vkCmdWriteTimestamp(localSerialiser, commandBuffer, pipelineStage, queryPool, query);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(queryPool), eFrameRef_Read);
}
}
bool WrappedVulkan::Serialise_vkCmdCopyQueryPoolResults(
Serialiser *localSerialiser, VkCommandBuffer commandBuffer, VkQueryPool queryPool,
uint32_t firstQuery, uint32_t queryCount, VkBuffer destBuffer, VkDeviceSize destOffset,
VkDeviceSize destStride, VkQueryResultFlags flags)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, qid, GetResID(queryPool));
SERIALISE_ELEMENT(uint32_t, first, firstQuery);
SERIALISE_ELEMENT(uint32_t, count, queryCount);
SERIALISE_ELEMENT(ResourceId, bufid, GetResID(destBuffer));
SERIALISE_ELEMENT(VkDeviceSize, offs, destOffset);
SERIALISE_ELEMENT(VkDeviceSize, stride, destStride);
SERIALISE_ELEMENT(VkQueryResultFlagBits, f, (VkQueryResultFlagBits)flags);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(qid);
destBuffer = GetResourceManager()->GetLiveHandle<VkBuffer>(bufid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)
->CmdCopyQueryPoolResults(Unwrap(commandBuffer), Unwrap(queryPool), first, count,
Unwrap(destBuffer), offs, stride, f);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(qid);
destBuffer = GetResourceManager()->GetLiveHandle<VkBuffer>(bufid);
ObjDisp(commandBuffer)
->CmdCopyQueryPoolResults(Unwrap(commandBuffer), Unwrap(queryPool), first, count,
Unwrap(destBuffer), offs, stride, f);
}
return true;
}
void WrappedVulkan::vkCmdCopyQueryPoolResults(VkCommandBuffer commandBuffer, VkQueryPool queryPool,
uint32_t firstQuery, uint32_t queryCount,
VkBuffer destBuffer, VkDeviceSize destOffset,
VkDeviceSize destStride, VkQueryResultFlags flags)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)
->CmdCopyQueryPoolResults(Unwrap(commandBuffer), Unwrap(queryPool), firstQuery, queryCount,
Unwrap(destBuffer), destOffset, destStride, flags);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(COPY_QUERY_RESULTS);
Serialise_vkCmdCopyQueryPoolResults(localSerialiser, commandBuffer, queryPool, firstQuery,
queryCount, destBuffer, destOffset, destStride, flags);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(queryPool), eFrameRef_Read);
VkResourceRecord *buf = GetRecord(destBuffer);
// mark buffer just as read, and memory behind as write & dirtied
record->MarkResourceFrameReferenced(buf->GetResourceID(), eFrameRef_Read);
record->MarkResourceFrameReferenced(buf->baseResource, eFrameRef_Write);
if(buf->baseResource != ResourceId())
record->cmdInfo->dirtied.insert(buf->baseResource);
if(buf->sparseInfo)
record->cmdInfo->sparse.insert(buf->sparseInfo);
}
}
bool WrappedVulkan::Serialise_vkCmdBeginQuery(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer, VkQueryPool queryPool,
uint32_t query, VkQueryControlFlags flags)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, qid, GetResID(queryPool));
SERIALISE_ELEMENT(uint32_t, q, query);
SERIALISE_ELEMENT(
VkQueryControlFlagBits, f,
(VkQueryControlFlagBits)flags); // serialise as 'bits' type to get nice enum values
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(qid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)->CmdBeginQuery(Unwrap(commandBuffer), Unwrap(queryPool), q, f);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(qid);
ObjDisp(commandBuffer)->CmdBeginQuery(Unwrap(commandBuffer), Unwrap(queryPool), q, f);
}
return true;
}
void WrappedVulkan::vkCmdBeginQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool,
uint32_t query, VkQueryControlFlags flags)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)->CmdBeginQuery(Unwrap(commandBuffer), Unwrap(queryPool), query, flags);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BEGIN_QUERY);
Serialise_vkCmdBeginQuery(localSerialiser, commandBuffer, queryPool, query, flags);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(queryPool), eFrameRef_Read);
}
}
bool WrappedVulkan::Serialise_vkCmdEndQuery(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer, VkQueryPool queryPool,
uint32_t query)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, qid, GetResID(queryPool));
SERIALISE_ELEMENT(uint32_t, q, query);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(qid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)->CmdEndQuery(Unwrap(commandBuffer), Unwrap(queryPool), q);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(qid);
ObjDisp(commandBuffer)->CmdEndQuery(Unwrap(commandBuffer), Unwrap(queryPool), q);
}
return true;
}
void WrappedVulkan::vkCmdEndQuery(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)->CmdEndQuery(Unwrap(commandBuffer), Unwrap(queryPool), query);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(END_QUERY);
Serialise_vkCmdEndQuery(localSerialiser, commandBuffer, queryPool, query);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(queryPool), eFrameRef_Read);
}
}
bool WrappedVulkan::Serialise_vkCmdResetQueryPool(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
VkQueryPool queryPool, uint32_t firstQuery,
uint32_t queryCount)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(ResourceId, qid, GetResID(queryPool));
SERIALISE_ELEMENT(uint32_t, first, firstQuery);
SERIALISE_ELEMENT(uint32_t, count, queryCount);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(qid);
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
ObjDisp(commandBuffer)->CmdResetQueryPool(Unwrap(commandBuffer), Unwrap(queryPool), first, count);
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
queryPool = GetResourceManager()->GetLiveHandle<VkQueryPool>(qid);
ObjDisp(commandBuffer)->CmdResetQueryPool(Unwrap(commandBuffer), Unwrap(queryPool), first, count);
}
return true;
}
void WrappedVulkan::vkCmdResetQueryPool(VkCommandBuffer commandBuffer, VkQueryPool queryPool,
uint32_t firstQuery, uint32_t queryCount)
{
SCOPED_DBG_SINK();
ObjDisp(commandBuffer)
->CmdResetQueryPool(Unwrap(commandBuffer), Unwrap(queryPool), firstQuery, queryCount);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(RESET_QUERY_POOL);
Serialise_vkCmdResetQueryPool(localSerialiser, commandBuffer, queryPool, firstQuery, queryCount);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(queryPool), eFrameRef_Read);
}
}
bool WrappedVulkan::Serialise_vkCmdExecuteCommands(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
uint32_t commandBufferCount,
const VkCommandBuffer *pCmdBuffers)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(uint32_t, count, commandBufferCount);
Serialise_DebugMessages(localSerialiser, false);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
vector<ResourceId> cmdids;
vector<VkCommandBuffer> cmds;
for(uint32_t i = 0; i < count; i++)
{
ResourceId id;
if(m_State >= WRITING)
{
VkResourceRecord *bakedCommands = GetRecord(pCmdBuffers[i])->bakedCommands;
if(bakedCommands)
id = bakedCommands->GetResourceID();
else
RDCERR("Command Buffer %p was not recorded", pCmdBuffers[i]);
}
localSerialiser->Serialise("pCmdBuffers[]", id);
if(m_State < WRITING && id != ResourceId())
{
cmdids.push_back(id);
cmds.push_back(Unwrap(GetResourceManager()->GetLiveHandle<VkCommandBuffer>(id)));
}
}
if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
// execute the commands
ObjDisp(commandBuffer)->CmdExecuteCommands(Unwrap(commandBuffer), count, &cmds[0]);
// apply barriers
for(uint32_t i = 0; i < count; i++)
{
ResourceId cmd = GetResourceManager()->GetLiveID(cmdids[i]);
GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts);
}
const string desc = localSerialiser->GetDebugStr();
AddEvent(desc);
DrawcallDescription draw;
draw.name = "vkCmdExecuteCommands(" + ToStr::Get(count) + ")";
draw.flags = DrawFlags::CmdList | DrawFlags::PushMarker;
AddDrawcall(draw, true);
BakedCmdBufferInfo &parentCmdBufInfo = m_BakedCmdBufferInfo[m_LastCmdBufferID];
parentCmdBufInfo.curEventID++;
for(uint32_t c = 0; c < count; c++)
{
string name = StringFormat::Fmt("=> vkCmdExecuteCommands()[%u]: vkBeginCommandBuffer(%s)", c,
ToStr::Get(cmdids[c]).c_str());
// add a fake marker
DrawcallDescription marker;
marker.name = name;
marker.flags = DrawFlags::PassBoundary | DrawFlags::BeginPass;
AddEvent(name);
AddDrawcall(marker, true);
parentCmdBufInfo.curEventID++;
BakedCmdBufferInfo &cmdBufInfo = m_BakedCmdBufferInfo[cmdids[c]];
if(m_BakedCmdBufferInfo[m_LastCmdBufferID].state.renderPass == ResourceId() &&
(cmdBufInfo.beginFlags & VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT))
{
AddDebugMessage(
MessageCategory::Execution, MessageSeverity::High, MessageSource::IncorrectAPIUse,
"Executing a command buffer with RENDER_PASS_CONTINUE_BIT outside of render pass");
}
// insert the baked command buffer in-line into this list of notes, assigning new event and
// drawIDs
parentCmdBufInfo.draw->InsertAndUpdateIDs(*cmdBufInfo.draw, parentCmdBufInfo.curEventID,
parentCmdBufInfo.drawCount);
for(size_t i = 0; i < cmdBufInfo.debugMessages.size(); i++)
{
parentCmdBufInfo.debugMessages.push_back(cmdBufInfo.debugMessages[i]);
parentCmdBufInfo.debugMessages.back().eventID += parentCmdBufInfo.curEventID;
}
// only primary command buffers can be submitted
m_Partial[Secondary].cmdBufferSubmits[cmdids[c]].push_back(parentCmdBufInfo.curEventID);
parentCmdBufInfo.draw->executedCmds.push_back(cmdids[c]);
parentCmdBufInfo.curEventID += cmdBufInfo.eventCount;
parentCmdBufInfo.drawCount += cmdBufInfo.drawCount;
name = StringFormat::Fmt("=> vkCmdExecuteCommands()[%u]: vkEndCommandBuffer(%s)", c,
ToStr::Get(cmdids[c]).c_str());
marker.name = name;
marker.flags = DrawFlags::PassBoundary | DrawFlags::EndPass;
AddEvent(name);
AddDrawcall(marker, true);
parentCmdBufInfo.curEventID++;
}
// add an extra pop marker
draw = DrawcallDescription();
draw.flags = DrawFlags::PopMarker;
AddDrawcall(draw, true);
// don't change curEventID here, as it will be incremented outside in the outer
// loop for the EXEC_CMDS event. in vkQueueSubmit we need to decrement curEventID
// because we don't have the extra popmarker event to 'absorb' the outer loop's
// increment, and it incremented once too many for the last vkEndCommandBuffer
// setmarker event in the loop over all commands
}
else if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
BakedCmdBufferInfo &parentCmdBufInfo = m_BakedCmdBufferInfo[m_LastCmdBufferID];
// if we're replaying a range but not from the start, we are guaranteed to only be replaying
// one of our executed command buffers and doing it to an outside command buffer. The outer
// loop will be doing SetOffset() to jump to each event, and any time we land here is just for
// the markers we've added, which have this file offset, so just skip all of our work.
if(m_FirstEventID > 1 && m_FirstEventID + 1 < m_LastEventID)
return true;
// account for the execute commands event
parentCmdBufInfo.curEventID++;
uint32_t startEID = parentCmdBufInfo.curEventID + m_Partial[Primary].baseEvent;
// advance m_CurEventID to match the events added when reading
for(uint32_t c = 0; c < count; c++)
{
// 2 extra for the virtual labels around the command buffer
parentCmdBufInfo.curEventID += 2 + m_BakedCmdBufferInfo[cmdids[c]].eventCount;
}
// same accounting for the outer loop as above means no need to change anything here
if(count == 0)
{
// do nothing, don't bother with the logic below
}
else if(m_FirstEventID == m_LastEventID)
{
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("ExecuteCommands no OnlyDraw %u", m_FirstEventID);
#endif
}
else if(m_LastEventID <= startEID)
{
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("ExecuteCommands no replay %u == %u", m_LastEventID, startEID);
#endif
}
else if(m_DrawcallCallback && m_DrawcallCallback->RecordAllCmds())
{
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("ExecuteCommands re-recording from %u", startEID);
#endif
vector<VkCommandBuffer> rerecordedCmds;
for(uint32_t c = 0; c < count; c++)
{
VkCommandBuffer cmd = RerecordCmdBuf(cmdids[c]);
ResourceId rerecord = GetResID(cmd);
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("ExecuteCommands fully re-recorded replay of %llu, using %llu", cmdids[c],
rerecord);
#endif
rerecordedCmds.push_back(Unwrap(cmd));
GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[rerecord].imgbarriers,
m_ImageLayouts);
}
ObjDisp(commandBuffer)->CmdExecuteCommands(Unwrap(commandBuffer), count, &rerecordedCmds[0]);
}
else if(m_LastEventID > startEID &&
m_LastEventID < parentCmdBufInfo.curEventID + m_Partial[Primary].baseEvent)
{
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("ExecuteCommands partial replay %u < %u", m_LastEventID,
parentCmdBufInfo.curEventID + m_Partial[Primary].baseEvent);
#endif
uint32_t eid = startEID;
vector<ResourceId> trimmedCmdIds;
vector<VkCommandBuffer> trimmedCmds;
for(uint32_t c = 0; c < count; c++)
{
// account for the virtual vkBeginCommandBuffer label at the start of the events here
// so it matches up to baseEvent
eid++;
uint32_t end = eid + m_BakedCmdBufferInfo[cmdids[c]].eventCount;
if(eid == m_Partial[Secondary].baseEvent)
{
ResourceId partial = GetResID(RerecordCmdBuf(cmdids[c], Secondary));
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("ExecuteCommands partial replay of %llu at %u, using %llu", cmdids[c], eid,
partial);
#endif
trimmedCmdIds.push_back(partial);
trimmedCmds.push_back(Unwrap(RerecordCmdBuf(cmdids[c], Secondary)));
}
else if(m_LastEventID >= end)
{
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("ExecuteCommands full replay %llu", cmdids[c]);
#endif
trimmedCmdIds.push_back(cmdids[c]);
trimmedCmds.push_back(
Unwrap(GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdids[c])));
}
else
{
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("not executing %llu", cmdids[c]);
#endif
}
// 1 extra to account for the virtual end command buffer label (begin is accounted for
// above)
eid += 1 + m_BakedCmdBufferInfo[cmdids[c]].eventCount;
}
if(trimmedCmds.size() > 0)
ObjDisp(commandBuffer)
->CmdExecuteCommands(Unwrap(commandBuffer), (uint32_t)trimmedCmds.size(),
&trimmedCmds[0]);
for(uint32_t i = 0; i < trimmedCmdIds.size(); i++)
{
ResourceId cmd = trimmedCmdIds[i];
GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts);
}
}
else
{
#if ENABLED(VERBOSE_PARTIAL_REPLAY)
RDCDEBUG("ExecuteCommands full replay %u >= %u", m_LastEventID,
parentCmdBufInfo.curEventID + m_Partial[Primary].baseEvent);
#endif
// execute the commands
ObjDisp(commandBuffer)->CmdExecuteCommands(Unwrap(commandBuffer), count, &cmds[0]);
for(uint32_t i = 0; i < count; i++)
{
ResourceId cmd = GetResourceManager()->GetLiveID(cmdids[i]);
GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts);
}
}
}
}
return true;
}
void WrappedVulkan::vkCmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount,
const VkCommandBuffer *pCmdBuffers)
{
SCOPED_DBG_SINK();
VkCommandBuffer *unwrapped = GetTempArray<VkCommandBuffer>(commandBufferCount);
for(uint32_t i = 0; i < commandBufferCount; i++)
unwrapped[i] = Unwrap(pCmdBuffers[i]);
ObjDisp(commandBuffer)->CmdExecuteCommands(Unwrap(commandBuffer), commandBufferCount, unwrapped);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(EXEC_CMDS);
Serialise_vkCmdExecuteCommands(localSerialiser, commandBuffer, commandBufferCount, pCmdBuffers);
record->AddChunk(scope.Get());
for(uint32_t i = 0; i < commandBufferCount; i++)
{
VkResourceRecord *execRecord = GetRecord(pCmdBuffers[i]);
if(execRecord->bakedCommands)
{
record->cmdInfo->dirtied.insert(execRecord->bakedCommands->cmdInfo->dirtied.begin(),
execRecord->bakedCommands->cmdInfo->dirtied.end());
record->cmdInfo->boundDescSets.insert(
execRecord->bakedCommands->cmdInfo->boundDescSets.begin(),
execRecord->bakedCommands->cmdInfo->boundDescSets.end());
record->cmdInfo->subcmds.push_back(execRecord);
GetResourceManager()->MergeBarriers(record->cmdInfo->imgbarriers,
execRecord->bakedCommands->cmdInfo->imgbarriers);
}
}
}
}
bool WrappedVulkan::Serialise_vkCmdDebugMarkerBeginEXT(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
const VkDebugMarkerMarkerInfoEXT *pMarker)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(string, name,
pMarker && pMarker->pMarkerName ? string(pMarker->pMarkerName) : "");
float color[4] = {};
if(m_State >= WRITING && pMarker)
memcpy(color, pMarker->color, sizeof(color));
localSerialiser->SerialisePODArray<4>("color", color);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
m_BakedCmdBufferInfo[m_LastCmdBufferID].markerCount++;
if(ObjDisp(commandBuffer)->CmdDebugMarkerBeginEXT)
{
VkDebugMarkerMarkerInfoEXT marker = {};
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
memcpy(marker.color, color, sizeof(color));
marker.pMarkerName = name.c_str();
ObjDisp(commandBuffer)->CmdDebugMarkerBeginEXT(Unwrap(commandBuffer), &marker);
}
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
if(ObjDisp(commandBuffer)->CmdDebugMarkerBeginEXT)
{
VkDebugMarkerMarkerInfoEXT marker = {};
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
memcpy(marker.color, color, sizeof(color));
marker.pMarkerName = name.c_str();
ObjDisp(commandBuffer)->CmdDebugMarkerBeginEXT(Unwrap(commandBuffer), &marker);
}
DrawcallDescription draw;
draw.name = name;
draw.flags |= DrawFlags::PushMarker;
draw.markerColor[0] = RDCCLAMP(color[0], 0.0f, 1.0f);
draw.markerColor[1] = RDCCLAMP(color[1], 0.0f, 1.0f);
draw.markerColor[2] = RDCCLAMP(color[2], 0.0f, 1.0f);
draw.markerColor[3] = RDCCLAMP(color[3], 0.0f, 1.0f);
AddDrawcall(draw, false);
}
return true;
}
void WrappedVulkan::vkCmdDebugMarkerBeginEXT(VkCommandBuffer commandBuffer,
const VkDebugMarkerMarkerInfoEXT *pMarker)
{
if(ObjDisp(commandBuffer)->CmdDebugMarkerBeginEXT)
ObjDisp(commandBuffer)->CmdDebugMarkerBeginEXT(Unwrap(commandBuffer), pMarker);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(BEGIN_EVENT);
Serialise_vkCmdDebugMarkerBeginEXT(localSerialiser, commandBuffer, pMarker);
record->AddChunk(scope.Get());
}
}
bool WrappedVulkan::Serialise_vkCmdDebugMarkerEndEXT(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == READING && !m_BakedCmdBufferInfo[m_LastCmdBufferID].curEvents.empty())
{
DrawcallDescription draw;
draw.name = "API Calls";
draw.flags = DrawFlags::SetMarker | DrawFlags::APICalls;
AddDrawcall(draw, true);
}
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
int &markerCount = m_BakedCmdBufferInfo[m_LastCmdBufferID].markerCount;
markerCount = RDCMAX(0, markerCount - 1);
if(ObjDisp(commandBuffer)->CmdDebugMarkerEndEXT)
ObjDisp(commandBuffer)->CmdDebugMarkerEndEXT(Unwrap(commandBuffer));
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
if(ObjDisp(commandBuffer)->CmdDebugMarkerEndEXT)
ObjDisp(commandBuffer)->CmdDebugMarkerEndEXT(Unwrap(commandBuffer));
// dummy draw that is consumed when this command buffer
// is being in-lined into the call stream
DrawcallDescription draw;
draw.name = "Pop()";
draw.flags = DrawFlags::PopMarker;
AddDrawcall(draw, false);
}
return true;
}
void WrappedVulkan::vkCmdDebugMarkerEndEXT(VkCommandBuffer commandBuffer)
{
if(ObjDisp(commandBuffer)->CmdDebugMarkerEndEXT)
ObjDisp(commandBuffer)->CmdDebugMarkerEndEXT(Unwrap(commandBuffer));
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(END_EVENT);
Serialise_vkCmdDebugMarkerEndEXT(localSerialiser, commandBuffer);
record->AddChunk(scope.Get());
}
}
bool WrappedVulkan::Serialise_vkCmdDebugMarkerInsertEXT(Serialiser *localSerialiser,
VkCommandBuffer commandBuffer,
const VkDebugMarkerMarkerInfoEXT *pMarker)
{
SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(commandBuffer));
SERIALISE_ELEMENT(string, name,
pMarker && pMarker->pMarkerName ? string(pMarker->pMarkerName) : "");
float color[4] = {};
if(m_State >= WRITING && pMarker)
memcpy(color, pMarker->color, sizeof(color));
localSerialiser->SerialisePODArray<4>("color", color);
if(m_State < WRITING)
m_LastCmdBufferID = cmdid;
if(m_State == EXECUTING)
{
if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid))
{
commandBuffer = RerecordCmdBuf(cmdid);
if(ObjDisp(commandBuffer)->CmdDebugMarkerInsertEXT)
{
VkDebugMarkerMarkerInfoEXT marker = {};
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
memcpy(marker.color, color, sizeof(color));
marker.pMarkerName = name.c_str();
ObjDisp(commandBuffer)->CmdDebugMarkerInsertEXT(Unwrap(commandBuffer), &marker);
}
}
}
else if(m_State == READING)
{
commandBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid);
if(ObjDisp(commandBuffer)->CmdDebugMarkerInsertEXT)
{
VkDebugMarkerMarkerInfoEXT marker = {};
marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
memcpy(marker.color, color, sizeof(color));
marker.pMarkerName = name.c_str();
ObjDisp(commandBuffer)->CmdDebugMarkerInsertEXT(Unwrap(commandBuffer), &marker);
}
DrawcallDescription draw;
draw.name = name;
draw.flags |= DrawFlags::SetMarker;
draw.markerColor[0] = RDCCLAMP(color[0], 0.0f, 1.0f);
draw.markerColor[1] = RDCCLAMP(color[1], 0.0f, 1.0f);
draw.markerColor[2] = RDCCLAMP(color[2], 0.0f, 1.0f);
draw.markerColor[3] = RDCCLAMP(color[3], 0.0f, 1.0f);
AddDrawcall(draw, false);
}
return true;
}
void WrappedVulkan::vkCmdDebugMarkerInsertEXT(VkCommandBuffer commandBuffer,
const VkDebugMarkerMarkerInfoEXT *pMarker)
{
if(ObjDisp(commandBuffer)->CmdDebugMarkerInsertEXT)
ObjDisp(commandBuffer)->CmdDebugMarkerInsertEXT(Unwrap(commandBuffer), pMarker);
if(m_State >= WRITING)
{
VkResourceRecord *record = GetRecord(commandBuffer);
CACHE_THREAD_SERIALISER();
SCOPED_SERIALISE_CONTEXT(SET_MARKER);
Serialise_vkCmdDebugMarkerInsertEXT(localSerialiser, commandBuffer, pMarker);
record->AddChunk(scope.Get());
}
}