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_vkResetEvent( Serialiser* localSerialiser, VkDevice device, VkEvent event) { SERIALISE_ELEMENT(ResourceId, id, GetResID(device)); SERIALISE_ELEMENT(ResourceId, eid, GetResID(event)); Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) { // see top of this file for current event/fence handling } return true; }
bool WrappedVulkan::Serialise_vkGetEventStatus( Serialiser* localSerialiser, VkDevice device, VkEvent event) { SERIALISE_ELEMENT(ResourceId, id, GetResID(device)); SERIALISE_ELEMENT(ResourceId, eid, GetResID(event)); Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) { device = GetResourceManager()->GetLiveHandle<VkDevice>(id); ObjDisp(device)->DeviceWaitIdle(Unwrap(device)); } 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_vkWaitForFences( Serialiser* localSerialiser, VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout) { SERIALISE_ELEMENT(ResourceId, id, GetResID(device)); SERIALISE_ELEMENT(VkBool32, wait, waitAll); SERIALISE_ELEMENT(uint64_t, tmout, timeout); SERIALISE_ELEMENT(uint32_t, count, fenceCount); Serialise_DebugMessages(localSerialiser, false); vector<VkFence> fences; for(uint32_t i=0; i < count; i++) { ResourceId fence; if(m_State >= WRITING) fence = GetResID(pFences[i]); localSerialiser->Serialise("pFences[]", fence); if(m_State < WRITING && GetResourceManager()->HasLiveResource(fence)) fences.push_back(Unwrap(GetResourceManager()->GetLiveHandle<VkFence>(fence))); } if(m_State < WRITING) { device = GetResourceManager()->GetLiveHandle<VkDevice>(id); ObjDisp(device)->DeviceWaitIdle(Unwrap(device)); } return true; }
bool WrappedVulkan::Serialise_vkResetFences( Serialiser* localSerialiser, VkDevice device, uint32_t fenceCount, const VkFence* pFences) { SERIALISE_ELEMENT(ResourceId, id, GetResID(device)); SERIALISE_ELEMENT(uint32_t, count, fenceCount); Serialise_DebugMessages(localSerialiser, false); vector<VkFence> fences; for(uint32_t i=0; i < count; i++) { ResourceId fence; if(m_State >= WRITING) fence = GetResID(pFences[i]); localSerialiser->Serialise("pFences[]", fence); if(m_State < WRITING && GetResourceManager()->HasLiveResource(fence)) fences.push_back(Unwrap(GetResourceManager()->GetLiveHandle<VkFence>(fence))); } if(m_State < WRITING && !fences.empty()) { // we don't care about fence states ourselves as we cannot record them perfectly and just // do full waitidle flushes. device = GetResourceManager()->GetLiveHandle<VkDevice>(id); // since we don't have anything signalling or waiting on fences, don't bother to reset them // either //ObjDisp(device)->ResetFences(Unwrap(device), (uint32_t)fences.size(), &fences[0]); } 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_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; }
bool WrappedVulkan::Serialise_vkUpdateDescriptorSets(Serialiser *localSerialiser, VkDevice device, uint32_t writeCount, const VkWriteDescriptorSet *pDescriptorWrites, uint32_t copyCount, const VkCopyDescriptorSet *pDescriptorCopies) { SERIALISE_ELEMENT(ResourceId, devId, GetResID(device)); SERIALISE_ELEMENT(bool, writes, writeCount == 1); VkWriteDescriptorSet writeDesc = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, 0}; VkCopyDescriptorSet copyDesc = {VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET, 0}; if(writes) { SERIALISE_ELEMENT(VkWriteDescriptorSet, w, *pDescriptorWrites); writeDesc = w; // take ownership of the arrays (we will delete manually) w.pBufferInfo = NULL; w.pImageInfo = NULL; w.pTexelBufferView = NULL; } else { SERIALISE_ELEMENT(VkCopyDescriptorSet, c, *pDescriptorCopies); copyDesc = c; } Serialise_DebugMessages(localSerialiser, false); if(m_State < WRITING) { device = GetResourceManager()->GetLiveHandle<VkDevice>(devId); if(writes) { // check for validity - if a resource wasn't referenced other than in this update // (ie. the descriptor set was overwritten or never bound), then the write descriptor // will be invalid with some missing handles. It's safe though to just skip this // update as we only get here if it's never used. // if a set was never bound, it will have been omitted and we just drop any writes to it bool valid = (writeDesc.dstSet != VK_NULL_HANDLE); if(!valid) return true; const DescSetLayout &layout = m_CreationInfo.m_DescSetLayout [m_DescriptorSetState[GetResourceManager()->GetNonDispWrapper(writeDesc.dstSet)->id].layout]; const DescSetLayout::Binding *layoutBinding = &layout.bindings[writeDesc.dstBinding]; uint32_t curIdx = writeDesc.dstArrayElement; switch(writeDesc.descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: { for(uint32_t i = 0; i < writeDesc.descriptorCount; i++) valid &= (writeDesc.pImageInfo[i].sampler != VK_NULL_HANDLE); break; } case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { for(uint32_t i = 0; i < writeDesc.descriptorCount; i++, curIdx++) { // allow consecutive descriptor bind updates. See vkUpdateDescriptorSets for more // explanation if(curIdx >= layoutBinding->descriptorCount) { layoutBinding++; curIdx = 0; } valid &= (writeDesc.pImageInfo[i].sampler != VK_NULL_HANDLE) || (layoutBinding->immutableSampler && layoutBinding->immutableSampler[curIdx] != ResourceId()); valid &= (writeDesc.pImageInfo[i].imageView != VK_NULL_HANDLE); } break; } case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: { for(uint32_t i = 0; i < writeDesc.descriptorCount; i++) valid &= (writeDesc.pImageInfo[i].imageView != VK_NULL_HANDLE); break; } case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: { for(uint32_t i = 0; i < writeDesc.descriptorCount; i++) valid &= (writeDesc.pTexelBufferView[i] != VK_NULL_HANDLE); break; } case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { for(uint32_t i = 0; i < writeDesc.descriptorCount; i++) valid &= (writeDesc.pBufferInfo[i].buffer != VK_NULL_HANDLE); break; } default: RDCERR("Unexpected descriptor type %d", writeDesc.descriptorType); } if(valid) { ObjDisp(device)->UpdateDescriptorSets(Unwrap(device), 1, &writeDesc, 0, NULL); // update our local tracking vector<DescriptorSetSlot *> &bindings = m_DescriptorSetState[GetResourceManager()->GetNonDispWrapper(writeDesc.dstSet)->id] .currentBindings; { RDCASSERT(writeDesc.dstBinding < bindings.size()); DescriptorSetSlot **bind = &bindings[writeDesc.dstBinding]; layoutBinding = &layout.bindings[writeDesc.dstBinding]; curIdx = writeDesc.dstArrayElement; if(writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { for(uint32_t d = 0; d < writeDesc.descriptorCount; d++, curIdx++) { // allow consecutive descriptor bind updates. See vkUpdateDescriptorSets for more // explanation if(curIdx >= layoutBinding->descriptorCount) { layoutBinding++; bind++; curIdx = 0; } (*bind)[curIdx].texelBufferView = writeDesc.pTexelBufferView[d]; } } else if(writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || writeDesc.descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) { for(uint32_t d = 0; d < writeDesc.descriptorCount; d++, curIdx++) { // allow consecutive descriptor bind updates. See vkUpdateDescriptorSets for more // explanation if(curIdx >= layoutBinding->descriptorCount) { layoutBinding++; bind++; curIdx = 0; } (*bind)[curIdx].imageInfo = writeDesc.pImageInfo[d]; } } else { for(uint32_t d = 0; d < writeDesc.descriptorCount; d++, curIdx++) { // allow consecutive descriptor bind updates. See vkUpdateDescriptorSets for more // explanation if(curIdx >= layoutBinding->descriptorCount) { layoutBinding++; bind++; curIdx = 0; } (*bind)[curIdx].bufferInfo = writeDesc.pBufferInfo[d]; } } } } } else { // if a set was never bound, it will have been omitted and we just drop any copies to it if(copyDesc.dstSet == VK_NULL_HANDLE || copyDesc.srcSet == VK_NULL_HANDLE) return true; ObjDisp(device)->UpdateDescriptorSets(Unwrap(device), 0, NULL, 1, ©Desc); ResourceId dstSetId = GetResourceManager()->GetNonDispWrapper(copyDesc.dstSet)->id; ResourceId srcSetId = GetResourceManager()->GetNonDispWrapper(copyDesc.srcSet)->id; // update our local tracking vector<DescriptorSetSlot *> &dstbindings = m_DescriptorSetState[dstSetId].currentBindings; vector<DescriptorSetSlot *> &srcbindings = m_DescriptorSetState[srcSetId].currentBindings; { RDCASSERT(copyDesc.dstBinding < dstbindings.size()); RDCASSERT(copyDesc.srcBinding < srcbindings.size()); const DescSetLayout &dstlayout = m_CreationInfo.m_DescSetLayout[m_DescriptorSetState[dstSetId].layout]; const DescSetLayout &srclayout = m_CreationInfo.m_DescSetLayout[m_DescriptorSetState[srcSetId].layout]; const DescSetLayout::Binding *layoutSrcBinding = &srclayout.bindings[copyDesc.srcBinding]; const DescSetLayout::Binding *layoutDstBinding = &dstlayout.bindings[copyDesc.dstBinding]; DescriptorSetSlot **dstbind = &dstbindings[copyDesc.dstBinding]; DescriptorSetSlot **srcbind = &srcbindings[copyDesc.srcBinding]; uint32_t curDstIdx = copyDesc.dstArrayElement; uint32_t curSrcIdx = copyDesc.srcArrayElement; for(uint32_t d = 0; d < copyDesc.descriptorCount; d++, curSrcIdx++, curDstIdx++) { // allow consecutive descriptor bind updates. See vkUpdateDescriptorSets for more // explanation if(curSrcIdx >= layoutSrcBinding->descriptorCount) { layoutSrcBinding++; srcbind++; curSrcIdx = 0; } // src and dst could wrap independently - think copying from // { sampler2D, sampler2D[4], sampler2D } to a { sampler2D[3], sampler2D[3] } // or copying from different starting array elements if(curDstIdx >= layoutDstBinding->descriptorCount) { layoutDstBinding++; dstbind++; curDstIdx = 0; } (*dstbind)[curDstIdx] = (*srcbind)[curSrcIdx]; } } } // delete serialised descriptors array delete[] writeDesc.pBufferInfo; delete[] writeDesc.pImageInfo; delete[] writeDesc.pTexelBufferView; } return true; }