VkResult VKAPI_CALL vkAllocateCommandBuffers( VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers) { dispatch_key key = get_dispatch_key(device); layer_data *my_data = get_my_data_ptr(key, layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; VkResult result; startReadObject(my_data, device); startWriteObject(my_data, pAllocateInfo->commandPool); result = pTable->AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers); finishReadObject(my_data, device); finishWriteObject(my_data, pAllocateInfo->commandPool); // Record mapping from command buffer to command pool if (VK_SUCCESS == result) { for (int index=0;index<pAllocateInfo->commandBufferCount;index++) { loader_platform_thread_lock_mutex(&threadingLock); command_pool_map[pCommandBuffers[index]] = pAllocateInfo->commandPool; loader_platform_thread_unlock_mutex(&threadingLock); } } return result; }
/* Various dispatchable objects will use the same underlying dispatch table if * they * are created from that "parent" object. Thus use pointer to dispatch table * as the key to these table maps. * Instance -> PhysicalDevice * Device -> CommandBuffer or Queue * If use the object themselves as key to map then implies Create entrypoints * have to be intercepted * and a new key inserted into map */ VkLayerDebugMarkerDispatchTable *initDebugMarkerTable(VkDevice device) { VkLayerDebugMarkerDispatchTable *pDebugMarkerTable; assert(device); VkLayerDispatchTable *pDisp = *(VkLayerDispatchTable **)device; std::unordered_map<void *, VkLayerDebugMarkerDispatchTable *>::const_iterator it = tableDebugMarkerMap.find((void *)pDisp); if (it == tableDebugMarkerMap.end()) { pDebugMarkerTable = new VkLayerDebugMarkerDispatchTable; tableDebugMarkerMap[(void *)pDisp] = pDebugMarkerTable; } else { return it->second; } pDebugMarkerTable->CmdDbgMarkerBegin = (PFN_vkCmdDbgMarkerBegin)pDisp->GetDeviceProcAddr( device, "vkCmdDbgMarkerBegin"); pDebugMarkerTable->CmdDbgMarkerEnd = (PFN_vkCmdDbgMarkerEnd)pDisp->GetDeviceProcAddr(device, "vkCmdDbgMarkerEnd"); pDebugMarkerTable->DbgSetObjectTag = (PFN_vkDbgSetObjectTag)pDisp->GetDeviceProcAddr(device, "vkDbgSetObjectTag"); pDebugMarkerTable->DbgSetObjectName = (PFN_vkDbgSetObjectName)pDisp->GetDeviceProcAddr(device, "vkDbgSetObjectName"); return pDebugMarkerTable; }
VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev, const char *funcName) { #define ADD_HOOK(fn) \ if (!strncmp(#fn, funcName, sizeof(#fn))) \ return (PFN_vkVoidFunction)fn ADD_HOOK(vkGetDeviceProcAddr); ADD_HOOK(vkDestroyDevice); ADD_HOOK(vkCreateSwapchainKHR); ADD_HOOK(vkGetSwapchainImagesKHR); ADD_HOOK(vkQueuePresentKHR); ADD_HOOK(vkDestroySwapchainKHR); ADD_HOOK(vkQueueSubmit); #undef ADD_HOOK if (dev == NULL) return NULL; layer_data *dev_data; dev_data = get_my_data_ptr(get_dispatch_key(dev), layer_data_map); VkLayerDispatchTable *pTable = dev_data->device_dispatch_table; if (pTable->GetDeviceProcAddr == NULL) return NULL; return pTable->GetDeviceProcAddr(dev, funcName); }
void VKAPI_CALL vkFreeCommandBuffers( VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers) { dispatch_key key = get_dispatch_key(device); layer_data *my_data = get_my_data_ptr(key, layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; const bool lockCommandPool = false; // pool is already directly locked startReadObject(my_data, device); startWriteObject(my_data, commandPool); for (int index=0;index<commandBufferCount;index++) { startWriteObject(my_data, pCommandBuffers[index], lockCommandPool); } pTable->FreeCommandBuffers(device,commandPool,commandBufferCount,pCommandBuffers); finishReadObject(my_data, device); finishWriteObject(my_data, commandPool); for (int index=0;index<commandBufferCount;index++) { finishWriteObject(my_data, pCommandBuffers[index], lockCommandPool); loader_platform_thread_lock_mutex(&threadingLock); command_pool_map.erase(pCommandBuffers[index]); loader_platform_thread_unlock_mutex(&threadingLock); } }
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo *pSubmits, VkFence fence) { layer_data *my_data = GetLayerDataPtr(get_dispatch_key(queue), layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; my_data->cmdBuffersThisFrame += submitCount; // XXX WRONG return pTable->QueueSubmit(queue, submitCount, pSubmits, fence); }
VK_LAYER_EXPORT VKAPI_ATTR void VKAPI_CALL vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) { dispatch_key key = get_dispatch_key(device); layer_data *my_data = GetLayerDataPtr(key, layer_data_map); my_data->Cleanup(); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; pTable->DeviceWaitIdle(device); pTable->DestroyDevice(device, pAllocator); delete pTable; layer_data_map.erase(key); }
void WsiImageData::Cleanup(VkDevice dev) { layer_data *my_data = get_my_data_ptr(get_dispatch_key(dev), layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; // XXX: needs device data // pTable->FreeCommandBuffers(dev, cmd, nullptr); pTable->DestroyFramebuffer(dev, framebuffer, nullptr); pTable->DestroyImageView(dev, view, nullptr); pTable->DestroyBuffer(dev, vertexBuffer, nullptr); pTable->FreeMemory(dev, vertexBufferMemory, nullptr); }
void SwapChainData::Cleanup(VkDevice dev) { layer_data *my_data = GetLayerDataPtr(get_dispatch_key(dev), layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; for (uint32_t i = 0; i < presentableImages.size(); i++) { presentableImages[i]->Cleanup(dev); delete presentableImages[i]; } presentableImages.clear(); pTable->DestroyPipeline(dev, pipeline, nullptr); pTable->DestroyRenderPass(dev, render_pass, nullptr); }
VKAPI_ATTR void VKAPI_CALL DestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) { DeviceMapStruct *devMap = get_dev_info(device); assert(devMap); VkLayerDispatchTable *pDisp = devMap->device_dispatch_table; pDisp->DestroyDevice(device, pAllocator); loader_platform_thread_lock_mutex(&globalLock); delete pDisp; delete devMap; deviceMap.erase(device); loader_platform_thread_unlock_mutex(&globalLock); }
VKAPI_ATTR VkResult VKAPI_CALL GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t *pCount, VkImage *pSwapchainImages) { DeviceMapStruct *devMap = get_dev_info(device); assert(devMap); VkLayerDispatchTable *pDisp = devMap->device_dispatch_table; VkResult result = pDisp->GetSwapchainImagesKHR(device, swapchain, pCount, pSwapchainImages); // Save the swapchain images in a map if we are taking screenshots loader_platform_thread_lock_mutex(&globalLock); if (screenshotEnvQueried && screenshotFrames.empty()) { // No screenshots in the list to take loader_platform_thread_unlock_mutex(&globalLock); return result; } if (result == VK_SUCCESS && pSwapchainImages && !swapchainMap.empty() && swapchainMap.find(swapchain) != swapchainMap.end()) { unsigned i; for (i = 0; i < *pCount; i++) { // Create a mapping for an image to a device, image extent, and // format if (imageMap[pSwapchainImages[i]] == NULL) { ImageMapStruct *imageMapElem = new ImageMapStruct; imageMap[pSwapchainImages[i]] = imageMapElem; } imageMap[pSwapchainImages[i]]->device = swapchainMap[swapchain]->device; imageMap[pSwapchainImages[i]]->imageExtent = swapchainMap[swapchain]->imageExtent; imageMap[pSwapchainImages[i]]->format = swapchainMap[swapchain]->format; } // Add list of images to swapchain to image map SwapchainMapStruct *swapchainMapElem = swapchainMap[swapchain]; if (i >= 1 && swapchainMapElem) { VkImage *imageList = new VkImage[i]; swapchainMapElem->imageList = imageList; for (unsigned j = 0; j < i; j++) { swapchainMapElem->imageList[j] = pSwapchainImages[j]; } } } loader_platform_thread_unlock_mutex(&globalLock); return result; }
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice device, const char *funcName) { PFN_vkVoidFunction addr; layer_data *dev_data; assert(device); addr = layer_intercept_proc(funcName); if (addr) return addr; dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map); VkLayerDispatchTable *pTable = dev_data->device_dispatch_table; if (pTable->GetDeviceProcAddr == NULL) return NULL; return pTable->GetDeviceProcAddr(device, funcName); }
VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice device, const char *funcName) { PFN_vkVoidFunction addr; layer_data *dev_data; if (device == VK_NULL_HANDLE) { return NULL; } addr = layer_intercept_proc(funcName); if (addr) return addr; dev_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map); VkLayerDispatchTable *pTable = dev_data->device_dispatch_table; if (pTable->GetDeviceProcAddr == NULL) return NULL; return pTable->GetDeviceProcAddr(device, funcName); }
VKAPI_ATTR VkResult VKAPI_CALL CreateSwapchainKHR( VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) { DeviceMapStruct *devMap = get_dev_info(device); assert(devMap); VkLayerDispatchTable *pDisp = devMap->device_dispatch_table; // This layer does an image copy later on, and the copy command expects the // transfer src bit to be on. VkSwapchainCreateInfoKHR myCreateInfo = *pCreateInfo; myCreateInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; VkResult result = pDisp->CreateSwapchainKHR(device, &myCreateInfo, pAllocator, pSwapchain); // Save the swapchain in a map of we are taking screenshots. loader_platform_thread_lock_mutex(&globalLock); if (screenshotEnvQueried && screenshotFrames.empty()) { // No screenshots in the list to take loader_platform_thread_unlock_mutex(&globalLock); return result; } if (result == VK_SUCCESS) { // Create a mapping for a swapchain to a device, image extent, and // format SwapchainMapStruct *swapchainMapElem = new SwapchainMapStruct; swapchainMapElem->device = device; swapchainMapElem->imageExtent = pCreateInfo->imageExtent; swapchainMapElem->format = pCreateInfo->imageFormat; swapchainMap.insert(make_pair(*pSwapchain, swapchainMapElem)); // Create a mapping for the swapchain object into the dispatch table // TODO is this needed? screenshot_device_table_map.emplace((void // *)pSwapchain, pTable); } loader_platform_thread_unlock_mutex(&globalLock); return result; }
VKAPI_ATTR VkResult VKAPI_CALL CreateCommandPool( VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool) { DeviceMapStruct *devMap = get_dev_info(device); assert(devMap); VkLayerDispatchTable *pDisp = devMap->device_dispatch_table; VkResult result = pDisp->CreateCommandPool(device, pCreateInfo, pAllocator, pCommandPool); // Save the command pool on a map if we are taking screenshots. loader_platform_thread_lock_mutex(&globalLock); if (screenshotEnvQueried && screenshotFrames.empty()) { // No screenshots in the list to take loader_platform_thread_unlock_mutex(&globalLock); return result; } // Create a mapping from a device to a commandPool devMap->commandPool = *pCommandPool; loader_platform_thread_unlock_mutex(&globalLock); return result; }
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice dev, const char *funcName) { PFN_vkVoidFunction proc = intercept_core_device_command(funcName); if (proc) return proc; if (dev == NULL) { return NULL; } proc = intercept_khr_swapchain_command(funcName, dev); if (proc) return proc; DeviceMapStruct *devMap = get_dev_info(dev); assert(devMap); VkLayerDispatchTable *pDisp = devMap->device_dispatch_table; if (pDisp->GetDeviceProcAddr == NULL) return NULL; return pDisp->GetDeviceProcAddr(dev, funcName); }
VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev, const char *funcName) { if (!strcmp(funcName, "vkGetDeviceProcAddr")) return (PFN_vkVoidFunction)vkGetDeviceProcAddr; if (!strcmp(funcName, "vkDestroyDevice")) return (PFN_vkVoidFunction)vkDestroyDevice; if (!strcmp(funcName, "vkGetDeviceQueue")) return (PFN_vkVoidFunction)vkGetDeviceQueue; if (!strcmp(funcName, "vkCreateRenderPass")) return (PFN_vkVoidFunction)vkCreateRenderPass; if (!strcmp(funcName, "vkCreateCommandPool")) return (PFN_vkVoidFunction)vkCreateCommandPool; if (!strcmp(funcName, "vkDestroyCommandPool")) return (PFN_vkVoidFunction)vkDestroyCommandPool; if (!strcmp(funcName, "vkResetCommandPool")) return (PFN_vkVoidFunction)vkResetCommandPool; if (!strcmp(funcName, "vkAllocateCommandBuffers")) return (PFN_vkVoidFunction)vkAllocateCommandBuffers; if (!strcmp(funcName, "vkFreeCommandBuffers")) return (PFN_vkVoidFunction)vkFreeCommandBuffers; if (!strcmp(funcName, "vkBeginCommandBuffer")) return (PFN_vkVoidFunction)vkBeginCommandBuffer; if (!strcmp(funcName, "vkCmdUpdateBuffer")) return (PFN_vkVoidFunction)vkCmdUpdateBuffer; if (!strcmp(funcName, "vkUpdateDescriptorSets")) return (PFN_vkVoidFunction)vkUpdateDescriptorSets; if (!strcmp(funcName, "vkCmdFillBuffer")) return (PFN_vkVoidFunction)vkCmdFillBuffer; if (dev == NULL) return NULL; layer_data *my_data = get_my_data_ptr(get_dispatch_key(dev), layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; { if (pTable->GetDeviceProcAddr == NULL) return NULL; return pTable->GetDeviceProcAddr(dev, funcName); } }
VKAPI_ATTR VkResult VKAPI_CALL AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo, VkCommandBuffer *pCommandBuffers) { dispatch_key key = get_dispatch_key(device); layer_data *my_data = get_my_data_ptr(key, layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; VkResult result; startReadObject(my_data, device); startWriteObject(my_data, pAllocateInfo->commandPool); result = pTable->AllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers); finishReadObject(my_data, device); finishWriteObject(my_data, pAllocateInfo->commandPool); // Record mapping from command buffer to command pool if (VK_SUCCESS == result) { for (uint32_t index = 0; index < pAllocateInfo->commandBufferCount; index++) { std::lock_guard<std::mutex> lock(global_lock); command_pool_map[pCommandBuffers[index]] = pAllocateInfo->commandPool; } } return result; }
VKAPI_ATTR void VKAPI_CALL GetDeviceQueue(VkDevice device, uint32_t queueNodeIndex, uint32_t queueIndex, VkQueue *pQueue) { DeviceMapStruct *devMap = get_dev_info(device); assert(devMap); VkLayerDispatchTable *pDisp = devMap->device_dispatch_table; pDisp->GetDeviceQueue(device, queueNodeIndex, queueIndex, pQueue); // Save the device queue in a map if we are taking screenshots. loader_platform_thread_lock_mutex(&globalLock); if (screenshotEnvQueried && screenshotFrames.empty()) { // No screenshots in the list to take loader_platform_thread_unlock_mutex(&globalLock); return; } VkDevice que = static_cast<VkDevice>(static_cast<void *>(*pQueue)); deviceMap.emplace(que, devMap); // Create a mapping from a device to a queue devMap->queue = *pQueue; loader_platform_thread_unlock_mutex(&globalLock); }
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapChain, uint32_t *pCount, VkImage *pImages) { layer_data *my_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; VkResult result = my_data->pfnGetSwapchainImagesKHR(device, swapChain, pCount, pImages); VkResult U_ASSERT_ONLY err; /* GetSwapChainImagesWSI may be called without an images buffer, in which * case it * just returns the count to the caller. We're only interested in acting on * the * /actual/ fetch of the images. */ if (pImages) { auto data = (*my_data->swapChains)[swapChain]; for (uint32_t i = 0; i < *pCount; i++) { /* Create attachment view for each */ VkImageViewCreateInfo ivci; ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; ivci.pNext = nullptr; ivci.format = data->format; ivci.components.r = VK_COMPONENT_SWIZZLE_R; ivci.components.g = VK_COMPONENT_SWIZZLE_G; ivci.components.b = VK_COMPONENT_SWIZZLE_B; ivci.components.a = VK_COMPONENT_SWIZZLE_A; ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ivci.subresourceRange.baseMipLevel = 0; ivci.subresourceRange.levelCount = 1; ivci.subresourceRange.baseArrayLayer = 0; ivci.subresourceRange.layerCount = 1; ivci.image = pImages[i]; ivci.flags = 0; VkImageView v; pTable->CreateImageView(device, &ivci, nullptr, &v); /* Create framebuffer for each */ VkFramebufferCreateInfo fci; fci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; fci.pNext = nullptr; fci.flags = 0; fci.renderPass = data->render_pass; fci.attachmentCount = 1; fci.pAttachments = &v; fci.width = data->width; fci.height = data->height; fci.layers = 1; VkFramebuffer fb; pTable->CreateFramebuffer(device, &fci, nullptr, &fb); /* Create command buffer for each */ VkCommandBufferAllocateInfo cbai; cbai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cbai.pNext = nullptr; cbai.commandPool = my_data->pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1; VkCommandBuffer cmd; pTable->AllocateCommandBuffers(device, &cbai, &cmd); /* We have just created a dispatchable object, but the dispatch * table has not been placed in the object yet. * When a "normal" application creates a command buffer, * the dispatch table is installed by the top-level binding * (trampoline.c). * But here, we have to do it ourselves. */ if (!my_data->pfn_dev_init) { *((const void **)cmd) = *(void **)device; } else { err = my_data->pfn_dev_init(device, (void *)cmd); assert(!err); } /* Create vertex buffer */ VkBufferCreateInfo bci; memset(&bci, 0, sizeof(bci)); bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bci.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; bci.size = sizeof(vertex) * MAX_TEXT_VERTICES; VkBuffer buf; err = pTable->CreateBuffer(device, &bci, nullptr, &buf); assert(!err); VkMemoryRequirements mem_reqs; pTable->GetBufferMemoryRequirements(device, buf, &mem_reqs); assert(!err); VkMemoryAllocateInfo mem_alloc; memset(&mem_alloc, 0, sizeof(mem_alloc)); mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_alloc.allocationSize = mem_reqs.size; mem_alloc.memoryTypeIndex = choose_memory_type( my_data->gpu, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); VkDeviceMemory mem; err = pTable->AllocateMemory(device, &mem_alloc, nullptr, &mem); assert(!err); err = pTable->BindBufferMemory(device, buf, mem, 0); assert(!err); auto imageData = new WsiImageData; imageData->image = pImages[i]; imageData->view = v; imageData->framebuffer = fb; imageData->cmd = cmd; imageData->vertexBuffer = buf; imageData->vertexBufferMemory = mem; imageData->numVertices = 0; imageData->vertexBufferSize = mem_alloc.allocationSize; data->presentableImages.push_back(imageData); } } return result; }
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; }
// Save an image to a PPM image file. // // This function issues commands to copy/convert the swapchain image // from whatever compatible format the swapchain image uses // to a single format (VK_FORMAT_R8G8B8A8_UNORM) so that the converted // result can be easily written to a PPM file. // // Error handling: If there is a problem, this function should silently // fail without affecting the Present operation going on in the caller. // The numerous debug asserts are to catch programming errors and are not // expected to assert. Recovery and clean up are implemented for image memory // allocation failures. // (TODO) It would be nice to pass any failure info to DebugReport or something. static void writePPM(const char *filename, VkImage image1) { VkResult err; bool pass; // Bail immediately if we can't find the image. if (imageMap.empty() || imageMap.find(image1) == imageMap.end()) return; // Collect object info from maps. This info is generally recorded // by the other functions hooked in this layer. VkDevice device = imageMap[image1]->device; VkPhysicalDevice physicalDevice = deviceMap[device]->physicalDevice; VkInstance instance = physDeviceMap[physicalDevice]->instance; VkQueue queue = deviceMap[device]->queue; DeviceMapStruct *devMap = get_dev_info(device); if (NULL == devMap) { assert(0); return; } VkLayerDispatchTable *pTableDevice = devMap->device_dispatch_table; VkLayerDispatchTable *pTableQueue = get_dev_info(static_cast<VkDevice>(static_cast<void *>(queue))) ->device_dispatch_table; VkLayerInstanceDispatchTable *pInstanceTable; pInstanceTable = instance_dispatch_table(instance); // Gather incoming image info and check image format for compatibility with // the target format. // This function supports both 24-bit and 32-bit swapchain images. VkFormat const target32bitFormat = VK_FORMAT_R8G8B8A8_UNORM; VkFormat const target24bitFormat = VK_FORMAT_R8G8B8_UNORM; uint32_t const width = imageMap[image1]->imageExtent.width; uint32_t const height = imageMap[image1]->imageExtent.height; VkFormat const format = imageMap[image1]->format; uint32_t const numChannels = vk_format_get_channel_count(format); if ((vk_format_get_compatibility_class(target24bitFormat) != vk_format_get_compatibility_class(format)) && (vk_format_get_compatibility_class(target32bitFormat) != vk_format_get_compatibility_class(format))) { assert(0); return; } if ((3 != numChannels) && (4 != numChannels)) { assert(0); return; } // General Approach // // The idea here is to copy/convert the swapchain image into another image // that can be mapped and read by the CPU to produce a PPM file. // The image must be untiled and converted to a specific format for easy // parsing. The memory for the final image must be host-visible. // Note that in Vulkan, a BLIT operation must be used to perform a format // conversion. // // Devices vary in their ability to blit to/from linear and optimal tiling. // So we must query the device properties to get this information. // // If the device cannot BLIT to a LINEAR image, then the operation must be // done in two steps: // 1) BLIT the swapchain image (image1) to a temp image (image2) that is // created with TILING_OPTIMAL. // 2) COPY image2 to another temp image (image3) that is created with // TILING_LINEAR. // 3) Map image 3 and write the PPM file. // // If the device can BLIT to a LINEAR image, then: // 1) BLIT the swapchain image (image1) to a temp image (image2) that is // created with TILING_LINEAR. // 2) Map image 2 and write the PPM file. // // There seems to be no way to tell if the swapchain image (image1) is tiled // or not. We therefore assume that the BLIT operation can always read from // both linear and optimal tiled (swapchain) images. // There is therefore no point in looking at the BLIT_SRC properties. // // There is also the optimization where the incoming and target formats are // the same. In this case, just do a COPY. VkFormatProperties targetFormatProps; pInstanceTable->GetPhysicalDeviceFormatProperties( physicalDevice, (3 == numChannels) ? target24bitFormat : target32bitFormat, &targetFormatProps); bool need2steps = false; bool copyOnly = false; if ((target24bitFormat == format) || (target32bitFormat == format)) { copyOnly = true; } else { bool const bltLinear = targetFormatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT ? true : false; bool const bltOptimal = targetFormatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT ? true : false; if (!bltLinear && !bltOptimal) { // Cannot blit to either target tiling type. It should be pretty // unlikely to have a device that cannot blit to either type. // But punt by just doing a copy and possibly have the wrong // colors. This should be quite rare. copyOnly = true; } else if (!bltLinear && bltOptimal) { // Cannot blit to a linear target but can blt to optimal, so copy // after blit is needed. need2steps = true; } // Else bltLinear is available and only 1 step is needed. } // Put resources that need to be cleaned up in a struct with a destructor // so that things get cleaned up when this function is exited. WritePPMCleanupData data = {}; data.device = device; data.pTableDevice = pTableDevice; // Set up the image creation info for both the blit and copy images, in case // both are needed. VkImageCreateInfo imgCreateInfo2 = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, NULL, 0, VK_IMAGE_TYPE_2D, VK_FORMAT_R8G8B8A8_UNORM, {width, height, 1}, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, NULL, VK_IMAGE_LAYOUT_UNDEFINED, }; VkImageCreateInfo imgCreateInfo3 = imgCreateInfo2; // If we need both images, set up image2 to be read/write and tiled. if (need2steps) { imgCreateInfo2.tiling = VK_IMAGE_TILING_OPTIMAL; imgCreateInfo2.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; } VkMemoryAllocateInfo memAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, NULL, 0, // allocationSize, queried later 0 // memoryTypeIndex, queried later }; VkMemoryRequirements memRequirements; VkPhysicalDeviceMemoryProperties memoryProperties; // Create image2 and allocate its memory. It could be the intermediate or // final image. err = pTableDevice->CreateImage(device, &imgCreateInfo2, NULL, &data.image2); assert(!err); if (VK_SUCCESS != err) return; pTableDevice->GetImageMemoryRequirements(device, data.image2, &memRequirements); memAllocInfo.allocationSize = memRequirements.size; pInstanceTable->GetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); pass = memory_type_from_properties( &memoryProperties, memRequirements.memoryTypeBits, need2steps ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); assert(pass); err = pTableDevice->AllocateMemory(device, &memAllocInfo, NULL, &data.mem2); assert(!err); if (VK_SUCCESS != err) return; err = pTableQueue->BindImageMemory(device, data.image2, data.mem2, 0); assert(!err); if (VK_SUCCESS != err) return; // Create image3 and allocate its memory, if needed. if (need2steps) { err = pTableDevice->CreateImage(device, &imgCreateInfo3, NULL, &data.image3); assert(!err); if (VK_SUCCESS != err) return; pTableDevice->GetImageMemoryRequirements(device, data.image3, &memRequirements); memAllocInfo.allocationSize = memRequirements.size; pInstanceTable->GetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); pass = memory_type_from_properties( &memoryProperties, memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memAllocInfo.memoryTypeIndex); assert(pass); err = pTableDevice->AllocateMemory(device, &memAllocInfo, NULL, &data.mem3); assert(!err); if (VK_SUCCESS != err) return; err = pTableQueue->BindImageMemory(device, data.image3, data.mem3, 0); assert(!err); if (VK_SUCCESS != err) return; } // Set up the command buffer. We get a command buffer from a pool we saved // in a hooked function, which would be the application's pool. const VkCommandBufferAllocateInfo allocCommandBufferInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, NULL, deviceMap[device]->commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1}; data.commandPool = deviceMap[device]->commandPool; err = pTableDevice->AllocateCommandBuffers(device, &allocCommandBufferInfo, &data.commandBuffer); assert(!err); if (VK_SUCCESS != err) return; VkDevice cmdBuf = static_cast<VkDevice>(static_cast<void *>(data.commandBuffer)); deviceMap.emplace(cmdBuf, devMap); VkLayerDispatchTable *pTableCommandBuffer; pTableCommandBuffer = get_dev_info(cmdBuf)->device_dispatch_table; // We have just created a dispatchable object, but the dispatch table has // not been placed in the object yet. When a "normal" application creates // a command buffer, the dispatch table is installed by the top-level api // binding (trampoline.c). But here, we have to do it ourselves. if (!devMap->pfn_dev_init) { *((const void **)data.commandBuffer) = *(void **)device; } else { err = devMap->pfn_dev_init(device, (void *)data.commandBuffer); assert(!err); } const VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, }; err = pTableCommandBuffer->BeginCommandBuffer(data.commandBuffer, &commandBufferBeginInfo); assert(!err); // This barrier is used to transition from/to present Layout VkImageMemoryBarrier presentMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, image1, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; // This barrier is used to transition from a newly-created layout to a blt // or copy destination layout. VkImageMemoryBarrier destMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, data.image2, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; // This barrier is used to transition a dest layout to general layout. VkImageMemoryBarrier generalMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, NULL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, data.image2, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; VkPipelineStageFlags srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT; VkPipelineStageFlags dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT; // The source image needs to be transitioned from present to transfer // source. pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &presentMemoryBarrier); // image2 needs to be transitioned from its undefined state to transfer // destination. pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &destMemoryBarrier); const VkImageCopy imageCopyRegion = {{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, {0, 0, 0}, {width, height, 1}}; if (copyOnly) { pTableCommandBuffer->CmdCopyImage( data.commandBuffer, image1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion); } else { VkImageBlit imageBlitRegion = {}; imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.srcSubresource.baseArrayLayer = 0; imageBlitRegion.srcSubresource.layerCount = 1; imageBlitRegion.srcSubresource.mipLevel = 0; imageBlitRegion.srcOffsets[1].x = width; imageBlitRegion.srcOffsets[1].y = height; imageBlitRegion.srcOffsets[1].z = 1; imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.dstSubresource.baseArrayLayer = 0; imageBlitRegion.dstSubresource.layerCount = 1; imageBlitRegion.dstSubresource.mipLevel = 0; imageBlitRegion.dstOffsets[1].x = width; imageBlitRegion.dstOffsets[1].y = height; imageBlitRegion.dstOffsets[1].z = 1; pTableCommandBuffer->CmdBlitImage( data.commandBuffer, image1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlitRegion, VK_FILTER_NEAREST); if (need2steps) { // image 3 needs to be transitioned from its undefined state to a // transfer destination. destMemoryBarrier.image = data.image3; pTableCommandBuffer->CmdPipelineBarrier( data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &destMemoryBarrier); // Transition image2 so that it can be read for the upcoming copy to // image 3. destMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; destMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; destMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; destMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; destMemoryBarrier.image = data.image2; pTableCommandBuffer->CmdPipelineBarrier( data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &destMemoryBarrier); // This step essentially untiles the image. pTableCommandBuffer->CmdCopyImage( data.commandBuffer, data.image2, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image3, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion); generalMemoryBarrier.image = data.image3; } } // The destination needs to be transitioned from the optimal copy format to // the format we can read with the CPU. pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &generalMemoryBarrier); // Restore the swap chain image layout to what it was before. // This may not be strictly needed, but it is generally good to restore // things to original state. presentMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; presentMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; presentMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; presentMemoryBarrier.dstAccessMask = 0; pTableCommandBuffer->CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &presentMemoryBarrier); err = pTableCommandBuffer->EndCommandBuffer(data.commandBuffer); assert(!err); VkFence nullFence = {VK_NULL_HANDLE}; VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = NULL; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = NULL; submitInfo.pWaitDstStageMask = NULL; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &data.commandBuffer; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = NULL; err = pTableQueue->QueueSubmit(queue, 1, &submitInfo, nullFence); assert(!err); err = pTableQueue->QueueWaitIdle(queue); assert(!err); err = pTableDevice->DeviceWaitIdle(device); assert(!err); // Map the final image so that the CPU can read it. const VkImageSubresource sr = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0}; VkSubresourceLayout srLayout; const char *ptr; if (!need2steps) { pTableDevice->GetImageSubresourceLayout(device, data.image2, &sr, &srLayout); err = pTableDevice->MapMemory(device, data.mem2, 0, VK_WHOLE_SIZE, 0, (void **)&ptr); assert(!err); if (VK_SUCCESS != err) return; data.mem2mapped = true; } else { pTableDevice->GetImageSubresourceLayout(device, data.image3, &sr, &srLayout); err = pTableDevice->MapMemory(device, data.mem3, 0, VK_WHOLE_SIZE, 0, (void **)&ptr); assert(!err); if (VK_SUCCESS != err) return; data.mem3mapped = true; } // Write the data to a PPM file. ofstream file(filename, ios::binary); file << "P6\n"; file << width << "\n"; file << height << "\n"; file << 255 << "\n"; ptr += srLayout.offset; if (3 == numChannels) { for (uint32_t y = 0; y < height; y++) { file.write(ptr, 3 * width); ptr += srLayout.rowPitch; } } else if (4 == numChannels) { for (uint32_t y = 0; y < height; y++) { const unsigned int *row = (const unsigned int *)ptr; for (uint32_t x = 0; x < width; x++) { file.write((char *)row, 3); row++; } ptr += srLayout.rowPitch; } } file.close(); // Clean up handled by ~WritePPMCleanupData() }
static void before_present(VkQueue queue, layer_data *my_data, SwapChainData *swapChain, unsigned imageIndex) { VkLayerDispatchTable *pTable = my_data->device_dispatch_table; if (!my_data->fontUploadComplete) { VkSubmitInfo si = {}; si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.pNext = nullptr; si.waitSemaphoreCount = 0; si.commandBufferCount = 1; si.signalSemaphoreCount = 0; si.pCommandBuffers = &my_data->fontUploadCmdBuffer; pTable->QueueSubmit(queue, 1, &si, VK_NULL_HANDLE); my_data->fontUploadComplete = true; #ifdef OVERLAY_DEBUG printf("Font image layout transition queued\n"); #endif } WsiImageData *id = swapChain->presentableImages[imageIndex]; /* update the overlay content */ vertex *vertices = nullptr; /* guaranteed not in flight due to WSI surface being available */ VkResult U_ASSERT_ONLY err = pTable->MapMemory(my_data->dev, id->vertexBufferMemory, 0, id->vertexBufferSize, 0, (void **)&vertices); assert(!err); /* write vertices for string in here */ id->numVertices = fill_vertex_buffer(my_data, vertices, imageIndex); pTable->UnmapMemory(my_data->dev, id->vertexBufferMemory); /* JIT record a command buffer to draw the overlay */ VkCommandBufferBeginInfo cbbi; cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cbbi.pNext = nullptr; cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; cbbi.pInheritanceInfo = nullptr; VkImageMemoryBarrier imb; imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imb.pNext = nullptr; imb.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; imb.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; imb.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; imb.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; imb.image = id->image; imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imb.subresourceRange.baseMipLevel = 0; imb.subresourceRange.levelCount = 1; imb.subresourceRange.baseArrayLayer = 0; imb.subresourceRange.layerCount = 1; imb.srcQueueFamilyIndex = my_data->graphicsQueueFamilyIndex; imb.dstQueueFamilyIndex = my_data->graphicsQueueFamilyIndex; VkRenderPassBeginInfo rpbi; rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rpbi.pNext = nullptr; rpbi.renderPass = swapChain->render_pass; rpbi.framebuffer = id->framebuffer; rpbi.renderArea.offset.x = 0; rpbi.renderArea.offset.y = 0; rpbi.renderArea.extent.width = swapChain->width; rpbi.renderArea.extent.height = swapChain->height; rpbi.clearValueCount = 0; rpbi.pClearValues = nullptr; pTable->BeginCommandBuffer(id->cmd, &cbbi); pTable->CmdPipelineBarrier(id->cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0 /* dependency flags */, 0, nullptr, /* memory barriers */ 0, nullptr, /* buffer memory barriers */ 1, &imb); /* image memory barriers */ pTable->CmdBeginRenderPass(id->cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE); pTable->CmdBindPipeline(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, swapChain->pipeline); pTable->CmdBindDescriptorSets(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, my_data->pl, 0, 1, &my_data->desc_set, 0, nullptr); VkDeviceSize offsets[] = {0}; VkBuffer buffers[] = {id->vertexBuffer}; pTable->CmdBindVertexBuffers(id->cmd, 0, 1, buffers, offsets); pTable->CmdDraw(id->cmd, id->numVertices, 1, 0, 0); pTable->CmdEndRenderPass(id->cmd); pTable->EndCommandBuffer(id->cmd); /* Schedule this command buffer for execution. TODO: Do we need to protect * ourselves from an app that didn't wait for the presentation image to * be idle before mangling it? If the app is well-behaved, our command * buffer is guaranteed to have been retired before the app tries to * present it again. */ VkSubmitInfo si = {}; si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.pNext = nullptr; si.waitSemaphoreCount = 0; si.commandBufferCount = 1; si.signalSemaphoreCount = 0; si.pCommandBuffers = &id->cmd; pTable->WaitForFences(my_data->dev, 1, &my_data->fence, VK_TRUE, UINT64_MAX); pTable->ResetFences(my_data->dev, 1, &my_data->fence); pTable->QueueSubmit(queue, 1, &si, my_data->fence); /* Reset per-frame stats */ my_data->cmdBuffersThisFrame = 0; }
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; }
VK_LAYER_EXPORT VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapChain) { layer_data *my_data = GetLayerDataPtr(get_dispatch_key(device), layer_data_map); VkLayerDispatchTable *pTable = my_data->device_dispatch_table; VkResult result = my_data->pfnCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapChain); if (result == VK_SUCCESS) { auto &data = (*my_data->swapChains)[*pSwapChain]; data = new SwapChainData; data->width = pCreateInfo->imageExtent.width; data->height = pCreateInfo->imageExtent.height; data->format = pCreateInfo->imageFormat; #ifdef OVERLAY_DEBUG printf( "Creating resources for scribbling on swapchain format %u width " "%u height %u\n", data->format, data->width, data->height); #endif /* Create a renderpass for drawing into this swapchain */ VkAttachmentDescription ad; ad.flags = 0; ad.format = data->format; ad.samples = VK_SAMPLE_COUNT_1_BIT; ad.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; ad.storeOp = VK_ATTACHMENT_STORE_OP_STORE; ad.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; ad.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; /* TODO: deal with the image possibly being in a different * layout - we need to care about general, * VK_IMAGE_LAYOUT_PRESENT_SOURCE_WSI, * etc etc */ ad.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; ad.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentReference ar; ar.attachment = 0; /* TODO: see previous layout comment */ ar.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference dr; dr.attachment = VK_ATTACHMENT_UNUSED; dr.layout = VK_IMAGE_LAYOUT_UNDEFINED; VkSubpassDescription sd; sd.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; sd.flags = 0; sd.inputAttachmentCount = 0; sd.pInputAttachments = nullptr; sd.colorAttachmentCount = 1; sd.pColorAttachments = &ar; sd.pResolveAttachments = nullptr; sd.pDepthStencilAttachment = &dr; /* TODO: do we need to mark the color attachment here? */ sd.preserveAttachmentCount = 0; sd.pPreserveAttachments = nullptr; VkRenderPassCreateInfo rpci; rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; rpci.pNext = nullptr; rpci.flags = 0; rpci.attachmentCount = 1; rpci.pAttachments = &ad; rpci.subpassCount = 1; rpci.pSubpasses = &sd; rpci.dependencyCount = 0; rpci.pDependencies = nullptr; pTable->CreateRenderPass(device, &rpci, nullptr, &data->render_pass); /* Create the pipeline to use in this renderpass */ VkPipelineShaderStageCreateInfo stages[2]; stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].pNext = nullptr; stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = my_data->vsShaderModule; stages[0].pName = "main"; stages[0].flags = 0; stages[0].pSpecializationInfo = nullptr; stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[1].pNext = nullptr; stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = my_data->fsShaderModule; stages[1].pName = "main"; stages[1].flags = 0; stages[1].pSpecializationInfo = nullptr; VkPipelineInputAssemblyStateCreateInfo piasci; memset(&piasci, 0, sizeof(piasci)); piasci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; piasci.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; VkViewport viewport; memset(&viewport, 0, sizeof(viewport)); viewport.width = (float)data->width; viewport.height = (float)data->height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor; memset(&scissor, 0, sizeof(scissor)); scissor.extent.width = data->width; scissor.extent.height = data->height; VkPipelineViewportStateCreateInfo pvsci; memset(&pvsci, 0, sizeof(pvsci)); pvsci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; pvsci.viewportCount = 1; pvsci.pViewports = &viewport; pvsci.scissorCount = 1; pvsci.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo prsci; memset(&prsci, 0, sizeof(prsci)); prsci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; prsci.polygonMode = VK_POLYGON_MODE_FILL; prsci.cullMode = VK_CULL_MODE_NONE; prsci.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo pmsci; memset(&pmsci, 0, sizeof(pmsci)); pmsci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; pmsci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkPipelineDepthStencilStateCreateInfo pdssci; memset(&pdssci, 0, sizeof(pdssci)); pdssci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; pdssci.minDepthBounds = 0.0f; pdssci.maxDepthBounds = 1.0f; VkPipelineColorBlendAttachmentState pcbas; memset(&pcbas, 0, sizeof(pcbas)); pcbas.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; VkPipelineColorBlendStateCreateInfo pcbsci; memset(&pcbsci, 0, sizeof(pcbsci)); pcbsci.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; pcbsci.attachmentCount = 1; pcbsci.pAttachments = &pcbas; pcbsci.blendConstants[0] = 1.0f; pcbsci.blendConstants[1] = 1.0f; pcbsci.blendConstants[2] = 1.0f; pcbsci.blendConstants[3] = 1.0f; VkVertexInputBindingDescription bindings[] = { {0, sizeof(vertex), VK_VERTEX_INPUT_RATE_VERTEX}, }; VkVertexInputAttributeDescription attribs[] = { {0, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex, x)}, {1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(vertex, u)}, }; VkPipelineVertexInputStateCreateInfo pvisci; memset(&pvisci, 0, sizeof(pvisci)); pvisci.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; pvisci.vertexBindingDescriptionCount = sizeof(bindings) / sizeof(*bindings); pvisci.pVertexBindingDescriptions = &bindings[0]; pvisci.vertexAttributeDescriptionCount = sizeof(attribs) / sizeof(*attribs); pvisci.pVertexAttributeDescriptions = &attribs[0]; VkGraphicsPipelineCreateInfo gpci; gpci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; gpci.pNext = nullptr; gpci.stageCount = sizeof(stages) / sizeof(*stages); gpci.pStages = &stages[0]; gpci.pVertexInputState = &pvisci; gpci.pInputAssemblyState = &piasci; gpci.pTessellationState = nullptr; gpci.pViewportState = &pvsci; gpci.pRasterizationState = &prsci; gpci.pMultisampleState = &pmsci; gpci.pDepthStencilState = &pdssci; gpci.pColorBlendState = &pcbsci; gpci.pDynamicState = nullptr; gpci.flags = 0; gpci.layout = my_data->pl; gpci.renderPass = data->render_pass; gpci.subpass = 0; gpci.basePipelineHandle = VK_NULL_HANDLE; gpci.basePipelineIndex = 0; pTable->CreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &gpci, nullptr, &data->pipeline); } return result; }
void layer_data::Cleanup() { VkLayerDispatchTable *pTable = this->device_dispatch_table; pTable->DestroySampler(dev, sampler, nullptr); pTable->DestroyDescriptorPool(dev, desc_pool, nullptr); pTable->DestroyPipelineLayout(dev, pl, nullptr); pTable->DestroyDescriptorSetLayout(dev, dsl, nullptr); pTable->DestroyImageView(dev, fontGlyphsImageView, nullptr); pTable->DestroyImage(dev, fontGlyphsImage, nullptr); pTable->FreeMemory(dev, fontGlyphsMemory, nullptr); pTable->FreeCommandBuffers(dev, pool, 1, &fontUploadCmdBuffer); pTable->DestroyCommandPool(dev, pool, nullptr); pTable->DestroyShaderModule(dev, vsShaderModule, nullptr); pTable->DestroyShaderModule(dev, fsShaderModule, nullptr); pTable->DestroyFence(dev, fence, nullptr); }
static void after_device_create(VkPhysicalDevice gpu, VkDevice device, layer_data *data) { VkResult U_ASSERT_ONLY err; data->gpu = gpu; data->dev = device; data->frame = 0; data->cmdBuffersThisFrame = 0; VkLayerDispatchTable *pTable = data->device_dispatch_table; /* Get our WSI hooks in. */ data->pfnCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR)pTable->GetDeviceProcAddr(device, "vkCreateSwapchainKHR"); data->pfnGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)pTable->GetDeviceProcAddr(device, "vkGetSwapchainImagesKHR"); data->pfnQueuePresentKHR = (PFN_vkQueuePresentKHR)pTable->GetDeviceProcAddr(device, "vkQueuePresentKHR"); data->pfnDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR)pTable->GetDeviceProcAddr(device, "vkDestroySwapchainKHR"); data->swapChains = new std::unordered_map<VkSwapchainKHR, SwapChainData *>; /* Command pool */ VkCommandPoolCreateInfo cpci; cpci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; cpci.pNext = nullptr; cpci.queueFamilyIndex = data->graphicsQueueFamilyIndex; cpci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; err = pTable->CreateCommandPool(device, &cpci, nullptr, &data->pool); assert(!err); /* Create the objects we need */ /* Compile the shaders */ compile_shader(device, VULKAN_SAMPLES_BASE_DIR "/Layer-Samples/data/overlay-vert.spv", &data->vsShaderModule); compile_shader(device, VULKAN_SAMPLES_BASE_DIR "/Layer-Samples/data/overlay-frag.spv", &data->fsShaderModule); /* Upload the font bitmap */ VkImageCreateInfo ici; memset(&ici, 0, sizeof(ici)); ici.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; ici.imageType = VK_IMAGE_TYPE_2D; ici.format = VK_FORMAT_R8_UNORM; ici.extent.width = FONT_ATLAS_SIZE; ici.extent.height = FONT_ATLAS_SIZE; ici.extent.depth = 1; ici.mipLevels = 1; ici.arrayLayers = 1; ici.samples = VK_SAMPLE_COUNT_1_BIT; ici.tiling = VK_IMAGE_TILING_LINEAR; ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT; ici.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; err = pTable->CreateImage(device, &ici, nullptr, &data->fontGlyphsImage); assert(!err); VkMemoryRequirements mem_reqs; pTable->GetImageMemoryRequirements(device, data->fontGlyphsImage, &mem_reqs); VkMemoryAllocateInfo mem_alloc; memset(&mem_alloc, 0, sizeof(mem_alloc)); mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; mem_alloc.allocationSize = mem_reqs.size; mem_alloc.memoryTypeIndex = choose_memory_type(gpu, mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); err = pTable->AllocateMemory(device, &mem_alloc, nullptr, &data->fontGlyphsMemory); assert(!err); err = pTable->BindImageMemory(device, data->fontGlyphsImage, data->fontGlyphsMemory, 0); assert(!err); VkImageSubresource subres; subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subres.mipLevel = 0; subres.arrayLayer = 0; VkSubresourceLayout layout; void *bits; pTable->GetImageSubresourceLayout(device, data->fontGlyphsImage, &subres, &layout); /* ensure we can directly upload into this layout */ assert(!layout.offset); assert(layout.size >= FONT_ATLAS_SIZE * FONT_ATLAS_SIZE); assert(layout.rowPitch == FONT_ATLAS_SIZE); err = pTable->MapMemory(device, data->fontGlyphsMemory, 0, VK_WHOLE_SIZE, 0, &bits); assert(!err); /* Load the font glyphs directly into the mapped buffer */ std::vector<unsigned char> fontData; get_file_contents(VULKAN_SAMPLES_BASE_DIR "/Layer-Samples/data/FreeSans.ttf", fontData); stbtt_BakeFontBitmap(&fontData[0], 0, FONT_SIZE_PIXELS, (unsigned char *)bits, FONT_ATLAS_SIZE, FONT_ATLAS_SIZE, 32, 96, data->glyphs); pTable->UnmapMemory(device, data->fontGlyphsMemory); VkImageViewCreateInfo ivci; ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; ivci.viewType = VK_IMAGE_VIEW_TYPE_2D; ivci.pNext = nullptr; ivci.format = ici.format; ivci.components.r = VK_COMPONENT_SWIZZLE_R; ivci.components.g = VK_COMPONENT_SWIZZLE_G; ivci.components.b = VK_COMPONENT_SWIZZLE_B; ivci.components.a = VK_COMPONENT_SWIZZLE_A; ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; ivci.subresourceRange.baseMipLevel = 0; ivci.subresourceRange.levelCount = 1; ivci.subresourceRange.baseArrayLayer = 0; ivci.subresourceRange.layerCount = 1; ivci.image = data->fontGlyphsImage; ivci.flags = 0; err = pTable->CreateImageView(device, &ivci, nullptr, &data->fontGlyphsImageView); assert(!err); /* transition from undefined layout to shader readonly so we can use it. * requires a command buffer. */ VkCommandBufferAllocateInfo cbai; cbai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cbai.pNext = nullptr; cbai.commandPool = data->pool; cbai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cbai.commandBufferCount = 1; VkCommandBuffer cmd; err = pTable->AllocateCommandBuffers(device, &cbai, &cmd); assert(!err); /* We have just created a dispatchable object, but the dispatch table has * not been placed in the object yet. * When a "normal" application creates a command buffer, * the dispatch table is installed by the top-level binding (trampoline.c). * But here, we have to do it ourselves. */ if (!data->pfn_dev_init) { *((const void **)cmd) = *(void **)device; } else { err = data->pfn_dev_init(device, (void *)cmd); assert(!err); } VkCommandBufferBeginInfo cbbi; cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cbbi.pNext = nullptr; cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; cbbi.pInheritanceInfo = nullptr; err = pTable->BeginCommandBuffer(cmd, &cbbi); assert(!err); VkImageMemoryBarrier imb; imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imb.pNext = nullptr; imb.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; imb.srcAccessMask = 0; imb.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; imb.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imb.image = data->fontGlyphsImage; imb.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imb.subresourceRange.baseMipLevel = 0; imb.subresourceRange.levelCount = 1; imb.subresourceRange.baseArrayLayer = 0; imb.subresourceRange.layerCount = 1; imb.srcQueueFamilyIndex = data->graphicsQueueFamilyIndex; imb.dstQueueFamilyIndex = data->graphicsQueueFamilyIndex; pTable->CmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0 /* dependency flags */, 0, nullptr, /* memory barriers */ 0, nullptr, /* buffer memory barriers */ 1, &imb); /* image memory barriers */ pTable->EndCommandBuffer(cmd); data->fontUploadCmdBuffer = cmd; data->fontUploadComplete = false; /* we will schedule this at first present on this device */ #ifdef OVERLAY_DEBUG printf("Font upload done.\n"); #endif /* create a sampler to use with the texture */ VkSamplerCreateInfo sci; sci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sci.pNext = nullptr; sci.flags = 0; sci.magFilter = VK_FILTER_NEAREST; sci.minFilter = VK_FILTER_NEAREST; sci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; sci.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sci.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sci.mipLodBias = 0.0f; sci.anisotropyEnable = false; sci.maxAnisotropy = 1; sci.compareEnable = false; sci.compareOp = VK_COMPARE_OP_NEVER; sci.minLod = 0.0f; sci.maxLod = 0.0f; sci.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; sci.unnormalizedCoordinates = VK_FALSE; err = pTable->CreateSampler(device, &sci, nullptr, &data->sampler); assert(!err); /* descriptor set stuff so we can use the texture from a shader. */ VkDescriptorSetLayoutBinding dslb[1]; dslb[0].binding = 0; dslb[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; dslb[0].descriptorCount = 1; dslb[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; dslb[0].pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo dslci; memset(&dslci, 0, sizeof(dslci)); dslci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; dslci.pNext = nullptr; dslci.bindingCount = 1; dslci.pBindings = dslb; err = pTable->CreateDescriptorSetLayout(device, &dslci, nullptr, &data->dsl); assert(!err); VkPipelineLayoutCreateInfo plci; memset(&plci, 0, sizeof(plci)); plci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; plci.setLayoutCount = 1; plci.pSetLayouts = &data->dsl; err = pTable->CreatePipelineLayout(device, &plci, nullptr, &data->pl); assert(!err); VkDescriptorPoolSize dtc[1]; dtc[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; dtc[0].descriptorCount = 1; VkDescriptorPoolCreateInfo dpci; dpci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; dpci.pNext = nullptr; dpci.flags = 0; dpci.maxSets = 1; dpci.poolSizeCount = 1; dpci.pPoolSizes = dtc; err = pTable->CreateDescriptorPool(device, &dpci, nullptr, &data->desc_pool); assert(!err); VkDescriptorSetAllocateInfo dsai; dsai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; dsai.pNext = nullptr; dsai.descriptorPool = data->desc_pool; dsai.descriptorSetCount = 1; dsai.pSetLayouts = &data->dsl; err = pTable->AllocateDescriptorSets(device, &dsai, &data->desc_set); assert(!err); VkDescriptorImageInfo descs[1]; descs[0].sampler = data->sampler; descs[0].imageView = data->fontGlyphsImageView; descs[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL; // TODO: cube does this, is it correct? VkWriteDescriptorSet writes[1]; memset(&writes, 0, sizeof(writes)); writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writes[0].dstSet = data->desc_set; writes[0].dstBinding = 0; writes[0].descriptorCount = 1; writes[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writes[0].pImageInfo = descs; pTable->UpdateDescriptorSets(device, 1, writes, 0, nullptr); VkFenceCreateInfo fci; fci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fci.pNext = NULL; fci.flags = VK_FENCE_CREATE_SIGNALED_BIT; pTable->CreateFence(device, &fci, NULL, &data->fence); }
VKAPI_ATTR VkResult VKAPI_CALL QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) { static int frameNumber = 0; if (frameNumber == 10) { fflush(stdout); /* *((int*)0)=0; */ } DeviceMapStruct *devMap = get_dev_info((VkDevice)queue); assert(devMap); VkLayerDispatchTable *pDisp = devMap->device_dispatch_table; VkResult result = pDisp->QueuePresentKHR(queue, pPresentInfo); loader_platform_thread_lock_mutex(&globalLock); if (!screenshotEnvQueried) { const char *_vk_screenshot = local_getenv("_VK_SCREENSHOT"); if (_vk_screenshot && *_vk_screenshot) { string spec(_vk_screenshot), word; size_t start = 0, comma = 0; while (start < spec.size()) { int frameToAdd; comma = spec.find(',', start); if (comma == string::npos) word = string(spec, start); else word = string(spec, start, comma - start); frameToAdd = atoi(word.c_str()); // Add the frame number to set, but only do it if the word // started with a digit and if // it's not already in the list if (*(word.c_str()) >= '0' && *(word.c_str()) <= '9') { screenshotFrames.insert(frameToAdd); } if (comma == string::npos) break; start = comma + 1; } } local_free_getenv(_vk_screenshot); screenshotEnvQueried = true; } if (result == VK_SUCCESS && !screenshotFrames.empty()) { set<int>::iterator it; it = screenshotFrames.find(frameNumber); if (it != screenshotFrames.end()) { string fileName; fileName = to_string(frameNumber) + ".ppm"; VkImage image; VkSwapchainKHR swapchain; // We'll dump only one image: the first swapchain = pPresentInfo->pSwapchains[0]; image = swapchainMap[swapchain] ->imageList[pPresentInfo->pImageIndices[0]]; writePPM(fileName.c_str(), image); screenshotFrames.erase(it); if (screenshotFrames.empty()) { // Free all our maps since we are done with them. for (auto it = swapchainMap.begin(); it != swapchainMap.end(); it++) { SwapchainMapStruct *swapchainMapElem = it->second; delete swapchainMapElem; } for (auto it = imageMap.begin(); it != imageMap.end(); it++) { ImageMapStruct *imageMapElem = it->second; delete imageMapElem; } for (auto it = physDeviceMap.begin(); it != physDeviceMap.end(); it++) { PhysDeviceMapStruct *physDeviceMapElem = it->second; delete physDeviceMapElem; } swapchainMap.clear(); imageMap.clear(); physDeviceMap.clear(); } } } frameNumber++; loader_platform_thread_unlock_mutex(&globalLock); return result; }
static void before_present(VkQueue queue, layer_data *my_data, SwapChainData *swapChain, unsigned imageIndex) { VkLayerDispatchTable *pTable = my_data->device_dispatch_table; if (!my_data->fontUploadComplete) { VkSubmitInfo si; si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.pNext = nullptr; si.waitSemaphoreCount = 0; si.commandBufferCount = 1; si.signalSemaphoreCount = 0; si.pCommandBuffers = &my_data->fontUploadCmdBuffer; pTable->QueueSubmit(queue, 1, &si, VK_NULL_HANDLE); my_data->fontUploadComplete = true; #ifdef OVERLAY_DEBUG printf("Font image layout transition queued\n"); #endif } WsiImageData *id = swapChain->presentableImages[imageIndex]; /* update the overlay content */ vertex *vertices = nullptr; /* guaranteed not in flight due to WSI surface being available */ VkResult U_ASSERT_ONLY err = pTable->MapMemory(my_data->dev, id->vertexBufferMemory, 0, id->vertexBufferSize, 0, (void **)&vertices); assert(!err); /* write vertices for string in here */ id->numVertices = fill_vertex_buffer(my_data, vertices, imageIndex); pTable->UnmapMemory(my_data->dev, id->vertexBufferMemory); /* JIT record a command buffer to draw the overlay */ VkCommandBufferBeginInfo cbbi; cbbi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; cbbi.pNext = nullptr; cbbi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; cbbi.pInheritanceInfo = nullptr; VkRenderPassBeginInfo rpbi; rpbi.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; rpbi.pNext = nullptr; rpbi.renderPass = swapChain->render_pass; rpbi.framebuffer = id->framebuffer; rpbi.renderArea.offset.x = 0; rpbi.renderArea.offset.y = 0; rpbi.renderArea.extent.width = swapChain->width; rpbi.renderArea.extent.height = swapChain->height; rpbi.clearValueCount = 0; rpbi.pClearValues = nullptr; pTable->BeginCommandBuffer(id->cmd, &cbbi); pTable->CmdBeginRenderPass(id->cmd, &rpbi, VK_SUBPASS_CONTENTS_INLINE); pTable->CmdBindPipeline(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, swapChain->pipeline); pTable->CmdBindDescriptorSets(id->cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, my_data->pl, 0, 1, &my_data->desc_set, 0, nullptr); VkDeviceSize offsets[] = {0}; VkBuffer buffers[] = {id->vertexBuffer}; pTable->CmdBindVertexBuffers(id->cmd, 0, 1, buffers, offsets); pTable->CmdDraw(id->cmd, id->numVertices, 1, 0, 0); pTable->CmdEndRenderPass(id->cmd); pTable->EndCommandBuffer(id->cmd); /* Schedule this command buffer for execution. TODO: Do we need to protect * ourselves from an app that didn't wait for the presentation image to * be idle before mangling it? If the app is well-behaved, our command * buffer is guaranteed to have been retired before the app tries to * present it again. */ VkFence null_fence = VK_NULL_HANDLE; VkSubmitInfo si; si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; si.pNext = nullptr; si.waitSemaphoreCount = 0; si.commandBufferCount = 1; si.signalSemaphoreCount = 0; si.pCommandBuffers = &id->cmd; pTable->QueueSubmit(queue, 1, &si, null_fence); /* Reset per-frame stats */ my_data->cmdBuffersThisFrame = 0; }