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; }