7 #include "fml/synchronization/semaphore.h"
36 auto acquire_res = device.createFenceUnique(
37 vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled});
38 auto render_res = device.createSemaphoreUnique({});
39 auto present_res = device.createSemaphoreUnique({});
40 if (acquire_res.result != vk::Result::eSuccess ||
41 render_res.result != vk::Result::eSuccess ||
42 present_res.result != vk::Result::eSuccess) {
46 acquire = std::move(acquire_res.value);
55 if (
auto result = device.waitForFences(
58 std::numeric_limits<uint64_t>::max()
60 result != vk::Result::eSuccess) {
64 if (
auto result = device.resetFences(*
acquire);
65 result != vk::Result::eSuccess) {
66 VALIDATION_LOG <<
"Could not reset fence: " << vk::to_string(result);
74 vk::SurfaceFormatKHR format) {
75 return std::find(formats.begin(), formats.end(), format) != formats.end();
79 const std::vector<vk::SurfaceFormatKHR>& formats,
81 const auto colorspace = vk::ColorSpaceKHR::eSrgbNonlinear;
82 const auto vk_preference =
88 std::vector<vk::SurfaceFormatKHR> options = {
89 {vk::Format::eB8G8R8A8Unorm, colorspace},
90 {vk::Format::eR8G8B8A8Unorm, colorspace}};
91 for (
const auto& format : options) {
101 vk::CompositeAlphaFlagsKHR flags) {
102 if (flags & vk::CompositeAlphaFlagBitsKHR::eInherit) {
103 return vk::CompositeAlphaFlagBitsKHR::eInherit;
105 if (flags & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) {
106 return vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
108 if (flags & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) {
109 return vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
111 if (flags & vk::CompositeAlphaFlagBitsKHR::eOpaque) {
112 return vk::CompositeAlphaFlagBitsKHR::eOpaque;
119 const std::shared_ptr<Context>& context,
120 vk::UniqueSurfaceKHR surface,
123 vk::SwapchainKHR old_swapchain) {
125 context, std::move(surface), size, enable_msaa, old_swapchain));
128 KHRSwapchainImplVK::KHRSwapchainImplVK(
const std::shared_ptr<Context>& context,
129 vk::UniqueSurfaceKHR surface,
132 vk::SwapchainKHR old_swapchain) {
140 const auto [caps_result, surface_caps] =
141 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface);
142 if (caps_result != vk::Result::eSuccess) {
144 << vk::to_string(caps_result);
148 auto [formats_result, formats] =
149 vk_context.GetPhysicalDevice().getSurfaceFormatsKHR(*surface);
150 if (formats_result != vk::Result::eSuccess) {
152 << vk::to_string(formats_result);
157 formats, vk_context.GetCapabilities()->GetDefaultColorFormat());
158 if (!format.has_value()) {
162 vk_context.SetOffscreenFormat(
ToPixelFormat(format.value().format));
164 const auto composite =
166 if (!composite.has_value()) {
171 vk::SwapchainCreateInfoKHR swapchain_info;
172 swapchain_info.surface = *surface;
173 swapchain_info.imageFormat = format.value().format;
174 swapchain_info.imageColorSpace = format.value().colorSpace;
175 swapchain_info.presentMode = vk::PresentModeKHR::eFifo;
176 swapchain_info.imageExtent = vk::Extent2D{
177 std::clamp(
static_cast<uint32_t
>(size.
width),
178 surface_caps.minImageExtent.width,
179 surface_caps.maxImageExtent.width),
180 std::clamp(
static_cast<uint32_t
>(size.
height),
181 surface_caps.minImageExtent.height,
182 surface_caps.maxImageExtent.height),
184 swapchain_info.minImageCount =
185 std::clamp(surface_caps.minImageCount + 1u,
186 surface_caps.minImageCount,
187 surface_caps.maxImageCount == 0u
188 ? surface_caps.minImageCount + 1u
189 : surface_caps.maxImageCount
191 swapchain_info.imageArrayLayers = 1u;
194 swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
195 vk::ImageUsageFlagBits::eTransferDst |
196 vk::ImageUsageFlagBits::eInputAttachment;
197 swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
198 swapchain_info.compositeAlpha = composite.value();
202 swapchain_info.clipped =
true;
205 swapchain_info.imageSharingMode = vk::SharingMode::eExclusive;
206 swapchain_info.oldSwapchain = old_swapchain;
208 auto [swapchain_result, swapchain] =
209 vk_context.GetDevice().createSwapchainKHRUnique(swapchain_info);
210 if (swapchain_result != vk::Result::eSuccess) {
212 << vk::to_string(swapchain_result);
216 auto [images_result, images] =
217 vk_context.GetDevice().getSwapchainImagesKHR(*swapchain);
218 if (images_result != vk::Result::eSuccess) {
223 TextureDescriptor texture_desc;
226 texture_desc.format =
ToPixelFormat(swapchain_info.imageFormat);
227 texture_desc.size =
ISize::MakeWH(swapchain_info.imageExtent.width,
228 swapchain_info.imageExtent.height);
230 std::vector<std::shared_ptr<KHRSwapchainImageVK>> swapchain_images;
231 for (
const auto& image : images) {
232 auto swapchain_image = std::make_shared<KHRSwapchainImageVK>(
234 vk_context.GetDevice(),
237 if (!swapchain_image->IsValid()) {
242 vk_context.GetDevice(), swapchain_image->GetImage(),
243 "SwapchainImage" + std::to_string(swapchain_images.size()));
245 vk_context.GetDevice(), swapchain_image->GetImageView(),
246 "SwapchainImageView" + std::to_string(swapchain_images.size()));
248 swapchain_images.emplace_back(swapchain_image);
251 std::vector<std::unique_ptr<KHRFrameSynchronizerVK>> synchronizers;
254 std::make_unique<KHRFrameSynchronizerVK>(vk_context.GetDevice());
255 if (!sync->is_valid) {
259 synchronizers.emplace_back(std::move(sync));
261 FML_DCHECK(!synchronizers.empty());
264 surface_ = std::move(surface);
265 surface_format_ = swapchain_info.imageFormat;
266 swapchain_ = std::move(swapchain);
267 transients_ = std::make_shared<SwapchainTransientsVK>(context, texture_desc,
269 images_ = std::move(swapchain_images);
270 synchronizers_ = std::move(synchronizers);
271 current_frame_ = synchronizers_.size() - 1u;
273 enable_msaa_ = enable_msaa;
289 void KHRSwapchainImplVK::WaitIdle()
const {
290 if (
auto context = context_.lock()) {
291 [[maybe_unused]]
auto result =
296 std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
300 synchronizers_.clear();
303 return {std::move(surface_), std::move(swapchain_)};
307 return surface_format_;
311 return context_.lock();
315 auto context_strong = context_.lock();
316 if (!context_strong) {
322 current_frame_ = (current_frame_ + 1u) % synchronizers_.size();
324 const auto& sync = synchronizers_[current_frame_];
329 if (!sync->WaitForFence(context.GetDevice())) {
337 auto [acq_result, index] = context.GetDevice().acquireNextImageKHR(
339 1
'000'000
'000, // timeout (ns) 1000ms
340 *sync->render_ready, // signal semaphore
344 switch (acq_result) {
345 case vk::Result::eSuccess:
348 case vk::Result::eSuboptimalKHR:
349 case vk::Result::eErrorOutOfDateKHR:
350 // A recoverable error. Just say we are out of date.
351 return AcquireResult{true /* out of date */};
354 // An unrecoverable error.
355 VALIDATION_LOG << "Could not acquire next swapchain image: "
356 << vk::to_string(acq_result);
357 return AcquireResult{false /* out of date */};
360 if (index >= images_.size()) {
361 VALIDATION_LOG << "Swapchain returned an invalid image index.";
362 return KHRSwapchainImplVK::AcquireResult{};
366 context.GetGPUTracer()->MarkFrameStart();
368 auto image = images_[index % images_.size()];
369 uint32_t image_index = index;
370 return AcquireResult{SurfaceVK::WrapSwapchainImage(
371 transients_, // transients
372 image, // swapchain image
373 [weak_swapchain = weak_from_this(), image, image_index]() -> bool {
374 auto swapchain = weak_swapchain.lock();
378 return swapchain->Present(image, image_index);
383 bool KHRSwapchainImplVK::Present(
384 const std::shared_ptr<KHRSwapchainImageVK>& image,
386 auto context_strong = context_.lock();
387 if (!context_strong) {
391 const auto& context = ContextVK::Cast(*context_strong);
392 const auto& sync = synchronizers_[current_frame_];
393 context.GetGPUTracer()->MarkFrameEnd();
395 //----------------------------------------------------------------------------
398 sync->final_cmd_buffer = context.CreateCommandBuffer();
399 if (!sync->final_cmd_buffer) {
403 auto vk_final_cmd_buffer = CommandBufferVK::Cast(*sync->final_cmd_buffer)
405 ->GetCommandBuffer();
408 barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
409 barrier.cmd_buffer = vk_final_cmd_buffer;
410 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
411 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
412 barrier.dst_access = {};
413 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
415 if (!image->SetLayout(barrier).ok()) {
419 if (vk_final_cmd_buffer.end() != vk::Result::eSuccess) {
424 //----------------------------------------------------------------------------
428 vk::SubmitInfo submit_info;
429 vk::PipelineStageFlags wait_stage =
430 vk::PipelineStageFlagBits::eColorAttachmentOutput;
431 submit_info.setWaitDstStageMask(wait_stage);
432 submit_info.setWaitSemaphores(*sync->render_ready);
433 submit_info.setSignalSemaphores(*sync->present_ready);
434 submit_info.setCommandBuffers(vk_final_cmd_buffer);
436 context.GetGraphicsQueue()->Submit(submit_info, *sync->acquire);
437 if (result != vk::Result::eSuccess) {
438 VALIDATION_LOG << "Could not wait on render semaphore: "
439 << vk::to_string(result);
444 //----------------------------------------------------------------------------
447 uint32_t indices[] = {static_cast<uint32_t>(index)};
449 vk::PresentInfoKHR present_info;
450 present_info.setSwapchains(*swapchain_);
451 present_info.setImageIndices(indices);
452 present_info.setWaitSemaphores(*sync->present_ready);
454 auto result = context.GetGraphicsQueue()->Present(present_info);
457 case vk::Result::eErrorOutOfDateKHR:
458 // Caller will recreate the impl on acquisition, not submission.
460 case vk::Result::eErrorSurfaceLostKHR:
461 // Vulkan guarantees that the set of queue operations will still
462 // complete successfully.
464 case vk::Result::eSuboptimalKHR:
465 // Even though we're handling rotation changes via polling, we
470 case vk::Result::eSuccess:
473 VALIDATION_LOG <<
"Could not present queue: " << vk::to_string(result);