7 #include "fml/synchronization/count_down_latch.h"
17 #include "vulkan/vulkan_structs.hpp"
39 auto acquire_res = device.createFenceUnique(
40 vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled});
41 auto render_res = device.createSemaphoreUnique({});
42 auto present_res = device.createSemaphoreUnique({});
43 if (acquire_res.result != vk::Result::eSuccess ||
44 render_res.result != vk::Result::eSuccess ||
45 present_res.result != vk::Result::eSuccess) {
49 acquire = std::move(acquire_res.value);
60 if (
auto result = device.waitForFences(
63 std::numeric_limits<uint64_t>::max()
65 result != vk::Result::eSuccess) {
69 if (
auto result = device.resetFences(*
acquire);
70 result != vk::Result::eSuccess) {
71 VALIDATION_LOG <<
"Could not reset fence: " << vk::to_string(result);
80 vk::SurfaceFormatKHR format) {
81 return std::find(formats.begin(), formats.end(), format) != formats.end();
85 const std::vector<vk::SurfaceFormatKHR>& formats,
87 const auto colorspace = vk::ColorSpaceKHR::eSrgbNonlinear;
88 const auto vk_preference =
94 std::vector<vk::SurfaceFormatKHR> options = {
95 {vk::Format::eB8G8R8A8Unorm, colorspace},
96 {vk::Format::eR8G8B8A8Unorm, colorspace}};
97 for (
const auto& format : options) {
107 vk::CompositeAlphaFlagsKHR flags) {
108 if (flags & vk::CompositeAlphaFlagBitsKHR::eInherit) {
109 return vk::CompositeAlphaFlagBitsKHR::eInherit;
111 if (flags & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) {
112 return vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
114 if (flags & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) {
115 return vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
117 if (flags & vk::CompositeAlphaFlagBitsKHR::eOpaque) {
118 return vk::CompositeAlphaFlagBitsKHR::eOpaque;
125 const vk::PhysicalDevice& physical_device,
126 const vk::Device& device,
127 const vk::SurfaceKHR& surface) {
128 const auto families = physical_device.getQueueFamilyProperties();
129 for (
size_t family_index = 0u; family_index < families.size();
131 auto [result, supported] =
132 physical_device.getSurfaceSupportKHR(family_index, surface);
133 if (result == vk::Result::eSuccess && supported) {
134 return device.getQueue(family_index, 0u);
141 const std::shared_ptr<Context>& context,
142 vk::UniqueSurfaceKHR surface,
143 vk::SwapchainKHR old_swapchain,
144 vk::SurfaceTransformFlagBitsKHR last_transform) {
146 context, std::move(surface), old_swapchain, last_transform));
149 SwapchainImplVK::SwapchainImplVK(
150 const std::shared_ptr<Context>& context,
151 vk::UniqueSurfaceKHR surface,
152 vk::SwapchainKHR old_swapchain,
153 vk::SurfaceTransformFlagBitsKHR last_transform) {
161 auto [caps_result, caps] =
162 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface);
163 if (caps_result != vk::Result::eSuccess) {
165 << vk::to_string(caps_result);
169 auto [formats_result, formats] =
170 vk_context.GetPhysicalDevice().getSurfaceFormatsKHR(*surface);
171 if (formats_result != vk::Result::eSuccess) {
173 << vk::to_string(formats_result);
178 formats, vk_context.GetCapabilities()->GetDefaultColorFormat());
179 if (!format.has_value()) {
183 vk_context.SetOffscreenFormat(
ToPixelFormat(format.value().format));
185 const auto composite =
187 if (!composite.has_value()) {
193 vk_context.GetDevice(),
196 if (!present_queue.has_value()) {
201 vk::SwapchainCreateInfoKHR swapchain_info;
202 swapchain_info.surface = *surface;
203 swapchain_info.imageFormat = format.value().format;
204 swapchain_info.imageColorSpace = format.value().colorSpace;
205 swapchain_info.presentMode = vk::PresentModeKHR::eFifo;
206 swapchain_info.imageExtent = vk::Extent2D{
207 std::clamp(caps.currentExtent.width, caps.minImageExtent.width,
208 caps.maxImageExtent.width),
209 std::clamp(caps.currentExtent.height, caps.minImageExtent.height,
210 caps.maxImageExtent.height),
212 swapchain_info.minImageCount = std::clamp(
213 caps.minImageCount + 1u,
215 caps.maxImageCount == 0u ? caps.minImageCount + 1u
218 swapchain_info.imageArrayLayers = 1u;
221 swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
222 vk::ImageUsageFlagBits::eTransferDst;
223 swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
224 swapchain_info.compositeAlpha = composite.value();
228 swapchain_info.clipped =
true;
231 swapchain_info.imageSharingMode = vk::SharingMode::eExclusive;
232 swapchain_info.oldSwapchain = old_swapchain;
234 auto [swapchain_result, swapchain] =
235 vk_context.GetDevice().createSwapchainKHRUnique(swapchain_info);
236 if (swapchain_result != vk::Result::eSuccess) {
238 << vk::to_string(swapchain_result);
242 auto [images_result, images] =
243 vk_context.GetDevice().getSwapchainImagesKHR(*swapchain);
244 if (images_result != vk::Result::eSuccess) {
249 TextureDescriptor texture_desc;
253 texture_desc.format =
ToPixelFormat(swapchain_info.imageFormat);
254 texture_desc.size =
ISize::MakeWH(swapchain_info.imageExtent.width,
255 swapchain_info.imageExtent.height);
257 std::vector<std::shared_ptr<SwapchainImageVK>> swapchain_images;
258 for (
const auto& image : images) {
259 auto swapchain_image =
260 std::make_shared<SwapchainImageVK>(texture_desc,
261 vk_context.GetDevice(),
264 if (!swapchain_image->IsValid()) {
270 vk_context.GetDevice(), swapchain_image->GetImage(),
271 "SwapchainImage" + std::to_string(swapchain_images.size()));
273 vk_context.GetDevice(), swapchain_image->GetImageView(),
274 "SwapchainImageView" + std::to_string(swapchain_images.size()));
276 swapchain_images.emplace_back(swapchain_image);
279 std::vector<std::unique_ptr<FrameSynchronizer>> synchronizers;
281 auto sync = std::make_unique<FrameSynchronizer>(vk_context.GetDevice());
282 if (!sync->is_valid) {
286 synchronizers.emplace_back(std::move(sync));
288 FML_DCHECK(!synchronizers.empty());
291 surface_ = std::move(surface);
292 present_queue_ = present_queue.value();
293 surface_format_ = swapchain_info.imageFormat;
294 swapchain_ = std::move(swapchain);
295 images_ = std::move(swapchain_images);
296 synchronizers_ = std::move(synchronizers);
297 current_frame_ = synchronizers_.size() - 1u;
299 transform_if_changed_discard_swapchain_ = last_transform;
310 void SwapchainImplVK::WaitIdle()
const {
311 if (
auto context = context_.lock()) {
312 [[maybe_unused]]
auto result =
317 std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
321 synchronizers_.clear();
324 return {std::move(surface_), std::move(swapchain_)};
328 return surface_format_;
332 return transform_if_changed_discard_swapchain_;
336 return context_.lock();
340 auto context_strong = context_.lock();
341 if (!context_strong) {
347 current_frame_ = (current_frame_ + 1u) % synchronizers_.size();
349 const auto& sync = synchronizers_[current_frame_];
354 if (!sync->WaitForFence(context.GetDevice())) {
363 current_transform_poll_count_++;
365 current_transform_poll_count_ = 0u;
366 auto [caps_result, caps] =
367 context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface_);
368 if (caps_result != vk::Result::eSuccess) {
370 << vk::to_string(caps_result);
373 if (caps.currentTransform != transform_if_changed_discard_swapchain_) {
374 transform_if_changed_discard_swapchain_ = caps.currentTransform;
382 auto [acq_result, index] = context.GetDevice().acquireNextImageKHR(
384 1
'000'000
'000, // timeout (ns) 1000ms
385 *sync->render_ready, // signal semaphore
389 switch (acq_result) {
390 case vk::Result::eSuccess:
393 case vk::Result::eSuboptimalKHR:
394 case vk::Result::eErrorOutOfDateKHR:
395 // A recoverable error. Just say we are out of date.
396 return AcquireResult{true /* out of date */};
399 // An unrecoverable error.
400 VALIDATION_LOG << "Could not acquire next swapchain image: "
401 << vk::to_string(acq_result);
402 return AcquireResult{false /* out of date */};
405 if (index >= images_.size()) {
406 VALIDATION_LOG << "Swapchain returned an invalid image index.";
407 return SwapchainImplVK::AcquireResult{};
411 context.GetGPUTracer()->MarkFrameStart();
413 auto image = images_[index % images_.size()];
414 uint32_t image_index = index;
415 return AcquireResult{SurfaceVK::WrapSwapchainImage(
416 context_strong, // context
417 image, // swapchain image
418 [weak_swapchain = weak_from_this(), image, image_index]() -> bool {
419 auto swapchain = weak_swapchain.lock();
423 return swapchain->Present(image, image_index);
428 bool SwapchainImplVK::Present(const std::shared_ptr<SwapchainImageVK>& image,
430 auto context_strong = context_.lock();
431 if (!context_strong) {
435 const auto& context = ContextVK::Cast(*context_strong);
436 const auto& sync = synchronizers_[current_frame_];
438 //----------------------------------------------------------------------------
441 sync->final_cmd_buffer = context.CreateCommandBuffer();
442 if (!sync->final_cmd_buffer) {
446 auto vk_final_cmd_buffer = CommandBufferVK::Cast(*sync->final_cmd_buffer)
448 ->GetCommandBuffer();
451 barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
452 barrier.cmd_buffer = vk_final_cmd_buffer;
453 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
454 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
455 barrier.dst_access = {};
456 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
458 if (!image->SetLayout(barrier).ok()) {
462 if (vk_final_cmd_buffer.end() != vk::Result::eSuccess) {
467 //----------------------------------------------------------------------------
471 vk::SubmitInfo submit_info;
472 vk::PipelineStageFlags wait_stage =
473 vk::PipelineStageFlagBits::eColorAttachmentOutput;
474 submit_info.setWaitDstStageMask(wait_stage);
475 submit_info.setWaitSemaphores(*sync->render_ready);
476 submit_info.setSignalSemaphores(*sync->present_ready);
477 submit_info.setCommandBuffers(vk_final_cmd_buffer);
479 context.GetGraphicsQueue()->Submit(submit_info, *sync->acquire);
480 if (result != vk::Result::eSuccess) {
481 VALIDATION_LOG << "Could not wait on render semaphore: "
482 << vk::to_string(result);
487 context.GetGPUTracer()->MarkFrameEnd();
489 auto task = [&, index, current_frame = current_frame_] {
490 auto context_strong = context_.lock();
491 if (!context_strong) {
495 const auto& sync = synchronizers_[current_frame];
497 //----------------------------------------------------------------------------
500 uint32_t indices[] = {static_cast<uint32_t>(index)};
502 vk::PresentInfoKHR present_info;
503 present_info.setSwapchains(*swapchain_);
504 present_info.setImageIndices(indices);
505 present_info.setWaitSemaphores(*sync->present_ready);
507 auto result = present_queue_.presentKHR(present_info);
508 sync->present_latch->CountDown();
511 case vk::Result::eErrorOutOfDateKHR:
512 // Caller will recreate the impl on acquisition, not submission.
514 case vk::Result::eErrorSurfaceLostKHR:
515 // Vulkan guarantees that the set of queue operations will still
516 // complete successfully.
518 case vk::Result::eSuboptimalKHR:
519 // Even though we're handling rotation changes via polling, we
524 case vk::Result::eSuccess:
527 VALIDATION_LOG <<
"Could not present queue: " << vk::to_string(result);
532 if (context.GetSyncPresentation()) {
535 context.GetQueueSubmitRunner()->PostTask(task);