coral_swapchain.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. #include "coral_swapchain.h"
  2. // STD
  3. #include <array>
  4. #include <cstdlib>
  5. #include <cstring>
  6. #include <iostream>
  7. #include <limits>
  8. #include <set>
  9. #include <stdexcept>
  10. // VULKAN
  11. #include "vk_initializers.h"
  12. using namespace coral_3d;
  13. coral_swapchain::coral_swapchain(coral_device& device, VkExtent2D extent, std::shared_ptr<coral_swapchain> old_swapchain)
  14. : device_{ device }, window_extent_{ extent }, old_swapchain_{old_swapchain}
  15. {
  16. init();
  17. old_swapchain_ = nullptr;
  18. }
  19. coral_swapchain::coral_swapchain(coral_device& device, VkExtent2D extent)
  20. : device_{ device }, window_extent_{ extent }
  21. {
  22. init();
  23. }
  24. coral_swapchain::~coral_swapchain()
  25. {
  26. for (auto image_view : swapchain_image_views_)
  27. {
  28. vkDestroyImageView(device_.device(), image_view, nullptr);
  29. }
  30. swapchain_image_views_.clear();
  31. vkDestroySwapchainKHR(device_.device(), swapchain_, nullptr);
  32. swapchain_ = nullptr;
  33. deletion_queue_.flush();
  34. }
  35. VkFormat coral_swapchain::find_depth_format()
  36. {
  37. return device_.find_supported_format(
  38. { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
  39. VK_IMAGE_TILING_OPTIMAL,
  40. VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
  41. }
  42. VkResult coral_swapchain::aqcuire_next_image(uint32_t* image_index)
  43. {
  44. // NOTE: as of 04/07, vkDeviceWaitIdle is about 3% faster when rendering a single triangle.
  45. vkDeviceWaitIdle(device_.device());
  46. // vkWaitForFences(device_.device(), 1, &get_current_frame().render_fence, VK_TRUE, UINT64_MAX);
  47. vkResetFences(device_.device(), 1, &get_current_frame().render_fence);
  48. VkResult result = vkAcquireNextImageKHR(
  49. device_.device(),
  50. swapchain_,
  51. UINT64_MAX,
  52. get_current_frame().present_semaphore, // must be a not signaled semaphore
  53. VK_NULL_HANDLE,
  54. image_index);
  55. return result;
  56. }
  57. VkResult coral_swapchain::submit_command_buffer(const VkCommandBuffer* buffers, uint32_t* image_index)
  58. {
  59. // Prepare to submit to the queue
  60. VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  61. VkSubmitInfo submit{};
  62. submit.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
  63. submit.pNext = nullptr;
  64. submit.pWaitDstStageMask = &waitStage;
  65. // Wait for image to be ready for rendering before rendering to it
  66. submit.waitSemaphoreCount = 1;
  67. submit.pWaitSemaphores = &get_current_frame().present_semaphore;
  68. // Signal ready when finished rendering
  69. submit.signalSemaphoreCount = 1;
  70. submit.pSignalSemaphores = &get_current_frame().render_semaphore;
  71. submit.commandBufferCount = 1;
  72. submit.pCommandBuffers = buffers;
  73. // Submit to the graphics queue
  74. if (vkQueueSubmit(device_.graphics_queue(), 1, &submit, get_current_frame().render_fence) != VK_SUCCESS)
  75. throw std::runtime_error("ERROR! coral_swapchain::submit_command_buffer() >> Failed to submit draw command buffer!");
  76. // Present the image to the window when the rendering semaphore has signaled that rendering is done
  77. VkPresentInfoKHR presentInfo{};
  78. presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
  79. presentInfo.pNext = nullptr;
  80. presentInfo.pSwapchains = &swapchain_;
  81. presentInfo.swapchainCount = 1;
  82. presentInfo.waitSemaphoreCount = 1;
  83. presentInfo.pWaitSemaphores = &get_current_frame().render_semaphore;
  84. // Which image to present
  85. presentInfo.pImageIndices = image_index;
  86. auto result = vkQueuePresentKHR(device_.present_queue(), &presentInfo);
  87. current_frame_ = (current_frame_ + 1) % MAX_FRAMES_IN_FLIGHT;
  88. return result;
  89. }
  90. void coral_swapchain::init()
  91. {
  92. create_swapchain();
  93. create_image_views();
  94. create_render_pass();
  95. create_depth_resources();
  96. create_frame_buffers();
  97. create_sync_structures();
  98. }
  99. void coral_swapchain::create_swapchain()
  100. {
  101. SwapchainSupportDetails swap_chain_support = device_.get_swapchain_support();
  102. VkSurfaceFormatKHR surface_format = choose_swapchain_format(swap_chain_support.formats);
  103. VkPresentModeKHR present_mode = choose_present_mode(swap_chain_support.present_modes);
  104. VkExtent2D extent = choose_swap_extent(swap_chain_support.capabilities);
  105. uint32_t image_count = swap_chain_support.capabilities.minImageCount + 1;
  106. if (swap_chain_support.capabilities.maxImageCount > 0 &&
  107. image_count > swap_chain_support.capabilities.maxImageCount)
  108. {
  109. image_count = swap_chain_support.capabilities.maxImageCount;
  110. }
  111. VkSwapchainCreateInfoKHR create_info = {};
  112. create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
  113. create_info.surface = device_.surface();
  114. create_info.minImageCount = image_count;
  115. create_info.imageFormat = surface_format.format;
  116. create_info.imageColorSpace = surface_format.colorSpace;
  117. create_info.imageExtent = extent;
  118. create_info.imageArrayLayers = 1;
  119. create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
  120. QueueFamilyIndices indices = device_.find_physical_queue_families();
  121. uint32_t queueFamilyIndices[] = { indices.graphics_family, indices.present_family };
  122. if (indices.graphics_family != indices.present_family) {
  123. create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
  124. create_info.queueFamilyIndexCount = 2;
  125. create_info.pQueueFamilyIndices = queueFamilyIndices;
  126. }
  127. else
  128. {
  129. create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
  130. create_info.queueFamilyIndexCount = 0; // Optional
  131. create_info.pQueueFamilyIndices = nullptr; // Optional
  132. }
  133. create_info.preTransform = swap_chain_support.capabilities.currentTransform;
  134. create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
  135. create_info.presentMode = present_mode;
  136. create_info.clipped = VK_TRUE;
  137. create_info.oldSwapchain = old_swapchain_ == nullptr ? VK_NULL_HANDLE : old_swapchain_->swapchain_;
  138. if (vkCreateSwapchainKHR(device_.device(), &create_info, nullptr, &swapchain_) != VK_SUCCESS)
  139. throw std::runtime_error("ERROR! coral_swapchain::create_swapchain() >> Failed to create swapchain!");
  140. // we only specified a minimum number of images in the swap chain, so the implementation is
  141. // allowed to create a swap chain with more. That's why we'll first query the final number of
  142. // images with vkGetSwapchainImagesKHR, then resize the container and finally call it again to
  143. // retrieve the handles.
  144. vkGetSwapchainImagesKHR(device_.device(), swapchain_, &image_count, nullptr);
  145. swapchain_images_.resize(image_count);
  146. vkGetSwapchainImagesKHR(device_.device(), swapchain_, &image_count, swapchain_images_.data());
  147. swapchain_image_format_ = surface_format.format;
  148. swapchain_extent_ = extent;
  149. }
  150. void coral_swapchain::create_image_views()
  151. {
  152. swapchain_image_views_.resize(swapchain_images_.size());
  153. for (size_t i = 0; i < swapchain_images_.size(); i++)
  154. {
  155. VkImageViewCreateInfo viewInfo{ vkinit::image_view_ci(swapchain_image_format_, swapchain_images_[i], VK_IMAGE_ASPECT_COLOR_BIT)};
  156. if (vkCreateImageView(device_.device(), &viewInfo, nullptr, &swapchain_image_views_[i]) != VK_SUCCESS)
  157. throw std::runtime_error("ERROR! coral_swapchain::create_image_views() >> Failed to create image view!");
  158. }
  159. }
  160. void coral_swapchain::create_render_pass()
  161. {
  162. // Description of the image that we will be writing rendering commands to
  163. VkAttachmentDescription color_attachment{};
  164. // format should be the same as the swap chain images
  165. color_attachment.format = swapchain_image_format_;
  166. // MSAA samples, set to 1 (no MSAA) by default
  167. color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
  168. // Clear when render pass begins
  169. color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
  170. // Keep the attachment stored when render pass ends
  171. color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
  172. // Don't care about stencil data
  173. color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  174. color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
  175. // Image data layout before render pass starts (undefined = don't care)
  176. color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  177. // Image data layout after render pass (to change to), set to present by default
  178. color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
  179. VkAttachmentReference color_attachment_ref{};
  180. // Attachment number will index into the pAttachments array in the parent render pass itself
  181. color_attachment_ref.attachment = 0;
  182. // Optimal layout for writing to the image
  183. color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
  184. // Description of the depth image
  185. VkAttachmentDescription depth_attachment{};
  186. depth_attachment.flags = 0;
  187. depth_attachment.format = find_depth_format();
  188. depth_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
  189. depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
  190. depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
  191. depth_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
  192. depth_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
  193. depth_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
  194. depth_attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
  195. VkAttachmentReference depth_attachment_ref{};
  196. depth_attachment_ref.attachment = 1;
  197. depth_attachment_ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
  198. // Create one sub pass (minimum one sub pass required)
  199. VkSubpassDescription subpass{};
  200. subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
  201. subpass.colorAttachmentCount = 1;
  202. subpass.pColorAttachments = &color_attachment_ref;
  203. subpass.pDepthStencilAttachment = &depth_attachment_ref;
  204. // These dependencies tell Vulkan that the attachment cannot be used before the previous renderpasses have finished using it
  205. VkSubpassDependency dependency{};
  206. dependency.dstSubpass = 0;
  207. dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
  208. dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
  209. dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
  210. dependency.srcAccessMask = 0;
  211. dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
  212. std::array<VkAttachmentDescription, 2> attachments { color_attachment, depth_attachment };
  213. VkRenderPassCreateInfo render_pass_info{};
  214. render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
  215. render_pass_info.pNext = nullptr;
  216. // Connect the color attachment description to the info
  217. render_pass_info.attachmentCount = static_cast<uint32_t>(attachments.size());
  218. render_pass_info.pAttachments = attachments.data();
  219. // Connect the subpass(es) to the info
  220. render_pass_info.subpassCount = 1;
  221. render_pass_info.pSubpasses = &subpass;
  222. // Connect the dependency
  223. render_pass_info.dependencyCount = 1;
  224. render_pass_info.pDependencies = &dependency;
  225. if (vkCreateRenderPass(device_.device(), &render_pass_info, nullptr, &render_pass_) != VK_SUCCESS)
  226. throw std::runtime_error("ERROR! coral_swapchain::create_render_pass() >> Failed to create render pass!");
  227. deletion_queue_.deletors.emplace_back([=, this]() {
  228. vkDestroyRenderPass(device_.device(), render_pass_, nullptr);
  229. });
  230. }
  231. void coral_swapchain::create_depth_resources()
  232. {
  233. VkFormat depth_format{ find_depth_format() };
  234. swapchain_depth_format_ = depth_format;
  235. depth_images_.resize(image_count());
  236. depth_image_views_.resize(image_count());
  237. VkExtent3D depth_image_extent
  238. {
  239. swapchain_extent_.width,
  240. swapchain_extent_.height,
  241. 1
  242. };
  243. VkImageCreateInfo image_info{ vkinit::image_ci(depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, depth_image_extent) };
  244. for (size_t i = 0; i < depth_images_.size(); i++)
  245. {
  246. depth_images_[i] = device_.create_image(image_info, VMA_MEMORY_USAGE_GPU_ONLY);
  247. VkImageViewCreateInfo image_view_info{ vkinit::image_view_ci(depth_format, depth_images_[i].image, VK_IMAGE_ASPECT_DEPTH_BIT) };
  248. if (vkCreateImageView(device_.device(), &image_view_info, nullptr, &depth_image_views_[i]) != VK_SUCCESS)
  249. throw std::runtime_error("ERROR! coral_swapchain::create_depth_resources() >> Failed to create texture image view!");
  250. deletion_queue_.deletors.emplace_back([=, this]() {
  251. vkDestroyImageView(device_.device(), depth_image_views_[i], nullptr);
  252. vmaDestroyImage(device_.allocator(), depth_images_[i].image, depth_images_[i].allocation);
  253. });
  254. }
  255. }
  256. void coral_swapchain::create_frame_buffers()
  257. {
  258. swapchain_frame_buffers_.resize(image_count());
  259. VkFramebufferCreateInfo fb_info{};
  260. fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
  261. fb_info.renderPass = render_pass_;
  262. fb_info.width = swapchain_extent_.width;
  263. fb_info.height = swapchain_extent_.height;
  264. fb_info.layers = 1;
  265. for (size_t i = 0; i < swapchain_frame_buffers_.size(); i++)
  266. {
  267. std::array<VkImageView, 2> attachments{swapchain_image_views_[i], depth_image_views_[i]};
  268. fb_info.attachmentCount = static_cast<uint32_t>(attachments.size());
  269. fb_info.pAttachments = attachments.data();
  270. if (vkCreateFramebuffer(device_.device(), &fb_info, nullptr, &swapchain_frame_buffers_[i]) != VK_SUCCESS)
  271. throw std::runtime_error("ERROR! coral_swapchain::create_frame_buffers() >> Failed to create framebuffer!");
  272. deletion_queue_.deletors.emplace_back([=, this]() {
  273. vkDestroyFramebuffer(device_.device(), swapchain_frame_buffers_[i], nullptr);
  274. // vkDestroyImageView(device_.device(), swapchain_image_views_[i], nullptr);
  275. });
  276. }
  277. }
  278. void coral_swapchain::create_sync_structures()
  279. {
  280. // Render fence
  281. VkFenceCreateInfo fenceCreateInfo{ vkinit::fence_ci(VK_FENCE_CREATE_SIGNALED_BIT) };
  282. // Sempahore needs no flags
  283. VkSemaphoreCreateInfo semaphoreCreateInfo{ vkinit::semaphore_ci() };
  284. for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
  285. {
  286. if (vkCreateFence(device_.device(), &fenceCreateInfo, nullptr, &frames_[i].render_fence) != VK_SUCCESS)
  287. throw std::runtime_error("ERROR! coral_swapchain::create_sync_structures() >> Failed to create render fence!");
  288. deletion_queue_.deletors.emplace_back([=, this]() {
  289. vkDestroyFence(device_.device(), frames_[i].render_fence, nullptr);
  290. });
  291. if(vkCreateSemaphore(device_.device(), &semaphoreCreateInfo, nullptr, &frames_[i].present_semaphore) != VK_SUCCESS)
  292. throw std::runtime_error("ERROR! coral_swapchain::create_sync_structures() >> Failed to create present semaphore!");
  293. if(vkCreateSemaphore(device_.device(), &semaphoreCreateInfo, nullptr, &frames_[i].render_semaphore) != VK_SUCCESS)
  294. throw std::runtime_error("ERROR! coral_swapchain::create_sync_structures() >> Failed to create render semaphore!");
  295. deletion_queue_.deletors.emplace_back([=, this]()
  296. {
  297. vkDestroySemaphore(device_.device(), frames_[i].present_semaphore, nullptr);
  298. vkDestroySemaphore(device_.device(), frames_[i].render_semaphore, nullptr);
  299. });
  300. }
  301. }
  302. VkSurfaceFormatKHR coral_swapchain::choose_swapchain_format(const std::vector<VkSurfaceFormatKHR>& available_formats)
  303. {
  304. for (const auto& available_format : available_formats)
  305. {
  306. if (available_format.format == VK_FORMAT_B8G8R8A8_SRGB &&
  307. available_format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
  308. {
  309. return available_format;
  310. }
  311. }
  312. return available_formats[0];
  313. }
  314. VkPresentModeKHR coral_swapchain::choose_present_mode(const std::vector<VkPresentModeKHR>& available_present_modes)
  315. {
  316. for (const auto& available_present_mode : available_present_modes)
  317. {
  318. if (available_present_mode == VK_PRESENT_MODE_FIFO_KHR)
  319. {
  320. std::cout << "Present mode: FIFO" << std::endl;
  321. return available_present_mode;
  322. }
  323. }
  324. return VkPresentModeKHR();
  325. }
  326. VkExtent2D coral_swapchain::choose_swap_extent(const VkSurfaceCapabilitiesKHR& capabilities)
  327. {
  328. if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
  329. {
  330. return capabilities.currentExtent;
  331. }
  332. else
  333. {
  334. VkExtent2D actualExtent = window_extent_;
  335. actualExtent.width = std::max(
  336. capabilities.minImageExtent.width,
  337. std::min(capabilities.maxImageExtent.width, actualExtent.width));
  338. actualExtent.height = std::max(
  339. capabilities.minImageExtent.height,
  340. std::min(capabilities.maxImageExtent.height, actualExtent.height));
  341. return actualExtent;
  342. }
  343. }