7 #include "fml/synchronization/semaphore.h"
32 auto acquire_res = device.createFenceUnique(
33 vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled});
34 auto render_res = device.createSemaphoreUnique({});
35 if (acquire_res.result != vk::Result::eSuccess ||
36 render_res.result != vk::Result::eSuccess) {
40 acquire = std::move(acquire_res.value);
48 if (
auto result = device.waitForFences(
51 std::numeric_limits<uint64_t>::max()
53 result != vk::Result::eSuccess) {
57 if (
auto result = device.resetFences(*
acquire);
58 result != vk::Result::eSuccess) {
59 VALIDATION_LOG <<
"Could not reset fence: " << vk::to_string(result);
67 vk::SurfaceFormatKHR format) {
68 return std::find(formats.begin(), formats.end(), format) != formats.end();
72 const std::vector<vk::SurfaceFormatKHR>& formats,
74 const auto colorspace = vk::ColorSpaceKHR::eSrgbNonlinear;
75 const auto vk_preference =
81 std::vector<vk::SurfaceFormatKHR> options = {
82 {vk::Format::eB8G8R8A8Unorm, colorspace},
83 {vk::Format::eR8G8B8A8Unorm, colorspace}};
84 for (
const auto& format : options) {
94 vk::CompositeAlphaFlagsKHR flags) {
95 if (flags & vk::CompositeAlphaFlagBitsKHR::eInherit) {
96 return vk::CompositeAlphaFlagBitsKHR::eInherit;
98 if (flags & vk::CompositeAlphaFlagBitsKHR::ePreMultiplied) {
99 return vk::CompositeAlphaFlagBitsKHR::ePreMultiplied;
101 if (flags & vk::CompositeAlphaFlagBitsKHR::ePostMultiplied) {
102 return vk::CompositeAlphaFlagBitsKHR::ePostMultiplied;
104 if (flags & vk::CompositeAlphaFlagBitsKHR::eOpaque) {
105 return vk::CompositeAlphaFlagBitsKHR::eOpaque;
112 const std::shared_ptr<Context>& context,
113 vk::UniqueSurfaceKHR surface,
116 vk::SwapchainKHR old_swapchain) {
118 context, std::move(surface), size, enable_msaa, old_swapchain));
121 KHRSwapchainImplVK::KHRSwapchainImplVK(
const std::shared_ptr<Context>& context,
122 vk::UniqueSurfaceKHR surface,
125 vk::SwapchainKHR old_swapchain) {
133 const auto [caps_result, surface_caps] =
134 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface);
135 if (caps_result != vk::Result::eSuccess) {
137 << vk::to_string(caps_result);
141 auto [formats_result, formats] =
142 vk_context.GetPhysicalDevice().getSurfaceFormatsKHR(*surface);
143 if (formats_result != vk::Result::eSuccess) {
145 << vk::to_string(formats_result);
150 formats, vk_context.GetCapabilities()->GetDefaultColorFormat());
151 if (!format.has_value()) {
155 vk_context.SetOffscreenFormat(
ToPixelFormat(format.value().format));
157 const auto composite =
159 if (!composite.has_value()) {
164 vk::SwapchainCreateInfoKHR swapchain_info;
165 swapchain_info.surface = *surface;
166 swapchain_info.imageFormat = format.value().format;
167 swapchain_info.imageColorSpace = format.value().colorSpace;
168 swapchain_info.presentMode = vk::PresentModeKHR::eFifo;
169 swapchain_info.imageExtent = vk::Extent2D{
170 std::clamp(
static_cast<uint32_t
>(size.
width),
171 surface_caps.minImageExtent.width,
172 surface_caps.maxImageExtent.width),
173 std::clamp(
static_cast<uint32_t
>(size.
height),
174 surface_caps.minImageExtent.height,
175 surface_caps.maxImageExtent.height),
177 swapchain_info.minImageCount =
178 std::clamp(surface_caps.minImageCount + 1u,
179 surface_caps.minImageCount,
180 surface_caps.maxImageCount == 0u
181 ? surface_caps.minImageCount + 1u
182 : surface_caps.maxImageCount
184 swapchain_info.imageArrayLayers = 1u;
187 swapchain_info.imageUsage = vk::ImageUsageFlagBits::eColorAttachment |
188 vk::ImageUsageFlagBits::eInputAttachment;
189 swapchain_info.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
190 swapchain_info.compositeAlpha = composite.value();
194 swapchain_info.clipped =
true;
197 swapchain_info.imageSharingMode = vk::SharingMode::eExclusive;
198 swapchain_info.oldSwapchain = old_swapchain;
200 auto [swapchain_result, swapchain] =
201 vk_context.GetDevice().createSwapchainKHRUnique(swapchain_info);
202 if (swapchain_result != vk::Result::eSuccess) {
204 << vk::to_string(swapchain_result);
208 auto [images_result, images] =
209 vk_context.GetDevice().getSwapchainImagesKHR(*swapchain);
210 if (images_result != vk::Result::eSuccess) {
215 TextureDescriptor texture_desc;
218 texture_desc.format =
ToPixelFormat(swapchain_info.imageFormat);
219 texture_desc.size =
ISize::MakeWH(swapchain_info.imageExtent.width,
220 swapchain_info.imageExtent.height);
222 std::vector<std::shared_ptr<KHRSwapchainImageVK>> swapchain_images;
223 std::vector<vk::UniqueSemaphore> present_semaphores;
224 for (
const auto& image : images) {
225 auto swapchain_image = std::make_shared<KHRSwapchainImageVK>(
227 vk_context.GetDevice(),
230 if (!swapchain_image->IsValid()) {
235 vk_context.GetDevice(), swapchain_image->GetImage(),
236 "SwapchainImage" + std::to_string(swapchain_images.size()));
238 vk_context.GetDevice(), swapchain_image->GetImageView(),
239 "SwapchainImageView" + std::to_string(swapchain_images.size()));
241 swapchain_images.emplace_back(swapchain_image);
243 auto present_res = vk_context.GetDevice().createSemaphoreUnique({});
244 if (present_res.result != vk::Result::eSuccess) {
248 present_semaphores.push_back(std::move(present_res.value));
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 present_semaphores_ = std::move(present_semaphores);
272 current_frame_ = synchronizers_.size() - 1u;
274 enable_msaa_ = enable_msaa;
292 auto context = context_.lock();
298 const auto [result, surface_caps] =
299 vk_context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(surface_.get());
300 if (result != vk::Result::eSuccess) {
308 constexpr uint32_t kCurrentExtentsPlaceholder = 0xFFFFFFFF;
309 if (surface_caps.currentExtent.width == kCurrentExtentsPlaceholder ||
310 surface_caps.currentExtent.height == kCurrentExtentsPlaceholder) {
315 surface_caps.currentExtent.height);
322 void KHRSwapchainImplVK::WaitIdle()
const {
323 if (
auto context = context_.lock()) {
324 [[maybe_unused]]
auto result =
329 std::pair<vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR>
333 synchronizers_.clear();
336 return {std::move(surface_), std::move(swapchain_)};
340 return surface_format_;
344 return context_.lock();
348 auto context_strong = context_.lock();
349 if (!context_strong) {
355 current_frame_ = (current_frame_ + 1u) % synchronizers_.size();
357 const auto& sync = synchronizers_[current_frame_];
362 if (!sync->WaitForFence(context.GetDevice())) {
374 auto [acq_result, index] = context.GetDevice().acquireNextImageKHR(
376 std::numeric_limits<uint64_t>::max(),
381 switch (acq_result) {
382 case vk::Result::eSuccess:
385 case vk::Result::eSuboptimalKHR:
386 case vk::Result::eErrorOutOfDateKHR:
393 << vk::to_string(acq_result);
397 if (index >= images_.size()) {
403 context.GetGPUTracer()->MarkFrameStart();
405 auto image = images_[index % images_.size()];
406 uint32_t image_index = index;
410 [weak_swapchain = weak_from_this(), image, image_index]() ->
bool {
411 auto swapchain = weak_swapchain.lock();
415 return swapchain->Present(image, image_index);
421 std::shared_ptr<CommandBuffer> cmd_buffer) {
422 const auto& sync = synchronizers_[current_frame_];
423 sync->final_cmd_buffer = std::move(cmd_buffer);
424 sync->has_onscreen =
true;
427 bool KHRSwapchainImplVK::Present(
428 const std::shared_ptr<KHRSwapchainImageVK>& image,
430 auto context_strong = context_.lock();
431 if (!context_strong) {
436 const auto& sync = synchronizers_[current_frame_];
437 context.GetGPUTracer()->MarkFrameEnd();
442 if (!sync->has_onscreen) {
443 sync->final_cmd_buffer = context.CreateCommandBuffer();
445 sync->has_onscreen =
false;
446 if (!sync->final_cmd_buffer) {
450 auto vk_final_cmd_buffer =
454 barrier.new_layout = vk::ImageLayout::ePresentSrcKHR;
455 barrier.cmd_buffer = vk_final_cmd_buffer;
456 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
457 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
458 barrier.dst_access = {};
459 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
461 if (!image->SetLayout(barrier).ok()) {
465 if (vk_final_cmd_buffer.end() != vk::Result::eSuccess) {
474 vk::SubmitInfo submit_info;
475 vk::PipelineStageFlags wait_stage =
476 vk::PipelineStageFlagBits::eColorAttachmentOutput;
477 submit_info.setWaitDstStageMask(wait_stage);
478 submit_info.setWaitSemaphores(*sync->render_ready);
479 submit_info.setSignalSemaphores(*present_semaphores_[index]);
480 submit_info.setCommandBuffers(vk_final_cmd_buffer);
482 context.GetGraphicsQueue()->Submit(submit_info, *sync->acquire);
483 if (result != vk::Result::eSuccess) {
485 << vk::to_string(result);
493 uint32_t indices[] = {
static_cast<uint32_t
>(index)};
495 vk::PresentInfoKHR present_info;
496 present_info.setSwapchains(*swapchain_);
497 present_info.setImageIndices(indices);
498 present_info.setWaitSemaphores(*present_semaphores_[index]);
500 auto result = context.GetGraphicsQueue()->Present(present_info);
503 case vk::Result::eErrorOutOfDateKHR:
506 case vk::Result::eErrorSurfaceLostKHR:
510 case vk::Result::eSuboptimalKHR:
516 case vk::Result::eSuccess:
519 VALIDATION_LOG <<
"Could not present queue: " << vk::to_string(result);
static ContextVK & Cast(Context &base)
vk::CommandBuffer GetCommandBuffer() const
Retrieve the native command buffer from this object.
bool SetDebugName(T handle, std::string_view label) const
const vk::Device & GetDevice() const
An instance of a swapchain that does NOT adapt to going out of date with the underlying surface....
std::shared_ptr< Context > GetContext() const
void AddFinalCommandBuffer(std::shared_ptr< CommandBuffer > cmd_buffer)
static std::shared_ptr< KHRSwapchainImplVK > Create(const std::shared_ptr< Context > &context, vk::UniqueSurfaceKHR surface, const ISize &size, bool enable_msaa=true, vk::SwapchainKHR old_swapchain=VK_NULL_HANDLE)
AcquireResult AcquireNextDrawable()
vk::Format GetSurfaceFormat() const
std::optional< ISize > GetCurrentUnderlyingSurfaceSize() const
std::pair< vk::UniqueSurfaceKHR, vk::UniqueSwapchainKHR > DestroySwapchain()
const ISize & GetSize() const
static std::unique_ptr< SurfaceVK > WrapSwapchainImage(const std::shared_ptr< SwapchainTransientsVK > &transients, const std::shared_ptr< TextureSourceVK > &swapchain_image, SwapCallback swap_callback)
Wrap the swapchain image in a Surface, which provides the additional configuration required for usage...
constexpr PixelFormat ToPixelFormat(vk::Format format)
static std::optional< vk::SurfaceFormatKHR > ChooseSurfaceFormat(const std::vector< vk::SurfaceFormatKHR > &formats, PixelFormat preference)
static constexpr size_t kMaxFramesInFlight
static bool ContainsFormat(const std::vector< vk::SurfaceFormatKHR > &formats, vk::SurfaceFormatKHR format)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
constexpr vk::Format ToVKImageFormat(PixelFormat format)
static std::optional< vk::CompositeAlphaFlagBitsKHR > ChooseAlphaCompositionMode(vk::CompositeAlphaFlagsKHR flags)
vk::UniqueSemaphore render_ready
bool WaitForFence(const vk::Device &device)
KHRFrameSynchronizerVK(const vk::Device &device)
~KHRFrameSynchronizerVK()=default
std::shared_ptr< CommandBuffer > final_cmd_buffer
static constexpr TSize MakeWH(Type width, Type height)