diff --git a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc index 504f2a538bce1..81871c54862c4 100644 --- a/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_impl_vk.cc @@ -10,11 +10,17 @@ #include "impeller/renderer/backend/vulkan/formats_vk.h" #include "impeller/renderer/backend/vulkan/surface_vk.h" #include "impeller/renderer/backend/vulkan/swapchain_image_vk.h" +#include "vulkan/vulkan_structs.hpp" namespace impeller { static constexpr size_t kMaxFramesInFlight = 3u; +// Number of frames to poll for orientation changes. For example `1u` means +// that the orientation will be polled every frame, while `2u` means that the +// orientation will be polled every other frame. +static constexpr size_t kPollFramesForOrientation = 1u; + struct FrameSynchronizer { vk::UniqueFence acquire; vk::UniqueSemaphore render_ready; @@ -124,16 +130,17 @@ static std::optional ChoosePresentQueue( std::shared_ptr SwapchainImplVK::Create( const std::shared_ptr& context, vk::UniqueSurfaceKHR surface, - bool was_rotated, - vk::SwapchainKHR old_swapchain) { + vk::SwapchainKHR old_swapchain, + vk::SurfaceTransformFlagBitsKHR last_transform) { return std::shared_ptr(new SwapchainImplVK( - context, std::move(surface), was_rotated, old_swapchain)); + context, std::move(surface), old_swapchain, last_transform)); } -SwapchainImplVK::SwapchainImplVK(const std::shared_ptr& context, - vk::UniqueSurfaceKHR surface, - bool was_rotated, - vk::SwapchainKHR old_swapchain) { +SwapchainImplVK::SwapchainImplVK( + const std::shared_ptr& context, + vk::UniqueSurfaceKHR surface, + vk::SwapchainKHR old_swapchain, + vk::SurfaceTransformFlagBitsKHR last_transform) { if (!context) { return; } @@ -278,8 +285,7 @@ SwapchainImplVK::SwapchainImplVK(const std::shared_ptr& context, synchronizers_ = std::move(synchronizers); current_frame_ = synchronizers_.size() - 1u; is_valid_ = true; - was_rotated_ = was_rotated; - is_rotated_ = was_rotated; + transform_if_changed_discard_swapchain_ = last_transform; } SwapchainImplVK::~SwapchainImplVK() { @@ -311,6 +317,10 @@ vk::Format SwapchainImplVK::GetSurfaceFormat() const { return surface_format_; } +vk::SurfaceTransformFlagBitsKHR SwapchainImplVK::GetLastTransform() const { + return transform_if_changed_discard_swapchain_; +} + std::shared_ptr SwapchainImplVK::GetContext() const { return context_.lock(); } @@ -321,10 +331,6 @@ SwapchainImplVK::AcquireResult SwapchainImplVK::AcquireNextDrawable() { return {}; } - if (was_rotated_ != is_rotated_) { - return AcquireResult{true /* out of date */}; - } - const auto& context = ContextVK::Cast(*context_strong); current_frame_ = (current_frame_ + 1u) % synchronizers_.size(); @@ -339,6 +345,26 @@ SwapchainImplVK::AcquireResult SwapchainImplVK::AcquireNextDrawable() { return {}; } + //---------------------------------------------------------------------------- + /// Poll to see if the orientation has changed. + /// + /// https://developer.android.com/games/optimize/vulkan-prerotation#using_polling + current_transform_poll_count_++; + if (current_transform_poll_count_ >= kPollFramesForOrientation) { + current_transform_poll_count_ = 0u; + auto [caps_result, caps] = + context.GetPhysicalDevice().getSurfaceCapabilitiesKHR(*surface_); + if (caps_result != vk::Result::eSuccess) { + VALIDATION_LOG << "Could not get surface capabilities: " + << vk::to_string(caps_result); + return {}; + } + if (caps.currentTransform != transform_if_changed_discard_swapchain_) { + transform_if_changed_discard_swapchain_ = caps.currentTransform; + return AcquireResult{true /* out of date */}; + } + } + //---------------------------------------------------------------------------- /// Get the next image index. /// @@ -349,11 +375,6 @@ SwapchainImplVK::AcquireResult SwapchainImplVK::AcquireNextDrawable() { nullptr // fence ); - if (acq_result == vk::Result::eSuboptimalKHR) { - is_rotated_ = true; - return AcquireResult{true /* out of date */}; - } - if (acq_result == vk::Result::eErrorOutOfDateKHR) { return AcquireResult{true /* out of date */}; } @@ -469,11 +490,13 @@ bool SwapchainImplVK::Present(const std::shared_ptr& image, // Vulkan guarantees that the set of queue operations will still // complete successfully. [[fallthrough]]; - case vk::Result::eSuccess: - is_rotated_ = false; - return; case vk::Result::eSuboptimalKHR: - is_rotated_ = true; + // Even though we're handling rotation changes via polling, we + // still need to handle the case where the swapchain signals that + // it's suboptimal (i.e. every frame when we are rotated given we + // aren't doing Vulkan pre-rotation). + [[fallthrough]]; + case vk::Result::eSuccess: return; default: VALIDATION_LOG << "Could not present queue: " diff --git a/impeller/renderer/backend/vulkan/swapchain_impl_vk.h b/impeller/renderer/backend/vulkan/swapchain_impl_vk.h index 7f061971acc1b..cacc6b2494b6b 100644 --- a/impeller/renderer/backend/vulkan/swapchain_impl_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain_impl_vk.h @@ -9,6 +9,7 @@ #include "flutter/fml/macros.h" #include "impeller/renderer/backend/vulkan/vk.h" +#include "vulkan/vulkan_enums.hpp" namespace impeller { @@ -31,8 +32,9 @@ class SwapchainImplVK final static std::shared_ptr Create( const std::shared_ptr& context, vk::UniqueSurfaceKHR surface, - bool was_rotated, - vk::SwapchainKHR old_swapchain = VK_NULL_HANDLE); + vk::SwapchainKHR old_swapchain = VK_NULL_HANDLE, + vk::SurfaceTransformFlagBitsKHR last_transform = + vk::SurfaceTransformFlagBitsKHR::eIdentity); ~SwapchainImplVK(); @@ -48,12 +50,12 @@ class SwapchainImplVK final : surface(std::move(p_surface)) {} }; - bool GetIsRotated() const { return is_rotated_; } - AcquireResult AcquireNextDrawable(); vk::Format GetSurfaceFormat() const; + vk::SurfaceTransformFlagBitsKHR GetLastTransform() const; + std::shared_ptr GetContext() const; std::pair DestroySwapchain(); @@ -68,14 +70,13 @@ class SwapchainImplVK final std::vector> synchronizers_; size_t current_frame_ = 0u; bool is_valid_ = false; - - bool was_rotated_ = false; - bool is_rotated_ = false; + size_t current_transform_poll_count_ = 0u; + vk::SurfaceTransformFlagBitsKHR transform_if_changed_discard_swapchain_; SwapchainImplVK(const std::shared_ptr& context, vk::UniqueSurfaceKHR surface, - bool was_rotated, - vk::SwapchainKHR old_swapchain); + vk::SwapchainKHR old_swapchain, + vk::SurfaceTransformFlagBitsKHR last_transform); bool Present(const std::shared_ptr& image, uint32_t index); diff --git a/impeller/renderer/backend/vulkan/swapchain_vk.cc b/impeller/renderer/backend/vulkan/swapchain_vk.cc index ee958a44003c8..0a09a9eb371e0 100644 --- a/impeller/renderer/backend/vulkan/swapchain_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain_vk.cc @@ -12,8 +12,7 @@ namespace impeller { std::shared_ptr SwapchainVK::Create( const std::shared_ptr& context, vk::UniqueSurfaceKHR surface) { - auto impl = SwapchainImplVK::Create(context, std::move(surface), - /*was_rotated=*/false); + auto impl = SwapchainImplVK::Create(context, std::move(surface)); if (!impl || !impl->IsValid()) { return nullptr; } @@ -46,13 +45,12 @@ std::unique_ptr SwapchainVK::AcquireNextDrawable() { // This swapchain implementation indicates that it is out of date. Tear it // down and make a new one. auto context = impl_->GetContext(); - auto was_rotated = impl_->GetIsRotated(); auto [surface, old_swapchain] = impl_->DestroySwapchain(); - auto new_impl = SwapchainImplVK::Create(context, // - std::move(surface), // - was_rotated, // - *old_swapchain // + auto new_impl = SwapchainImplVK::Create(context, // + std::move(surface), // + *old_swapchain, // + impl_->GetLastTransform() // ); if (!new_impl || !new_impl->IsValid()) { VALIDATION_LOG << "Could not update swapchain.";