mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-10-22 12:01:10 +00:00
* 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.
2873 lines
100 KiB
C++
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());
|
|
}
|
|
}
|