Path: blob/master/Common/GPU/Vulkan/VulkanFrameData.cpp
3187 views
#include <mutex>12#include "VulkanFrameData.h"3#include "Common/Log.h"4#include "Common/StringUtils.h"56#if 0 // def _DEBUG7#define VLOG(...) NOTICE_LOG(Log::G3D, __VA_ARGS__)8#else9#define VLOG(...)10#endif1112void CachedReadback::Destroy(VulkanContext *vulkan) {13if (buffer) {14vulkan->Delete().QueueDeleteBufferAllocation(buffer, allocation);15}16bufferSize = 0;17}1819void FrameData::Init(VulkanContext *vulkan, int index) {20this->index = index;21VkDevice device = vulkan->GetDevice();2223static const VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };24VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore);25_dbg_assert_(res == VK_SUCCESS);2627VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };28cmd_pool_info.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex();29cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;30res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolInit);31_dbg_assert_(res == VK_SUCCESS);32res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolMain);33_dbg_assert_(res == VK_SUCCESS);3435VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };36cmd_alloc.commandPool = cmdPoolInit;37cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;38cmd_alloc.commandBufferCount = 1;39res = vkAllocateCommandBuffers(device, &cmd_alloc, &initCmd);40_dbg_assert_(res == VK_SUCCESS);41cmd_alloc.commandPool = cmdPoolMain;42res = vkAllocateCommandBuffers(device, &cmd_alloc, &mainCmd);43res = vkAllocateCommandBuffers(device, &cmd_alloc, &presentCmd);44_dbg_assert_(res == VK_SUCCESS);4546vulkan->SetDebugName(initCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("initCmd%d", index).c_str());47vulkan->SetDebugName(mainCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("mainCmd%d", index).c_str());48vulkan->SetDebugName(presentCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("presentCmd%d", index).c_str());4950// Creating the frame fence with true so they can be instantly waited on the first frame51fence = vulkan->CreateFence(true);52vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, StringFromFormat("fence%d", index).c_str());53readyForFence = true;5455VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };56query_ci.queryCount = MAX_TIMESTAMP_QUERIES;57query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP;58res = vkCreateQueryPool(device, &query_ci, nullptr, &profile.queryPool);59}6061void FrameData::Destroy(VulkanContext *vulkan) {62VkDevice device = vulkan->GetDevice();63vkDestroyCommandPool(device, cmdPoolInit, nullptr);64vkDestroyCommandPool(device, cmdPoolMain, nullptr);65vkDestroyFence(device, fence, nullptr);66vkDestroyQueryPool(device, profile.queryPool, nullptr);67vkDestroySemaphore(device, acquireSemaphore, nullptr);6869readbacks_.IterateMut([=](const ReadbackKey &key, CachedReadback *value) {70value->Destroy(vulkan);71delete value;72});73readbacks_.Clear();74}7576void FrameData::AcquireNextImage(VulkanContext *vulkan) {77_dbg_assert_(!hasAcquired);7879// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.80VkResult res = vkAcquireNextImageKHR(vulkan->GetDevice(), vulkan->GetSwapchain(), UINT64_MAX, acquireSemaphore, (VkFence)VK_NULL_HANDLE, &curSwapchainImage);81switch (res) {82case VK_SUCCESS:83hasAcquired = true;84break;85case VK_SUBOPTIMAL_KHR:86hasAcquired = true;87// Hopefully the resize will happen shortly. Ignore - one frame might look bad or something.88WARN_LOG(Log::G3D, "VK_SUBOPTIMAL_KHR returned - ignoring");89break;90case VK_ERROR_OUT_OF_DATE_KHR:91case VK_TIMEOUT:92case VK_NOT_READY:93// We do not set hasAcquired here!94WARN_LOG(Log::G3D, "%s returned from AcquireNextImage - processing the frame, but not presenting", VulkanResultToString(res));95skipSwap = true;96break;97case VK_ERROR_SURFACE_LOST_KHR:98ERROR_LOG(Log::G3D, "%s returned from AcquireNextImage - ignoring, but this better be during shutdown", VulkanResultToString(res));99skipSwap = true;100break;101default:102// Weird, shouldn't get any other values. Maybe lost device?103_assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res));104break;105}106}107108VkResult FrameData::QueuePresent(VulkanContext *vulkan, FrameDataShared &shared) {109_dbg_assert_(hasAcquired);110hasAcquired = false;111_dbg_assert_(!skipSwap);112113VkSwapchainKHR swapchain = vulkan->GetSwapchain();114VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };115present.swapchainCount = 1;116present.pSwapchains = &swapchain;117present.pImageIndices = &curSwapchainImage;118present.pWaitSemaphores = &shared.swapchainImages_[curSwapchainImage].renderingCompleteSemaphore;119present.waitSemaphoreCount = 1;120121// Can't move these into the if.122VkPresentIdKHR presentID{ VK_STRUCTURE_TYPE_PRESENT_ID_KHR };123VkPresentTimesInfoGOOGLE presentGOOGLE{ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE };124125uint64_t frameId = this->frameId;126VkPresentTimeGOOGLE presentTimeGOOGLE{ (uint32_t)frameId, 0 }; // it's ok to truncate this. it'll wrap around and work (if we ever reach 4 billion frames..)127128if (shared.measurePresentTime) {129if (vulkan->Extensions().KHR_present_id && vulkan->GetDeviceFeatures().enabled.presentId.presentId) {130ChainStruct(present, &presentID);131presentID.pPresentIds = &frameId;132presentID.swapchainCount = 1;133} else if (vulkan->Extensions().GOOGLE_display_timing) {134ChainStruct(present, &presentGOOGLE);135presentGOOGLE.pTimes = &presentTimeGOOGLE;136presentGOOGLE.swapchainCount = 1;137}138}139140return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present);141}142143VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {144if (!hasInitCommands) {145VkCommandBufferBeginInfo begin = {146VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,147nullptr,148VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT149};150vkResetCommandPool(vulkan->GetDevice(), cmdPoolInit, 0);151VkResult res = vkBeginCommandBuffer(initCmd, &begin);152if (res != VK_SUCCESS) {153return VK_NULL_HANDLE;154}155156// Good spot to reset the query pool.157if (profile.enabled) {158vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES);159vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, profile.queryPool, 0);160}161162hasInitCommands = true;163}164return initCmd;165}166167void FrameData::Submit(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &sharedData) {168VkCommandBuffer cmdBufs[3];169int numCmdBufs = 0;170171VkFence fenceToTrigger = VK_NULL_HANDLE;172173if (hasInitCommands) {174if (profile.enabled) {175// Pre-allocated query ID 1 - end of init cmdbuf.176vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile.queryPool, 1);177}178179VkResult res = vkEndCommandBuffer(initCmd);180cmdBufs[numCmdBufs++] = initCmd;181182_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res));183hasInitCommands = false;184}185186if ((hasMainCommands || hasPresentCommands) && type == FrameSubmitType::Sync) {187fenceToTrigger = sharedData.readbackFence;188}189190if (hasMainCommands) {191VkResult res = vkEndCommandBuffer(mainCmd);192_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res));193194cmdBufs[numCmdBufs++] = mainCmd;195hasMainCommands = false;196}197198if (hasPresentCommands) {199_dbg_assert_(type != FrameSubmitType::Pending);200VkResult res = vkEndCommandBuffer(presentCmd);201202_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (present)! result=%s", VulkanResultToString(res));203204cmdBufs[numCmdBufs++] = presentCmd;205hasPresentCommands = false;206}207208if (type == FrameSubmitType::FinishFrame) {209_dbg_assert_(!fenceToTrigger);210fenceToTrigger = fence;211}212213if (!numCmdBufs && fenceToTrigger == VK_NULL_HANDLE) {214// Nothing to do.215return;216}217218VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };219VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };220if (type == FrameSubmitType::FinishFrame && !skipSwap) {221_dbg_assert_(hasAcquired);222submit_info.waitSemaphoreCount = 1;223submit_info.pWaitSemaphores = &acquireSemaphore;224submit_info.pWaitDstStageMask = waitStage;225}226submit_info.commandBufferCount = (uint32_t)numCmdBufs;227submit_info.pCommandBuffers = cmdBufs;228if (type == FrameSubmitType::FinishFrame && !skipSwap) {229submit_info.signalSemaphoreCount = 1;230submit_info.pSignalSemaphores = &sharedData.swapchainImages_[curSwapchainImage].renderingCompleteSemaphore;231}232233VkResult res;234if (fenceToTrigger == fence) {235VLOG("Doing queue submit, fencing frame %d", this->index);236// The fence is waited on by the main thread, they are not allowed to access it simultaneously.237res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);238if (sharedData.useMultiThreading) {239std::lock_guard<std::mutex> lock(fenceMutex);240readyForFence = true;241fenceCondVar.notify_one();242}243} else {244VLOG("Doing queue submit, fencing something (%p)", fenceToTrigger);245res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);246}247248if (res == VK_ERROR_DEVICE_LOST) {249_assert_msg_(false, "Lost the Vulkan device in vkQueueSubmit! If this happens again, switch Graphics Backend away from Vulkan");250} else {251_assert_msg_(res == VK_SUCCESS, "vkQueueSubmit failed (main)! result=%s", VulkanResultToString(res));252}253254if (type == FrameSubmitType::Sync) {255// Hard stall of the GPU, not ideal, but necessary so the CPU has the contents of the readback.256vkWaitForFences(vulkan->GetDevice(), 1, &sharedData.readbackFence, true, UINT64_MAX);257vkResetFences(vulkan->GetDevice(), 1, &sharedData.readbackFence);258syncDone = true;259}260}261262void FrameDataShared::Init(VulkanContext *vulkan, bool useMultiThreading, bool measurePresentTime) {263// This fence is used for synchronizing readbacks. Does not need preinitialization.264readbackFence = vulkan->CreateFence(false);265vulkan->SetDebugName(readbackFence, VK_OBJECT_TYPE_FENCE, "readbackFence");266267this->useMultiThreading = useMultiThreading;268this->measurePresentTime = measurePresentTime;269}270271void FrameDataShared::Destroy(VulkanContext *vulkan) {272VkDevice device = vulkan->GetDevice();273vkDestroyFence(device, readbackFence, nullptr);274}275276277