bool WrappedVulkan::Serialise_vkCmdSetDepthBounds(Serialiser *localSerialiser, VkCommandBuffer cmdBuffer, float minDepthBounds, float maxDepthBounds) { SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer)); SERIALISE_ELEMENT(float, mind, minDepthBounds); SERIALISE_ELEMENT(float, maxd, maxDepthBounds); Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) m_LastCmdBufferID = cmdid; if(m_State == EXECUTING) { if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid)) { cmdBuffer = RerecordCmdBuf(cmdid); ObjDisp(cmdBuffer)->CmdSetDepthBounds(Unwrap(cmdBuffer), mind, maxd); m_RenderState.mindepth = mind; m_RenderState.maxdepth = maxd; } } else if(m_State == READING) { cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid); ObjDisp(cmdBuffer)->CmdSetDepthBounds(Unwrap(cmdBuffer), mind, maxd); } return true; }
bool WrappedVulkan::Serialise_vkCmdSetDepthBias(Serialiser *localSerialiser, VkCommandBuffer cmdBuffer, float depthBias, float depthBiasClamp, float slopeScaledDepthBias) { SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer)); SERIALISE_ELEMENT(float, bias, depthBias); SERIALISE_ELEMENT(float, biasclamp, depthBiasClamp); SERIALISE_ELEMENT(float, slope, slopeScaledDepthBias); Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) m_LastCmdBufferID = cmdid; if(m_State == EXECUTING) { if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid)) { cmdBuffer = RerecordCmdBuf(cmdid); ObjDisp(cmdBuffer)->CmdSetDepthBias(Unwrap(cmdBuffer), bias, biasclamp, slope); m_RenderState.bias.depth = bias; m_RenderState.bias.biasclamp = biasclamp; m_RenderState.bias.slope = slope; } } else if(m_State == READING) { cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid); ObjDisp(cmdBuffer)->CmdSetDepthBias(Unwrap(cmdBuffer), bias, biasclamp, slope); } return true; }
bool WrappedVulkan::Serialise_vkCmdSetScissor(Serialiser *localSerialiser, VkCommandBuffer cmdBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D *pScissors) { SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer)); SERIALISE_ELEMENT(uint32_t, first, firstScissor); SERIALISE_ELEMENT(uint32_t, count, scissorCount); SERIALISE_ELEMENT_ARR(VkRect2D, scissors, pScissors, count); Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) m_LastCmdBufferID = cmdid; if(m_State == EXECUTING) { if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid)) { cmdBuffer = RerecordCmdBuf(cmdid); ObjDisp(cmdBuffer)->CmdSetScissor(Unwrap(cmdBuffer), first, count, scissors); if(m_RenderState.scissors.size() < first + count) m_RenderState.scissors.resize(first + count); for(uint32_t i = 0; i < count; i++) m_RenderState.scissors[first + i] = scissors[i]; } } else if(m_State == READING) { cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid); ObjDisp(cmdBuffer)->CmdSetScissor(Unwrap(cmdBuffer), first, count, scissors); } SAFE_DELETE_ARRAY(scissors); return true; }
bool WrappedVulkan::Serialise_vkCmdSetBlendConstants(Serialiser *localSerialiser, VkCommandBuffer cmdBuffer, const float *blendConst) { SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer)); float blendFactor[4]; if(m_State >= WRITING) { blendFactor[0] = blendConst[0]; blendFactor[1] = blendConst[1]; blendFactor[2] = blendConst[2]; blendFactor[3] = blendConst[3]; } localSerialiser->SerialisePODArray<4>("blendConst", blendFactor); Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) m_LastCmdBufferID = cmdid; if(m_State == EXECUTING) { if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid)) { cmdBuffer = RerecordCmdBuf(cmdid); ObjDisp(cmdBuffer)->CmdSetBlendConstants(Unwrap(cmdBuffer), blendFactor); memcpy(m_RenderState.blendConst, blendFactor, sizeof(blendFactor)); } } else if(m_State == READING) { cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid); ObjDisp(cmdBuffer)->CmdSetBlendConstants(Unwrap(cmdBuffer), blendFactor); } return true; }
bool WrappedVulkan::Serialise_vkCmdResetEvent( Serialiser* localSerialiser, VkCommandBuffer cmdBuffer, VkEvent event, VkPipelineStageFlags stageMask) { SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer)); SERIALISE_ELEMENT(ResourceId, eid, GetResID(event)); SERIALISE_ELEMENT(VkPipelineStageFlagBits, mask, (VkPipelineStageFlagBits)stageMask); Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) m_LastCmdBufferID = cmdid; // see top of this file for current event/fence handling if(m_State == EXECUTING) { event = GetResourceManager()->GetLiveHandle<VkEvent>(eid); if(ShouldRerecordCmd(cmdid) && InRerecordRange()) { cmdBuffer = RerecordCmdBuf(cmdid); //ObjDisp(cmdBuffer)->CmdResetEvent(Unwrap(cmdBuffer), Unwrap(event), mask); } } else if(m_State == READING) { cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid); event = GetResourceManager()->GetLiveHandle<VkEvent>(eid); //ObjDisp(cmdBuffer)->CmdResetEvent(Unwrap(cmdBuffer), Unwrap(event), mask); } return true; }
bool WrappedVulkan::Serialise_vkCmdSetStencilReference(Serialiser *localSerialiser, VkCommandBuffer cmdBuffer, VkStencilFaceFlags faceMask, uint32_t reference) { SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer)); SERIALISE_ELEMENT(VkStencilFaceFlagBits, face, (VkStencilFaceFlagBits)faceMask); SERIALISE_ELEMENT(uint32_t, mask, reference); Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) m_LastCmdBufferID = cmdid; if(m_State == EXECUTING) { if(ShouldRerecordCmd(cmdid) && InRerecordRange(cmdid)) { cmdBuffer = RerecordCmdBuf(cmdid); ObjDisp(cmdBuffer)->CmdSetStencilReference(Unwrap(cmdBuffer), face, mask); if(face & VK_STENCIL_FACE_FRONT_BIT) m_RenderState.front.ref = mask; if(face & VK_STENCIL_FACE_BACK_BIT) m_RenderState.back.ref = mask; } } else if(m_State == READING) { cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid); ObjDisp(cmdBuffer)->CmdSetStencilReference(Unwrap(cmdBuffer), face, mask); } return true; }
bool WrappedVulkan::Serialise_vkCmdWaitEvents( Serialiser* localSerialiser, VkCommandBuffer cmdBuffer, uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers) { SERIALISE_ELEMENT(ResourceId, cmdid, GetResID(cmdBuffer)); SERIALISE_ELEMENT(VkPipelineStageFlagBits, srcStages, (VkPipelineStageFlagBits)srcStageMask); SERIALISE_ELEMENT(VkPipelineStageFlagBits, destStages, (VkPipelineStageFlagBits)dstStageMask); // we don't serialise the original events as we are going to replace this // with our own SERIALISE_ELEMENT(uint32_t, memCount, memoryBarrierCount); SERIALISE_ELEMENT(uint32_t, bufCount, bufferMemoryBarrierCount); SERIALISE_ELEMENT(uint32_t, imgCount, imageMemoryBarrierCount); // we keep the original memory barriers SERIALISE_ELEMENT_ARR(VkMemoryBarrier, memBarriers, pMemoryBarriers, memCount); SERIALISE_ELEMENT_ARR(VkBufferMemoryBarrier, bufMemBarriers, pBufferMemoryBarriers, bufCount); SERIALISE_ELEMENT_ARR(VkImageMemoryBarrier, imgMemBarriers, pImageMemoryBarriers, imgCount); 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); } } } SAFE_DELETE_ARRAY(bufMemBarriers); SAFE_DELETE_ARRAY(imgMemBarriers); // see top of this file for current event/fence handling if(m_State == EXECUTING) { if(ShouldRerecordCmd(cmdid) && InRerecordRange()) { cmdBuffer = RerecordCmdBuf(cmdid); VkEventCreateInfo evInfo = { VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, NULL, 0, }; VkEvent ev = VK_NULL_HANDLE; ObjDisp(cmdBuffer)->CreateEvent(Unwrap(GetDev()), &evInfo, NULL, &ev); // don't wrap this event ObjDisp(cmdBuffer)->ResetEvent(Unwrap(GetDev()), ev); ObjDisp(cmdBuffer)->CmdSetEvent(Unwrap(cmdBuffer), ev, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); ObjDisp(cmdBuffer)->CmdWaitEvents(Unwrap(cmdBuffer), 1, &ev, (VkPipelineStageFlags)srcStages, (VkPipelineStageFlags)destStages, memCount, memBarriers, (uint32_t)bufBarriers.size(), &bufBarriers[0], (uint32_t)imgBarriers.size(), &imgBarriers[0]); // register to clean this event up once we're done replaying this section of the log m_CleanupEvents.push_back(ev); ResourceId cmd = GetResID(RerecordCmdBuf(cmdid)); GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, (uint32_t)imgBarriers.size(), &imgBarriers[0]); } } else if(m_State == READING) { cmdBuffer = GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdid); VkEventCreateInfo evInfo = { VK_STRUCTURE_TYPE_EVENT_CREATE_INFO, NULL, 0, }; VkEvent ev = VK_NULL_HANDLE; ObjDisp(cmdBuffer)->CreateEvent(Unwrap(GetDev()), &evInfo, NULL, &ev); // don't wrap this event ObjDisp(cmdBuffer)->ResetEvent(Unwrap(GetDev()), ev); ObjDisp(cmdBuffer)->CmdSetEvent(Unwrap(cmdBuffer), ev, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); ObjDisp(cmdBuffer)->CmdWaitEvents(Unwrap(cmdBuffer), 1, &ev, (VkPipelineStageFlags)srcStages, (VkPipelineStageFlags)destStages, memCount, memBarriers, (uint32_t)bufBarriers.size(), &bufBarriers[0], (uint32_t)imgBarriers.size(), &imgBarriers[0]); // register to clean this event up once we're done replaying this section of the log m_CleanupEvents.push_back(ev); ResourceId cmd = GetResID(cmdBuffer); GetResourceManager()->RecordBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts, (uint32_t)imgBarriers.size(), &imgBarriers[0]); } SAFE_DELETE_ARRAY(memBarriers); return true; }
bool WrappedVulkan::Serialise_vkQueueSubmit(Serialiser *localSerialiser, VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { SERIALISE_ELEMENT(ResourceId, queueId, GetResID(queue)); SERIALISE_ELEMENT(ResourceId, fenceId, fence != VK_NULL_HANDLE ? GetResID(fence) : ResourceId()); SERIALISE_ELEMENT(uint32_t, numCmds, pSubmits->commandBufferCount); vector<ResourceId> cmdIds; VkCommandBuffer *cmds = m_State >= WRITING ? NULL : new VkCommandBuffer[numCmds]; for(uint32_t i = 0; i < numCmds; i++) { ResourceId bakedId; if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(pSubmits->pCommandBuffers[i]); RDCASSERT(record->bakedCommands); if(record->bakedCommands) bakedId = record->bakedCommands->GetResourceID(); } SERIALISE_ELEMENT(ResourceId, id, bakedId); if(m_State < WRITING) { cmdIds.push_back(id); cmds[i] = id != ResourceId() ? Unwrap(GetResourceManager()->GetLiveHandle<VkCommandBuffer>(id)) : NULL; } } if(m_State < WRITING) { queue = GetResourceManager()->GetLiveHandle<VkQueue>(queueId); if(fenceId != ResourceId()) fence = GetResourceManager()->GetLiveHandle<VkFence>(fenceId); else fence = VK_NULL_HANDLE; } // we don't serialise semaphores at all, just whether we waited on any. // For waiting semaphores, since we don't track state we have to just conservatively // wait for queue idle. Since we do that, there's equally no point in signalling semaphores SERIALISE_ELEMENT(uint32_t, numWaitSems, pSubmits->waitSemaphoreCount); if(m_State < WRITING && numWaitSems > 0) ObjDisp(queue)->QueueWaitIdle(Unwrap(queue)); VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO, NULL, 0, NULL, NULL, // wait semaphores numCmds, cmds, // command buffers 0, NULL, // signal semaphores }; const string desc = localSerialiser->GetDebugStr(); Serialise_DebugMessages(localSerialiser, true); if(m_State == READING) { // don't submit the fence, since we have nothing to wait on it being signalled, and we might // not have it correctly in the unsignalled state. ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); for(uint32_t i = 0; i < numCmds; i++) { ResourceId cmd = GetResourceManager()->GetLiveID(cmdIds[i]); GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts); } AddEvent(QUEUE_SUBMIT, desc); // we're adding multiple events, need to increment ourselves m_RootEventID++; string basename = "vkQueueSubmit(" + ToStr::Get(numCmds) + ")"; for(uint32_t c = 0; c < numCmds; c++) { string name = StringFormat::Fmt("=> %s[%u]: vkBeginCommandBuffer(%s)", basename.c_str(), c, ToStr::Get(cmdIds[c]).c_str()); // add a fake marker FetchDrawcall draw; draw.name = name; draw.flags |= eDraw_SetMarker; AddEvent(SET_MARKER, name); AddDrawcall(draw, true); m_RootEventID++; BakedCmdBufferInfo &cmdBufInfo = m_BakedCmdBufferInfo[cmdIds[c]]; // insert the baked command buffer in-line into this list of notes, assigning new event and // drawIDs InsertDrawsAndRefreshIDs(cmdBufInfo.draw->children); for(size_t e = 0; e < cmdBufInfo.draw->executedCmds.size(); e++) { vector<uint32_t> &submits = m_Partial[Secondary].cmdBufferSubmits[cmdBufInfo.draw->executedCmds[e]]; for(size_t s = 0; s < submits.size(); s++) submits[s] += m_RootEventID; } for(size_t i = 0; i < cmdBufInfo.debugMessages.size(); i++) { m_DebugMessages.push_back(cmdBufInfo.debugMessages[i]); m_DebugMessages.back().eventID += m_RootEventID; } // only primary command buffers can be submitted m_Partial[Primary].cmdBufferSubmits[cmdIds[c]].push_back(m_RootEventID); m_RootEventID += cmdBufInfo.eventCount; m_RootDrawcallID += cmdBufInfo.drawCount; name = StringFormat::Fmt("=> %s[%u]: vkEndCommandBuffer(%s)", basename.c_str(), c, ToStr::Get(cmdIds[c]).c_str()); draw.name = name; AddEvent(SET_MARKER, name); AddDrawcall(draw, true); m_RootEventID++; } // account for the outer loop thinking we've added one event and incrementing, // since we've done all the handling ourselves this will be off by one. m_RootEventID--; } else if(m_State == EXECUTING) { // account for the queue submit event m_RootEventID++; uint32_t startEID = m_RootEventID; // advance m_CurEventID to match the events added when reading for(uint32_t c = 0; c < numCmds; c++) { // 2 extra for the virtual labels around the command buffer m_RootEventID += 2 + m_BakedCmdBufferInfo[cmdIds[c]].eventCount; m_RootDrawcallID += 2 + m_BakedCmdBufferInfo[cmdIds[c]].drawCount; } // same accounting for the outer loop as above m_RootEventID--; if(numCmds == 0) { // do nothing, don't bother with the logic below } else if(m_LastEventID <= startEID) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit no replay %u == %u", m_LastEventID, startEID); #endif } else if(m_DrawcallCallback && m_DrawcallCallback->RecordAllCmds()) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit re-recording from %u", m_RootEventID); #endif vector<VkCommandBuffer> rerecordedCmds; for(uint32_t c = 0; c < numCmds; c++) { VkCommandBuffer cmd = RerecordCmdBuf(cmdIds[c]); ResourceId rerecord = GetResID(cmd); #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit 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); } submitInfo.commandBufferCount = (uint32_t)rerecordedCmds.size(); submitInfo.pCommandBuffers = &rerecordedCmds[0]; // don't submit the fence, since we have nothing to wait on it being signalled, and we might // not have it correctly in the unsignalled state. ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); } else if(m_LastEventID > startEID && m_LastEventID < m_RootEventID) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit partial replay %u < %u", m_LastEventID, m_RootEventID); #endif uint32_t eid = startEID; vector<ResourceId> trimmedCmdIds; vector<VkCommandBuffer> trimmedCmds; for(uint32_t c = 0; c < numCmds; 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[Primary].baseEvent) { ResourceId partial = GetResID(RerecordCmdBuf(cmdIds[c], Primary)); #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit partial replay of %llu at %u, using %llu", cmdIds[c], eid, partial); #endif trimmedCmdIds.push_back(partial); trimmedCmds.push_back(Unwrap(RerecordCmdBuf(cmdIds[c], Primary))); } else if(m_LastEventID >= end) { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit full replay %llu", cmdIds[c]); #endif trimmedCmdIds.push_back(cmdIds[c]); trimmedCmds.push_back( Unwrap(GetResourceManager()->GetLiveHandle<VkCommandBuffer>(cmdIds[c]))); } else { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue not submitting %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; } RDCASSERT(trimmedCmds.size() > 0); submitInfo.commandBufferCount = (uint32_t)trimmedCmds.size(); submitInfo.pCommandBuffers = &trimmedCmds[0]; // don't submit the fence, since we have nothing to wait on it being signalled, and we might // not have it correctly in the unsignalled state. ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); for(uint32_t i = 0; i < trimmedCmdIds.size(); i++) { ResourceId cmd = trimmedCmdIds[i]; GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts); } } else { #ifdef VERBOSE_PARTIAL_REPLAY RDCDEBUG("Queue Submit full replay %u >= %u", m_LastEventID, m_RootEventID); #endif // don't submit the fence, since we have nothing to wait on it being signalled, and we might // not have it correctly in the unsignalled state. ObjDisp(queue)->QueueSubmit(Unwrap(queue), 1, &submitInfo, VK_NULL_HANDLE); for(uint32_t i = 0; i < numCmds; i++) { ResourceId cmd = GetResourceManager()->GetLiveID(cmdIds[i]); GetResourceManager()->ApplyBarriers(m_BakedCmdBufferInfo[cmd].imgbarriers, m_ImageLayouts); } } } SAFE_DELETE_ARRAY(cmds); return true; }