// needs to be separate because it releases internal resources void WrappedVulkan::vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR obj, const VkAllocationCallbacks* pAllocator) { // release internal rendering objects we created for rendering the overlay { SwapchainInfo &info = *GetRecord(obj)->swapInfo; RenderDoc::Inst().RemoveFrameCapturer(LayerDisp(m_Instance), info.wndHandle); VkRenderPass unwrappedRP = Unwrap(info.rp); GetResourceManager()->ReleaseWrappedResource(info.rp, true); ObjDisp(device)->DestroyRenderPass(Unwrap(device), unwrappedRP, NULL); for(size_t i=0; i < info.images.size(); i++) { VkFramebuffer unwrappedFB = Unwrap(info.images[i].fb); VkImageView unwrappedView = Unwrap(info.images[i].view); GetResourceManager()->ReleaseWrappedResource(info.images[i].fb, true); // note, image doesn't have to be destroyed, just untracked GetResourceManager()->ReleaseWrappedResource(info.images[i].im, true); GetResourceManager()->ReleaseWrappedResource(info.images[i].view, true); ObjDisp(device)->DestroyFramebuffer(Unwrap(device), unwrappedFB, NULL); ObjDisp(device)->DestroyImageView(Unwrap(device), unwrappedView, NULL); } } VkSwapchainKHR unwrappedObj = Unwrap(obj); GetResourceManager()->ReleaseWrappedResource(obj, true); ObjDisp(device)->DestroySwapchainKHR(Unwrap(device), unwrappedObj, pAllocator); }
void WrappedVulkan::vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator) { RDCASSERT(m_Instance == instance); if(ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT && m_DbgMsgCallback != VK_NULL_HANDLE) ObjDisp(m_Instance)->DestroyDebugReportCallbackEXT(Unwrap(m_Instance), m_DbgMsgCallback, NULL); // the device should already have been destroyed, assuming that the // application is well behaved. If not, we just leak. ObjDisp(m_Instance)->DestroyInstance(Unwrap(m_Instance), NULL); GetResourceManager()->ReleaseWrappedResource(m_Instance); RenderDoc::Inst().RemoveDeviceFrameCapturer(LayerDisp(m_Instance)); m_Instance = VK_NULL_HANDLE; }
VkResult WrappedVulkan::vkCreateInstance( const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { RDCASSERT(pCreateInfo); // don't support any extensions for this createinfo RDCASSERT(pCreateInfo->pApplicationInfo == NULL || pCreateInfo->pApplicationInfo->pNext == NULL); VkLayerInstanceCreateInfo *layerCreateInfo = (VkLayerInstanceCreateInfo *)pCreateInfo->pNext; // step through the chain of pNext until we get to the link info while(layerCreateInfo && (layerCreateInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO || layerCreateInfo->function != VK_LAYER_LINK_INFO) ) { layerCreateInfo = (VkLayerInstanceCreateInfo *)layerCreateInfo->pNext; } RDCASSERT(layerCreateInfo); PFN_vkGetInstanceProcAddr gpa = layerCreateInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; // move chain on for next layer layerCreateInfo->u.pLayerInfo = layerCreateInfo->u.pLayerInfo->pNext; PFN_vkCreateInstance createFunc = (PFN_vkCreateInstance)gpa(VK_NULL_HANDLE, "vkCreateInstance"); VkInstanceCreateInfo modifiedCreateInfo; modifiedCreateInfo = *pCreateInfo; const char **addedExts = new const char *[modifiedCreateInfo.enabledExtensionCount+1]; for(uint32_t i=0; i < modifiedCreateInfo.enabledExtensionCount; i++) addedExts[i] = modifiedCreateInfo.ppEnabledExtensionNames[i]; if(RenderDoc::Inst().GetCaptureOptions().APIValidation) addedExts[modifiedCreateInfo.enabledExtensionCount++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; modifiedCreateInfo.ppEnabledExtensionNames = addedExts; VkResult ret = createFunc(&modifiedCreateInfo, pAllocator, pInstance); m_Instance = *pInstance; InitInstanceTable(m_Instance, gpa); GetResourceManager()->WrapResource(m_Instance, m_Instance); *pInstance = m_Instance; // should only be called during capture RDCASSERT(m_State >= WRITING); m_InitParams.Set(pCreateInfo, GetResID(m_Instance)); VkResourceRecord *record = GetResourceManager()->AddResourceRecord(m_Instance); record->instDevInfo = new InstanceDeviceInfo(); #undef CheckExt #define CheckExt(name) if(!strcmp(modifiedCreateInfo.ppEnabledExtensionNames[i], STRINGIZE(name))) { record->instDevInfo->name = true; } for(uint32_t i=0; i < modifiedCreateInfo.enabledExtensionCount; i++) { CheckInstanceExts(); } delete[] addedExts; InitInstanceExtensionTables(m_Instance); RenderDoc::Inst().AddDeviceFrameCapturer(LayerDisp(m_Instance), this); m_DbgMsgCallback = VK_NULL_HANDLE; m_PhysicalDevice = VK_NULL_HANDLE; m_Device = VK_NULL_HANDLE; m_QueueFamilyIdx = ~0U; m_Queue = VK_NULL_HANDLE; m_InternalCmds.Reset(); if(RenderDoc::Inst().GetCaptureOptions().APIValidation && ObjDisp(m_Instance)->CreateDebugReportCallbackEXT) { VkDebugReportCallbackCreateInfoEXT debugInfo = {}; debugInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; debugInfo.pNext = NULL; debugInfo.pfnCallback = &DebugCallbackStatic; debugInfo.pUserData = this; debugInfo.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT|VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT|VK_DEBUG_REPORT_ERROR_BIT_EXT; ObjDisp(m_Instance)->CreateDebugReportCallbackEXT(Unwrap(m_Instance), &debugInfo, NULL, &m_DbgMsgCallback); } if(ret == VK_SUCCESS) { RDCLOG("Initialised capture layer in Vulkan instance."); } return ret; }
VkResult WrappedVulkan::vkQueuePresentKHR( VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { if(m_State == WRITING_IDLE) { RenderDoc::Inst().Tick(); GetResourceManager()->FlushPendingDirty(); } m_FrameCounter++; // first present becomes frame #1, this function is at the end of the frame if(pPresentInfo->swapchainCount > 1 && (m_FrameCounter % 100) == 0) { RDCWARN("Presenting multiple swapchains at once - only first will be processed"); } vector<VkSwapchainKHR> unwrappedSwaps; vector<VkSemaphore> unwrappedSems; VkPresentInfoKHR unwrappedInfo = *pPresentInfo; for(uint32_t i=0; i < unwrappedInfo.swapchainCount; i++) unwrappedSwaps.push_back(Unwrap(unwrappedInfo.pSwapchains[i])); for(uint32_t i=0; i < unwrappedInfo.waitSemaphoreCount; i++) unwrappedSems.push_back(Unwrap(unwrappedInfo.pWaitSemaphores[i])); unwrappedInfo.pSwapchains = unwrappedInfo.swapchainCount ? &unwrappedSwaps[0] : NULL; unwrappedInfo.pWaitSemaphores = unwrappedInfo.waitSemaphoreCount ? &unwrappedSems[0] : NULL; // Don't support any extensions for present info RDCASSERT(pPresentInfo->pNext == NULL); VkResourceRecord *swaprecord = GetRecord(pPresentInfo->pSwapchains[0]); RDCASSERT(swaprecord->swapInfo); SwapchainInfo &swapInfo = *swaprecord->swapInfo; bool activeWindow = RenderDoc::Inst().IsActiveWindow(LayerDisp(m_Instance), swapInfo.wndHandle); // need to record which image was last flipped so we can get the correct backbuffer // for a thumbnail in EndFrameCapture swapInfo.lastPresent = pPresentInfo->pImageIndices[0]; m_LastSwap = swaprecord->GetResourceID(); VkImage backbuffer = swapInfo.images[pPresentInfo->pImageIndices[0]].im; if(m_State == WRITING_IDLE) { m_FrameTimes.push_back(m_FrameTimer.GetMilliseconds()); m_TotalTime += m_FrameTimes.back(); m_FrameTimer.Restart(); // update every second if(m_TotalTime > 1000.0) { m_MinFrametime = 10000.0; m_MaxFrametime = 0.0; m_AvgFrametime = 0.0; m_TotalTime = 0.0; for(size_t i=0; i < m_FrameTimes.size(); i++) { m_AvgFrametime += m_FrameTimes[i]; if(m_FrameTimes[i] < m_MinFrametime) m_MinFrametime = m_FrameTimes[i]; if(m_FrameTimes[i] > m_MaxFrametime) m_MaxFrametime = m_FrameTimes[i]; } m_AvgFrametime /= double(m_FrameTimes.size()); m_FrameTimes.clear(); } uint32_t overlay = RenderDoc::Inst().GetOverlayBits(); if(overlay & eRENDERDOC_Overlay_Enabled) { VkRenderPass rp = swapInfo.rp; VkImage im = swapInfo.images[pPresentInfo->pImageIndices[0]].im; VkFramebuffer fb = swapInfo.images[pPresentInfo->pImageIndices[0]].fb; VkLayerDispatchTable *vt = ObjDisp(GetDev()); TextPrintState textstate = { GetNextCmd(), rp, fb, swapInfo.extent.width, swapInfo.extent.height }; VkCommandBufferBeginInfo beginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT }; VkResult vkr = vt->BeginCommandBuffer(Unwrap(textstate.cmd), &beginInfo); RDCASSERTEQUAL(vkr, VK_SUCCESS); VkImageMemoryBarrier bbBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, 0, 0, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(im), { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } }; bbBarrier.srcAccessMask = VK_ACCESS_ALL_READ_BITS; bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; DoPipelineBarrier(textstate.cmd, 1, &bbBarrier); GetDebugManager()->BeginText(textstate); if(activeWindow) { vector<RENDERDOC_InputButton> keys = RenderDoc::Inst().GetCaptureKeys(); string overlayText = "Vulkan. "; for(size_t i=0; i < keys.size(); i++) { if(i > 0) overlayText += ", "; overlayText += ToStr::Get(keys[i]); } if(!keys.empty()) overlayText += " to capture."; if(overlay & eRENDERDOC_Overlay_FrameNumber) { overlayText += StringFormat::Fmt(" Frame: %d.", m_FrameCounter); } if(overlay & eRENDERDOC_Overlay_FrameRate) { overlayText += StringFormat::Fmt(" %.2lf ms (%.2lf .. %.2lf) (%.0lf FPS)", m_AvgFrametime, m_MinFrametime, m_MaxFrametime, 1000.0f/m_AvgFrametime); } float y=0.0f; if(!overlayText.empty()) { GetDebugManager()->RenderText(textstate, 0.0f, y, overlayText.c_str()); y += 1.0f; } if(overlay & eRENDERDOC_Overlay_CaptureList) { GetDebugManager()->RenderText(textstate, 0.0f, y, "%d Captures saved.\n", (uint32_t)m_FrameRecord.size()); y += 1.0f; uint64_t now = Timing::GetUnixTimestamp(); for(size_t i=0; i < m_FrameRecord.size(); i++) { if(now - m_FrameRecord[i].frameInfo.captureTime < 20) { GetDebugManager()->RenderText(textstate, 0.0f, y, "Captured frame %d.\n", m_FrameRecord[i].frameInfo.frameNumber); y += 1.0f; } } } #if !defined(RELEASE) GetDebugManager()->RenderText(textstate, 0.0f, y, "%llu chunks - %.2f MB", Chunk::NumLiveChunks(), float(Chunk::TotalMem())/1024.0f/1024.0f); y += 1.0f; #endif } else { vector<RENDERDOC_InputButton> keys = RenderDoc::Inst().GetFocusKeys(); string str = "Vulkan. Inactive swapchain."; for(size_t i=0; i < keys.size(); i++) { if(i == 0) str += " "; else str += ", "; str += ToStr::Get(keys[i]); } if(!keys.empty()) str += " to cycle between swapchains"; GetDebugManager()->RenderText(textstate, 0.0f, 0.0f, str.c_str()); } GetDebugManager()->EndText(textstate); std::swap(bbBarrier.oldLayout, bbBarrier.newLayout); bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; bbBarrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS; DoPipelineBarrier(textstate.cmd, 1, &bbBarrier); ObjDisp(textstate.cmd)->EndCommandBuffer(Unwrap(textstate.cmd)); SubmitCmds(); FlushQ(); } } VkResult vkr = ObjDisp(queue)->QueuePresentKHR(Unwrap(queue), &unwrappedInfo); if(!activeWindow) return vkr; RenderDoc::Inst().SetCurrentDriver(RDC_Vulkan); // kill any current capture that isn't application defined if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture) RenderDoc::Inst().EndFrameCapture(LayerDisp(m_Instance), swapInfo.wndHandle); if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE) { RenderDoc::Inst().StartFrameCapture(LayerDisp(m_Instance), swapInfo.wndHandle); m_AppControlledCapture = false; } return vkr; }
VkResult WrappedVulkan::vkCreateSwapchainKHR( VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapChain) { VkSwapchainCreateInfoKHR createInfo = *pCreateInfo; // make sure we can readback to get the screenshot createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; createInfo.surface = Unwrap(createInfo.surface); createInfo.oldSwapchain = Unwrap(createInfo.oldSwapchain); VkResult ret = ObjDisp(device)->CreateSwapchainKHR(Unwrap(device), &createInfo, pAllocator, pSwapChain); if(ret == VK_SUCCESS) { ResourceId id = GetResourceManager()->WrapResource(Unwrap(device), *pSwapChain); if(m_State >= WRITING) { Chunk *chunk = NULL; { CACHE_THREAD_SERIALISER(); SCOPED_SERIALISE_CONTEXT(CREATE_SWAP_BUFFER); Serialise_vkCreateSwapchainKHR(localSerialiser, device, pCreateInfo, NULL, pSwapChain); chunk = scope.Get(); } VkResourceRecord *record = GetResourceManager()->AddResourceRecord(*pSwapChain); record->AddChunk(chunk); record->swapInfo = new SwapchainInfo(); SwapchainInfo &swapInfo = *record->swapInfo; // sneaky casting of window handle into record swapInfo.wndHandle = (RENDERDOC_WindowHandle)GetRecord(pCreateInfo->surface); { SCOPED_LOCK(m_SwapLookupLock); m_SwapLookup[swapInfo.wndHandle] = *pSwapChain; } RenderDoc::Inst().AddFrameCapturer(LayerDisp(m_Instance), swapInfo.wndHandle, this); swapInfo.format = pCreateInfo->imageFormat; swapInfo.extent = pCreateInfo->imageExtent; swapInfo.arraySize = pCreateInfo->imageArrayLayers; VkResult vkr = VK_SUCCESS; const VkLayerDispatchTable *vt = ObjDisp(device); { VkAttachmentDescription attDesc = { 0, pCreateInfo->imageFormat, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, }; VkAttachmentReference attRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; VkSubpassDescription sub = { 0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, NULL, // inputs 1, &attRef, // color NULL, // resolve NULL, // depth-stencil 0, NULL, // preserve }; VkRenderPassCreateInfo rpinfo = { VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, NULL, 0, 1, &attDesc, 1, &sub, 0, NULL, // dependencies }; vkr = vt->CreateRenderPass(Unwrap(device), &rpinfo, NULL, &swapInfo.rp); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapInfo.rp); } // serialise out the swap chain images { uint32_t numSwapImages; VkResult ret = vt->GetSwapchainImagesKHR(Unwrap(device), Unwrap(*pSwapChain), &numSwapImages, NULL); RDCASSERTEQUAL(ret, VK_SUCCESS); swapInfo.lastPresent = 0; swapInfo.images.resize(numSwapImages); for(uint32_t i=0; i < numSwapImages; i++) { swapInfo.images[i].im = VK_NULL_HANDLE; swapInfo.images[i].view = VK_NULL_HANDLE; swapInfo.images[i].fb = VK_NULL_HANDLE; } VkImage* images = new VkImage[numSwapImages]; // go through our own function so we assign these images IDs ret = vkGetSwapchainImagesKHR(device, *pSwapChain, &numSwapImages, images); RDCASSERTEQUAL(ret, VK_SUCCESS); for(uint32_t i=0; i < numSwapImages; i++) { SwapchainInfo::SwapImage &swapImInfo = swapInfo.images[i]; // memory doesn't exist for genuine WSI created images swapImInfo.im = images[i]; ResourceId imid = GetResID(images[i]); VkImageSubresourceRange range; range.baseMipLevel = range.baseArrayLayer = 0; range.levelCount = 1; range.layerCount = pCreateInfo->imageArrayLayers; range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; // fill out image info so we track resource state barriers { SCOPED_LOCK(m_ImageLayoutsLock); m_ImageLayouts[imid].subresourceStates.clear(); m_ImageLayouts[imid].subresourceStates.push_back(ImageRegionState(range, UNKNOWN_PREV_IMG_LAYOUT, VK_IMAGE_LAYOUT_UNDEFINED)); } { VkImageViewCreateInfo info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, NULL, 0, Unwrap(images[i]), VK_IMAGE_VIEW_TYPE_2D, pCreateInfo->imageFormat, { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }, }; vkr = vt->CreateImageView(Unwrap(device), &info, NULL, &swapImInfo.view); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapImInfo.view); VkFramebufferCreateInfo fbinfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, NULL, 0, Unwrap(swapInfo.rp), 1, UnwrapPtr(swapImInfo.view), (uint32_t)pCreateInfo->imageExtent.width, (uint32_t)pCreateInfo->imageExtent.height, 1, }; vkr = vt->CreateFramebuffer(Unwrap(device), &fbinfo, NULL, &swapImInfo.fb); RDCASSERTEQUAL(vkr, VK_SUCCESS); GetResourceManager()->WrapResource(Unwrap(device), swapImInfo.fb); } } SAFE_DELETE_ARRAY(images); } } else { GetResourceManager()->AddLiveResource(id, *pSwapChain); } } return ret; }
VkResult WrappedVulkan::vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) { if(m_State == WRITING_IDLE) { RenderDoc::Inst().Tick(); GetResourceManager()->FlushPendingDirty(); } m_FrameCounter++; // first present becomes frame #1, this function is at the end of the frame if(pPresentInfo->swapchainCount > 1 && (m_FrameCounter % 100) == 0) { RDCWARN("Presenting multiple swapchains at once - only first will be processed"); } vector<VkSwapchainKHR> unwrappedSwaps; vector<VkSemaphore> unwrappedSems; VkPresentInfoKHR unwrappedInfo = *pPresentInfo; for(uint32_t i = 0; i < unwrappedInfo.swapchainCount; i++) unwrappedSwaps.push_back(Unwrap(unwrappedInfo.pSwapchains[i])); for(uint32_t i = 0; i < unwrappedInfo.waitSemaphoreCount; i++) unwrappedSems.push_back(Unwrap(unwrappedInfo.pWaitSemaphores[i])); unwrappedInfo.pSwapchains = unwrappedInfo.swapchainCount ? &unwrappedSwaps[0] : NULL; unwrappedInfo.pWaitSemaphores = unwrappedInfo.waitSemaphoreCount ? &unwrappedSems[0] : NULL; // Don't support any extensions for present info RDCASSERT(pPresentInfo->pNext == NULL); VkResourceRecord *swaprecord = GetRecord(pPresentInfo->pSwapchains[0]); RDCASSERT(swaprecord->swapInfo); SwapchainInfo &swapInfo = *swaprecord->swapInfo; bool activeWindow = RenderDoc::Inst().IsActiveWindow(LayerDisp(m_Instance), swapInfo.wndHandle); // need to record which image was last flipped so we can get the correct backbuffer // for a thumbnail in EndFrameCapture swapInfo.lastPresent = pPresentInfo->pImageIndices[0]; m_LastSwap = swaprecord->GetResourceID(); if(m_State == WRITING_IDLE) { uint32_t overlay = RenderDoc::Inst().GetOverlayBits(); if(overlay & eRENDERDOC_Overlay_Enabled) { VkRenderPass rp = swapInfo.rp; VkImage im = swapInfo.images[pPresentInfo->pImageIndices[0]].im; VkFramebuffer fb = swapInfo.images[pPresentInfo->pImageIndices[0]].fb; VkLayerDispatchTable *vt = ObjDisp(GetDev()); TextPrintState textstate = { GetNextCmd(), rp, fb, swapInfo.extent.width, swapInfo.extent.height, swapInfo.format}; VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; VkResult vkr = vt->BeginCommandBuffer(Unwrap(textstate.cmd), &beginInfo); RDCASSERTEQUAL(vkr, VK_SUCCESS); VkImageMemoryBarrier bbBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, 0, 0, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, Unwrap(im), {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; bbBarrier.srcAccessMask = VK_ACCESS_ALL_READ_BITS; bbBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; DoPipelineBarrier(textstate.cmd, 1, &bbBarrier); GetDebugManager()->BeginText(textstate); int flags = activeWindow ? RenderDoc::eOverlay_ActiveWindow : 0; string overlayText = RenderDoc::Inst().GetOverlayText(RDC_Vulkan, m_FrameCounter, flags); if(!overlayText.empty()) GetDebugManager()->RenderText(textstate, 0.0f, 0.0f, overlayText.c_str()); GetDebugManager()->EndText(textstate); std::swap(bbBarrier.oldLayout, bbBarrier.newLayout); bbBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; bbBarrier.dstAccessMask = VK_ACCESS_ALL_READ_BITS; DoPipelineBarrier(textstate.cmd, 1, &bbBarrier); ObjDisp(textstate.cmd)->EndCommandBuffer(Unwrap(textstate.cmd)); SubmitCmds(); FlushQ(); } } VkResult vkr = ObjDisp(queue)->QueuePresentKHR(Unwrap(queue), &unwrappedInfo); if(!activeWindow) return vkr; RenderDoc::Inst().SetCurrentDriver(RDC_Vulkan); // kill any current capture that isn't application defined if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture) RenderDoc::Inst().EndFrameCapture(LayerDisp(m_Instance), swapInfo.wndHandle); if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE) { RenderDoc::Inst().StartFrameCapture(LayerDisp(m_Instance), swapInfo.wndHandle); m_AppControlledCapture = false; } return vkr; }