7 #include "fml/synchronization/semaphore.h"
18 #include "vulkan/vulkan_structs.hpp"
37 auto acquire_res = device.createFenceUnique(
38 vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled});
39 auto render_res = device.createSemaphoreUnique({});
40 auto present_res = device.createSemaphoreUnique({});
41 if (acquire_res.result != vk::Result::eSuccess ||
42 render_res.result != vk::Result::eSuccess ||
43 present_res.result != vk::Result::eSuccess) {
47 acquire = std::move(acquire_res.value);
56 if (
auto result = device.waitForFences(
59 std::numeric_limits<uint64_t>::max()
61 result != vk::Result::eSuccess) {
65 if (
auto result = device.resetFences(*
acquire);
66 result != vk::Result::eSuccess) {
67 VALIDATION_LOG <<
"Could not reset fence: " << vk::to_string(result);
75 vk::SurfaceFormatKHR format) {
76 return std::find(formats.begin(), formats.end(), format) != formats.end();
80 const std::vector<vk::SurfaceFormatKHR>& formats,
82 const auto colorspace = vk::ColorSpaceKHR::eSrgbNonlinear;
83 const auto vk_preference =
89 std::vector<vk::SurfaceFormatKHR> options = {
90 {vk::Format::eB8G8R8A8Unorm, colorspace},
91 {vk::Format::eR8G8B8A8Unorm, colorspace}};
92 for (
const auto& format : options) {
102 vk::CompositeAlphaFlagsKHR flags) {
103 if (flags & vk::CompositeAlphaFlagBitsKHR::eInherit) {
104 return vk::CompositeAlphaFlagBitsKHR::eInherit;
106 if (flags & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) {
107 return vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
109 if (flags & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) {
110 return vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
112 if (flags & vk::CompositeAlphaFlagBitsKHR::eOpaque) {
113 return vk::CompositeAlphaFlagBitsKHR::eOpaque;
120 const std::shared_ptr<Context>& context,
121 vk::UniqueSurfaceKHR surface,
124 vk::SwapchainKHR old_swapchain) {
126 context, std::move(surface), size, enable_msaa, old_swapchain));
129 KHRSwapchainImplVK::KHRSwapchainImplVK(
const std::shared_ptr<Context>& context,
130 vk::UniqueSurfaceKHR surface,
133 vk::SwapchainKHR old_swapchain) {
141 const auto [caps_result, surface_caps] =
142 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface);
143 if (caps_result != vk::Result::eSuccess) {
145 << vk::to_string(caps_result);
149 auto [formats_result, formats] =
150 vk_context.GetPhysicalDevice().getSurfaceFormatsKHR(*surface);
151 if (formats_result != vk::Result::eSuccess) {
153 << vk::to_string(formats_result);
158 formats, vk_context.GetCapabilities()->GetDefaultColorFormat());
159 if (!format.has_value()) {
163 vk_context.SetOffscreenFormat(
ToPixelFormat(format.value().format));
165 const auto composite =
167 if (!composite.has_value()) {
172 vk::SwapchainCreateInfoKHR swapchain_info;
173 swapchain_info.surface = *surface;
174 swapchain_info.imageFormat = format.value().format;
175 swapchain_info.imageColorSpace = format.value().colorSpace;
176 swapchain_info.presentMode = vk::PresentModeKHR::eFifo;
177 swapchain_info.imageExtent = vk::Extent2D{
178 std::clamp(
static_cast<uint32_t
>(size.
width),
179 surface_caps.minImageExtent.width,
180 surface_caps.maxImageExtent.width),
181 std::clamp(
static_cast<uint32_t
>(size.
height),
182 surface_caps.minImageExtent.height,
183 surface_caps.maxImageExtent.height),
185 swapchain_info.minImageCount =
186 std::clamp(surface_caps.minImageCount + 1u,
187 surface_caps.minImageCount,
188 surface_caps.maxImageCount == 0u
189 ? surface_caps.minImageCount + 1u
190 : surface_caps.maxImageCount
192 swapchain_info.imageArrayLayers = 1u;
195 swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
196 vk::ImageUsageFlagBits::eTransferDst |
197 vk::ImageUsageFlagBits::eInputAttachment;
198 swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
199 swapchain_info.compositeAlpha = composite.value();
203 swapchain_info.clipped =
true;
206 swapchain_info.imageSharingMode = vk::SharingMode::eExclusive;
207 swapchain_info.oldSwapchain = old_swapchain;
209 auto [swapchain_result, swapchain] =
210 vk_context.GetDevice().createSwapchainKHRUnique(swapchain_info);
211 if (swapchain_result != vk::Result::eSuccess) {
213 << vk::to_string(swapchain_result);
217 auto [images_result, images] =
218 vk_context.GetDevice().getSwapchainImagesKHR(*swapchain);
219 if (images_result != vk::Result::eSuccess) {
224 TextureDescriptor texture_desc;
227 texture_desc.format =
ToPixelFormat(swapchain_info.imageFormat);
228 texture_desc.size =
ISize::MakeWH(swapchain_info.imageExtent.width,
229 swapchain_info.imageExtent.height);
233 TextureDescriptor msaa_desc;
237 msaa_desc.format = texture_desc.format;
238 msaa_desc.size = texture_desc.size;
244 TextureDescriptor depth_stencil_desc;
253 depth_stencil_desc.format =
254 context->GetCapabilities()->GetDefaultDepthStencilFormat();
255 depth_stencil_desc.size = texture_desc.size;
258 std::shared_ptr<Texture> msaa_texture;
260 msaa_texture = context->GetResourceAllocator()->CreateTexture(msaa_desc);
262 std::shared_ptr<Texture> depth_stencil_texture =
263 context->GetResourceAllocator()->CreateTexture(depth_stencil_desc);
265 std::vector<std::shared_ptr<KHRSwapchainImageVK>> swapchain_images;
266 for (
const auto& image : images) {
267 auto swapchain_image = std::make_shared<KHRSwapchainImageVK>(
269 vk_context.GetDevice(),
272 if (!swapchain_image->IsValid()) {
276 swapchain_image->SetMSAATexture(msaa_texture);
277 swapchain_image->SetDepthStencilTexture(depth_stencil_texture);
280 vk_context.GetDevice(), swapchain_image->GetImage(),
281 "SwapchainImage" + std::to_string(swapchain_images.size()));
283 vk_context.GetDevice(), swapchain_image->GetImageView(),
284 "SwapchainImageView" + std::to_string(swapchain_images.size()));
286 swapchain_images.emplace_back(swapchain_image);
289 std::vector<std::unique_ptr<KHRFrameSynchronizerVK>> synchronizers;
292 std::make_unique<KHRFrameSynchronizerVK>(vk_context.GetDevice());
293 if (!sync->is_valid) {
297 synchronizers.emplace_back(std::move(sync));
299 FML_DCHECK(!synchronizers.empty());
302 surface_ = std::move(surface);
303 surface_format_ = swapchain_info.imageFormat;
304 swapchain_ = std::move(swapchain);
305 images_ = std::move(swapchain_images);
306 synchronizers_ = std::move(synchronizers);
307 current_frame_ = synchronizers_.size() - 1u;
309 enable_msaa_ = enable_msaa;
325 void KHRSwapchainImplVK::WaitIdle()
const {
326 if (
auto context = context_.lock()) {
327 [[maybe_unused]]
auto result =
332 std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
336 synchronizers_.clear();
339 return {std::move(surface_), std::move(swapchain_)};
343 return surface_format_;
347 return context_.lock();
351 auto context_strong = context_.lock();
352 if (!context_strong) {
358 current_frame_ = (current_frame_ + 1u) % synchronizers_.size();
360 const auto& sync = synchronizers_[current_frame_];
365 if (!sync->WaitForFence(context.GetDevice())) {
373 auto [acq_result, index] = context.GetDevice().acquireNextImageKHR(
375 1
'000'000
'000, // timeout (ns) 1000ms
376 *sync->render_ready, // signal semaphore
380 switch (acq_result) {
381 case vk::Result::eSuccess:
384 case vk::Result::eSuboptimalKHR:
385 case vk::Result::eErrorOutOfDateKHR:
386 // A recoverable error. Just say we are out of date.
387 return AcquireResult{true /* out of date */};
390 // An unrecoverable error.
391 VALIDATION_LOG << "Could not acquire next swapchain image: "
392 << vk::to_string(acq_result);
393 return AcquireResult{false /* out of date */};
396 if (index >= images_.size()) {
397 VALIDATION_LOG << "Swapchain returned an invalid image index.";
398 return KHRSwapchainImplVK::AcquireResult{};
402 context.GetGPUTracer()->MarkFrameStart();
404 auto image = images_[index % images_.size()];
405 uint32_t image_index = index;
406 return AcquireResult{KHRSurfaceVK::WrapSwapchainImage(
407 context_strong, // context
408 image, // swapchain image
409 [weak_swapchain = weak_from_this(), image, image_index]() -> bool {
410 auto swapchain = weak_swapchain.lock();
414 return swapchain->Present(image, image_index);
420 bool KHRSwapchainImplVK::Present(
421 const std::shared_ptr<KHRSwapchainImageVK>& image,
423 auto context_strong = context_.lock();
424 if (!context_strong) {
428 const auto& context = ContextVK::Cast(*context_strong);
429 const auto& sync = synchronizers_[current_frame_];
430 context.GetGPUTracer()->MarkFrameEnd();
432 //----------------------------------------------------------------------------
435 sync->final_cmd_buffer = context.CreateCommandBuffer();
436 if (!sync->final_cmd_buffer) {
440 auto vk_final_cmd_buffer = CommandBufferVK::Cast(*sync->final_cmd_buffer)
442 ->GetCommandBuffer();
445 barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
446 barrier.cmd_buffer = vk_final_cmd_buffer;
447 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
448 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
449 barrier.dst_access = {};
450 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
452 if (!image->SetLayout(barrier).ok()) {
456 if (vk_final_cmd_buffer.end() != vk::Result::eSuccess) {
461 //----------------------------------------------------------------------------
465 vk::SubmitInfo submit_info;
466 vk::PipelineStageFlags wait_stage =
467 vk::PipelineStageFlagBits::eColorAttachmentOutput;
468 submit_info.setWaitDstStageMask(wait_stage);
469 submit_info.setWaitSemaphores(*sync->render_ready);
470 submit_info.setSignalSemaphores(*sync->present_ready);
471 submit_info.setCommandBuffers(vk_final_cmd_buffer);
473 context.GetGraphicsQueue()->Submit(submit_info, *sync->acquire);
474 if (result != vk::Result::eSuccess) {
475 VALIDATION_LOG << "Could not wait on render semaphore: "
476 << vk::to_string(result);
481 //----------------------------------------------------------------------------
484 uint32_t indices[] = {static_cast<uint32_t>(index)};
486 vk::PresentInfoKHR present_info;
487 present_info.setSwapchains(*swapchain_);
488 present_info.setImageIndices(indices);
489 present_info.setWaitSemaphores(*sync->present_ready);
491 auto result = context.GetGraphicsQueue()->Present(present_info);
494 case vk::Result::eErrorOutOfDateKHR:
495 // Caller will recreate the impl on acquisition, not submission.
497 case vk::Result::eErrorSurfaceLostKHR:
498 // Vulkan guarantees that the set of queue operations will still
499 // complete successfully.
501 case vk::Result::eSuboptimalKHR:
502 // Even though we're handling rotation changes via polling, we
507 case vk::Result::eSuccess:
510 VALIDATION_LOG <<
"Could not present queue: " << vk::to_string(result);