CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/Vulkan/VulkanFrameData.cpp
Views: 1401
#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();2223VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };24semaphoreCreateInfo.flags = 0;25VkResult res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &acquireSemaphore);26_dbg_assert_(res == VK_SUCCESS);27res = vkCreateSemaphore(vulkan->GetDevice(), &semaphoreCreateInfo, nullptr, &renderingCompleteSemaphore);28_dbg_assert_(res == VK_SUCCESS);2930VkCommandPoolCreateInfo cmd_pool_info = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };31cmd_pool_info.queueFamilyIndex = vulkan->GetGraphicsQueueFamilyIndex();32cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;33res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolInit);34_dbg_assert_(res == VK_SUCCESS);35res = vkCreateCommandPool(device, &cmd_pool_info, nullptr, &cmdPoolMain);36_dbg_assert_(res == VK_SUCCESS);3738VkCommandBufferAllocateInfo cmd_alloc = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };39cmd_alloc.commandPool = cmdPoolInit;40cmd_alloc.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;41cmd_alloc.commandBufferCount = 1;42res = vkAllocateCommandBuffers(device, &cmd_alloc, &initCmd);43_dbg_assert_(res == VK_SUCCESS);44cmd_alloc.commandPool = cmdPoolMain;45res = vkAllocateCommandBuffers(device, &cmd_alloc, &mainCmd);46res = vkAllocateCommandBuffers(device, &cmd_alloc, &presentCmd);47_dbg_assert_(res == VK_SUCCESS);4849vulkan->SetDebugName(initCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("initCmd%d", index).c_str());50vulkan->SetDebugName(mainCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("mainCmd%d", index).c_str());51vulkan->SetDebugName(presentCmd, VK_OBJECT_TYPE_COMMAND_BUFFER, StringFromFormat("presentCmd%d", index).c_str());5253// Creating the frame fence with true so they can be instantly waited on the first frame54fence = vulkan->CreateFence(true);55vulkan->SetDebugName(fence, VK_OBJECT_TYPE_FENCE, StringFromFormat("fence%d", index).c_str());56readyForFence = true;5758VkQueryPoolCreateInfo query_ci{ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO };59query_ci.queryCount = MAX_TIMESTAMP_QUERIES;60query_ci.queryType = VK_QUERY_TYPE_TIMESTAMP;61res = vkCreateQueryPool(device, &query_ci, nullptr, &profile.queryPool);62}6364void FrameData::Destroy(VulkanContext *vulkan) {65VkDevice device = vulkan->GetDevice();66vkDestroyCommandPool(device, cmdPoolInit, nullptr);67vkDestroyCommandPool(device, cmdPoolMain, nullptr);68vkDestroyFence(device, fence, nullptr);69vkDestroyQueryPool(device, profile.queryPool, nullptr);70vkDestroySemaphore(device, acquireSemaphore, nullptr);71vkDestroySemaphore(device, renderingCompleteSemaphore, nullptr);7273readbacks_.IterateMut([=](const ReadbackKey &key, CachedReadback *value) {74value->Destroy(vulkan);75delete value;76});77readbacks_.Clear();78}7980void FrameData::AcquireNextImage(VulkanContext *vulkan) {81_dbg_assert_(!hasAcquired);8283// Get the index of the next available swapchain image, and a semaphore to block command buffer execution on.84VkResult res = vkAcquireNextImageKHR(vulkan->GetDevice(), vulkan->GetSwapchain(), UINT64_MAX, acquireSemaphore, (VkFence)VK_NULL_HANDLE, &curSwapchainImage);85switch (res) {86case VK_SUCCESS:87hasAcquired = true;88break;89case VK_SUBOPTIMAL_KHR:90hasAcquired = true;91// Hopefully the resize will happen shortly. Ignore - one frame might look bad or something.92WARN_LOG(Log::G3D, "VK_SUBOPTIMAL_KHR returned - ignoring");93break;94case VK_ERROR_OUT_OF_DATE_KHR:95case VK_TIMEOUT:96case VK_NOT_READY:97// We do not set hasAcquired here!98WARN_LOG(Log::G3D, "%s returned from AcquireNextImage - processing the frame, but not presenting", VulkanResultToString(res));99skipSwap = true;100break;101case VK_ERROR_SURFACE_LOST_KHR:102ERROR_LOG(Log::G3D, "%s returned from AcquireNextImage - ignoring, but this better be during shutdown", VulkanResultToString(res));103skipSwap = true;104break;105default:106// Weird, shouldn't get any other values. Maybe lost device?107_assert_msg_(false, "vkAcquireNextImageKHR failed! result=%s", VulkanResultToString(res));108break;109}110}111112VkResult FrameData::QueuePresent(VulkanContext *vulkan, FrameDataShared &shared) {113_dbg_assert_(hasAcquired);114hasAcquired = false;115_dbg_assert_(!skipSwap);116117VkSwapchainKHR swapchain = vulkan->GetSwapchain();118VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };119present.swapchainCount = 1;120present.pSwapchains = &swapchain;121present.pImageIndices = &curSwapchainImage;122present.pWaitSemaphores = &renderingCompleteSemaphore;123present.waitSemaphoreCount = 1;124125// Can't move these into the if.126VkPresentIdKHR presentID{ VK_STRUCTURE_TYPE_PRESENT_ID_KHR };127VkPresentTimesInfoGOOGLE presentGOOGLE{ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE };128129uint64_t frameId = this->frameId;130VkPresentTimeGOOGLE presentTimeGOOGLE{ (uint32_t)frameId, 0 }; // it's ok to truncate this. it'll wrap around and work (if we ever reach 4 billion frames..)131132if (shared.measurePresentTime) {133if (vulkan->Extensions().KHR_present_id && vulkan->GetDeviceFeatures().enabled.presentId.presentId) {134ChainStruct(present, &presentID);135presentID.pPresentIds = &frameId;136presentID.swapchainCount = 1;137} else if (vulkan->Extensions().GOOGLE_display_timing) {138ChainStruct(present, &presentGOOGLE);139presentGOOGLE.pTimes = &presentTimeGOOGLE;140presentGOOGLE.swapchainCount = 1;141}142}143144return vkQueuePresentKHR(vulkan->GetGraphicsQueue(), &present);145}146147VkCommandBuffer FrameData::GetInitCmd(VulkanContext *vulkan) {148if (!hasInitCommands) {149VkCommandBufferBeginInfo begin = {150VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,151nullptr,152VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT153};154vkResetCommandPool(vulkan->GetDevice(), cmdPoolInit, 0);155VkResult res = vkBeginCommandBuffer(initCmd, &begin);156if (res != VK_SUCCESS) {157return VK_NULL_HANDLE;158}159160// Good spot to reset the query pool.161if (profile.enabled) {162vkCmdResetQueryPool(initCmd, profile.queryPool, 0, MAX_TIMESTAMP_QUERIES);163vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, profile.queryPool, 0);164}165166hasInitCommands = true;167}168return initCmd;169}170171void FrameData::Submit(VulkanContext *vulkan, FrameSubmitType type, FrameDataShared &sharedData) {172VkCommandBuffer cmdBufs[3];173int numCmdBufs = 0;174175VkFence fenceToTrigger = VK_NULL_HANDLE;176177if (hasInitCommands) {178if (profile.enabled) {179// Pre-allocated query ID 1 - end of init cmdbuf.180vkCmdWriteTimestamp(initCmd, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, profile.queryPool, 1);181}182183VkResult res = vkEndCommandBuffer(initCmd);184cmdBufs[numCmdBufs++] = initCmd;185186_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (init)! result=%s", VulkanResultToString(res));187hasInitCommands = false;188}189190if ((hasMainCommands || hasPresentCommands) && type == FrameSubmitType::Sync) {191fenceToTrigger = sharedData.readbackFence;192}193194if (hasMainCommands) {195VkResult res = vkEndCommandBuffer(mainCmd);196_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (main)! result=%s", VulkanResultToString(res));197198cmdBufs[numCmdBufs++] = mainCmd;199hasMainCommands = false;200}201202if (hasPresentCommands) {203_dbg_assert_(type != FrameSubmitType::Pending);204VkResult res = vkEndCommandBuffer(presentCmd);205206_assert_msg_(res == VK_SUCCESS, "vkEndCommandBuffer failed (present)! result=%s", VulkanResultToString(res));207208cmdBufs[numCmdBufs++] = presentCmd;209hasPresentCommands = false;210}211212if (type == FrameSubmitType::FinishFrame) {213_dbg_assert_(!fenceToTrigger);214fenceToTrigger = fence;215}216217if (!numCmdBufs && fenceToTrigger == VK_NULL_HANDLE) {218// Nothing to do.219return;220}221222VkSubmitInfo submit_info{ VK_STRUCTURE_TYPE_SUBMIT_INFO };223VkPipelineStageFlags waitStage[1]{ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };224if (type == FrameSubmitType::FinishFrame && !skipSwap) {225_dbg_assert_(hasAcquired);226submit_info.waitSemaphoreCount = 1;227submit_info.pWaitSemaphores = &acquireSemaphore;228submit_info.pWaitDstStageMask = waitStage;229}230submit_info.commandBufferCount = (uint32_t)numCmdBufs;231submit_info.pCommandBuffers = cmdBufs;232if (type == FrameSubmitType::FinishFrame && !skipSwap) {233submit_info.signalSemaphoreCount = 1;234submit_info.pSignalSemaphores = &renderingCompleteSemaphore;235}236237VkResult res;238if (fenceToTrigger == fence) {239VLOG("Doing queue submit, fencing frame %d", this->index);240// The fence is waited on by the main thread, they are not allowed to access it simultaneously.241res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);242if (sharedData.useMultiThreading) {243std::lock_guard<std::mutex> lock(fenceMutex);244readyForFence = true;245fenceCondVar.notify_one();246}247} else {248VLOG("Doing queue submit, fencing something (%p)", fenceToTrigger);249res = vkQueueSubmit(vulkan->GetGraphicsQueue(), 1, &submit_info, fenceToTrigger);250}251252if (res == VK_ERROR_DEVICE_LOST) {253_assert_msg_(false, "Lost the Vulkan device in vkQueueSubmit! If this happens again, switch Graphics Backend away from Vulkan");254} else {255_assert_msg_(res == VK_SUCCESS, "vkQueueSubmit failed (main)! result=%s", VulkanResultToString(res));256}257258if (type == FrameSubmitType::Sync) {259// Hard stall of the GPU, not ideal, but necessary so the CPU has the contents of the readback.260vkWaitForFences(vulkan->GetDevice(), 1, &sharedData.readbackFence, true, UINT64_MAX);261vkResetFences(vulkan->GetDevice(), 1, &sharedData.readbackFence);262syncDone = true;263}264}265266void FrameDataShared::Init(VulkanContext *vulkan, bool useMultiThreading, bool measurePresentTime) {267// This fence is used for synchronizing readbacks. Does not need preinitialization.268readbackFence = vulkan->CreateFence(false);269vulkan->SetDebugName(readbackFence, VK_OBJECT_TYPE_FENCE, "readbackFence");270271this->useMultiThreading = useMultiThreading;272this->measurePresentTime = measurePresentTime;273}274275void FrameDataShared::Destroy(VulkanContext *vulkan) {276VkDevice device = vulkan->GetDevice();277vkDestroyFence(device, readbackFence, nullptr);278}279280281