13 #include "vulkan/vulkan_structs.hpp"
32 auto acquire_res = device.createFenceUnique(
33 vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled});
34 auto render_res = device.createSemaphoreUnique({});
35 auto present_res = device.createSemaphoreUnique({});
36 if (acquire_res.result != vk::Result::eSuccess ||
37 render_res.result != vk::Result::eSuccess ||
38 present_res.result != vk::Result::eSuccess) {
42 acquire = std::move(acquire_res.value);
51 if (
auto result = device.waitForFences(
54 std::numeric_limits<uint64_t>::max()
56 result != vk::Result::eSuccess) {
60 if (
auto result = device.resetFences(*
acquire);
61 result != vk::Result::eSuccess) {
62 VALIDATION_LOG <<
"Could not reset fence: " << vk::to_string(result);
70 vk::SurfaceFormatKHR format) {
71 return std::find(formats.begin(), formats.end(), format) != formats.end();
75 const std::vector<vk::SurfaceFormatKHR>& formats,
77 const auto colorspace = vk::ColorSpaceKHR::eSrgbNonlinear;
78 const auto vk_preference =
84 std::vector<vk::SurfaceFormatKHR> options = {
85 {vk::Format::eB8G8R8A8Unorm, colorspace},
86 {vk::Format::eR8G8B8A8Unorm, colorspace}};
87 for (
const auto& format : options) {
97 vk::CompositeAlphaFlagsKHR flags) {
98 if (flags & vk::CompositeAlphaFlagBitsKHR::eInherit) {
99 return vk::CompositeAlphaFlagBitsKHR::eInherit;
101 if (flags & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) {
102 return vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
104 if (flags & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) {
105 return vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
107 if (flags & vk::CompositeAlphaFlagBitsKHR::eOpaque) {
108 return vk::CompositeAlphaFlagBitsKHR::eOpaque;
115 const vk::PhysicalDevice& physical_device,
116 const vk::Device& device,
117 const vk::SurfaceKHR& surface) {
118 const auto families = physical_device.getQueueFamilyProperties();
119 for (
size_t family_index = 0u; family_index < families.size();
121 auto [result, supported] =
122 physical_device.getSurfaceSupportKHR(family_index, surface);
123 if (result == vk::Result::eSuccess && supported) {
124 return device.getQueue(family_index, 0u);
131 const std::shared_ptr<Context>& context,
132 vk::UniqueSurfaceKHR surface,
133 vk::SwapchainKHR old_swapchain,
134 vk::SurfaceTransformFlagBitsKHR last_transform) {
136 context, std::move(surface), old_swapchain, last_transform));
139 SwapchainImplVK::SwapchainImplVK(
140 const std::shared_ptr<Context>& context,
141 vk::UniqueSurfaceKHR surface,
142 vk::SwapchainKHR old_swapchain,
143 vk::SurfaceTransformFlagBitsKHR last_transform) {
150 auto [caps_result, caps] =
151 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface);
152 if (caps_result != vk::Result::eSuccess) {
154 << vk::to_string(caps_result);
158 auto [formats_result, formats] =
159 vk_context.GetPhysicalDevice().getSurfaceFormatsKHR(*surface);
160 if (formats_result != vk::Result::eSuccess) {
162 << vk::to_string(formats_result);
167 formats, vk_context.GetCapabilities()->GetDefaultColorFormat());
168 if (!format.has_value()) {
172 vk_context.SetOffscreenFormat(
ToPixelFormat(format.value().format));
174 const auto composite =
176 if (!composite.has_value()) {
182 vk_context.GetDevice(),
185 if (!present_queue.has_value()) {
190 vk::SwapchainCreateInfoKHR swapchain_info;
191 swapchain_info.surface = *surface;
192 swapchain_info.imageFormat = format.value().format;
193 swapchain_info.imageColorSpace = format.value().colorSpace;
194 swapchain_info.presentMode = vk::PresentModeKHR::eFifo;
195 swapchain_info.imageExtent = vk::Extent2D{
196 std::clamp(caps.currentExtent.width, caps.minImageExtent.width,
197 caps.maxImageExtent.width),
198 std::clamp(caps.currentExtent.height, caps.minImageExtent.height,
199 caps.maxImageExtent.height),
201 swapchain_info.minImageCount = std::clamp(
202 caps.minImageCount + 1u,
204 caps.maxImageCount == 0u ? caps.minImageCount + 1u
207 swapchain_info.imageArrayLayers = 1u;
210 swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
211 vk::ImageUsageFlagBits::eTransferDst;
212 swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
213 swapchain_info.compositeAlpha = composite.value();
217 swapchain_info.clipped =
true;
220 swapchain_info.imageSharingMode = vk::SharingMode::eExclusive;
221 swapchain_info.oldSwapchain = old_swapchain;
223 auto [swapchain_result, swapchain] =
224 vk_context.GetDevice().createSwapchainKHRUnique(swapchain_info);
225 if (swapchain_result != vk::Result::eSuccess) {
227 << vk::to_string(swapchain_result);
231 auto [images_result, images] =
232 vk_context.GetDevice().getSwapchainImagesKHR(*swapchain);
233 if (images_result != vk::Result::eSuccess) {
238 TextureDescriptor texture_desc;
242 texture_desc.format =
ToPixelFormat(swapchain_info.imageFormat);
243 texture_desc.size =
ISize::MakeWH(swapchain_info.imageExtent.width,
244 swapchain_info.imageExtent.height);
246 std::vector<std::shared_ptr<SwapchainImageVK>> swapchain_images;
247 for (
const auto& image : images) {
248 auto swapchain_image =
249 std::make_shared<SwapchainImageVK>(texture_desc,
250 vk_context.GetDevice(),
253 if (!swapchain_image->IsValid()) {
259 vk_context.GetDevice(), swapchain_image->GetImage(),
260 "SwapchainImage" + std::to_string(swapchain_images.size()));
262 vk_context.GetDevice(), swapchain_image->GetImageView(),
263 "SwapchainImageView" + std::to_string(swapchain_images.size()));
265 swapchain_images.emplace_back(swapchain_image);
268 std::vector<std::unique_ptr<FrameSynchronizer>> synchronizers;
270 auto sync = std::make_unique<FrameSynchronizer>(vk_context.GetDevice());
271 if (!sync->is_valid) {
275 synchronizers.emplace_back(std::move(sync));
277 FML_DCHECK(!synchronizers.empty());
280 surface_ = std::move(surface);
281 present_queue_ = present_queue.value();
282 surface_format_ = swapchain_info.imageFormat;
283 swapchain_ = std::move(swapchain);
284 images_ = std::move(swapchain_images);
285 synchronizers_ = std::move(synchronizers);
286 current_frame_ = synchronizers_.size() - 1u;
288 transform_if_changed_discard_swapchain_ = last_transform;
299 void SwapchainImplVK::WaitIdle()
const {
300 if (
auto context = context_.lock()) {
301 [[maybe_unused]]
auto result =
306 std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
310 synchronizers_.clear();
313 return {std::move(surface_), std::move(swapchain_)};
317 return surface_format_;
321 return transform_if_changed_discard_swapchain_;
325 return context_.lock();
329 auto context_strong = context_.lock();
330 if (!context_strong) {
336 current_frame_ = (current_frame_ + 1u) % synchronizers_.size();
338 const auto& sync = synchronizers_[current_frame_];
343 if (!sync->WaitForFence(context.GetDevice())) {
352 current_transform_poll_count_++;
354 current_transform_poll_count_ = 0u;
355 auto [caps_result, caps] =
356 context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface_);
357 if (caps_result != vk::Result::eSuccess) {
359 << vk::to_string(caps_result);
362 if (caps.currentTransform != transform_if_changed_discard_swapchain_) {
363 transform_if_changed_discard_swapchain_ = caps.currentTransform;
371 auto [acq_result, index] = context.GetDevice().acquireNextImageKHR(
373 1
'000'000
'000, // timeout (ns) 1000ms
374 *sync->render_ready, // signal semaphore
378 switch (acq_result) {
379 case vk::Result::eSuccess:
382 case vk::Result::eSuboptimalKHR:
383 case vk::Result::eErrorOutOfDateKHR:
384 // A recoverable error. Just say we are out of date.
385 return AcquireResult{true /* out of date */};
388 // An unrecoverable error.
389 VALIDATION_LOG << "Could not acquire next swapchain image: "
390 << vk::to_string(acq_result);
391 return AcquireResult{false /* out of date */};
394 if (index >= images_.size()) {
395 VALIDATION_LOG << "Swapchain returned an invalid image index.";
399 auto image = images_[index % images_.size()];
400 uint32_t image_index = index;
401 return AcquireResult{SurfaceVK::WrapSwapchainImage(
402 context_strong, // context
403 image, // swapchain image
404 [weak_swapchain = weak_from_this(), image, image_index]() -> bool {
405 auto swapchain = weak_swapchain.lock();
409 return swapchain->Present(image, image_index);
414 bool SwapchainImplVK::Present(const std::shared_ptr<SwapchainImageVK>& image,
416 auto context_strong = context_.lock();
417 if (!context_strong) {
421 const auto& context = ContextVK::Cast(*context_strong);
422 const auto& sync = synchronizers_[current_frame_];
424 //----------------------------------------------------------------------------
427 sync->final_cmd_buffer = context.CreateCommandBuffer();
428 if (!sync->final_cmd_buffer) {
432 auto vk_final_cmd_buffer = CommandBufferVK::Cast(*sync->final_cmd_buffer)
434 ->GetCommandBuffer();
437 barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
438 barrier.cmd_buffer = vk_final_cmd_buffer;
439 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
440 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
441 barrier.dst_access = {};
442 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
444 if (!image->SetLayout(barrier).ok()) {
448 if (vk_final_cmd_buffer.end() != vk::Result::eSuccess) {
453 //----------------------------------------------------------------------------
457 vk::SubmitInfo submit_info;
458 vk::PipelineStageFlags wait_stage =
459 vk::PipelineStageFlagBits::eColorAttachmentOutput;
460 submit_info.setWaitDstStageMask(wait_stage);
461 submit_info.setWaitSemaphores(*sync->render_ready);
462 submit_info.setSignalSemaphores(*sync->present_ready);
463 submit_info.setCommandBuffers(vk_final_cmd_buffer);
465 context.GetGraphicsQueue()->Submit(submit_info, *sync->acquire);
466 if (result != vk::Result::eSuccess) {
467 VALIDATION_LOG << "Could not wait on render semaphore: "
468 << vk::to_string(result);
473 auto task = [&, index, current_frame = current_frame_] {
474 auto context_strong = context_.lock();
475 if (!context_strong) {
479 const auto& sync = synchronizers_[current_frame];
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 switch (auto result = present_queue_.presentKHR(present_info)) {
492 case vk::Result::eErrorOutOfDateKHR:
493 // Caller will recreate the impl on acquisition, not submission.
495 case vk::Result::eErrorSurfaceLostKHR:
496 // Vulkan guarantees that the set of queue operations will still
497 // complete successfully.
499 case vk::Result::eSuboptimalKHR:
500 // Even though we're handling rotation changes via polling, we
505 case vk::Result::eSuccess:
508 VALIDATION_LOG <<
"Could not present queue: " << vk::to_string(result);
513 if (context.GetSyncPresentation()) {
516 context.GetConcurrentWorkerTaskRunner()->PostTask(task);