From 49643c4e7b3fc8bfa1ccfdc9d212e2ddc0560af3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 12 Apr 2024 11:18:20 -0700 Subject: [PATCH] [Impeller] Wire up hardware buffer backed swapchains on Android. This wires up Android Hardware Buffer backed swapchains on platform that support it (Android >= 29) with a KHR swapchain fallback (which can be removed later to save on some binary size if needed). Some salient features of these swapchains and their differences with the KHR variant: * All swapchain images are guaranteed to R8G8B8A8. This could potentially allow for earlier creation of the context and the PSO libraries. * All swapchain allocations are lazy. This should greatly reduce the time it takes to create and resize a swapchain. However, swapchain image acquisition may take longer for the first few frame where there is no pre-pooled image available. Resizes should be similarly faster since the swapchain images for the intermediate sizes are never created. * Swapchain transients allocations (the attachments for the root render target) are also lazily allocated. * Swapchain images are pool and reused. The size of the pool is user specified (currently 2). If an image in the pool ages past a user supplied value (currently 1 second), it is collected as well. Applications that don't render frames for a long period of time should see less memory use because of swapchain image allocations. * The present mode of AHB swapchains behave similar to KHR swapchains but with VK_PRESENT_MODE_MAILBOX_KHR. In cases where there is no application managed frame pipelining, this might cause images to never be presented if a newer image is available. This wasted work can only be avoided by application provided pipelining. * There are no client side waits during image presentation. Instead, a new type of fence is wired up that exports its state as a sync file descriptor. The fence signal operation is enqueued on the client side and the buffer is set on the surface control. The presentation engine then performs the wait. * On Qualcomm devices, Chromium seems to be setting vendor specified flags for opting the hardware buffers into using UBWC. AFAICT, this is similar to AFBC (and NOT AFRC) on ARM Mali. This has not been wired up since I don't have a Qualcomm device at the moment and cant verify bandwidth use using GPU counters. I would also like to verify that UBWC is safe to use to images that can be used as input attachments. --- ci/licenses_golden/licenses_flutter | 18 ++ fml/closure.h | 13 +- impeller/renderer/backend/vulkan/BUILD.gn | 13 + .../vulkan/android/ahb_texture_source_vk.cc | 43 +++- .../vulkan/android/ahb_texture_source_vk.h | 11 +- .../backend/vulkan/capabilities_vk.cc | 4 + .../renderer/backend/vulkan/capabilities_vk.h | 14 ++ .../vulkan/driver_info_vk_unittests.cc | 4 +- impeller/renderer/backend/vulkan/queue_vk.cc | 5 + impeller/renderer/backend/vulkan/queue_vk.h | 2 + .../backend/vulkan/shared_object_vk.h | 2 + .../backend/vulkan/surface_context_vk.cc | 40 +--- .../backend/vulkan/surface_context_vk.h | 8 +- .../vulkan/swapchain/ahb/ahb_formats.h | 23 ++ .../swapchain/ahb/ahb_swapchain_impl_vk.cc | 222 ++++++++++++++++++ .../swapchain/ahb/ahb_swapchain_impl_vk.h | 125 ++++++++++ .../vulkan/swapchain/ahb/ahb_swapchain_vk.cc | 71 ++++++ .../vulkan/swapchain/ahb/ahb_swapchain_vk.h | 65 +++++ .../swapchain/ahb/ahb_texture_pool_vk.cc | 96 ++++++++ .../swapchain/ahb/ahb_texture_pool_vk.h | 123 ++++++++++ .../vulkan/swapchain/ahb/external_fence_vk.cc | 64 +++++ .../vulkan/swapchain/ahb/external_fence_vk.h | 83 +++++++ .../backend/vulkan/swapchain/swapchain_vk.cc | 64 ++++- .../backend/vulkan/swapchain/swapchain_vk.h | 12 + impeller/toolkit/android/hardware_buffer.cc | 48 +++- impeller/toolkit/android/hardware_buffer.h | 28 +++ impeller/toolkit/android/proc_table.h | 2 + impeller/toolkit/android/surface_control.h | 3 + .../toolkit/android/surface_transaction.cc | 13 +- .../toolkit/android/surface_transaction.h | 10 +- .../android_surface_vulkan_impeller.cc | 31 ++- 31 files changed, 1181 insertions(+), 79 deletions(-) create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.cc create mode 100644 impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 2daa27b314f39..a04f3fb83cf9c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -40731,6 +40731,15 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/shared_object_vk.cc + ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/shared_object_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/surface_context_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_image_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_image_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc + ../../../flutter/LICENSE @@ -43609,6 +43618,15 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/shared_object_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/shared_object_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/surface_context_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/surface_context_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.cc +FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_image_vk.cc FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_image_vk.h FILE: ../../../flutter/impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_impl_vk.cc diff --git a/fml/closure.h b/fml/closure.h index 3a0de0dcbbc6d..264a95f7462a0 100644 --- a/fml/closure.h +++ b/fml/closure.h @@ -45,11 +45,7 @@ class ScopedCleanupClosure final { explicit ScopedCleanupClosure(const fml::closure& closure) : closure_(closure) {} - ~ScopedCleanupClosure() { - if (closure_) { - closure_(); - } - } + ~ScopedCleanupClosure() { Reset(); } fml::closure SetClosure(const fml::closure& closure) { auto old_closure = closure_; @@ -63,6 +59,13 @@ class ScopedCleanupClosure final { return closure; } + void Reset() { + if (closure_) { + closure_(); + closure_ = nullptr; + } + } + private: fml::closure closure_; diff --git a/impeller/renderer/backend/vulkan/BUILD.gn b/impeller/renderer/backend/vulkan/BUILD.gn index 36cc15cc58c4b..04693bf9dab13 100644 --- a/impeller/renderer/backend/vulkan/BUILD.gn +++ b/impeller/renderer/backend/vulkan/BUILD.gn @@ -133,6 +133,15 @@ impeller_component("vulkan") { sources += [ "android/ahb_texture_source_vk.cc", "android/ahb_texture_source_vk.h", + "swapchain/ahb/ahb_formats.h", + "swapchain/ahb/ahb_swapchain_impl_vk.cc", + "swapchain/ahb/ahb_swapchain_impl_vk.h", + "swapchain/ahb/ahb_swapchain_vk.cc", + "swapchain/ahb/ahb_swapchain_vk.h", + "swapchain/ahb/ahb_texture_pool_vk.cc", + "swapchain/ahb/ahb_texture_pool_vk.h", + "swapchain/ahb/external_fence_vk.cc", + "swapchain/ahb/external_fence_vk.h", ] } @@ -144,4 +153,8 @@ impeller_component("vulkan") { "//flutter/third_party/vulkan-deps/vulkan-headers/src:vulkan_headers", "//flutter/third_party/vulkan_memory_allocator", ] + + if (is_android) { + public_deps += [ "../../../toolkit/android" ] + } } diff --git a/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.cc b/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.cc index 2ff45078fa5dc..c03d450daa9a3 100644 --- a/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.cc +++ b/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.cc @@ -40,6 +40,11 @@ static vk::UniqueImage CreateVKImageWrapperForAndroidHarwareBuffer( if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) { image_usage_flags |= vk::ImageUsageFlagBits::eColorAttachment; } + if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY) { + image_usage_flags |= vk::ImageUsageFlagBits::eColorAttachment; + image_usage_flags |= vk::ImageUsageFlagBits::eInputAttachment; + image_usage_flags |= vk::ImageUsageFlagBits::eTransferDst; + } vk::ImageCreateFlags image_create_flags; if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) { @@ -280,17 +285,18 @@ static TextureDescriptor ToTextureDescriptor( } AHBTextureSourceVK::AHBTextureSourceVK( - const std::shared_ptr& context, + const std::shared_ptr& p_context, struct AHardwareBuffer* ahb, const AHardwareBuffer_Desc& ahb_desc) : TextureSourceVK(ToTextureDescriptor(ahb_desc)) { - if (!context) { - VALIDATION_LOG << "Invalid context."; + if (!p_context) { return; } - const auto& device = context->GetDevice(); - const auto& physical_device = context->GetPhysicalDevice(); + const auto& context = ContextVK::Cast(*p_context); + + const auto& device = context.GetDevice(); + const auto& physical_device = context.GetPhysicalDevice(); AHBProperties ahb_props; @@ -327,7 +333,7 @@ AHBTextureSourceVK::AHBTextureSourceVK( } // Figure out how to perform YUV conversions. - auto yuv_conversion = CreateYUVConversion(*context, ahb_props); + auto yuv_conversion = CreateYUVConversion(context, ahb_props); if (!yuv_conversion || !yuv_conversion->IsValid()) { return; } @@ -350,15 +356,26 @@ AHBTextureSourceVK::AHBTextureSourceVK( image_view_ = std::move(image_view); #ifdef IMPELLER_DEBUG - context->SetDebugName(device_memory_.get(), "AHB Device Memory"); - context->SetDebugName(image_.get(), "AHB Image"); - context->SetDebugName(yuv_conversion_->GetConversion(), "AHB YUV Conversion"); - context->SetDebugName(image_view_.get(), "AHB ImageView"); + context.SetDebugName(device_memory_.get(), "AHB Device Memory"); + context.SetDebugName(image_.get(), "AHB Image"); + context.SetDebugName(yuv_conversion_->GetConversion(), "AHB YUV Conversion"); + context.SetDebugName(image_view_.get(), "AHB ImageView"); #endif // IMPELLER_DEBUG is_valid_ = true; } +AHBTextureSourceVK::AHBTextureSourceVK( + const std::shared_ptr& context, + std::unique_ptr backing_store, + bool is_swapchain_image) + : AHBTextureSourceVK(context, + backing_store->GetHandle(), + backing_store->GetAndroidDescriptor()) { + backing_store_ = std::move(backing_store); + is_swapchain_image_ = is_swapchain_image; +} + // |TextureSourceVK| AHBTextureSourceVK::~AHBTextureSourceVK() = default; @@ -383,7 +400,7 @@ vk::ImageView AHBTextureSourceVK::GetRenderTargetView() const { // |TextureSourceVK| bool AHBTextureSourceVK::IsSwapchainImage() const { - return false; + return is_swapchain_image_; } // |TextureSourceVK| @@ -391,4 +408,8 @@ std::shared_ptr AHBTextureSourceVK::GetYUVConversion() const { return needs_yuv_conversion_ ? yuv_conversion_ : nullptr; } +const android::HardwareBuffer* AHBTextureSourceVK::GetBackingStore() const { + return backing_store_.get(); +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h b/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h index c36a7c5652e4e..546996a421ae5 100644 --- a/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h +++ b/impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h @@ -11,6 +11,7 @@ #include "impeller/renderer/backend/vulkan/texture_source_vk.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/backend/vulkan/yuv_conversion_vk.h" +#include "impeller/toolkit/android/hardware_buffer.h" #include #include @@ -33,10 +34,14 @@ class ContextVK; /// class AHBTextureSourceVK final : public TextureSourceVK { public: - AHBTextureSourceVK(const std::shared_ptr& context, + AHBTextureSourceVK(const std::shared_ptr& context, struct AHardwareBuffer* hardware_buffer, const AHardwareBuffer_Desc& hardware_buffer_desc); + AHBTextureSourceVK(const std::shared_ptr& context, + std::unique_ptr backing_store, + bool is_swapchain_image); + // |TextureSourceVK| ~AHBTextureSourceVK() override; @@ -57,12 +62,16 @@ class AHBTextureSourceVK final : public TextureSourceVK { // |TextureSourceVK| std::shared_ptr GetYUVConversion() const override; + const android::HardwareBuffer* GetBackingStore() const; + private: + std::unique_ptr backing_store_; vk::UniqueDeviceMemory device_memory_ = {}; vk::UniqueImage image_ = {}; vk::UniqueImageView image_view_ = {}; std::shared_ptr yuv_conversion_ = {}; bool needs_yuv_conversion_ = false; + bool is_swapchain_image_ = false; bool is_valid_ = false; AHBTextureSourceVK(const AHBTextureSourceVK&) = delete; diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.cc b/impeller/renderer/backend/vulkan/capabilities_vk.cc index 8ab50f5c60ede..0b79dbab5d350 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -179,6 +179,10 @@ static const char* GetExtensionName(RequiredAndroidDeviceExtensionVK ext) { return VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME; case RequiredAndroidDeviceExtensionVK::kKHRDedicatedAllocation: return VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME; + case RequiredAndroidDeviceExtensionVK::kKHRExternalFenceFd: + return VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME; + case RequiredAndroidDeviceExtensionVK::kKHRExternalFence: + return VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME; case RequiredAndroidDeviceExtensionVK::kLast: return "Unknown"; } diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.h b/impeller/renderer/backend/vulkan/capabilities_vk.h index 01579a165414a..0a38d7f55a1e4 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -78,6 +78,20 @@ enum class RequiredAndroidDeviceExtensionVK : uint32_t { /// kKHRDedicatedAllocation, + //---------------------------------------------------------------------------- + /// For exporting file descriptors from fences to interact with platform APIs. + /// + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_fence_fd.html + /// + kKHRExternalFenceFd, + + //---------------------------------------------------------------------------- + /// Dependency of kKHRExternalFenceFd. + /// + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_fence.html + /// + kKHRExternalFence, + kLast, }; diff --git a/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc b/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc index aacced65e6e64..dd047a9dfc2e6 100644 --- a/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/driver_info_vk_unittests.cc @@ -14,7 +14,7 @@ INSTANTIATE_VULKAN_PLAYGROUND_SUITE(DriverInfoVKTest); TEST_P(DriverInfoVKTest, CanQueryDriverInfo) { ASSERT_TRUE(GetContext()); const auto& driver_info = - SurfaceContextVK::Cast(*GetContext()).GetParent().GetDriverInfo(); + SurfaceContextVK::Cast(*GetContext()).GetParent()->GetDriverInfo(); ASSERT_NE(driver_info, nullptr); // 1.1 is the base Impeller version. The driver can't be lower than that. ASSERT_TRUE(driver_info->GetAPIVersion().IsAtLeast(Version{1, 1, 0})); @@ -26,7 +26,7 @@ TEST_P(DriverInfoVKTest, CanQueryDriverInfo) { TEST_P(DriverInfoVKTest, CanDumpToLog) { ASSERT_TRUE(GetContext()); const auto& driver_info = - SurfaceContextVK::Cast(*GetContext()).GetParent().GetDriverInfo(); + SurfaceContextVK::Cast(*GetContext()).GetParent()->GetDriverInfo(); ASSERT_NE(driver_info, nullptr); fml::testing::LogCapture log; driver_info->DumpToLog(); diff --git a/impeller/renderer/backend/vulkan/queue_vk.cc b/impeller/renderer/backend/vulkan/queue_vk.cc index c35dc21e6812d..c5cea22dafe8b 100644 --- a/impeller/renderer/backend/vulkan/queue_vk.cc +++ b/impeller/renderer/backend/vulkan/queue_vk.cc @@ -23,6 +23,11 @@ vk::Result QueueVK::Submit(const vk::SubmitInfo& submit_info, return queue_.submit(submit_info, fence); } +vk::Result QueueVK::Submit(const vk::Fence& fence) const { + Lock lock(queue_mutex_); + return queue_.submit({}, fence); +} + vk::Result QueueVK::Present(const vk::PresentInfoKHR& present_info) { Lock lock(queue_mutex_); return queue_.presentKHR(present_info); diff --git a/impeller/renderer/backend/vulkan/queue_vk.h b/impeller/renderer/backend/vulkan/queue_vk.h index 62d650bfc6176..eab025a34ff8d 100644 --- a/impeller/renderer/backend/vulkan/queue_vk.h +++ b/impeller/renderer/backend/vulkan/queue_vk.h @@ -39,6 +39,8 @@ class QueueVK { vk::Result Submit(const vk::SubmitInfo& submit_info, const vk::Fence& fence) const; + vk::Result Submit(const vk::Fence& fence) const; + vk::Result Present(const vk::PresentInfoKHR& present_info); void InsertDebugMarker(std::string_view label) const; diff --git a/impeller/renderer/backend/vulkan/shared_object_vk.h b/impeller/renderer/backend/vulkan/shared_object_vk.h index 7b4bbdce8686b..423a4f9251919 100644 --- a/impeller/renderer/backend/vulkan/shared_object_vk.h +++ b/impeller/renderer/backend/vulkan/shared_object_vk.h @@ -31,6 +31,8 @@ class SharedObjectVKT : public SharedObjectVK { const Resource& Get() const { return *resource_; } + const UniqueResource& GetUniqueWrapper() const { return resource_; } + private: UniqueResource resource_; diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.cc b/impeller/renderer/backend/vulkan/surface_context_vk.cc index 39eab3c8a5acf..9feabd38b843a 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.cc +++ b/impeller/renderer/backend/vulkan/surface_context_vk.cc @@ -64,13 +64,12 @@ void SurfaceContextVK::Shutdown() { bool SurfaceContextVK::SetWindowSurface(vk::UniqueSurfaceKHR surface, const ISize& size) { - auto swapchain = SwapchainVK::Create(parent_, std::move(surface), size); - if (!swapchain) { - VALIDATION_LOG << "Could not create swapchain."; - return false; - } - if (!swapchain->IsValid()) { - VALIDATION_LOG << "Could not create valid swapchain."; + return SetSwapchain(SwapchainVK::Create(parent_, std::move(surface), size)); +} + +bool SurfaceContextVK::SetSwapchain(std::shared_ptr swapchain) { + if (!swapchain || !swapchain->IsValid()) { + VALIDATION_LOG << "Invalid swapchain."; return false; } swapchain_ = std::move(swapchain); @@ -96,29 +95,6 @@ void SurfaceContextVK::UpdateSurfaceSize(const ISize& size) const { swapchain_->UpdateSurfaceSize(size); } -#ifdef FML_OS_ANDROID - -vk::UniqueSurfaceKHR SurfaceContextVK::CreateAndroidSurface( - ANativeWindow* window) const { - if (!parent_->GetInstance()) { - return vk::UniqueSurfaceKHR{VK_NULL_HANDLE}; - } - - auto create_info = vk::AndroidSurfaceCreateInfoKHR().setWindow(window); - auto surface_res = - parent_->GetInstance().createAndroidSurfaceKHRUnique(create_info); - - if (surface_res.result != vk::Result::eSuccess) { - VALIDATION_LOG << "Could not create Android surface, error: " - << vk::to_string(surface_res.result); - return vk::UniqueSurfaceKHR{VK_NULL_HANDLE}; - } - - return std::move(surface_res.value); -} - -#endif // FML_OS_ANDROID - const vk::Device& SurfaceContextVK::GetDevice() const { return parent_->GetDevice(); } @@ -127,8 +103,8 @@ void SurfaceContextVK::InitializeCommonlyUsedShadersIfNeeded() const { parent_->InitializeCommonlyUsedShadersIfNeeded(); } -const ContextVK& SurfaceContextVK::GetParent() const { - return *parent_; +const std::shared_ptr& SurfaceContextVK::GetParent() const { + return parent_; } } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/surface_context_vk.h b/impeller/renderer/backend/vulkan/surface_context_vk.h index 37de3eb9eb5db..e80d12e795045 100644 --- a/impeller/renderer/backend/vulkan/surface_context_vk.h +++ b/impeller/renderer/backend/vulkan/surface_context_vk.h @@ -72,6 +72,8 @@ class SurfaceContextVK : public Context, [[nodiscard]] bool SetWindowSurface(vk::UniqueSurfaceKHR surface, const ISize& size); + [[nodiscard]] bool SetSwapchain(std::shared_ptr swapchain); + std::unique_ptr AcquireNextSurface(); /// @brief Mark the current swapchain configuration as dirty, forcing it to be @@ -80,13 +82,9 @@ class SurfaceContextVK : public Context, void InitializeCommonlyUsedShadersIfNeeded() const override; -#ifdef FML_OS_ANDROID - vk::UniqueSurfaceKHR CreateAndroidSurface(ANativeWindow* window) const; -#endif // FML_OS_ANDROID - const vk::Device& GetDevice() const; - const ContextVK& GetParent() const; + const std::shared_ptr& GetParent() const; private: std::shared_ptr parent_; diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h new file mode 100644 index 0000000000000..4575716ad954c --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_FORMATS_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_FORMATS_H_ + +#include "impeller/core/formats.h" +#include "impeller/toolkit/android/hardware_buffer.h" + +namespace impeller { + +constexpr PixelFormat ToPixelFormat(android::HardwareBufferFormat format) { + switch (format) { + case android::HardwareBufferFormat::kR8G8B8A8UNormInt: + return PixelFormat::kR8G8B8A8UNormInt; + } + FML_UNREACHABLE(); +} + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_FORMATS_H_ diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc new file mode 100644 index 0000000000000..8ea3065b0e21e --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc @@ -0,0 +1,222 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h" + +#include "flutter/fml/trace_event.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/backend/vulkan/barrier_vk.h" +#include "impeller/renderer/backend/vulkan/command_buffer_vk.h" +#include "impeller/renderer/backend/vulkan/command_encoder_vk.h" +#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h" +#include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h" +#include "impeller/toolkit/android/surface_transaction.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// The maximum number of presents pending in the compositor after which the +/// acquire calls will block. +/// +static constexpr const size_t kMaxPendingPresents = 2u; + +static TextureDescriptor ToSwapchainTextureDescriptor( + const android::HardwareBufferDescriptor& ahb_desc) { + TextureDescriptor desc; + desc.storage_mode = StorageMode::kDevicePrivate; + desc.type = TextureType::kTexture2D; + desc.format = ToPixelFormat(ahb_desc.format); + desc.size = ahb_desc.size; + desc.mip_count = 1u; + desc.usage = TextureUsage::kRenderTarget; + desc.sample_count = SampleCount::kCount1; + desc.compression_type = CompressionType::kLossless; + return desc; +} + +std::shared_ptr AHBSwapchainImplVK::Create( + const std::weak_ptr& context, + std::weak_ptr surface_control, + const ISize& size, + bool enable_msaa) { + auto impl = std::shared_ptr(new AHBSwapchainImplVK( + context, std::move(surface_control), size, enable_msaa)); + return impl->IsValid() ? impl : nullptr; +} + +AHBSwapchainImplVK::AHBSwapchainImplVK( + const std::weak_ptr& context, + std::weak_ptr surface_control, + const ISize& size, + bool enable_msaa) + : surface_control_(std::move(surface_control)), + pending_presents_(std::make_shared(kMaxPendingPresents)) { + desc_ = android::HardwareBufferDescriptor::MakeForSwapchainImage(size); + pool_ = std::make_shared(context, desc_); + if (!pool_->IsValid()) { + return; + } + transients_ = std::make_shared( + context, ToSwapchainTextureDescriptor(desc_), enable_msaa); + is_valid_ = true; +} + +AHBSwapchainImplVK::~AHBSwapchainImplVK() = default; + +const ISize& AHBSwapchainImplVK::GetSize() const { + return desc_.size; +} + +bool AHBSwapchainImplVK::IsValid() const { + return is_valid_; +} + +const android::HardwareBufferDescriptor& AHBSwapchainImplVK::GetDescriptor() + const { + return desc_; +} + +std::unique_ptr AHBSwapchainImplVK::AcquireNextDrawable() { + { + TRACE_EVENT0("impeller", "CompositorPendingWait"); + if (!pending_presents_->Wait()) { + return nullptr; + } + } + + AutoSemaSignaler auto_sema_signaler = + std::make_shared( + [sema = pending_presents_]() { sema->Signal(); }); + + if (!is_valid_) { + return nullptr; + } + + auto texture = pool_->Pop(); + + if (!texture) { + VALIDATION_LOG << "Could not create AHB texture source."; + return nullptr; + } + + auto surface = SurfaceVK::WrapSwapchainImage( + transients_, texture, + [signaler = auto_sema_signaler, weak = weak_from_this(), texture]() { + auto thiz = weak.lock(); + if (!thiz) { + VALIDATION_LOG << "Swapchain died before image could be presented."; + return false; + } + return thiz->Present(signaler, texture); + }); + + if (!surface) { + return nullptr; + } + + return surface; +} + +bool AHBSwapchainImplVK::Present( + const AutoSemaSignaler& signaler, + const std::shared_ptr& texture) { + auto control = surface_control_.lock(); + if (!control || !control->IsValid()) { + VALIDATION_LOG << "Surface control died before swapchain image could be " + "presented."; + return false; + } + + if (!texture) { + return false; + } + + auto fence = SubmitCompletionSignal(texture); + + if (!fence) { + VALIDATION_LOG << "Could not submit completion signal."; + return false; + } + + android::SurfaceTransaction transaction; + if (!transaction.SetContents(control.get(), // + texture->GetBackingStore(), // + fence->CreateFD() // + )) { + VALIDATION_LOG << "Could not set swapchain image contents on the surface " + "control."; + return false; + } + return transaction.Apply([signaler, texture, weak = weak_from_this()]() { + auto thiz = weak.lock(); + if (!thiz) { + return; + } + thiz->OnTextureSetOnSurfaceControl(signaler, texture); + }); +} + +std::shared_ptr AHBSwapchainImplVK::SubmitCompletionSignal( + const std::shared_ptr& texture) const { + auto context = transients_->GetContext().lock(); + if (!context) { + return nullptr; + } + auto fence = std::make_shared(context); + if (!fence || !fence->IsValid()) { + return nullptr; + } + + auto command_buffer = context->CreateCommandBuffer(); + if (!command_buffer) { + return nullptr; + } + command_buffer->SetLabel("AHBPresentCommandBuffer"); + const auto& encoder = CommandBufferVK::Cast(*command_buffer).GetEncoder(); + + const auto command_encoder_vk = encoder->GetCommandBuffer(); + + BarrierVK barrier; + barrier.cmd_buffer = command_encoder_vk; + barrier.new_layout = vk::ImageLayout::eGeneral; + barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput; + barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite; + barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe; + barrier.dst_access = {}; + + if (!texture->SetLayout(barrier).ok()) { + return nullptr; + } + + encoder->Track(fence->GetSharedHandle()); + + if (!encoder->EndCommandBuffer()) { + return nullptr; + } + + vk::SubmitInfo submit_info; + submit_info.setCommandBuffers(command_encoder_vk); + + auto result = ContextVK::Cast(*context).GetGraphicsQueue()->Submit( + submit_info, fence->GetHandle()); + if (result != vk::Result::eSuccess) { + return nullptr; + } + return fence; +} + +void AHBSwapchainImplVK::OnTextureSetOnSurfaceControl( + const AutoSemaSignaler& signaler, + std::shared_ptr texture) { + signaler->Reset(); + // The transaction completion indicates that the surface control now + // references the hardware buffer. We can recycle the previous set buffer + // safely. + Lock lock(currently_displayed_texture_mutex_); + auto old_texture = currently_displayed_texture_; + currently_displayed_texture_ = std::move(texture); + pool_->Push(std::move(old_texture)); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h new file mode 100644 index 0000000000000..82d0b96752159 --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h @@ -0,0 +1,125 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_SWAPCHAIN_IMPL_VK_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_SWAPCHAIN_IMPL_VK_H_ + +#include + +#include "flutter/fml/closure.h" +#include "flutter/fml/synchronization/semaphore.h" +#include "impeller/base/thread.h" +#include "impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h" +#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h" +#include "impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.h" +#include "impeller/renderer/backend/vulkan/swapchain/swapchain_transients_vk.h" +#include "impeller/renderer/surface.h" +#include "impeller/toolkit/android/hardware_buffer.h" +#include "impeller/toolkit/android/surface_control.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief The implementation of a swapchain at a specific size. Resizes to +/// the surface will cause the instance of the swapchain impl at +/// that size to be discarded along with all its caches and +/// transients. +/// +class AHBSwapchainImplVK final + : public std::enable_shared_from_this { + public: + //---------------------------------------------------------------------------- + /// @brief Create a swapchain of a specific size whose images will be + /// presented to the provided surface control. + /// + /// @param[in] context The context whose allocators will be used to + /// create swapchain image resources. + /// @param[in] surface_control The surface control to which the swapchain + /// images will be presented. + /// @param[in] size The size of the swapchain images. This is + /// constant for the lifecycle of the swapchain + /// impl. + /// @param[in] enable_msaa If the swapchain images will be presented + /// using a render target that enables MSAA. This + /// allows for additional caching of transients. + /// + /// @return A valid swapchain impl if one can be created. `nullptr` + /// otherwise. + /// + static std::shared_ptr Create( + const std::weak_ptr& context, + std::weak_ptr surface_control, + const ISize& size, + bool enable_msaa); + + ~AHBSwapchainImplVK(); + + AHBSwapchainImplVK(const AHBSwapchainImplVK&) = delete; + + AHBSwapchainImplVK& operator=(const AHBSwapchainImplVK&) = delete; + + //---------------------------------------------------------------------------- + /// @return The size of the swapchain images that will be displayed on the + /// surface control. + /// + const ISize& GetSize() const; + + //---------------------------------------------------------------------------- + /// @return If the swapchain impl is valid. If it is not, the instance + /// must be discarded. There is no error recovery. + /// + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Get the descriptor used to create the hardware buffers that + /// will be displayed on the surface control. + /// + /// @return The descriptor. + /// + const android::HardwareBufferDescriptor& GetDescriptor() const; + + //---------------------------------------------------------------------------- + /// @brief Acquire the next surface that can be used to present to the + /// swapchain. + /// + /// @return A surface if one can be created. If one cannot be created, it + /// is likely due to resource exhaustion. + /// + std::unique_ptr AcquireNextDrawable(); + + private: + using AutoSemaSignaler = std::shared_ptr; + + std::weak_ptr surface_control_; + android::HardwareBufferDescriptor desc_; + std::shared_ptr pool_; + std::shared_ptr transients_; + // In C++20, this mutex can be replaced by the shared pointer specialization + // of std::atomic. + Mutex currently_displayed_texture_mutex_; + std::shared_ptr currently_displayed_texture_ + IPLR_GUARDED_BY(currently_displayed_texture_mutex_); + std::shared_ptr pending_presents_; + bool is_valid_ = false; + + explicit AHBSwapchainImplVK( + const std::weak_ptr& context, + std::weak_ptr surface_control, + const ISize& size, + bool enable_msaa); + + bool Present(const AutoSemaSignaler& signaler, + const std::shared_ptr& texture); + + std::shared_ptr SubmitCompletionSignal( + const std::shared_ptr& texture) const; + + void OnTextureSetOnSurfaceControl( + const AutoSemaSignaler& signaler, + std::shared_ptr texture); +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_SWAPCHAIN_IMPL_VK_H_ diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc new file mode 100644 index 0000000000000..2532a36ef2483 --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.cc @@ -0,0 +1,71 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h" + +#include "flutter/fml/trace_event.h" +#include "impeller/renderer/backend/vulkan/formats_vk.h" +#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h" + +namespace impeller { + +bool AHBSwapchainVK::IsAvailableOnPlatform() { + return android::SurfaceControl::IsAvailableOnPlatform() && + android::HardwareBuffer::IsAvailableOnPlatform(); +} + +AHBSwapchainVK::AHBSwapchainVK(const std::shared_ptr& context, + ANativeWindow* window, + const ISize& size, + bool enable_msaa) + : context_(context), + surface_control_( + std::make_shared(window, "ImpellerSurface")), + enable_msaa_(enable_msaa) { + UpdateSurfaceSize(size); +} + +AHBSwapchainVK::~AHBSwapchainVK() = default; + +// |SwapchainVK| +bool AHBSwapchainVK::IsValid() const { + return impl_ ? impl_->IsValid() : false; +} + +// |SwapchainVK| +std::unique_ptr AHBSwapchainVK::AcquireNextDrawable() { + if (!IsValid()) { + return nullptr; + } + + TRACE_EVENT0("impeller", __FUNCTION__); + return impl_->AcquireNextDrawable(); +} + +// |SwapchainVK| +vk::Format AHBSwapchainVK::GetSurfaceFormat() const { + return IsValid() + ? ToVKImageFormat(ToPixelFormat(impl_->GetDescriptor().format)) + : vk::Format::eUndefined; +} + +// |SwapchainVK| +void AHBSwapchainVK::UpdateSurfaceSize(const ISize& size) { + if (impl_ && impl_->GetSize() == size) { + return; + } + TRACE_EVENT0("impeller", __FUNCTION__); + auto impl = AHBSwapchainImplVK::Create(context_, // + surface_control_, // + size, // + enable_msaa_ // + ); + if (!impl || !impl->IsValid()) { + VALIDATION_LOG << "Could not resize swapchain to size: " << size; + return; + } + impl_ = std::move(impl); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h new file mode 100644 index 0000000000000..b95e1c2e9287f --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_SWAPCHAIN_VK_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_SWAPCHAIN_VK_H_ + +#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h" +#include "impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h" +#include "impeller/toolkit/android/native_window.h" +#include "impeller/toolkit/android/surface_control.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief The implementation of a swapchain that uses hardware buffers +/// presented to a given surface control on Android. +/// +/// @warning This swapchain implementation is not available on all Android +/// versions supported by Flutter. Perform the +/// `IsAvailableOnPlatform` check and fallback to KHR swapchains if +/// this type of swapchain cannot be created. The available building +/// blocks for these kinds of swapchains are only available on +/// Android API levels >= 29. +/// +class AHBSwapchainVK final : public SwapchainVK { + public: + static bool IsAvailableOnPlatform(); + + // |SwapchainVK| + ~AHBSwapchainVK() override; + + AHBSwapchainVK(const AHBSwapchainVK&) = delete; + + AHBSwapchainVK& operator=(const AHBSwapchainVK&) = delete; + + // |SwapchainVK| + bool IsValid() const override; + + // |SwapchainVK| + std::unique_ptr AcquireNextDrawable() override; + + // |SwapchainVK| + vk::Format GetSurfaceFormat() const override; + + // |SwapchainVK| + void UpdateSurfaceSize(const ISize& size) override; + + private: + friend class SwapchainVK; + + std::weak_ptr context_; + std::shared_ptr surface_control_; + const bool enable_msaa_; + std::shared_ptr impl_; + + explicit AHBSwapchainVK(const std::shared_ptr& context, + ANativeWindow* window, + const ISize& size, + bool enable_msaa); +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_SWAPCHAIN_VK_H_ diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc new file mode 100644 index 0000000000000..154b573997909 --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc @@ -0,0 +1,96 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h" + +#include "flutter/fml/trace_event.h" + +namespace impeller { + +AHBTexturePoolVK::AHBTexturePoolVK(std::weak_ptr context, + android::HardwareBufferDescriptor desc, + size_t max_entries, + std::chrono::milliseconds max_extry_age) + : context_(std::move(context)), + desc_(desc), + max_entries_(max_entries), + max_extry_age_(max_extry_age) { + if (!desc_.IsAllocatable()) { + VALIDATION_LOG << "Swapchain image is not allocatable."; + return; + } + is_valid_ = true; +} + +AHBTexturePoolVK::~AHBTexturePoolVK() = default; + +std::shared_ptr AHBTexturePoolVK::Pop() { + { + Lock lock(pool_mutex_); + if (!pool_.empty()) { + auto texture = pool_.back().item; + pool_.pop_back(); + return texture; + } + } + return CreateTexture(); +} + +void AHBTexturePoolVK::Push(std::shared_ptr texture) { + if (!texture) { + return; + } + Lock lock(pool_mutex_); + pool_.push_back(PoolEntry{std::move(texture)}); + PerformGCLocked(); +} + +std::shared_ptr AHBTexturePoolVK::CreateTexture() const { + TRACE_EVENT0("impeller", "CreateSwapchainTexture"); + auto context = context_.lock(); + if (!context) { + VALIDATION_LOG << "Context died before image could be created."; + return nullptr; + } + + auto ahb = std::make_unique(desc_); + if (!ahb->IsValid()) { + VALIDATION_LOG << "Could not create hardware buffer of size: " + << desc_.size; + return nullptr; + } + + auto ahb_texture_source = + std::make_shared(context, std::move(ahb), true); + if (!ahb_texture_source->IsValid()) { + VALIDATION_LOG << "Could not create hardware buffer texture source for " + "swapchain image of size: " + << desc_.size; + return nullptr; + } + + return ahb_texture_source; +} + +void AHBTexturePoolVK::PerformGC() { + Lock lock(pool_mutex_); + PerformGCLocked(); +} + +void AHBTexturePoolVK::PerformGCLocked() { + // Push-Pop operations happen at the back of the deque so the front ages as + // much as possible. So that's where we collect entries. + auto now = Clock::now(); + while (!pool_.empty() && + (pool_.size() > max_entries_ || + now - pool_.front().last_access_time > max_extry_age_)) { + pool_.pop_front(); + } +} + +bool AHBTexturePoolVK::IsValid() const { + return is_valid_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h new file mode 100644 index 0000000000000..d40050fb08130 --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h @@ -0,0 +1,123 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_TEXTURE_POOL_VK_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_TEXTURE_POOL_VK_H_ + +#include + +#include "impeller/base/thread.h" +#include "impeller/base/timing.h" +#include "impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief Maintains a bounded pool of hardware buffer backed texture +/// sources that can be used as swapchain images. +/// +/// The number of cached entries in the texture pool is capped to a +/// caller specified value. Within this cap, no entry may be older +/// than the caller specified duration. +/// +/// If a previously cached entry cannot be obtained from the pool, a +/// new entry is created. The only case where a valid texture source +/// cannot be obtained is due to resource exhaustion. +/// +/// Pools are thread-safe. +/// +class AHBTexturePoolVK { + public: + //---------------------------------------------------------------------------- + /// @brief Create a new (empty) texture pool. + /// + /// @param[in] context The context whose allocators will be used to + /// create the resources for the texture sources. + /// @param[in] desc The descriptor of the hardware buffers that + /// will be used to create the backing stores of + /// the texture sources. + /// @param[in] max_entries The maximum entries that will remain cached + /// in the pool. + /// @param[in] max_extry_age The maximum duration an entry will remain + /// cached in the pool. + /// + explicit AHBTexturePoolVK( + std::weak_ptr context, + android::HardwareBufferDescriptor desc, + size_t max_entries = 2u, + std::chrono::milliseconds max_extry_age = std::chrono::seconds{1}); + + ~AHBTexturePoolVK(); + + AHBTexturePoolVK(const AHBTexturePoolVK&) = delete; + + AHBTexturePoolVK& operator=(const AHBTexturePoolVK&) = delete; + + //---------------------------------------------------------------------------- + /// @brief If the pool can create and pool hardware buffer backed texture + /// sources. The only reason valid textures cannot be obtained + /// from a valid pool is because of resource exhaustion. + /// + /// @return `true` if valid, `false` otherwise. + /// + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Pops an texture source from the pool. If the pool is empty, a + /// new texture source is created and returned. + /// + /// This operation is thread-safe. + /// + /// @return A texture source that can be used as a swapchain image. This + /// can be nullptr in case of resource exhaustion. + /// + std::shared_ptr Pop(); + + //---------------------------------------------------------------------------- + /// @brief Push a popped texture back into the pool. This also performs a + /// GC. + /// + /// This operation is thread-safe. + /// + /// @warning Only a texture source obtained from the same pool can be + /// returned to it. It is user error to mix and match texture + /// sources from different pools. + /// + /// @param[in] texture The texture to be returned to the pool. + /// + void Push(std::shared_ptr texture); + + //---------------------------------------------------------------------------- + /// @brief Perform an explicit GC of the pool items. This happens + /// implicitly when a texture source us pushed into the pool but + /// one may be necessary explicitly if there is no push back into + /// the pool for a long time. + /// + void PerformGC(); + + private: + struct PoolEntry { + TimePoint last_access_time; + std::shared_ptr item; + + explicit PoolEntry(std::shared_ptr p_item) + : last_access_time(Clock::now()), item(std::move(p_item)) {} + }; + + const std::weak_ptr context_; + const android::HardwareBufferDescriptor desc_; + const size_t max_entries_; + const std::chrono::milliseconds max_extry_age_; + bool is_valid_ = false; + Mutex pool_mutex_; + std::deque pool_ IPLR_GUARDED_BY(pool_mutex_); + + void PerformGCLocked() IPLR_REQUIRES(pool_mutex_); + + std::shared_ptr CreateTexture() const; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_AHB_TEXTURE_POOL_VK_H_ diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.cc b/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.cc new file mode 100644 index 0000000000000..8eb7a8c0b919d --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.cc @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.h" + +#include "impeller/base/validation.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" + +namespace impeller { + +ExternalFenceVK::ExternalFenceVK(const std::shared_ptr& context) { + if (!context) { + return; + } + vk::StructureChain info; + + info.get().handleTypes = + vk::ExternalFenceHandleTypeFlagBits::eSyncFd; + + const auto& context_vk = ContextVK::Cast(*context); + auto [result, fence] = context_vk.GetDevice().createFenceUnique(info.get()); + if (result != vk::Result::eSuccess) { + VALIDATION_LOG << "Could not create external fence: " + << vk::to_string(result); + return; + } + + context_vk.SetDebugName(fence.get(), "ExternalFenceSyncFD"); + + fence_ = MakeSharedVK(std::move(fence)); +} + +ExternalFenceVK::~ExternalFenceVK() = default; + +bool ExternalFenceVK::IsValid() const { + return !!fence_; +} + +fml::UniqueFD ExternalFenceVK::CreateFD() const { + if (!IsValid()) { + return {}; + } + vk::FenceGetFdInfoKHR info; + info.fence = fence_->Get(); + info.handleType = vk::ExternalFenceHandleTypeFlagBits::eSyncFd; + auto [result, fd] = fence_->GetUniqueWrapper().getOwner().getFenceFdKHR(info); + if (result != vk::Result::eSuccess) { + VALIDATION_LOG << "Could not export external fence FD: " + << vk::to_string(result); + return {}; + } + return fml::UniqueFD{fd}; +} + +const vk::Fence& ExternalFenceVK::GetHandle() const { + return fence_->Get(); +} + +const SharedHandleVK& ExternalFenceVK::GetSharedHandle() const { + return fence_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.h b/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.h new file mode 100644 index 0000000000000..3ec2a53ca67eb --- /dev/null +++ b/impeller/renderer/backend/vulkan/swapchain/ahb/external_fence_vk.h @@ -0,0 +1,83 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_EXTERNAL_FENCE_VK_H_ +#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_EXTERNAL_FENCE_VK_H_ + +#include "flutter/fml/unique_fd.h" +#include "impeller/renderer/backend/vulkan/shared_object_vk.h" +#include "impeller/renderer/backend/vulkan/vk.h" +#include "impeller/renderer/context.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief A Vulkan fence that can be exported as a platform specific file +/// descriptor. +/// +/// The fences are exported as sync file descriptors. +/// +/// @warning Only fences that have been signaled or have a single operation +/// pending can be exported. Make sure to submit a fence signalling +/// operation to a queue before attempted to obtain a file +/// descriptor for the fence. See +/// VUID-VkFenceGetFdInfoKHR-handleType-01454 for additional details +/// on the implementation. +/// +class ExternalFenceVK { + public: + //---------------------------------------------------------------------------- + /// @brief Create a new un-signaled fence that can be exported as a sync + /// file descriptor. + /// + /// @param[in] context The device context. + /// + explicit ExternalFenceVK(const std::shared_ptr& context); + + ~ExternalFenceVK(); + + ExternalFenceVK(const ExternalFenceVK&) = delete; + + ExternalFenceVK& operator=(const ExternalFenceVK&) = delete; + + //---------------------------------------------------------------------------- + /// @brief If a valid fence could be created. + /// + /// @return True if valid, False otherwise. + /// + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Create a new sync file descriptor for the underlying fence. + /// The fence must already be signaled or have a signal operation + /// pending in a queue. There are no checks for this in the + /// implementation and only Vulkan validation will catch such a + /// misuse and undefined behavior. + /// + /// @warning Implementations are also allowed to return invalid file + /// descriptors in case a fence has already been signaled. So it + /// is not necessary an error to obtain an invalid descriptor from + /// this call. For APIs that are meant to consume such + /// descriptors, pass -1 as the file handle. + /// + /// Since this call can return an invalid FD even in case of + /// success, make sure to make the `IsValid` check before + /// attempting to export a FD. + /// + /// @return A (potentially invalid even in case of success) file + /// descriptor. + /// + fml::UniqueFD CreateFD() const; + + const vk::Fence& GetHandle() const; + + const SharedHandleVK& GetSharedHandle() const; + + private: + SharedHandleVK fence_; +}; + +} // namespace impeller + +#endif // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_SWAPCHAIN_AHB_EXTERNAL_FENCE_VK_H_ diff --git a/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.cc b/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.cc index 66dc1e64a5407..70e9553d454df 100644 --- a/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.cc +++ b/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.cc @@ -4,7 +4,14 @@ #include "impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h" +#include "flutter/fml/trace_event.h" +#include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/swapchain/khr/khr_swapchain_vk.h" +#include "impeller/renderer/backend/vulkan/vk.h" + +#if FML_OS_ANDROID +#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_vk.h" +#endif // FML_OS_ANDROID namespace impeller { @@ -13,9 +20,64 @@ std::shared_ptr SwapchainVK::Create( vk::UniqueSurfaceKHR surface, const ISize& size, bool enable_msaa) { - return std::shared_ptr( + auto swapchain = std::shared_ptr( new KHRSwapchainVK(context, std::move(surface), size, enable_msaa)); + if (!swapchain->IsValid()) { + VALIDATION_LOG << "Could not create valid swapchain."; + return nullptr; + } + return swapchain; +} + +#if FML_OS_ANDROID +std::shared_ptr SwapchainVK::Create( + const std::shared_ptr& context, + ANativeWindow* p_window, + bool enable_msaa) { + TRACE_EVENT0("impeller", "CreateAndroidSwapchain"); + if (!context) { + return nullptr; + } + + android::NativeWindow window(p_window); + if (!window.IsValid()) { + return nullptr; + } + + // TODO(147533): AHB swapchains on emulators are not functional. + const auto emulator = ContextVK::Cast(*context).GetDriverInfo()->IsEmulator(); + + // Try AHB swapchains first. + if (!emulator && AHBSwapchainVK::IsAvailableOnPlatform()) { + auto ahb_swapchain = std::shared_ptr(new AHBSwapchainVK( + context, // + window.GetHandle(), // + window.GetSize(), // + enable_msaa // + )); + + if (ahb_swapchain->IsValid()) { + return ahb_swapchain; + } else { + VALIDATION_LOG + << "Could not create AHB swapchain. Falling back to KHR variant."; + } + } + + // Fallback to KHR swapchains if AHB swapchains aren't available. + vk::AndroidSurfaceCreateInfoKHR surface_info; + surface_info.setWindow(window.GetHandle()); + auto [result, surface] = + ContextVK::Cast(*context).GetInstance().createAndroidSurfaceKHRUnique( + surface_info); + if (result != vk::Result::eSuccess) { + VALIDATION_LOG << "Could not create KHR Android Surface: " + << vk::to_string(result); + return nullptr; + } + return Create(context, std::move(surface), window.GetSize(), enable_msaa); } +#endif // FML_OS_ANDROID SwapchainVK::SwapchainVK() = default; diff --git a/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h b/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h index 624b99dbf003b..5de25d1469c6e 100644 --- a/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h +++ b/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h @@ -7,11 +7,16 @@ #include +#include "flutter/fml/build_config.h" #include "impeller/geometry/size.h" #include "impeller/renderer/backend/vulkan/vk.h" #include "impeller/renderer/context.h" #include "impeller/renderer/surface.h" +#if FML_OS_ANDROID +#include "impeller/toolkit/android/native_window.h" +#endif // FML_OS_ANDROID + namespace impeller { //------------------------------------------------------------------------------ @@ -28,6 +33,13 @@ class SwapchainVK { const ISize& size, bool enable_msaa = true); +#if FML_OS_ANDROID + static std::shared_ptr Create( + const std::shared_ptr& context, + ANativeWindow* window, + bool enable_msaa = true); +#endif // FML_OS_ANDROID + virtual ~SwapchainVK(); SwapchainVK(const SwapchainVK&) = delete; diff --git a/impeller/toolkit/android/hardware_buffer.cc b/impeller/toolkit/android/hardware_buffer.cc index 689974ab56350..57137f305593c 100644 --- a/impeller/toolkit/android/hardware_buffer.cc +++ b/impeller/toolkit/android/hardware_buffer.cc @@ -25,7 +25,8 @@ static AHardwareBuffer_Desc ToAHardwareBufferDesc( ahb_desc.format = ToAHardwareBufferFormat(desc.format); ahb_desc.layers = 1u; if (desc.usage & HardwareBufferUsageFlags::kFrameBufferAttachment) { - ahb_desc.usage |= AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; + ahb_desc.usage |= (AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | + AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT); } if (desc.usage & HardwareBufferUsageFlags::kCompositorOverlay) { ahb_desc.usage |= AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY; @@ -33,6 +34,18 @@ static AHardwareBuffer_Desc ToAHardwareBufferDesc( if (desc.usage & HardwareBufferUsageFlags::kSampledImage) { ahb_desc.usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; } + if (desc.usage & HardwareBufferUsageFlags::kCPUReadRarely) { + ahb_desc.usage |= AHARDWAREBUFFER_USAGE_CPU_READ_RARELY; + } + if (desc.usage & HardwareBufferUsageFlags::kCPUReadOften) { + ahb_desc.usage |= AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; + } + if (desc.usage & HardwareBufferUsageFlags::kCPUWriteRarely) { + ahb_desc.usage |= AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY; + } + if (desc.usage & HardwareBufferUsageFlags::kCPUWriteOften) { + ahb_desc.usage |= AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; + } return ahb_desc; } @@ -121,4 +134,37 @@ std::optional HardwareBuffer::Describe( return desc; } +void* HardwareBuffer::Lock(CPUAccessType type) const { + if (!is_valid_ || !GetProcTable().AHardwareBuffer_lock) { + return nullptr; + } + uint64_t usage = 0; + switch (type) { + case CPUAccessType::kRead: + usage |= AHARDWAREBUFFER_USAGE_CPU_READ_MASK; + break; + case CPUAccessType::kWrite: + usage |= AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK; + break; + } + void* buffer = nullptr; + const auto result = GetProcTable().AHardwareBuffer_lock( // + buffer_.get(), // buffer + usage, // usage + -1, // fence + nullptr, // rect + &buffer // out-addr + ); + return result == 0 ? buffer : nullptr; +} + +bool HardwareBuffer::Unlock() const { + if (!is_valid_ || !GetProcTable().AHardwareBuffer_unlock) { + return false; + } + const auto result = + GetProcTable().AHardwareBuffer_unlock(buffer_.get(), nullptr); + return result == 0; +} + } // namespace impeller::android diff --git a/impeller/toolkit/android/hardware_buffer.h b/impeller/toolkit/android/hardware_buffer.h index 4fe1a0089beb2..bce356da7604b 100644 --- a/impeller/toolkit/android/hardware_buffer.h +++ b/impeller/toolkit/android/hardware_buffer.h @@ -7,6 +7,7 @@ #include +#include "flutter/fml/unique_fd.h" #include "flutter/fml/unique_object.h" #include "impeller/base/mask.h" #include "impeller/geometry/size.h" @@ -32,6 +33,10 @@ enum class HardwareBufferUsageFlags { kFrameBufferAttachment = 1u << 0u, kCompositorOverlay = 1u << 1u, kSampledImage = 1u << 2u, + kCPUReadRarely = 1u << 3u, + kCPUReadOften = 1u << 4u, + kCPUWriteRarely = 1u << 5u, + kCPUWriteOften = 1u << 6u, }; using HardwareBufferUsage = Mask; @@ -127,6 +132,29 @@ class HardwareBuffer { /// static std::optional GetSystemUniqueID(AHardwareBuffer* buffer); + enum class CPUAccessType { + kRead, + kWrite, + }; + //---------------------------------------------------------------------------- + /// @brief Lock the buffer for CPU access. This call may fail if the + /// buffer was not created with one the usages that allow for CPU + /// access. + /// + /// @param[in] type The type + /// + /// @return A host-accessible buffer if there was no error related to + /// usage or buffer validity. + /// + void* Lock(CPUAccessType type) const; + + //---------------------------------------------------------------------------- + /// @brief Unlock a mapping previously locked for CPU access. + /// + /// @return If the unlock was successful. + /// + bool Unlock() const; + private: struct UniqueAHardwareBufferTraits { static AHardwareBuffer* InvalidValue() { return nullptr; } diff --git a/impeller/toolkit/android/proc_table.h b/impeller/toolkit/android/proc_table.h index af398fd396bf9..964489e2ee493 100644 --- a/impeller/toolkit/android/proc_table.h +++ b/impeller/toolkit/android/proc_table.h @@ -44,7 +44,9 @@ namespace impeller::android { INVOKE(AHardwareBuffer_fromHardwareBuffer, 26) \ INVOKE(AHardwareBuffer_getId, 31) \ INVOKE(AHardwareBuffer_isSupported, 29) \ + INVOKE(AHardwareBuffer_lock, 26) \ INVOKE(AHardwareBuffer_release, 26) \ + INVOKE(AHardwareBuffer_unlock, 26) \ INVOKE(ANativeWindow_acquire, 0) \ INVOKE(ANativeWindow_getHeight, 0) \ INVOKE(ANativeWindow_getWidth, 0) \ diff --git a/impeller/toolkit/android/surface_control.h b/impeller/toolkit/android/surface_control.h index af51a4bc5b748..b853ed488040a 100644 --- a/impeller/toolkit/android/surface_control.h +++ b/impeller/toolkit/android/surface_control.h @@ -60,6 +60,9 @@ class SurfaceControl { /// @brief Remove the surface control from the hierarchy of nodes /// presented by the system compositor. /// + /// This is called implicitly when the surface control is + /// collected. + /// /// @return `true` If the control will be removed from the hierarchy of /// nodes presented by the system compositor. /// diff --git a/impeller/toolkit/android/surface_transaction.cc b/impeller/toolkit/android/surface_transaction.cc index a96eef6a26a57..a69c6f08b0fb3 100644 --- a/impeller/toolkit/android/surface_transaction.cc +++ b/impeller/toolkit/android/surface_transaction.cc @@ -52,15 +52,18 @@ bool SurfaceTransaction::Apply(OnCompleteCallback callback) { } bool SurfaceTransaction::SetContents(const SurfaceControl* control, - const HardwareBuffer* buffer) { + const HardwareBuffer* buffer, + fml::UniqueFD acquire_fence) { if (control == nullptr || buffer == nullptr) { VALIDATION_LOG << "Invalid control or buffer."; return false; } - GetProcTable().ASurfaceTransaction_setBuffer(transaction_.get(), // - control->GetHandle(), // - buffer->GetHandle(), // - -1); + GetProcTable().ASurfaceTransaction_setBuffer( + transaction_.get(), // + control->GetHandle(), // + buffer->GetHandle(), // + acquire_fence.is_valid() ? acquire_fence.release() : -1 // + ); return true; } diff --git a/impeller/toolkit/android/surface_transaction.h b/impeller/toolkit/android/surface_transaction.h index b9460f35e0978..a2bca4512367f 100644 --- a/impeller/toolkit/android/surface_transaction.h +++ b/impeller/toolkit/android/surface_transaction.h @@ -8,6 +8,7 @@ #include #include +#include "flutter/fml/unique_fd.h" #include "flutter/fml/unique_object.h" #include "impeller/geometry/color.h" #include "impeller/toolkit/android/proc_table.h" @@ -56,13 +57,16 @@ class SurfaceTransaction { /// /// @see `SurfaceTransaction::Apply`. /// - /// @param[in] control The control - /// @param[in] buffer The hardware buffer + /// @param[in] control The control. + /// @param[in] buffer The hardware buffer. + /// @param[in] acquire_fence The fence to wait on before setting the + /// contents. /// /// @return If the update was encoded in the transaction. /// [[nodiscard]] bool SetContents(const SurfaceControl* control, - const HardwareBuffer* buffer); + const HardwareBuffer* buffer, + fml::UniqueFD acquire_fence = {}); //---------------------------------------------------------------------------- /// @brief Encodes the updated background color of the surface control. diff --git a/shell/platform/android/android_surface_vulkan_impeller.cc b/shell/platform/android/android_surface_vulkan_impeller.cc index 2082d7fef8489..61fc5bfec2899 100644 --- a/shell/platform/android/android_surface_vulkan_impeller.cc +++ b/shell/platform/android/android_surface_vulkan_impeller.cc @@ -11,6 +11,7 @@ #include "flutter/fml/logging.h" #include "flutter/fml/memory/ref_ptr.h" #include "flutter/impeller/renderer/backend/vulkan/context_vk.h" +#include "flutter/impeller/renderer/backend/vulkan/swapchain/swapchain_vk.h" #include "flutter/shell/gpu/gpu_surface_vulkan_impeller.h" #include "flutter/vulkan/vulkan_native_surface_android.h" @@ -71,22 +72,26 @@ bool AndroidSurfaceVulkanImpeller::ResourceContextClearCurrent() { bool AndroidSurfaceVulkanImpeller::SetNativeWindow( fml::RefPtr window) { - native_window_ = std::move(window); - bool success = native_window_ && native_window_->IsValid(); - if (success) { - auto surface = - surface_context_vk_->CreateAndroidSurface(native_window_->handle()); - - if (!surface) { - FML_LOG(ERROR) << "Could not create a vulkan surface."; - return false; - } - auto size = native_window_->GetSize(); - return surface_context_vk_->SetWindowSurface( - std::move(surface), impeller::ISize{size.width(), size.height()}); + if (window && (native_window_ == window)) { + return OnScreenSurfaceResize(window->GetSize()); } native_window_ = nullptr; + + if (!window || !window->IsValid()) { + return false; + } + + auto swapchain = impeller::SwapchainVK::Create( + std::reinterpret_pointer_cast( + surface_context_vk_->GetParent()), + window->handle()); + + if (surface_context_vk_->SetSwapchain(std::move(swapchain))) { + native_window_ = std::move(window); + return true; + } + return false; }