VkResult WrappedVulkan::vkWaitForFences( VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout) { SCOPED_DBG_SINK(); VkFence *unwrapped = GetTempArray<VkFence>(fenceCount); for (uint32_t i = 0; i < fenceCount; i++) unwrapped[i] = Unwrap(pFences[i]); VkResult ret = ObjDisp(device)->WaitForFences(Unwrap(device), fenceCount, unwrapped, waitAll, timeout); if(m_State >= WRITING_CAPFRAME) { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(WAIT_FENCES); Serialise_vkWaitForFences(localSerialiser, device, fenceCount, pFences, waitAll, timeout); m_FrameCaptureRecord->AddChunk(scope.Get()); } return ret; }
void WrappedVulkan::vkCmdSetBlendConstants(VkCommandBuffer cmdBuffer, const float *blendConst) { SCOPED_DBG_SINK(); ObjDisp(cmdBuffer)->CmdSetBlendConstants(Unwrap(cmdBuffer), blendConst); if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(cmdBuffer); CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(SET_BLEND_CONST); Serialise_vkCmdSetBlendConstants(localSerialiser, cmdBuffer, blendConst); record->AddChunk(scope.Get()); } }
void WrappedVulkan::vkCmdSetLineWidth(VkCommandBuffer cmdBuffer, float lineWidth) { SCOPED_DBG_SINK(); ObjDisp(cmdBuffer)->CmdSetLineWidth(Unwrap(cmdBuffer), lineWidth); if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(cmdBuffer); CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(SET_LINE_WIDTH); Serialise_vkCmdSetLineWidth(localSerialiser, cmdBuffer, lineWidth); record->AddChunk(scope.Get()); } }
void WrappedVulkan::vkCmdSetViewport(VkCommandBuffer cmdBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport *pViewports) { SCOPED_DBG_SINK(); ObjDisp(cmdBuffer)->CmdSetViewport(Unwrap(cmdBuffer), firstViewport, viewportCount, pViewports); if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(cmdBuffer); CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(SET_VP); Serialise_vkCmdSetViewport(localSerialiser, cmdBuffer, firstViewport, viewportCount, pViewports); record->AddChunk(scope.Get()); } }
void WrappedVulkan::vkCmdSetStencilReference(VkCommandBuffer cmdBuffer, VkStencilFaceFlags faceMask, uint32_t reference) { SCOPED_DBG_SINK(); ObjDisp(cmdBuffer)->CmdSetStencilReference(Unwrap(cmdBuffer), faceMask, reference); if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(cmdBuffer); CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(SET_STENCIL_REF); Serialise_vkCmdSetStencilReference(localSerialiser, cmdBuffer, faceMask, reference); record->AddChunk(scope.Get()); } }
void WrappedVulkan::vkCmdSetDepthBounds(VkCommandBuffer cmdBuffer, float minDepthBounds, float maxDepthBounds) { SCOPED_DBG_SINK(); ObjDisp(cmdBuffer)->CmdSetDepthBounds(Unwrap(cmdBuffer), minDepthBounds, maxDepthBounds); if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(cmdBuffer); CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(SET_DEPTH_BOUNDS); Serialise_vkCmdSetDepthBounds(localSerialiser, cmdBuffer, minDepthBounds, maxDepthBounds); record->AddChunk(scope.Get()); } }
void WrappedVulkan::vkCmdSetScissor(VkCommandBuffer cmdBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D *pScissors) { SCOPED_DBG_SINK(); ObjDisp(cmdBuffer)->CmdSetScissor(Unwrap(cmdBuffer), firstScissor, scissorCount, pScissors); if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(cmdBuffer); CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(SET_SCISSOR); Serialise_vkCmdSetScissor(localSerialiser, cmdBuffer, firstScissor, scissorCount, pScissors); record->AddChunk(scope.Get()); } }
VkResult WrappedVulkan::vkGetEventStatus( VkDevice device, VkEvent event) { SCOPED_DBG_SINK(); VkResult ret = ObjDisp(device)->GetEventStatus(Unwrap(device), Unwrap(event)); if(m_State >= WRITING_CAPFRAME) { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(GET_EVENT_STATUS); Serialise_vkGetEventStatus(localSerialiser, device, event); m_FrameCaptureRecord->AddChunk(scope.Get()); } return ret; }
void WrappedVulkan::vkCmdSetDepthBias(VkCommandBuffer cmdBuffer, float depthBias, float depthBiasClamp, float slopeScaledDepthBias) { SCOPED_DBG_SINK(); ObjDisp(cmdBuffer)->CmdSetDepthBias(Unwrap(cmdBuffer), depthBias, depthBiasClamp, slopeScaledDepthBias); if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(cmdBuffer); CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(SET_DEPTH_BIAS); Serialise_vkCmdSetDepthBias(localSerialiser, cmdBuffer, depthBias, depthBiasClamp, slopeScaledDepthBias); record->AddChunk(scope.Get()); } }
void WrappedVulkan::vkCmdResetEvent( VkCommandBuffer cmdBuffer, VkEvent event, VkPipelineStageFlags stageMask) { SCOPED_DBG_SINK(); ObjDisp(cmdBuffer)->CmdResetEvent(Unwrap(cmdBuffer), Unwrap(event), stageMask); if(m_State >= WRITING) { VkResourceRecord *record = GetRecord(cmdBuffer); CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(CMD_RESET_EVENT); Serialise_vkCmdResetEvent(localSerialiser, cmdBuffer, event, stageMask); record->AddChunk(scope.Get()); record->MarkResourceFrameReferenced(GetResID(event), eFrameRef_Read); } }
VkResult WrappedVulkan::vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { SCOPED_DBG_SINK(); size_t tempmemSize = sizeof(VkSubmitInfo) * submitCount; // need to count how many semaphore and command buffer arrays to allocate for for(uint32_t i = 0; i < submitCount; i++) { tempmemSize += pSubmits[i].commandBufferCount * sizeof(VkCommandBuffer); tempmemSize += pSubmits[i].signalSemaphoreCount * sizeof(VkSemaphore); tempmemSize += pSubmits[i].waitSemaphoreCount * sizeof(VkSemaphore); } byte *memory = GetTempMemory(tempmemSize); VkSubmitInfo *unwrappedSubmits = (VkSubmitInfo *)memory; VkSemaphore *unwrappedWaitSems = (VkSemaphore *)(unwrappedSubmits + submitCount); for(uint32_t i = 0; i < submitCount; i++) { RDCASSERT(pSubmits[i].sType == VK_STRUCTURE_TYPE_SUBMIT_INFO && pSubmits[i].pNext == NULL); unwrappedSubmits[i] = pSubmits[i]; unwrappedSubmits[i].pWaitSemaphores = unwrappedSubmits[i].waitSemaphoreCount ? unwrappedWaitSems : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].waitSemaphoreCount; o++) unwrappedWaitSems[o] = Unwrap(pSubmits[i].pWaitSemaphores[o]); unwrappedWaitSems += unwrappedSubmits[i].waitSemaphoreCount; VkCommandBuffer *unwrappedCommandBuffers = (VkCommandBuffer *)unwrappedWaitSems; unwrappedSubmits[i].pCommandBuffers = unwrappedSubmits[i].commandBufferCount ? unwrappedCommandBuffers : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].commandBufferCount; o++) unwrappedCommandBuffers[o] = Unwrap(pSubmits[i].pCommandBuffers[o]); unwrappedCommandBuffers += unwrappedSubmits[i].commandBufferCount; VkSemaphore *unwrappedSignalSems = (VkSemaphore *)unwrappedCommandBuffers; unwrappedSubmits[i].pSignalSemaphores = unwrappedSubmits[i].signalSemaphoreCount ? unwrappedSignalSems : NULL; for(uint32_t o = 0; o < unwrappedSubmits[i].signalSemaphoreCount; o++) unwrappedSignalSems[o] = Unwrap(pSubmits[i].pSignalSemaphores[o]); } VkResult ret = ObjDisp(queue)->QueueSubmit(Unwrap(queue), submitCount, unwrappedSubmits, Unwrap(fence)); bool capframe = false; set<ResourceId> refdIDs; for(uint32_t s = 0; s < submitCount; s++) { for(uint32_t i = 0; i < pSubmits[s].commandBufferCount; i++) { ResourceId cmd = GetResID(pSubmits[s].pCommandBuffers[i]); VkResourceRecord *record = GetRecord(pSubmits[s].pCommandBuffers[i]); { SCOPED_LOCK(m_ImageLayoutsLock); GetResourceManager()->ApplyBarriers(record->bakedCommands->cmdInfo->imgbarriers, m_ImageLayouts); } // need to lock the whole section of code, not just the check on // m_State, as we also need to make sure we don't check the state, // start marking dirty resources then while we're doing so the // state becomes capframe. // the next sections where we mark resources referenced and add // the submit chunk to the frame record don't have to be protected. // Only the decision of whether we're inframe or not, and marking // dirty. { SCOPED_LOCK(m_CapTransitionLock); if(m_State == WRITING_CAPFRAME) { for(auto it = record->bakedCommands->cmdInfo->dirtied.begin(); it != record->bakedCommands->cmdInfo->dirtied.end(); ++it) GetResourceManager()->MarkPendingDirty(*it); capframe = true; } else { for(auto it = record->bakedCommands->cmdInfo->dirtied.begin(); it != record->bakedCommands->cmdInfo->dirtied.end(); ++it) GetResourceManager()->MarkDirtyResource(*it); } } if(capframe) { // for each bound descriptor set, mark it referenced as well as all resources currently // bound to it for(auto it = record->bakedCommands->cmdInfo->boundDescSets.begin(); it != record->bakedCommands->cmdInfo->boundDescSets.end(); ++it) { GetResourceManager()->MarkResourceFrameReferenced(GetResID(*it), eFrameRef_Read); VkResourceRecord *setrecord = GetRecord(*it); for(auto refit = setrecord->descInfo->bindFrameRefs.begin(); refit != setrecord->descInfo->bindFrameRefs.end(); ++refit) { refdIDs.insert(refit->first); GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second); if(refit->second.first & DescriptorSetData::SPARSE_REF_BIT) { VkResourceRecord *sparserecord = GetResourceManager()->GetResourceRecord(refit->first); GetResourceManager()->MarkSparseMapReferenced(sparserecord->sparseInfo); } } } for(auto it = record->bakedCommands->cmdInfo->sparse.begin(); it != record->bakedCommands->cmdInfo->sparse.end(); ++it) GetResourceManager()->MarkSparseMapReferenced(*it); // pull in frame refs from this baked command buffer record->bakedCommands->AddResourceReferences(GetResourceManager()); record->bakedCommands->AddReferencedIDs(refdIDs); // ref the parent command buffer by itself, this will pull in the cmd buffer pool GetResourceManager()->MarkResourceFrameReferenced(record->GetResourceID(), eFrameRef_Read); for(size_t sub = 0; sub < record->bakedCommands->cmdInfo->subcmds.size(); sub++) { record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddResourceReferences( GetResourceManager()); record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddReferencedIDs(refdIDs); GetResourceManager()->MarkResourceFrameReferenced( record->bakedCommands->cmdInfo->subcmds[sub]->GetResourceID(), eFrameRef_Read); record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands->AddRef(); } GetResourceManager()->MarkResourceFrameReferenced(GetResID(queue), eFrameRef_Read); if(fence != VK_NULL_HANDLE) GetResourceManager()->MarkResourceFrameReferenced(GetResID(fence), eFrameRef_Read); { SCOPED_LOCK(m_CmdBufferRecordsLock); m_CmdBufferRecords.push_back(record->bakedCommands); for(size_t sub = 0; sub < record->bakedCommands->cmdInfo->subcmds.size(); sub++) m_CmdBufferRecords.push_back(record->bakedCommands->cmdInfo->subcmds[sub]->bakedCommands); } record->bakedCommands->AddRef(); } record->cmdInfo->dirtied.clear(); } } if(capframe) { vector<VkResourceRecord *> maps; { SCOPED_LOCK(m_CoherentMapsLock); maps = m_CoherentMaps; } for(auto it = maps.begin(); it != maps.end(); ++it) { VkResourceRecord *record = *it; MemMapState &state = *record->memMapState; // potential persistent map if(state.mapCoherent && state.mappedPtr && !state.mapFlushed) { // only need to flush memory that could affect this submitted batch of work if(refdIDs.find(record->GetResourceID()) == refdIDs.end()) { RDCDEBUG("Map of memory %llu not referenced in this queue - not flushing", record->GetResourceID()); continue; } size_t diffStart = 0, diffEnd = 0; bool found = true; // enabled as this is necessary for programs with very large coherent mappings // (> 1GB) as otherwise more than a couple of vkQueueSubmit calls leads to vast // memory allocation. There might still be bugs lurking in here though #if 1 // this causes vkFlushMappedMemoryRanges call to allocate and copy to refData // from serialised buffer. We want to copy *precisely* the serialised data, // otherwise there is a gap in time between serialising out a snapshot of // the buffer and whenever we then copy into the ref data, e.g. below. // during this time, data could be written to the buffer and it won't have // been caught in the serialised snapshot, and if it doesn't change then // it *also* won't be caught in any future FindDiffRange() calls. // // Likewise once refData is allocated, the call below will also update it // with the data serialised out for the same reason. // // Note: it's still possible that data is being written to by the // application while it's being serialised out in the snapshot below. That // is OK, since the application is responsible for ensuring it's not writing // data that would be needed by the GPU in this submit. As long as the // refdata we use for future use is identical to what was serialised, we // shouldn't miss anything state.needRefData = true; // if we have a previous set of data, compare. // otherwise just serialise it all if(state.refData) found = FindDiffRange((byte *)state.mappedPtr, state.refData, (size_t)state.mapSize, diffStart, diffEnd); else #endif diffEnd = (size_t)state.mapSize; if(found) { // MULTIDEVICE should find the device for this queue. // MULTIDEVICE only want to flush maps associated with this queue VkDevice dev = GetDev(); { RDCLOG("Persistent map flush forced for %llu (%llu -> %llu)", record->GetResourceID(), (uint64_t)diffStart, (uint64_t)diffEnd); VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, NULL, (VkDeviceMemory)(uint64_t)record->Resource, state.mapOffset + diffStart, diffEnd - diffStart}; vkFlushMappedMemoryRanges(dev, 1, &range); state.mapFlushed = false; } GetResourceManager()->MarkPendingDirty(record->GetResourceID()); } else { RDCDEBUG("Persistent map flush not needed for %llu", record->GetResourceID()); } } } { CACHE_THREAD_SERIALISER(); for(uint32_t s = 0; s < submitCount; s++) { SCOPED_SERIALISE_CONTEXT(QUEUE_SUBMIT); Serialise_vkQueueSubmit(localSerialiser, queue, 1, &pSubmits[s], fence); m_FrameCaptureRecord->AddChunk(scope.Get()); for(uint32_t sem = 0; sem < pSubmits[s].waitSemaphoreCount; sem++) GetResourceManager()->MarkResourceFrameReferenced( GetResID(pSubmits[s].pWaitSemaphores[sem]), eFrameRef_Read); for(uint32_t sem = 0; sem < pSubmits[s].signalSemaphoreCount; sem++) GetResourceManager()->MarkResourceFrameReferenced( GetResID(pSubmits[s].pSignalSemaphores[sem]), eFrameRef_Read); } } } return ret; }
void WrappedVulkan::vkUpdateDescriptorSets(VkDevice device, uint32_t writeCount, const VkWriteDescriptorSet *pDescriptorWrites, uint32_t copyCount, const VkCopyDescriptorSet *pDescriptorCopies) { SCOPED_DBG_SINK(); { // need to count up number of descriptor infos, to be able to alloc enough space uint32_t numInfos = 0; for(uint32_t i = 0; i < writeCount; i++) numInfos += pDescriptorWrites[i].descriptorCount; byte *memory = GetTempMemory(sizeof(VkDescriptorBufferInfo) * numInfos + sizeof(VkWriteDescriptorSet) * writeCount + sizeof(VkCopyDescriptorSet) * copyCount); RDCCOMPILE_ASSERT(sizeof(VkDescriptorBufferInfo) >= sizeof(VkDescriptorImageInfo), "Descriptor structs sizes are unexpected, ensure largest size is used"); VkWriteDescriptorSet *unwrappedWrites = (VkWriteDescriptorSet *)memory; VkCopyDescriptorSet *unwrappedCopies = (VkCopyDescriptorSet *)(unwrappedWrites + writeCount); VkDescriptorBufferInfo *nextDescriptors = (VkDescriptorBufferInfo *)(unwrappedCopies + copyCount); for(uint32_t i = 0; i < writeCount; i++) { unwrappedWrites[i] = pDescriptorWrites[i]; unwrappedWrites[i].dstSet = Unwrap(unwrappedWrites[i].dstSet); VkDescriptorBufferInfo *bufInfos = nextDescriptors; VkDescriptorImageInfo *imInfos = (VkDescriptorImageInfo *)bufInfos; VkBufferView *bufViews = (VkBufferView *)bufInfos; nextDescriptors += pDescriptorWrites[i].descriptorCount; RDCCOMPILE_ASSERT(sizeof(VkDescriptorBufferInfo) >= sizeof(VkDescriptorImageInfo), "Structure sizes mean not enough space is allocated for write data"); RDCCOMPILE_ASSERT(sizeof(VkDescriptorBufferInfo) >= sizeof(VkBufferView), "Structure sizes mean not enough space is allocated for write data"); // unwrap and assign the appropriate array if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { unwrappedWrites[i].pTexelBufferView = (VkBufferView *)bufInfos; for(uint32_t j = 0; j < pDescriptorWrites[i].descriptorCount; j++) bufViews[j] = Unwrap(pDescriptorWrites[i].pTexelBufferView[j]); } else if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) { bool hasSampler = (pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); bool hasImage = (pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT); unwrappedWrites[i].pImageInfo = (VkDescriptorImageInfo *)bufInfos; for(uint32_t j = 0; j < pDescriptorWrites[i].descriptorCount; j++) { if(hasImage) imInfos[j].imageView = Unwrap(pDescriptorWrites[i].pImageInfo[j].imageView); if(hasSampler) imInfos[j].sampler = Unwrap(pDescriptorWrites[i].pImageInfo[j].sampler); imInfos[j].imageLayout = pDescriptorWrites[i].pImageInfo[j].imageLayout; } } else { unwrappedWrites[i].pBufferInfo = bufInfos; for(uint32_t j = 0; j < pDescriptorWrites[i].descriptorCount; j++) { bufInfos[j].buffer = Unwrap(pDescriptorWrites[i].pBufferInfo[j].buffer); bufInfos[j].offset = pDescriptorWrites[i].pBufferInfo[j].offset; bufInfos[j].range = pDescriptorWrites[i].pBufferInfo[j].range; } } } for(uint32_t i = 0; i < copyCount; i++) { unwrappedCopies[i] = pDescriptorCopies[i]; unwrappedCopies[i].dstSet = Unwrap(unwrappedCopies[i].dstSet); unwrappedCopies[i].srcSet = Unwrap(unwrappedCopies[i].srcSet); } ObjDisp(device)->UpdateDescriptorSets(Unwrap(device), writeCount, unwrappedWrites, copyCount, unwrappedCopies); } bool capframe = false; { SCOPED_LOCK(m_CapTransitionLock); capframe = (m_State == WRITING_CAPFRAME); } if(capframe) { // don't have to mark referenced any of the resources pointed to by the descriptor set - that's // handled // on queue submission by marking ref'd all the current bindings of the sets referenced by the // cmd buffer for(uint32_t i = 0; i < writeCount; i++) { { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(UPDATE_DESC_SET); Serialise_vkUpdateDescriptorSets(localSerialiser, device, 1, &pDescriptorWrites[i], 0, NULL); m_FrameCaptureRecord->AddChunk(scope.Get()); } // as long as descriptor sets are forced to have initial states, we don't have to mark them // ref'd for // write here. The reason being that as long as we only mark them as ref'd when they're // actually bound, // we can safely skip the ref here and it means any descriptor set updates of descriptor sets // that are // never used in the frame can be ignored. // GetResourceManager()->MarkResourceFrameReferenced(GetResID(pDescriptorWrites[i].destSet), // eFrameRef_Write); } for(uint32_t i = 0; i < copyCount; i++) { { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(UPDATE_DESC_SET); Serialise_vkUpdateDescriptorSets(localSerialiser, device, 0, NULL, 1, &pDescriptorCopies[i]); m_FrameCaptureRecord->AddChunk(scope.Get()); } // Like writes we don't have to mark the written descriptor set as used because unless it's // bound somewhere // we don't need it anyway. However we DO have to mark the source set as used because it // doesn't have to // be bound to still be needed (think about if the dest set is bound somewhere after this copy // - what refs // the source set?). // At the same time as ref'ing the source set, we must ref all of its resources (via the // bindFrameRefs). // We just ref all rather than looking at only the copied sets to keep things simple. // This does mean a slightly conservative ref'ing if the dest set doesn't end up getting // bound, but we only // do this during frame capture so it's not too bad. // GetResourceManager()->MarkResourceFrameReferenced(GetResID(pDescriptorCopies[i].destSet), // eFrameRef_Write); { GetResourceManager()->MarkResourceFrameReferenced(GetResID(pDescriptorCopies[i].srcSet), eFrameRef_Read); VkResourceRecord *setrecord = GetRecord(pDescriptorCopies[i].srcSet); for(auto refit = setrecord->descInfo->bindFrameRefs.begin(); refit != setrecord->descInfo->bindFrameRefs.end(); ++refit) { GetResourceManager()->MarkResourceFrameReferenced(refit->first, refit->second.second); if(refit->second.first & DescriptorSetData::SPARSE_REF_BIT) { VkResourceRecord *record = GetResourceManager()->GetResourceRecord(refit->first); GetResourceManager()->MarkSparseMapReferenced(record->sparseInfo); } } } } } // need to track descriptor set contents whether capframing or idle if(m_State >= WRITING) { for(uint32_t i = 0; i < writeCount; i++) { VkResourceRecord *record = GetRecord(pDescriptorWrites[i].dstSet); RDCASSERT(record->descInfo && record->descInfo->layout); const DescSetLayout &layout = *record->descInfo->layout; RDCASSERT(pDescriptorWrites[i].dstBinding < record->descInfo->descBindings.size()); DescriptorSetSlot **binding = &record->descInfo->descBindings[pDescriptorWrites[i].dstBinding]; const DescSetLayout::Binding *layoutBinding = &layout.bindings[pDescriptorWrites[i].dstBinding]; FrameRefType ref = eFrameRef_Write; switch(layoutBinding->descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: ref = eFrameRef_Read; break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: ref = eFrameRef_Write; break; default: RDCERR("Unexpected descriptor type"); } // We need to handle the cases where these bindings are stale: // ie. image handle 0xf00baa is allocated // bound into a descriptor set // image is released // descriptor set is bound but this image is never used by shader etc. // // worst case, a new image or something has been added with this handle - // in this case we end up ref'ing an image that isn't actually used. // Worst worst case, we ref an image as write when actually it's not, but // this is likewise not a serious problem, and rather difficult to solve // (would need to version handles somehow, but don't have enough bits // to do that reliably). // // This is handled by RemoveBindFrameRef silently dropping id == ResourceId() // start at the dstArrayElement uint32_t curIdx = pDescriptorWrites[i].dstArrayElement; for(uint32_t d = 0; d < pDescriptorWrites[i].descriptorCount; d++, curIdx++) { // roll over onto the next binding, on the assumption that it is the same // type and there is indeed a next binding at all. See spec language: // // If the dstBinding has fewer than descriptorCount array elements remaining starting from // dstArrayElement, then the remainder will be used to update the subsequent binding - // dstBinding+1 starting at array element zero. This behavior applies recursively, with the // update affecting consecutive bindings as needed to update all descriptorCount // descriptors. All consecutive bindings updated via a single VkWriteDescriptorSet structure // must have identical descriptorType and stageFlags, and must all either use immutable // samplers or must all not use immutable samplers. if(curIdx >= layoutBinding->descriptorCount) { layoutBinding++; binding++; curIdx = 0; } DescriptorSetSlot &bind = (*binding)[curIdx]; if(bind.texelBufferView != VK_NULL_HANDLE) { record->RemoveBindFrameRef(GetResID(bind.texelBufferView)); VkResourceRecord *viewRecord = GetRecord(bind.texelBufferView); if(viewRecord && viewRecord->baseResource != ResourceId()) record->RemoveBindFrameRef(viewRecord->baseResource); } if(bind.imageInfo.imageView != VK_NULL_HANDLE) { record->RemoveBindFrameRef(GetResID(bind.imageInfo.imageView)); VkResourceRecord *viewRecord = GetRecord(bind.imageInfo.imageView); if(viewRecord) { record->RemoveBindFrameRef(viewRecord->baseResource); if(viewRecord->baseResourceMem != ResourceId()) record->RemoveBindFrameRef(viewRecord->baseResourceMem); } } if(bind.imageInfo.sampler != VK_NULL_HANDLE) { record->RemoveBindFrameRef(GetResID(bind.imageInfo.sampler)); } if(bind.bufferInfo.buffer != VK_NULL_HANDLE) { record->RemoveBindFrameRef(GetResID(bind.bufferInfo.buffer)); VkResourceRecord *bufRecord = GetRecord(bind.bufferInfo.buffer); if(bufRecord && bufRecord->baseResource != ResourceId()) record->RemoveBindFrameRef(bufRecord->baseResource); } // NULL everything out now so that we don't accidentally reference an object // that was removed already bind.texelBufferView = VK_NULL_HANDLE; bind.bufferInfo.buffer = VK_NULL_HANDLE; bind.imageInfo.imageView = VK_NULL_HANDLE; bind.imageInfo.sampler = VK_NULL_HANDLE; if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) { bind.texelBufferView = pDescriptorWrites[i].pTexelBufferView[d]; } else if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT) { bind.imageInfo = pDescriptorWrites[i].pImageInfo[d]; // ignore descriptors not part of the write, by NULL'ing out those members // as they might not even point to a valid object if(pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) bind.imageInfo.imageView = VK_NULL_HANDLE; else if(pDescriptorWrites[i].descriptorType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) bind.imageInfo.sampler = VK_NULL_HANDLE; } else { bind.bufferInfo = pDescriptorWrites[i].pBufferInfo[d]; } if(bind.texelBufferView != VK_NULL_HANDLE) { record->AddBindFrameRef(GetResID(bind.texelBufferView), eFrameRef_Read, GetRecord(bind.texelBufferView)->sparseInfo != NULL); if(GetRecord(bind.texelBufferView)->baseResource != ResourceId()) record->AddBindFrameRef(GetRecord(bind.texelBufferView)->baseResource, ref); } if(bind.imageInfo.imageView != VK_NULL_HANDLE) { record->AddBindFrameRef(GetResID(bind.imageInfo.imageView), eFrameRef_Read, GetRecord(bind.imageInfo.imageView)->sparseInfo != NULL); record->AddBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResource, ref); if(GetRecord(bind.imageInfo.imageView)->baseResourceMem != ResourceId()) record->AddBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResourceMem, eFrameRef_Read); } if(bind.imageInfo.sampler != VK_NULL_HANDLE) { record->AddBindFrameRef(GetResID(bind.imageInfo.sampler), eFrameRef_Read); } if(bind.bufferInfo.buffer != VK_NULL_HANDLE) { record->AddBindFrameRef(GetResID(bind.bufferInfo.buffer), eFrameRef_Read, GetRecord(bind.bufferInfo.buffer)->sparseInfo != NULL); if(GetRecord(bind.bufferInfo.buffer)->baseResource != ResourceId()) record->AddBindFrameRef(GetRecord(bind.bufferInfo.buffer)->baseResource, ref); } } } // this is almost identical to the above loop, except that instead of sourcing the descriptors // from the writedescriptor struct, we source it from our stored bindings on the source // descrpitor set for(uint32_t i = 0; i < copyCount; i++) { VkResourceRecord *dstrecord = GetRecord(pDescriptorCopies[i].dstSet); RDCASSERT(dstrecord->descInfo && dstrecord->descInfo->layout); const DescSetLayout &dstlayout = *dstrecord->descInfo->layout; VkResourceRecord *srcrecord = GetRecord(pDescriptorCopies[i].srcSet); RDCASSERT(srcrecord->descInfo && srcrecord->descInfo->layout); const DescSetLayout &srclayout = *srcrecord->descInfo->layout; RDCASSERT(pDescriptorCopies[i].dstBinding < dstrecord->descInfo->descBindings.size()); RDCASSERT(pDescriptorCopies[i].srcBinding < srcrecord->descInfo->descBindings.size()); DescriptorSetSlot **dstbinding = &dstrecord->descInfo->descBindings[pDescriptorCopies[i].dstBinding]; DescriptorSetSlot **srcbinding = &srcrecord->descInfo->descBindings[pDescriptorCopies[i].srcBinding]; const DescSetLayout::Binding *dstlayoutBinding = &dstlayout.bindings[pDescriptorCopies[i].dstBinding]; const DescSetLayout::Binding *srclayoutBinding = &srclayout.bindings[pDescriptorCopies[i].srcBinding]; FrameRefType ref = eFrameRef_Write; switch(dstlayoutBinding->descriptorType) { case VK_DESCRIPTOR_TYPE_SAMPLER: case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: ref = eFrameRef_Read; break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: ref = eFrameRef_Write; break; default: RDCERR("Unexpected descriptor type"); } // allow roll-over between consecutive bindings. See above in the plain write case for more // explanation uint32_t curSrcIdx = pDescriptorCopies[i].srcArrayElement; uint32_t curDstIdx = pDescriptorCopies[i].dstArrayElement; for(uint32_t d = 0; d < pDescriptorCopies[i].descriptorCount; d++, curSrcIdx++, curDstIdx++) { if(curDstIdx >= dstlayoutBinding->descriptorCount) { dstlayoutBinding++; dstbinding++; curDstIdx = 0; } // dst and src indices must roll-over independently if(curSrcIdx >= srclayoutBinding->descriptorCount) { srclayoutBinding++; srcbinding++; curSrcIdx = 0; } DescriptorSetSlot &bind = (*dstbinding)[curDstIdx]; if(bind.texelBufferView != VK_NULL_HANDLE) { dstrecord->RemoveBindFrameRef(GetResID(bind.texelBufferView)); if(GetRecord(bind.texelBufferView)->baseResource != ResourceId()) dstrecord->RemoveBindFrameRef(GetRecord(bind.texelBufferView)->baseResource); } if(bind.imageInfo.imageView != VK_NULL_HANDLE) { dstrecord->RemoveBindFrameRef(GetResID(bind.imageInfo.imageView)); dstrecord->RemoveBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResource); if(GetRecord(bind.imageInfo.imageView)->baseResourceMem != ResourceId()) dstrecord->RemoveBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResourceMem); } if(bind.imageInfo.sampler != VK_NULL_HANDLE) { dstrecord->RemoveBindFrameRef(GetResID(bind.imageInfo.sampler)); } if(bind.bufferInfo.buffer != VK_NULL_HANDLE) { dstrecord->RemoveBindFrameRef(GetResID(bind.bufferInfo.buffer)); if(GetRecord(bind.bufferInfo.buffer)->baseResource != ResourceId()) dstrecord->RemoveBindFrameRef(GetRecord(bind.bufferInfo.buffer)->baseResource); } bind = (*srcbinding)[curSrcIdx]; if(bind.texelBufferView != VK_NULL_HANDLE) { dstrecord->AddBindFrameRef(GetResID(bind.texelBufferView), eFrameRef_Read, GetRecord(bind.texelBufferView)->sparseInfo != NULL); if(GetRecord(bind.texelBufferView)->baseResource != ResourceId()) dstrecord->AddBindFrameRef(GetRecord(bind.texelBufferView)->baseResource, ref); } if(bind.imageInfo.imageView != VK_NULL_HANDLE) { dstrecord->AddBindFrameRef(GetResID(bind.imageInfo.imageView), eFrameRef_Read, GetRecord(bind.imageInfo.imageView)->sparseInfo != NULL); dstrecord->AddBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResource, ref); if(GetRecord(bind.imageInfo.imageView)->baseResourceMem != ResourceId()) dstrecord->AddBindFrameRef(GetRecord(bind.imageInfo.imageView)->baseResourceMem, eFrameRef_Read); } if(bind.imageInfo.sampler != VK_NULL_HANDLE) { dstrecord->AddBindFrameRef(GetResID(bind.imageInfo.sampler), ref); } if(bind.bufferInfo.buffer != VK_NULL_HANDLE) { dstrecord->AddBindFrameRef(GetResID(bind.bufferInfo.buffer), eFrameRef_Read, GetRecord(bind.bufferInfo.buffer)->sparseInfo != NULL); if(GetRecord(bind.bufferInfo.buffer)->baseResource != ResourceId()) dstrecord->AddBindFrameRef(GetRecord(bind.bufferInfo.buffer)->baseResource, ref); } } } } }