From e90ea69335157b3201d99ff4f2b3cc04a5a4bf4b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 11 Mar 2024 14:50:05 -0700 Subject: [PATCH 01/15] [Impeller] A toolkit for managed handles to Android NDK vended objects. Only available on Android device API levels >= 29. Proc table is setup has versioning checks. All handles are type safe. Collection of handles takes into account cleanup tasks (like reparenting surface controls). The proc table contains code duplicated in ndk_helpers and I will remove that in favor of this in a subsequent patch. Part of https://github.com/flutter/engine/pull/51213 being chopped up. --- BUILD.gn | 6 +- ci/licenses_golden/excluded_files | 2 + ci/licenses_golden/licenses_flutter | 20 +++ impeller/toolkit/android/BUILD.gn | 52 +++++++ impeller/toolkit/android/README.md | 7 + impeller/toolkit/android/hardware_buffer.cc | 105 ++++++++++++++ impeller/toolkit/android/hardware_buffer.h | 127 ++++++++++++++++ impeller/toolkit/android/native_window.cc | 34 +++++ impeller/toolkit/android/native_window.h | 57 ++++++++ impeller/toolkit/android/proc_table.cc | 69 +++++++++ impeller/toolkit/android/proc_table.h | 107 ++++++++++++++ impeller/toolkit/android/surface_control.cc | 55 +++++++ impeller/toolkit/android/surface_control.h | 86 +++++++++++ .../toolkit/android/surface_transaction.cc | 104 ++++++++++++++ .../toolkit/android/surface_transaction.h | 135 ++++++++++++++++++ .../android/toolkit_android_unittests.cc | 63 ++++++++ testing/run_tests.py | 13 +- 17 files changed, 1036 insertions(+), 6 deletions(-) create mode 100644 impeller/toolkit/android/BUILD.gn create mode 100644 impeller/toolkit/android/README.md create mode 100644 impeller/toolkit/android/hardware_buffer.cc create mode 100644 impeller/toolkit/android/hardware_buffer.h create mode 100644 impeller/toolkit/android/native_window.cc create mode 100644 impeller/toolkit/android/native_window.h create mode 100644 impeller/toolkit/android/proc_table.cc create mode 100644 impeller/toolkit/android/proc_table.h create mode 100644 impeller/toolkit/android/surface_control.cc create mode 100644 impeller/toolkit/android/surface_control.h create mode 100644 impeller/toolkit/android/surface_transaction.cc create mode 100644 impeller/toolkit/android/surface_transaction.h create mode 100644 impeller/toolkit/android/toolkit_android_unittests.cc diff --git a/BUILD.gn b/BUILD.gn index d9b7d40b14449..86c63580228e3 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -167,8 +167,10 @@ group("unittests") { public_deps = [] if (is_android) { - public_deps += - [ "//flutter/shell/platform/android:flutter_shell_native_unittests" ] + public_deps += [ + "//flutter/impeller/toolkit/android:unittests", + "//flutter/shell/platform/android:flutter_shell_native_unittests", + ] } if (is_ios) { diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 5635e1fd82483..a4a4944b86efa 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -200,6 +200,8 @@ ../../../flutter/impeller/tessellator/dart/pubspec.lock ../../../flutter/impeller/tessellator/dart/pubspec.yaml ../../../flutter/impeller/tessellator/tessellator_unittests.cc +../../../flutter/impeller/toolkit/android/README.md +../../../flutter/impeller/toolkit/android/toolkit_android_unittests.cc ../../../flutter/impeller/tools/build_metal_library.py ../../../flutter/impeller/tools/check_licenses.py ../../../flutter/impeller/tools/malioc_cores.py diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c50c6ab586b4b..6bb2d982e5969 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -40151,6 +40151,16 @@ ORIGIN: ../../../flutter/impeller/tessellator/c/tessellator.h + ../../../flutter ORIGIN: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/tessellator/tessellator.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/tessellator/tessellator.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/hardware_buffer.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/hardware_buffer.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/native_window.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/native_window.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/proc_table.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/proc_table.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/surface_control.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/surface_control.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/surface_transaction.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/surface_transaction.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/toolkit/egl/config.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/toolkit/egl/config.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/toolkit/egl/context.cc + ../../../flutter/LICENSE @@ -43008,6 +43018,16 @@ FILE: ../../../flutter/impeller/tessellator/c/tessellator.h FILE: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart FILE: ../../../flutter/impeller/tessellator/tessellator.cc FILE: ../../../flutter/impeller/tessellator/tessellator.h +FILE: ../../../flutter/impeller/toolkit/android/hardware_buffer.cc +FILE: ../../../flutter/impeller/toolkit/android/hardware_buffer.h +FILE: ../../../flutter/impeller/toolkit/android/native_window.cc +FILE: ../../../flutter/impeller/toolkit/android/native_window.h +FILE: ../../../flutter/impeller/toolkit/android/proc_table.cc +FILE: ../../../flutter/impeller/toolkit/android/proc_table.h +FILE: ../../../flutter/impeller/toolkit/android/surface_control.cc +FILE: ../../../flutter/impeller/toolkit/android/surface_control.h +FILE: ../../../flutter/impeller/toolkit/android/surface_transaction.cc +FILE: ../../../flutter/impeller/toolkit/android/surface_transaction.h FILE: ../../../flutter/impeller/toolkit/egl/config.cc FILE: ../../../flutter/impeller/toolkit/egl/config.h FILE: ../../../flutter/impeller/toolkit/egl/context.cc diff --git a/impeller/toolkit/android/BUILD.gn b/impeller/toolkit/android/BUILD.gn new file mode 100644 index 0000000000000..19fa19f66fdba --- /dev/null +++ b/impeller/toolkit/android/BUILD.gn @@ -0,0 +1,52 @@ +# 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. + +import("../../tools/impeller.gni") + +config("public_android_config") { + defines = [ "__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__" ] +} + +impeller_component("android") { + sources = [ + "hardware_buffer.cc", + "hardware_buffer.h", + "native_window.cc", + "native_window.h", + "proc_table.cc", + "proc_table.h", + "surface_control.cc", + "surface_control.h", + "surface_transaction.cc", + "surface_transaction.h", + ] + + public_deps = [ + "../../base", + "../../geometry", + "//flutter/fml", + ] + + public_configs = [ ":public_android_config" ] +} + +test_fixtures("unittests_fixtures") { + fixtures = [] +} + +executable("unittests") { + assert(is_android) + + testonly = true + + output_name = "impeller_toolkit_android_unittests" + + sources = [ "toolkit_android_unittests.cc" ] + + deps = [ + ":android", + ":unittests_fixtures", + "//flutter/testing", + ] +} diff --git a/impeller/toolkit/android/README.md b/impeller/toolkit/android/README.md new file mode 100644 index 0000000000000..b52d8f6ea8f7f --- /dev/null +++ b/impeller/toolkit/android/README.md @@ -0,0 +1,7 @@ +Android Toolkit +=============== + +Type-safe managed wrappers around Android objects vended by the NDK. Does not +require linking to libandroid.so. The symbols are resolved via dynamic runtime +lookup so that the toolkit can be built with an older NDK but still run on +modern Android versions and use the latest features. diff --git a/impeller/toolkit/android/hardware_buffer.cc b/impeller/toolkit/android/hardware_buffer.cc new file mode 100644 index 0000000000000..0f541a7cb9eb1 --- /dev/null +++ b/impeller/toolkit/android/hardware_buffer.cc @@ -0,0 +1,105 @@ +// 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/toolkit/android/hardware_buffer.h" + +#include "impeller/base/validation.h" + +namespace impeller::android { + +static AHardwareBuffer_Format ToAHardwareBufferFormat( + HardwareBufferFormat format) { + switch (format) { + case HardwareBufferFormat::kR8G8B8A8UNormInt: + return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + } + FML_UNREACHABLE(); +} + +static AHardwareBuffer_Desc ToAHardwareBufferDesc( + const HardwareBufferDescriptor& desc) { + AHardwareBuffer_Desc ahb_desc = {}; + ahb_desc.width = desc.size.width; + ahb_desc.height = desc.size.height; + ahb_desc.format = ToAHardwareBufferFormat(desc.format); + ahb_desc.layers = 1u; + if (desc.usage & static_cast( + HardwareBufferUsageFlags::kFrameBufferAttachment)) { + ahb_desc.usage |= AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; + } + if (desc.usage & static_cast( + HardwareBufferUsageFlags::kCompositorOverlay)) { + ahb_desc.usage |= AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY; + } + if (desc.usage & static_cast( + HardwareBufferUsageFlags::kSampledImage)) { + ahb_desc.usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; + } + return ahb_desc; +} + +bool HardwareBufferDescriptor::IsAllocatable() const { + const auto desc = ToAHardwareBufferDesc(*this); + return GetProcTable().AHardwareBuffer_isSupported(&desc) != 0u; +} + +HardwareBuffer::HardwareBuffer(HardwareBufferDescriptor descriptor) + : descriptor_(descriptor), + android_descriptor_(ToAHardwareBufferDesc(descriptor_)) { + if (!descriptor_.IsAllocatable()) { + VALIDATION_LOG << "The hardware buffer descriptor is not allocatable."; + return; + } + const auto& proc_table = GetProcTable(); + + AHardwareBuffer* buffer = nullptr; + if (auto result = + proc_table.AHardwareBuffer_allocate(&android_descriptor_, &buffer); + result != 0 || buffer == nullptr) { + VALIDATION_LOG << "Could not allocate hardware buffer. Error: " << result; + return; + } + buffer_.reset(buffer); + is_valid_ = true; +} + +HardwareBuffer::~HardwareBuffer() = default; + +bool HardwareBuffer::IsValid() const { + return is_valid_; +} + +AHardwareBuffer* HardwareBuffer::GetHandle() const { + return buffer_.get(); +} + +HardwareBufferDescriptor HardwareBufferDescriptor::MakeForSwapchainImage( + const ISize& size) { + HardwareBufferDescriptor desc; + desc.format = HardwareBufferFormat::kR8G8B8A8UNormInt; + // Zero sized hardware buffers cannot be allocated. + desc.size = size.Max(ISize{1u, 1u}); + desc.usage = + static_cast( + HardwareBufferUsageFlags::kFrameBufferAttachment) | + static_cast( + HardwareBufferUsageFlags::kCompositorOverlay) | + static_cast(HardwareBufferUsageFlags::kSampledImage); + return desc; +} + +const HardwareBufferDescriptor& HardwareBuffer::GetDescriptor() const { + return descriptor_; +} + +const AHardwareBuffer_Desc& HardwareBuffer::GetAndroidDescriptor() const { + return android_descriptor_; +} + +bool HardwareBuffer::IsAvailableOnPlatform() { + return GetProcTable().IsValid() && + GetProcTable().AHardwareBuffer_allocate.IsAvailable(); +} + +} // namespace impeller::android diff --git a/impeller/toolkit/android/hardware_buffer.h b/impeller/toolkit/android/hardware_buffer.h new file mode 100644 index 0000000000000..7b15bd9497318 --- /dev/null +++ b/impeller/toolkit/android/hardware_buffer.h @@ -0,0 +1,127 @@ +// 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_TOOLKIT_ANDROID_HARDWARE_BUFFER_H_ +#define FLUTTER_IMPELLER_TOOLKIT_ANDROID_HARDWARE_BUFFER_H_ + +#include "flutter/fml/unique_object.h" +#include "impeller/geometry/size.h" +#include "impeller/toolkit/android/proc_table.h" + +namespace impeller::android { + +enum class HardwareBufferFormat { + //---------------------------------------------------------------------------- + /// This format is guaranteed to be supported on all versions of Android. This + /// format can also be converted to an Impeller and Vulkan format. + /// + /// @see Vulkan Format: VK_FORMAT_R8G8B8A8_UNORM + /// @see OpenGL ES Format: GL_RGBA8 + /// + /// Why have many format when one format do trick? + /// + kR8G8B8A8UNormInt, +}; + +using HardwareBufferUsage = uint8_t; + +enum class HardwareBufferUsageFlags : HardwareBufferUsage { + kFrameBufferAttachment = 1u << 0u, + kCompositorOverlay = 1u << 1u, + kSampledImage = 1u << 2u, +}; + +//------------------------------------------------------------------------------ +/// @brief A descriptor use to specify hardware buffer allocations. +/// +struct HardwareBufferDescriptor { + HardwareBufferFormat format = HardwareBufferFormat::kR8G8B8A8UNormInt; + ISize size; + HardwareBufferUsage usage = 0u; + + //---------------------------------------------------------------------------- + /// @brief Create a descriptor of the given size that is suitable for use + /// as a swapchain image. + /// + /// @warning Descriptors of zero size are not allocatable. The next best + /// valid size is picked. So make sure to check the actual size of + /// the descriptor after this call is made to determine the size + /// of the allocated hardware buffer. + /// + /// @param[in] size The size. See the restrictions about valid sizes above. + /// + /// @return The hardware buffer descriptor. + /// + static HardwareBufferDescriptor MakeForSwapchainImage(const ISize& size); + + //---------------------------------------------------------------------------- + /// @brief If hardware buffers can be created using this descriptor. + /// Allocatable descriptors may still cause failing allocations in + /// case of resource exhaustion. + /// + /// @return `true` if allocatable (unless resource exhaustion). + /// + bool IsAllocatable() const; + + constexpr bool operator==(const HardwareBufferDescriptor& o) const { + return format == o.format && size == o.size && usage == o.usage; + } + + constexpr bool operator!=(const HardwareBufferDescriptor& o) const { + return !(*this == o); + } +}; + +//------------------------------------------------------------------------------ +/// @brief A wrapper for AHardwareBuffer +/// https://developer.android.com/ndk/reference/group/a-hardware-buffer +/// +/// This wrapper creates and owns a handle to a managed hardware +/// buffer. That is, there is no ability to take a reference to an +/// externally created hardware buffer. +/// +/// This wrapper is only available on Android API 29 and above. +/// +class HardwareBuffer { + public: + static bool IsAvailableOnPlatform(); + + explicit HardwareBuffer(HardwareBufferDescriptor descriptor); + + ~HardwareBuffer(); + + HardwareBuffer(const HardwareBuffer&) = delete; + + HardwareBuffer& operator=(const HardwareBuffer&) = delete; + + bool IsValid() const; + + AHardwareBuffer* GetHandle() const; + + const HardwareBufferDescriptor& GetDescriptor() const; + + const AHardwareBuffer_Desc& GetAndroidDescriptor() const; + + private: + struct UniqueAHardwareBufferTraits { + static AHardwareBuffer* InvalidValue() { return nullptr; } + + static bool IsValid(AHardwareBuffer* value) { + return value != InvalidValue(); + } + + static void Free(AHardwareBuffer* value) { + GetProcTable().AHardwareBuffer_release(value); + } + }; + + const HardwareBufferDescriptor descriptor_; + const AHardwareBuffer_Desc android_descriptor_; + fml::UniqueObject buffer_; + bool is_valid_ = false; +}; + +} // namespace impeller::android + +#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_HARDWARE_BUFFER_H_ diff --git a/impeller/toolkit/android/native_window.cc b/impeller/toolkit/android/native_window.cc new file mode 100644 index 0000000000000..663a482f624a3 --- /dev/null +++ b/impeller/toolkit/android/native_window.cc @@ -0,0 +1,34 @@ +// 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/toolkit/android/native_window.h" + +namespace impeller::android { + +NativeWindow::NativeWindow(ANativeWindow* window) : window_(window) { + if (window_.get()) { + GetProcTable().ANativeWindow_acquire(window_.get()); + } +} + +NativeWindow::~NativeWindow() = default; + +bool NativeWindow::IsValid() const { + return window_.is_valid(); +} + +ISize NativeWindow::GetSize() const { + if (!IsValid()) { + return {}; + } + const int32_t width = ANativeWindow_getWidth(window_.get()); + const int32_t height = ANativeWindow_getHeight(window_.get()); + return ISize::MakeWH(std::max(width, 0), std::max(height, 0)); +} + +ANativeWindow* NativeWindow::GetHandle() const { + return window_.get(); +} + +} // namespace impeller::android diff --git a/impeller/toolkit/android/native_window.h b/impeller/toolkit/android/native_window.h new file mode 100644 index 0000000000000..8af6cfb4ba5a6 --- /dev/null +++ b/impeller/toolkit/android/native_window.h @@ -0,0 +1,57 @@ +// 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_TOOLKIT_ANDROID_NATIVE_WINDOW_H_ +#define FLUTTER_IMPELLER_TOOLKIT_ANDROID_NATIVE_WINDOW_H_ + +#include "flutter/fml/unique_object.h" +#include "impeller/geometry/size.h" +#include "impeller/toolkit/android/proc_table.h" + +namespace impeller::android { + +//------------------------------------------------------------------------------ +/// @brief A wrapper for ANativeWindow +/// https://developer.android.com/ndk/reference/group/a-native-window +/// +/// This wrapper is only available on Android. +/// +class NativeWindow { + public: + explicit NativeWindow(ANativeWindow* window); + + ~NativeWindow(); + + NativeWindow(const NativeWindow&) = delete; + + NativeWindow& operator=(const NativeWindow&) = delete; + + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @return The current size of the native window. + /// + ISize GetSize() const; + + ANativeWindow* GetHandle() const; + + private: + struct UniqueANativeWindowTraits { + static ANativeWindow* InvalidValue() { return nullptr; } + + static bool IsValid(ANativeWindow* value) { + return value != InvalidValue(); + } + + static void Free(ANativeWindow* value) { + GetProcTable().ANativeWindow_release(value); + } + }; + + fml::UniqueObject window_; +}; + +} // namespace impeller::android + +#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_NATIVE_WINDOW_H_ diff --git a/impeller/toolkit/android/proc_table.cc b/impeller/toolkit/android/proc_table.cc new file mode 100644 index 0000000000000..55791b238ceee --- /dev/null +++ b/impeller/toolkit/android/proc_table.cc @@ -0,0 +1,69 @@ +// 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 "flutter/impeller/toolkit/android/proc_table.h" + +#include "impeller/base/validation.h" + +namespace impeller::android { + +const ProcTable& GetProcTable() { + static ProcTable gProcTable; + return gProcTable; +} + +ProcTable::ProcTable() + : lib_android_(fml::NativeLibrary::Create("libandroid.so")) { + if (!lib_android_) { + VALIDATION_LOG << "Could not open libandroid.so"; + return; + } + + auto proc = + lib_android_->ResolveFunction( + "android_get_device_api_level"); + if (!proc.has_value()) { + return; + } + + int api = proc.value()(); + if (api < 0) { + VALIDATION_LOG << "Could not get Android API level."; + return; + } + + device_api_level_ = api; + +#define RESOLVE_PROC(table_member, api) \ + { \ + if (device_api_level_ >= table_member.api_availability) { \ + if (auto resolved = \ + lib_android_->ResolveFunction( \ + table_member.proc_name); \ + resolved.has_value()) { \ + table_member.proc = resolved.value(); \ + } else { \ + VALIDATION_LOG << "Could not resolve function: " \ + << table_member.proc_name; \ + return; \ + } \ + } \ + } + FOR_EACH_ANDROID_PROC(RESOLVE_PROC); +#undef RESOLVE_PROC + + is_valid_ = true; +} + +ProcTable::~ProcTable() = default; + +bool ProcTable::IsValid() const { + return is_valid_; +} + +uint32_t ProcTable::GetAndroidDeviceAPILevel() const { + return device_api_level_; +} + +} // namespace impeller::android diff --git a/impeller/toolkit/android/proc_table.h b/impeller/toolkit/android/proc_table.h new file mode 100644 index 0000000000000..eb56fe6bd50d1 --- /dev/null +++ b/impeller/toolkit/android/proc_table.h @@ -0,0 +1,107 @@ +// 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_TOOLKIT_ANDROID_PROC_TABLE_H_ +#define FLUTTER_IMPELLER_TOOLKIT_ANDROID_PROC_TABLE_H_ + +#include +#include +#include + +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/native_library.h" + +namespace impeller::android { + +#define FOR_EACH_ANDROID_PROC(INVOKE) \ + INVOKE(AHardwareBuffer_allocate, 26) \ + INVOKE(AHardwareBuffer_acquire, 26) \ + INVOKE(AHardwareBuffer_release, 26) \ + INVOKE(AHardwareBuffer_isSupported, 29) \ + INVOKE(AHardwareBuffer_describe, 26) \ + INVOKE(ANativeWindow_acquire, 0) \ + INVOKE(ANativeWindow_release, 0) \ + INVOKE(ANativeWindow_getWidth, 0) \ + INVOKE(ANativeWindow_getHeight, 0) \ + INVOKE(ASurfaceControl_createFromWindow, 29) \ + INVOKE(ASurfaceControl_release, 29) \ + INVOKE(ASurfaceTransaction_create, 29) \ + INVOKE(ASurfaceTransaction_delete, 29) \ + INVOKE(ASurfaceTransaction_apply, 29) \ + INVOKE(ASurfaceTransaction_setOnComplete, 29) \ + INVOKE(ASurfaceTransaction_reparent, 29) \ + INVOKE(ASurfaceTransaction_setBuffer, 29) \ + INVOKE(ASurfaceTransaction_setColor, 29) + +template +struct AndroidProc { + using AndroidProcType = T; + + const char* proc_name = nullptr; + + size_t api_availability = 0; + + AndroidProcType* proc = nullptr; + + constexpr bool IsAvailable() const { return proc != nullptr; } + + template + auto operator()(Args&&... args) const { + FML_DCHECK(IsAvailable()) + << "Android method " << proc_name + << " is not available on this device. Missing check."; + return proc(std::forward(args)...); + } +}; + +//------------------------------------------------------------------------------ +/// @brief The table of Android procs that are resolved dynamically. Those +/// some members of are available at API levels lower than 29, this +/// proc table may only be used on API levels at or above 29. +/// +struct ProcTable { + ProcTable(); + + ~ProcTable(); + + ProcTable(const ProcTable&) = delete; + + ProcTable& operator=(const ProcTable&) = delete; + + //---------------------------------------------------------------------------- + /// @brief If a valid proc table could be setup. This may fail in case of + /// setup on non-Android platforms. + /// + /// @return `true` if valid. + /// + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Get the Android device API level. Due to the overall + /// availability restrictions of this class, this may only be at + /// or above 29 on a valid proc table. + /// + /// @return The Android device api level. + /// + uint32_t GetAndroidDeviceAPILevel() const; + +#define DEFINE_PROC(name, api) \ + AndroidProc name = {.proc_name = #name, \ + .api_availability = api}; + FOR_EACH_ANDROID_PROC(DEFINE_PROC); +#undef DEFINE_PROC + + private: + fml::RefPtr lib_android_; + uint32_t device_api_level_ = 0u; + bool is_valid_ = false; +}; + +const ProcTable& GetProcTable(); + +} // namespace impeller::android + +#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_PROC_TABLE_H_ diff --git a/impeller/toolkit/android/surface_control.cc b/impeller/toolkit/android/surface_control.cc new file mode 100644 index 0000000000000..71282e7ccc981 --- /dev/null +++ b/impeller/toolkit/android/surface_control.cc @@ -0,0 +1,55 @@ +// 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/toolkit/android/surface_control.h" + +#include "impeller/base/validation.h" +#include "impeller/toolkit/android/surface_transaction.h" + +namespace impeller::android { + +SurfaceControl::SurfaceControl(ANativeWindow* window, const char* debug_name) { + if (window == nullptr) { + VALIDATION_LOG << "Parent window of surface was null."; + return; + } + if (debug_name == nullptr) { + debug_name = "Impeller Layer"; + } + control_.reset( + GetProcTable().ASurfaceControl_createFromWindow(window, debug_name)); +} + +SurfaceControl::~SurfaceControl() { + if (IsValid() && !RemoveFromParent()) { + VALIDATION_LOG << "Surface control could not be removed from its parent. " + "Expect a leak."; + } +} + +bool SurfaceControl::IsValid() const { + return control_.is_valid(); +} + +ASurfaceControl* SurfaceControl::GetHandle() const { + return control_.get(); +} + +bool SurfaceControl::RemoveFromParent() const { + if (!IsValid()) { + return false; + } + SurfaceTransaction transaction; + if (!transaction.SetParent(*this, nullptr)) { + return false; + } + return transaction.Apply(); +} + +bool SurfaceControl::IsAvailableOnPlatform() { + return GetProcTable().IsValid() && + GetProcTable().ASurfaceControl_createFromWindow.IsAvailable(); +} + +} // namespace impeller::android diff --git a/impeller/toolkit/android/surface_control.h b/impeller/toolkit/android/surface_control.h new file mode 100644 index 0000000000000..af51a4bc5b748 --- /dev/null +++ b/impeller/toolkit/android/surface_control.h @@ -0,0 +1,86 @@ +// 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_TOOLKIT_ANDROID_SURFACE_CONTROL_H_ +#define FLUTTER_IMPELLER_TOOLKIT_ANDROID_SURFACE_CONTROL_H_ + +#include "flutter/fml/unique_object.h" +#include "impeller/toolkit/android/proc_table.h" + +namespace impeller::android { + +//------------------------------------------------------------------------------ +/// @brief A wrapper for ASurfaceControl. +/// https://developer.android.com/ndk/reference/group/native-activity#asurfacecontrol +/// +/// Instances of this class represent a node in the hierarchy of +/// surfaces sent to the system compositor for final composition. +/// +/// This wrapper is only available on Android API 29 and above. +/// +class SurfaceControl { + public: + //---------------------------------------------------------------------------- + /// @return `true` if any surface controls can be created on this + /// platform. + /// + static bool IsAvailableOnPlatform(); + + //---------------------------------------------------------------------------- + /// @brief Creates a new surface control and adds it as a child of the + /// given window. + /// + /// @param window The window + /// @param[in] debug_name A debug name. See it using + /// `adb shell dumpsys SurfaceFlinger` along with + /// other control properties. If no debug name is + /// specified, the value "Impeller Layer" is used. + /// + explicit SurfaceControl(ANativeWindow* window, + const char* debug_name = nullptr); + + //---------------------------------------------------------------------------- + /// @brief Removes the surface control from the presentation hierarchy + /// managed by the system compositor and release the client side + /// reference to the control. At this point, it may be collected + /// when the compositor is also done using it. + /// + ~SurfaceControl(); + + SurfaceControl(const SurfaceControl&) = delete; + + SurfaceControl& operator=(const SurfaceControl&) = delete; + + bool IsValid() const; + + ASurfaceControl* GetHandle() const; + + //---------------------------------------------------------------------------- + /// @brief Remove the surface control from the hierarchy of nodes + /// presented by the system compositor. + /// + /// @return `true` If the control will be removed from the hierarchy of + /// nodes presented by the system compositor. + /// + bool RemoveFromParent() const; + + private: + struct UniqueASurfaceControlTraits { + static ASurfaceControl* InvalidValue() { return nullptr; } + + static bool IsValid(ASurfaceControl* value) { + return value != InvalidValue(); + } + + static void Free(ASurfaceControl* value) { + GetProcTable().ASurfaceControl_release(value); + } + }; + + fml::UniqueObject control_; +}; + +} // namespace impeller::android + +#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_SURFACE_CONTROL_H_ diff --git a/impeller/toolkit/android/surface_transaction.cc b/impeller/toolkit/android/surface_transaction.cc new file mode 100644 index 0000000000000..a96eef6a26a57 --- /dev/null +++ b/impeller/toolkit/android/surface_transaction.cc @@ -0,0 +1,104 @@ +// 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 "flutter/impeller/toolkit/android/surface_transaction.h" + +#include "flutter/impeller/toolkit/android/hardware_buffer.h" +#include "flutter/impeller/toolkit/android/surface_control.h" +#include "impeller/base/validation.h" + +namespace impeller::android { + +SurfaceTransaction::SurfaceTransaction() + : transaction_(GetProcTable().ASurfaceTransaction_create()) {} + +SurfaceTransaction::~SurfaceTransaction() = default; + +bool SurfaceTransaction::IsValid() const { + return transaction_.is_valid(); +} + +struct TransactionInFlightData { + SurfaceTransaction::OnCompleteCallback callback; +}; + +bool SurfaceTransaction::Apply(OnCompleteCallback callback) { + if (!IsValid()) { + return false; + } + + if (!callback) { + callback = []() {}; + } + + const auto& proc_table = GetProcTable(); + + auto data = std::make_unique(); + data->callback = callback; + proc_table.ASurfaceTransaction_setOnComplete( + transaction_.get(), // + data.release(), // + [](void* context, ASurfaceTransactionStats* stats) -> void { + auto data = reinterpret_cast(context); + data->callback(); + delete data; + }); + proc_table.ASurfaceTransaction_apply(transaction_.get()); + + // Transactions may not be applied over and over. + transaction_.reset(); + return true; +} + +bool SurfaceTransaction::SetContents(const SurfaceControl* control, + const HardwareBuffer* buffer) { + if (control == nullptr || buffer == nullptr) { + VALIDATION_LOG << "Invalid control or buffer."; + return false; + } + GetProcTable().ASurfaceTransaction_setBuffer(transaction_.get(), // + control->GetHandle(), // + buffer->GetHandle(), // + -1); + return true; +} + +bool SurfaceTransaction::SetBackgroundColor(const SurfaceControl& control, + const Color& color) { + if (!IsValid() || !control.IsValid()) { + return false; + } + GetProcTable().ASurfaceTransaction_setColor(transaction_.get(), // + control.GetHandle(), // + color.red, // + color.green, // + color.blue, // + color.alpha, // + ADATASPACE_SRGB_LINEAR // + ); + return true; +} + +bool SurfaceTransaction::SetParent(const SurfaceControl& control, + const SurfaceControl* new_parent) { + if (!IsValid() || !control.IsValid()) { + return false; + } + if (new_parent && !new_parent->IsValid()) { + return false; + } + GetProcTable().ASurfaceTransaction_reparent( + transaction_.get(), // + control.GetHandle(), // + new_parent == nullptr ? nullptr : new_parent->GetHandle() // + ); + return true; +} + +bool SurfaceTransaction::IsAvailableOnPlatform() { + return GetProcTable().IsValid() && + GetProcTable().ASurfaceTransaction_create.IsAvailable(); +} + +} // namespace impeller::android diff --git a/impeller/toolkit/android/surface_transaction.h b/impeller/toolkit/android/surface_transaction.h new file mode 100644 index 0000000000000..b9460f35e0978 --- /dev/null +++ b/impeller/toolkit/android/surface_transaction.h @@ -0,0 +1,135 @@ +// 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_TOOLKIT_ANDROID_SURFACE_TRANSACTION_H_ +#define FLUTTER_IMPELLER_TOOLKIT_ANDROID_SURFACE_TRANSACTION_H_ + +#include +#include + +#include "flutter/fml/unique_object.h" +#include "impeller/geometry/color.h" +#include "impeller/toolkit/android/proc_table.h" + +namespace impeller::android { + +class SurfaceControl; +class HardwareBuffer; + +//------------------------------------------------------------------------------ +/// @brief A wrapper for ASurfaceTransaction. +/// https://developer.android.com/ndk/reference/group/native-activity#asurfacetransaction +/// +/// A surface transaction is a collection of updates to the +/// hierarchy of surfaces (represented by `ASurfaceControl` +/// instances) that are applied atomically in the compositor. +/// +/// This wrapper is only available on Android API 29 and above. +/// +/// @note Transactions should be short lived objects (create, apply, +/// collect). But, if these are used on multiple threads, they must +/// be externally synchronized. +/// +class SurfaceTransaction { + public: + //---------------------------------------------------------------------------- + /// @return `true` if any surface transactions can be created on this + /// platform. + /// + static bool IsAvailableOnPlatform(); + + SurfaceTransaction(); + + ~SurfaceTransaction(); + + SurfaceTransaction(const SurfaceTransaction&) = delete; + + SurfaceTransaction& operator=(const SurfaceTransaction&) = delete; + + bool IsValid() const; + + //---------------------------------------------------------------------------- + /// @brief Encodes that the updated contents of a surface control are + /// specified by the given hardware buffer. The update will not be + /// committed till the call to `Apply` however. + /// + /// @see `SurfaceTransaction::Apply`. + /// + /// @param[in] control The control + /// @param[in] buffer The hardware buffer + /// + /// @return If the update was encoded in the transaction. + /// + [[nodiscard]] bool SetContents(const SurfaceControl* control, + const HardwareBuffer* buffer); + + //---------------------------------------------------------------------------- + /// @brief Encodes the updated background color of the surface control. + /// The update will not be committed till the call to `Apply` + /// however. + /// + /// @see `SurfaceTransaction::Apply`. + /// + /// @param[in] control The control + /// @param[in] color The color + /// + /// @return `true` if the background control will be set when transaction + /// is applied. + /// + [[nodiscard]] bool SetBackgroundColor(const SurfaceControl& control, + const Color& color); + + using OnCompleteCallback = std::function; + + //---------------------------------------------------------------------------- + /// @brief Applies the updated encoded in the transaction and invokes the + /// callback when the updated are complete. + /// + /// @warning The callback will be invoked on a system managed thread. + /// + /// @note It is fine to immediately destroy the transaction after the + /// call to apply. It is not necessary to wait for transaction + /// completion to collect the transaction handle. + /// + /// @param[in] callback The callback + /// + /// @return `true` if the surface transaction was applied. `true` does not + /// indicate the application was completed however. Only the + /// invocation of the callback denotes transaction completion. + /// + [[nodiscard]] bool Apply(OnCompleteCallback callback = nullptr); + + //---------------------------------------------------------------------------- + /// @brief Set the new parent control of the given control. If the new + /// parent is null, it is removed from the control hierarchy. + /// + /// @param[in] control The control + /// @param[in] new_parent The new parent + /// + /// @return `true` if the control will be re-parented when the transaction + /// is applied. + /// + [[nodiscard]] bool SetParent(const SurfaceControl& control, + const SurfaceControl* new_parent = nullptr); + + private: + struct UniqueASurfaceTransactionTraits { + static ASurfaceTransaction* InvalidValue() { return nullptr; } + + static bool IsValid(ASurfaceTransaction* value) { + return value != InvalidValue(); + } + + static void Free(ASurfaceTransaction* value) { + GetProcTable().ASurfaceTransaction_delete(value); + } + }; + + fml::UniqueObject + transaction_; +}; + +} // namespace impeller::android + +#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_SURFACE_TRANSACTION_H_ diff --git a/impeller/toolkit/android/toolkit_android_unittests.cc b/impeller/toolkit/android/toolkit_android_unittests.cc new file mode 100644 index 0000000000000..6b34e2dead05d --- /dev/null +++ b/impeller/toolkit/android/toolkit_android_unittests.cc @@ -0,0 +1,63 @@ +// 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 "flutter/fml/synchronization/waitable_event.h" +#include "flutter/testing/testing.h" +#include "impeller/toolkit/android/hardware_buffer.h" +#include "impeller/toolkit/android/proc_table.h" +#include "impeller/toolkit/android/surface_control.h" +#include "impeller/toolkit/android/surface_transaction.h" + +namespace impeller::android::testing { + +class ToolkitAndroidTest : public ::testing::Test { + public: + void SetUp() override { + // The toolkit is only available on Android API levels over 29. Skip these + // tests everywhere else. + if (__builtin_available(android 29, *)) { + } else { + GTEST_SKIP() << "Platform too old for this test."; + } + } +}; + +TEST_F(ToolkitAndroidTest, CanCreateProcTable) { + ProcTable proc_table; + ASSERT_TRUE(proc_table.IsValid()); +} + +TEST_F(ToolkitAndroidTest, GuardsAgainstZeroSizedDescriptors) { + auto desc = HardwareBufferDescriptor::MakeForSwapchainImage({0, 0}); + ASSERT_GT(desc.size.width, 0u); + ASSERT_GT(desc.size.height, 0u); +} + +TEST_F(ToolkitAndroidTest, CanCreateHardwareBuffer) { + ASSERT_TRUE(HardwareBuffer::IsAvailableOnPlatform()); + auto desc = HardwareBufferDescriptor::MakeForSwapchainImage({100, 100}); + ASSERT_TRUE(desc.IsAllocatable()); + HardwareBuffer buffer(desc); + ASSERT_TRUE(buffer.IsValid()); +} + +TEST_F(ToolkitAndroidTest, CanApplySurfaceTransaction) { + ASSERT_TRUE(SurfaceTransaction::IsAvailableOnPlatform()); + SurfaceTransaction transaction; + ASSERT_TRUE(transaction.IsValid()); + fml::AutoResetWaitableEvent event; + ASSERT_TRUE(transaction.Apply([&event]() { event.Signal(); })); + event.Wait(); +} + +TEST_F(ToolkitAndroidTest, SurfacControlsAreAvailable) { + ASSERT_TRUE(SurfaceControl::IsAvailableOnPlatform()); +} + +TEST_F(ToolkitAndroidTest, CanAccessDeviceAPILevel) { + // This test will be skipped otherwise. + ASSERT_GE(GetProcTable().GetAndroidDeviceAPILevel(), 29u); +} + +} // namespace impeller::android::testing diff --git a/testing/run_tests.py b/testing/run_tests.py index d91ec5de3a629..d51ad01f2b1c5 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -729,16 +729,21 @@ def run_java_tests(executable_filter, android_variant='android_debug_unopt'): run_cmd(command, cwd=test_runner_dir, env=env) -def run_android_tests(android_variant='android_debug_unopt', adb_path=None): - test_runner_name = 'flutter_shell_native_unittests' +def run_android_unittest(test_runner_name, android_variant, adb_path): tests_path = os.path.join(OUT_DIR, android_variant, test_runner_name) remote_path = '/data/local/tmp' remote_tests_path = os.path.join(remote_path, test_runner_name) - if adb_path is None: - adb_path = 'adb' run_cmd([adb_path, 'push', tests_path, remote_path], cwd=BUILDROOT_DIR) run_cmd([adb_path, 'shell', remote_tests_path]) + +def run_android_tests(android_variant='android_debug_unopt', adb_path=None): + if adb_path is None: + adb_path = 'adb' + + run_android_unittest('flutter_shell_native_unittests', android_variant, adb_path) + run_android_unittest('impeller_toolkit_android_unittests', android_variant, adb_path) + systrace_test = os.path.join(BUILDROOT_DIR, 'flutter', 'testing', 'android_systrace_test.py') scenario_apk = os.path.join(OUT_DIR, android_variant, 'firebase_apks', 'scenario_app.apk') run_cmd([ From ed6c3ada5e2d1986e12c9d273ff4f5f74335972f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Mar 2024 15:05:48 -0700 Subject: [PATCH 02/15] Add support for choreographer callbacks. --- impeller/toolkit/android/BUILD.gn | 2 + impeller/toolkit/android/choreographer.cc | 93 +++++++++++++++++++ impeller/toolkit/android/choreographer.h | 52 +++++++++++ impeller/toolkit/android/proc_table.h | 57 +++++++----- .../android/toolkit_android_unittests.cc | 26 ++++++ 5 files changed, 209 insertions(+), 21 deletions(-) create mode 100644 impeller/toolkit/android/choreographer.cc create mode 100644 impeller/toolkit/android/choreographer.h diff --git a/impeller/toolkit/android/BUILD.gn b/impeller/toolkit/android/BUILD.gn index 19fa19f66fdba..fd0dde36bd9bf 100644 --- a/impeller/toolkit/android/BUILD.gn +++ b/impeller/toolkit/android/BUILD.gn @@ -10,6 +10,8 @@ config("public_android_config") { impeller_component("android") { sources = [ + "choreographer.cc", + "choreographer.h", "hardware_buffer.cc", "hardware_buffer.h", "native_window.cc", diff --git a/impeller/toolkit/android/choreographer.cc b/impeller/toolkit/android/choreographer.cc new file mode 100644 index 0000000000000..f4e7eebab6f96 --- /dev/null +++ b/impeller/toolkit/android/choreographer.cc @@ -0,0 +1,93 @@ +// 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 "flutter/impeller/toolkit/android/choreographer.h" + +#include "flutter/fml/message_loop.h" + +namespace impeller::android { + +Choreographer& Choreographer::GetInstance() { + static thread_local Choreographer tChoreographer; + return tChoreographer; +} + +Choreographer::Choreographer() { + if (!GetProcTable().AChoreographer_getInstance) { + return; + } + + const auto api_level = GetProcTable().GetAndroidDeviceAPILevel(); + if (api_level >= 29 && !GetProcTable().AChoreographer_postFrameCallback64) { + return; + } + + if (api_level >= 24 && !GetProcTable().AChoreographer_postFrameCallback) { + return; + } + + // We need a message loop on the current thread for the choreographer to + // schedule callbacks for us on. + fml::MessageLoop::EnsureInitializedForCurrentThread(); + instance_ = GetProcTable().AChoreographer_getInstance(); +} + +Choreographer::~Choreographer() = default; + +bool Choreographer::IsValid() const { + return !!instance_; +} + +static Choreographer::FrameTimePoint ClockMonotonicNanosToFrameTimePoint( + int64_t p_nanos) { + return Choreographer::FrameTimePoint{std::chrono::nanoseconds(p_nanos)}; +} + +bool Choreographer::PostFrameCallback(FrameCallback callback) const { + if (!callback || !IsValid()) { + return false; + } + + struct InFlightData { + FrameCallback callback; + }; + + auto data = std::make_unique(); + data->callback = std::move(callback); + + const auto& table = GetProcTable(); + const auto api_level = table.GetAndroidDeviceAPILevel(); + if (api_level >= 29) { + table.AChoreographer_postFrameCallback64( + const_cast(instance_), + [](int64_t nanos, void* p_data) { + auto data = reinterpret_cast(p_data); + data->callback(ClockMonotonicNanosToFrameTimePoint(nanos)); + delete data; + }, + data.release()); + return true; + } else if (api_level >= 24) { + table.AChoreographer_postFrameCallback( + const_cast(instance_), + [](long /*NOLINT*/ nanos, void* p_data) { + auto data = reinterpret_cast(p_data); + data->callback(ClockMonotonicNanosToFrameTimePoint(nanos)); + delete data; + }, + data.release()); + return true; + } + + // The validity check should have tripped by now. + FML_UNREACHABLE(); + return false; +} + +bool Choreographer::IsAvailableOnPlatform() { + return GetProcTable().AChoreographer_postFrameCallback64 || + GetProcTable().AChoreographer_postFrameCallback; +} + +} // namespace impeller::android diff --git a/impeller/toolkit/android/choreographer.h b/impeller/toolkit/android/choreographer.h new file mode 100644 index 0000000000000..535f4e86824cb --- /dev/null +++ b/impeller/toolkit/android/choreographer.h @@ -0,0 +1,52 @@ +// 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_TOOLKIT_ANDROID_CHOREOGRAPHER_H_ +#define FLUTTER_IMPELLER_TOOLKIT_ANDROID_CHOREOGRAPHER_H_ + +#include "impeller/toolkit/android/proc_table.h" + +#include +#include + +namespace impeller::android { + +//------------------------------------------------------------------------------ +/// @brief This class describes access to the choreographer instance for +/// the current thread. Choreographers are only available on API +/// levels above 24. On levels below 24, an invalid choreographer +/// will be returned. +/// +/// Since choreographer need an event loop on the current thread, +/// one will be setup if it doesn't already exist. +/// +class Choreographer { + public: + static bool IsAvailableOnPlatform(); + + static Choreographer& GetInstance(); + + ~Choreographer(); + + Choreographer(const Choreographer&) = delete; + + Choreographer& operator=(const Choreographer&) = delete; + + bool IsValid() const; + + using FrameClock = std::chrono::steady_clock; + using FrameTimePoint = std::chrono::time_point; + using FrameCallback = std::function; + + bool PostFrameCallback(FrameCallback callback) const; + + private: + AChoreographer* instance_ = nullptr; + + explicit Choreographer(); +}; + +} // namespace impeller::android + +#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_CHOREOGRAPHER_H_ diff --git a/impeller/toolkit/android/proc_table.h b/impeller/toolkit/android/proc_table.h index eb56fe6bd50d1..8d382334b6a9e 100644 --- a/impeller/toolkit/android/proc_table.h +++ b/impeller/toolkit/android/proc_table.h @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -16,24 +17,28 @@ namespace impeller::android { -#define FOR_EACH_ANDROID_PROC(INVOKE) \ - INVOKE(AHardwareBuffer_allocate, 26) \ - INVOKE(AHardwareBuffer_acquire, 26) \ - INVOKE(AHardwareBuffer_release, 26) \ - INVOKE(AHardwareBuffer_isSupported, 29) \ - INVOKE(AHardwareBuffer_describe, 26) \ - INVOKE(ANativeWindow_acquire, 0) \ - INVOKE(ANativeWindow_release, 0) \ - INVOKE(ANativeWindow_getWidth, 0) \ - INVOKE(ANativeWindow_getHeight, 0) \ - INVOKE(ASurfaceControl_createFromWindow, 29) \ - INVOKE(ASurfaceControl_release, 29) \ - INVOKE(ASurfaceTransaction_create, 29) \ - INVOKE(ASurfaceTransaction_delete, 29) \ - INVOKE(ASurfaceTransaction_apply, 29) \ - INVOKE(ASurfaceTransaction_setOnComplete, 29) \ - INVOKE(ASurfaceTransaction_reparent, 29) \ - INVOKE(ASurfaceTransaction_setBuffer, 29) \ +#define FOR_EACH_ANDROID_PROC(INVOKE) \ + INVOKE(ATrace_isEnabled, 23) \ + INVOKE(AChoreographer_getInstance, 24) \ + INVOKE(AChoreographer_postFrameCallback, 24) \ + INVOKE(AChoreographer_postFrameCallback64, 29) \ + INVOKE(AHardwareBuffer_allocate, 26) \ + INVOKE(AHardwareBuffer_acquire, 26) \ + INVOKE(AHardwareBuffer_release, 26) \ + INVOKE(AHardwareBuffer_isSupported, 29) \ + INVOKE(AHardwareBuffer_describe, 26) \ + INVOKE(ANativeWindow_acquire, 0) \ + INVOKE(ANativeWindow_release, 0) \ + INVOKE(ANativeWindow_getWidth, 0) \ + INVOKE(ANativeWindow_getHeight, 0) \ + INVOKE(ASurfaceControl_createFromWindow, 29) \ + INVOKE(ASurfaceControl_release, 29) \ + INVOKE(ASurfaceTransaction_create, 29) \ + INVOKE(ASurfaceTransaction_delete, 29) \ + INVOKE(ASurfaceTransaction_apply, 29) \ + INVOKE(ASurfaceTransaction_setOnComplete, 29) \ + INVOKE(ASurfaceTransaction_reparent, 29) \ + INVOKE(ASurfaceTransaction_setBuffer, 29) \ INVOKE(ASurfaceTransaction_setColor, 29) template @@ -48,6 +53,8 @@ struct AndroidProc { constexpr bool IsAvailable() const { return proc != nullptr; } + explicit constexpr operator bool() const { return IsAvailable(); } + template auto operator()(Args&&... args) const { FML_DCHECK(IsAvailable()) @@ -58,9 +65,7 @@ struct AndroidProc { }; //------------------------------------------------------------------------------ -/// @brief The table of Android procs that are resolved dynamically. Those -/// some members of are available at API levels lower than 29, this -/// proc table may only be used on API levels at or above 29. +/// @brief The table of Android procs that are resolved dynamically. /// struct ProcTable { ProcTable(); @@ -88,6 +93,16 @@ struct ProcTable { /// uint32_t GetAndroidDeviceAPILevel() const; + //---------------------------------------------------------------------------- + /// @brief Check if tracing in enabled in the process. This call can be + /// made at any API level. + /// + /// @return If tracing is enabled. + /// + bool TraceIsEnabled() const { + return this->ATrace_isEnabled ? this->ATrace_isEnabled() : false; + } + #define DEFINE_PROC(name, api) \ AndroidProc name = {.proc_name = #name, \ .api_availability = api}; diff --git a/impeller/toolkit/android/toolkit_android_unittests.cc b/impeller/toolkit/android/toolkit_android_unittests.cc index 6b34e2dead05d..f3f76286572f0 100644 --- a/impeller/toolkit/android/toolkit_android_unittests.cc +++ b/impeller/toolkit/android/toolkit_android_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/testing/testing.h" +#include "impeller/toolkit/android/choreographer.h" #include "impeller/toolkit/android/hardware_buffer.h" #include "impeller/toolkit/android/proc_table.h" #include "impeller/toolkit/android/surface_control.h" @@ -60,4 +61,29 @@ TEST_F(ToolkitAndroidTest, CanAccessDeviceAPILevel) { ASSERT_GE(GetProcTable().GetAndroidDeviceAPILevel(), 29u); } +TEST_F(ToolkitAndroidTest, ChoreographerIsAvailable) { + ASSERT_TRUE(Choreographer::IsAvailableOnPlatform()); +} + +TEST_F(ToolkitAndroidTest, CanPostAndNotWaitForFrameCallbacks) { + const auto& choreographer = Choreographer::GetInstance(); + ASSERT_TRUE(choreographer.IsValid()); + ASSERT_TRUE(choreographer.PostFrameCallback([](auto) {})); +} + +TEST_F(ToolkitAndroidTest, CanPostAndWaitForFrameCallbacks) { + if ((true)) { + GTEST_SKIP() + << "Disabled till the test harness is in an Android activity. " + "Running it without one will hang because the choreographer " + "frame callback will never execute."; + } + const auto& choreographer = Choreographer::GetInstance(); + ASSERT_TRUE(choreographer.IsValid()); + fml::AutoResetWaitableEvent event; + ASSERT_TRUE(choreographer.PostFrameCallback( + [&event](auto point) { event.Signal(); })); + event.Wait(); +} + } // namespace impeller::android::testing From f44366e47adea1c7dc191e8023844e56f2d89209 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Mar 2024 15:14:56 -0700 Subject: [PATCH 03/15] Get hardware buffer IDs. --- impeller/toolkit/android/hardware_buffer.cc | 11 +++++++++++ impeller/toolkit/android/hardware_buffer.h | 2 ++ impeller/toolkit/android/proc_table.h | 1 + .../toolkit/android/toolkit_android_unittests.cc | 12 ++++++++++++ 4 files changed, 26 insertions(+) diff --git a/impeller/toolkit/android/hardware_buffer.cc b/impeller/toolkit/android/hardware_buffer.cc index 0f541a7cb9eb1..01b4fc7599a8b 100644 --- a/impeller/toolkit/android/hardware_buffer.cc +++ b/impeller/toolkit/android/hardware_buffer.cc @@ -102,4 +102,15 @@ bool HardwareBuffer::IsAvailableOnPlatform() { GetProcTable().AHardwareBuffer_allocate.IsAvailable(); } +std::optional HardwareBuffer::GetSystemUniqueID() const { + if (!IsValid() || !GetProcTable().AHardwareBuffer_getId) { + return false; + } + uint64_t out_id = 0u; + if (GetProcTable().AHardwareBuffer_getId(GetHandle(), &out_id) != 0) { + return std::nullopt; + } + return out_id; +} + } // namespace impeller::android diff --git a/impeller/toolkit/android/hardware_buffer.h b/impeller/toolkit/android/hardware_buffer.h index 7b15bd9497318..d558257e95f81 100644 --- a/impeller/toolkit/android/hardware_buffer.h +++ b/impeller/toolkit/android/hardware_buffer.h @@ -103,6 +103,8 @@ class HardwareBuffer { const AHardwareBuffer_Desc& GetAndroidDescriptor() const; + std::optional GetSystemUniqueID() 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 8d382334b6a9e..4ba9237f7afaf 100644 --- a/impeller/toolkit/android/proc_table.h +++ b/impeller/toolkit/android/proc_table.h @@ -27,6 +27,7 @@ namespace impeller::android { INVOKE(AHardwareBuffer_release, 26) \ INVOKE(AHardwareBuffer_isSupported, 29) \ INVOKE(AHardwareBuffer_describe, 26) \ + INVOKE(AHardwareBuffer_getId, 31) \ INVOKE(ANativeWindow_acquire, 0) \ INVOKE(ANativeWindow_release, 0) \ INVOKE(ANativeWindow_getWidth, 0) \ diff --git a/impeller/toolkit/android/toolkit_android_unittests.cc b/impeller/toolkit/android/toolkit_android_unittests.cc index f3f76286572f0..18dc7b12f7083 100644 --- a/impeller/toolkit/android/toolkit_android_unittests.cc +++ b/impeller/toolkit/android/toolkit_android_unittests.cc @@ -43,6 +43,18 @@ TEST_F(ToolkitAndroidTest, CanCreateHardwareBuffer) { ASSERT_TRUE(buffer.IsValid()); } +TEST_F(ToolkitAndroidTest, CanGetHardwareBufferIDs) { + ASSERT_TRUE(HardwareBuffer::IsAvailableOnPlatform()); + if (!GetProcTable().AHardwareBuffer_getId.IsAvailable()) { + GTEST_SKIP() << "Hardware buffer IDs are not available on this platform."; + } + auto desc = HardwareBufferDescriptor::MakeForSwapchainImage({100, 100}); + ASSERT_TRUE(desc.IsAllocatable()); + HardwareBuffer buffer(desc); + ASSERT_TRUE(buffer.IsValid()); + ASSERT_TRUE(buffer.GetSystemUniqueID().has_value()); +} + TEST_F(ToolkitAndroidTest, CanApplySurfaceTransaction) { ASSERT_TRUE(SurfaceTransaction::IsAvailableOnPlatform()); SurfaceTransaction transaction; From a4f32e8300de5d262e79baa026359c25d88d0576 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Mar 2024 15:24:15 -0700 Subject: [PATCH 04/15] Describe hardware buffer handles. --- impeller/toolkit/android/hardware_buffer.cc | 10 ++++++++++ impeller/toolkit/android/hardware_buffer.h | 11 +++++++++++ .../toolkit/android/toolkit_android_unittests.cc | 12 ++++++++++++ 3 files changed, 33 insertions(+) diff --git a/impeller/toolkit/android/hardware_buffer.cc b/impeller/toolkit/android/hardware_buffer.cc index 01b4fc7599a8b..08526e61bc3be 100644 --- a/impeller/toolkit/android/hardware_buffer.cc +++ b/impeller/toolkit/android/hardware_buffer.cc @@ -113,4 +113,14 @@ std::optional HardwareBuffer::GetSystemUniqueID() const { return out_id; } +std::optional HardwareBuffer::Describe( + AHardwareBuffer* buffer) { + if (!buffer || !GetProcTable().AHardwareBuffer_describe) { + return std::nullopt; + } + AHardwareBuffer_Desc desc = {}; + GetProcTable().AHardwareBuffer_describe(buffer, &desc); + return desc; +} + } // namespace impeller::android diff --git a/impeller/toolkit/android/hardware_buffer.h b/impeller/toolkit/android/hardware_buffer.h index d558257e95f81..c49b00cc13ccc 100644 --- a/impeller/toolkit/android/hardware_buffer.h +++ b/impeller/toolkit/android/hardware_buffer.h @@ -5,6 +5,8 @@ #ifndef FLUTTER_IMPELLER_TOOLKIT_ANDROID_HARDWARE_BUFFER_H_ #define FLUTTER_IMPELLER_TOOLKIT_ANDROID_HARDWARE_BUFFER_H_ +#include + #include "flutter/fml/unique_object.h" #include "impeller/geometry/size.h" #include "impeller/toolkit/android/proc_table.h" @@ -103,6 +105,15 @@ class HardwareBuffer { const AHardwareBuffer_Desc& GetAndroidDescriptor() const; + static std::optional Describe(AHardwareBuffer* buffer); + + //---------------------------------------------------------------------------- + /// @brief Get the system wide unique ID of the hardware buffer if + /// possible. This is only available on Android API 31 and above. + /// Within the process, the handle are unique. + /// + /// @return The system unique id if one can be obtained. + /// std::optional GetSystemUniqueID() const; private: diff --git a/impeller/toolkit/android/toolkit_android_unittests.cc b/impeller/toolkit/android/toolkit_android_unittests.cc index 18dc7b12f7083..fc0297fb973ed 100644 --- a/impeller/toolkit/android/toolkit_android_unittests.cc +++ b/impeller/toolkit/android/toolkit_android_unittests.cc @@ -55,6 +55,18 @@ TEST_F(ToolkitAndroidTest, CanGetHardwareBufferIDs) { ASSERT_TRUE(buffer.GetSystemUniqueID().has_value()); } +TEST_F(ToolkitAndroidTest, CanDescribeHardwareBufferHandles) { + ASSERT_TRUE(HardwareBuffer::IsAvailableOnPlatform()); + auto desc = HardwareBufferDescriptor::MakeForSwapchainImage({100, 100}); + ASSERT_TRUE(desc.IsAllocatable()); + HardwareBuffer buffer(desc); + ASSERT_TRUE(buffer.IsValid()); + auto a_desc = HardwareBuffer::Describe(buffer.GetHandle()); + ASSERT_TRUE(a_desc.has_value()); + ASSERT_EQ(a_desc->width, 100u); + ASSERT_EQ(a_desc->height, 100u); +} + TEST_F(ToolkitAndroidTest, CanApplySurfaceTransaction) { ASSERT_TRUE(SurfaceTransaction::IsAvailableOnPlatform()); SurfaceTransaction transaction; From 6e9e10f74d89a4c270503ce9c1e609e2a477742f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Mar 2024 15:30:00 -0700 Subject: [PATCH 05/15] Add some docs. --- impeller/toolkit/android/choreographer.h | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/impeller/toolkit/android/choreographer.h b/impeller/toolkit/android/choreographer.h index 535f4e86824cb..a4b781df8535f 100644 --- a/impeller/toolkit/android/choreographer.h +++ b/impeller/toolkit/android/choreographer.h @@ -25,6 +25,19 @@ class Choreographer { public: static bool IsAvailableOnPlatform(); + //---------------------------------------------------------------------------- + /// @brief Create or get the thread local instance of a choreographer. A + /// message loop will be setup on the calling thread if none + /// exists. + /// + /// @warning Choreographers are only available on API levels 24 and above. + /// Below this level, this will return an invalid instance. + /// Availability can also be checked via the + /// `IsAvailableOnPlatform` call. + /// + /// @return The thread local choreographer instance. If none can be setup, + /// an invalid object reference will be returned. See `IsValid`. + /// static Choreographer& GetInstance(); ~Choreographer(); @@ -35,10 +48,29 @@ class Choreographer { bool IsValid() const; + //---------------------------------------------------------------------------- + /// A monotonic system clock. + /// using FrameClock = std::chrono::steady_clock; + + //---------------------------------------------------------------------------- + /// A timepoint on a monotonic system clock. + /// using FrameTimePoint = std::chrono::time_point; using FrameCallback = std::function; + //---------------------------------------------------------------------------- + /// @brief Posts a frame callback. The time that the frame is being + /// rendered will be available in the callback as an argument. + /// Multiple frame callbacks within the same frame interval will + /// receive the same argument. + /// + /// @param[in] callback The callback + /// + /// @return `true` if the frame callback could be posted. This may return + /// `false` if choreographers are not available on the platform. + /// See `IsAvailableOnPlatform`. + /// bool PostFrameCallback(FrameCallback callback) const; private: From 0f343f1182e0fe6915b0058e637450870f729b0b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Mar 2024 15:46:23 -0700 Subject: [PATCH 06/15] Deprecate all helpers and prep for removal. --- fml/platform/android/ndk_helpers.h | 33 ++++- fml/platform/android/ndk_helpers_unittests.cc | 119 +----------------- impeller/toolkit/android/proc_table.h | 2 + shell/platform/android/BUILD.gn | 1 + shell/platform/android/flutter_main.cc | 4 +- 5 files changed, 36 insertions(+), 123 deletions(-) diff --git a/fml/platform/android/ndk_helpers.h b/fml/platform/android/ndk_helpers.h index 9299776e3e691..b834cc18bce6e 100644 --- a/fml/platform/android/ndk_helpers.h +++ b/fml/platform/android/ndk_helpers.h @@ -35,53 +35,78 @@ class NDKHelpers { static void Init(); // API Version 23 + [[deprecated]] static bool ATrace_isEnabled(); // API Version 24 + [[deprecated]] static ChoreographerSupportStatus ChoreographerSupported(); - static AChoreographer* _Nullable AChoreographer_getInstance(); + + [[deprecated]] static AChoreographer* _Nullable AChoreographer_getInstance(); // Deprecated in 29, available since 24. + + [[deprecated]] static void AChoreographer_postFrameCallback( AChoreographer* _Nonnull choreographer, AChoreographer_frameCallback _Nonnull callback, void* _Nullable data); // API Version 26 + [[deprecated]] static bool HardwareBufferSupported(); - static AHardwareBuffer* _Nonnull AHardwareBuffer_fromHardwareBuffer( + + [[deprecated]] static AHardwareBuffer* _Nonnull AHardwareBuffer_fromHardwareBuffer( JNIEnv* _Nonnull env, jobject _Nonnull hardwareBufferObj); + + [[deprecated]] static void AHardwareBuffer_release(AHardwareBuffer* _Nonnull buffer); + + [[deprecated]] static void AHardwareBuffer_describe(AHardwareBuffer* _Nonnull buffer, AHardwareBuffer_Desc* _Nullable desc); + + [[deprecated]] static int AHardwareBuffer_allocate( const AHardwareBuffer_Desc* _Nonnull desc, AHardwareBuffer* _Nullable* _Nullable outBuffer); + static EGLClientBuffer _Nonnull eglGetNativeClientBufferANDROID( AHardwareBuffer* _Nonnull buffer); // API Version 29 + [[deprecated]] static int AHardwareBuffer_isSupported( const AHardwareBuffer_Desc* _Nonnull desc); + [[deprecated]] static void AChoreographer_postFrameCallback64( AChoreographer* _Nonnull choreographer, AChoreographer_frameCallback64 _Nonnull callback, void* _Nullable data); + [[deprecated]] static bool SurfaceControlAndTransactionSupported(); - static ASurfaceControl* _Nonnull ASurfaceControl_createFromWindow( + [[deprecated]] static ASurfaceControl* _Nonnull ASurfaceControl_createFromWindow( ANativeWindow* _Nonnull parent, const char* _Nullable debug_name); + + [[deprecated]] static void ASurfaceControl_release( ASurfaceControl* _Nonnull surface_control); - static ASurfaceTransaction* _Nonnull ASurfaceTransaction_create(); + [[deprecated]] static ASurfaceTransaction* _Nonnull ASurfaceTransaction_create(); + + [[deprecated]] static void ASurfaceTransaction_delete( ASurfaceTransaction* _Nonnull surface_transaction); + + [[deprecated]] static void ASurfaceTransaction_apply( ASurfaceTransaction* _Nonnull surface_transaction); + + [[deprecated]] static void ASurfaceTransaction_setBuffer( ASurfaceTransaction* _Nonnull transaction, ASurfaceControl* _Nonnull surface_control, diff --git a/fml/platform/android/ndk_helpers_unittests.cc b/fml/platform/android/ndk_helpers_unittests.cc index dfcd528d9072a..33895ecdfda59 100644 --- a/fml/platform/android/ndk_helpers_unittests.cc +++ b/fml/platform/android/ndk_helpers_unittests.cc @@ -11,124 +11,7 @@ namespace flutter { namespace testing { namespace android { -class NdkHelpersTest : public ::testing::Test { - public: - void SetUp() override { NDKHelpers::Init(); } - - static void OnVsync(int64_t frame_nanos, void* data) {} - static void OnVsync32( - long frame_nanos, // NOLINT - compat for deprecated call - void* data) {} -}; - -TEST_F(NdkHelpersTest, ATrace) { - ASSERT_GT(android_get_device_api_level(), 22); - EXPECT_FALSE(NDKHelpers::ATrace_isEnabled()); -} - -#if FML_ARCH_CPU_64_BITS -TEST_F(NdkHelpersTest, AChoreographer32) { - if (android_get_device_api_level() >= 29) { - GTEST_SKIP() << "This test is for less than API 29."; - } - - EXPECT_EQ(NDKHelpers::ChoreographerSupported(), - ChoreographerSupportStatus::kSupported32); - - EXPECT_FALSE(NDKHelpers::AChoreographer_getInstance()); - - fml::MessageLoop::EnsureInitializedForCurrentThread(); - - EXPECT_TRUE(NDKHelpers::AChoreographer_getInstance()); - - NDKHelpers::AChoreographer_postFrameCallback( - NDKHelpers::AChoreographer_getInstance(), &OnVsync32, nullptr); -} -#else -TEST_F(NdkHelpersTest, AChoreographer32NotSupported) { - if (android_get_device_api_level() >= 29) { - GTEST_SKIP() << "This test is for less than API 29."; - } - - // The 32 bit framecallback on 32 bit architectures does not deliver - // sufficient resolution. See - // https://github.com/flutter/engine/pull/31859#discussion_r822072987 - EXPECT_EQ(NDKHelpers::ChoreographerSupported(), - ChoreographerSupportStatus::kUnsupported); -} -#endif // FML_ARCH_CPU_64_BITS - -TEST_F(NdkHelpersTest, AChoreographer64) { - if (android_get_device_api_level() < 29) { - GTEST_SKIP() << "This test is for API 29 and above."; - } - - EXPECT_EQ(NDKHelpers::ChoreographerSupported(), - ChoreographerSupportStatus::kSupported64); - - EXPECT_FALSE(NDKHelpers::AChoreographer_getInstance()); - - fml::MessageLoop::EnsureInitializedForCurrentThread(); - - EXPECT_TRUE(NDKHelpers::AChoreographer_getInstance()); - - NDKHelpers::AChoreographer_postFrameCallback64( - NDKHelpers::AChoreographer_getInstance(), &OnVsync, nullptr); -} - -TEST_F(NdkHelpersTest, HardwareBuffer) { - if (android_get_device_api_level() < 26) { - GTEST_SKIP() << "Test requires at least API 26."; - } - - ASSERT_TRUE(NDKHelpers::HardwareBufferSupported()); - - AHardwareBuffer_Desc desc{ - .width = 4, - .height = 4, - .layers = 1, - .format = AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, - }; - if (android_get_device_api_level() >= 29) { - EXPECT_TRUE(NDKHelpers::AHardwareBuffer_isSupported(&desc)); - } - - AHardwareBuffer* buffer = nullptr; - // AHardwareBuffer_allocate returns 0 on success. - EXPECT_EQ(NDKHelpers::AHardwareBuffer_allocate(&desc, &buffer), 0); - EXPECT_TRUE(buffer); - - AHardwareBuffer_Desc out_desc = {}; - NDKHelpers::AHardwareBuffer_describe(buffer, &out_desc); - EXPECT_EQ(desc.width, out_desc.width); - EXPECT_EQ(desc.height, out_desc.height); - EXPECT_EQ(desc.layers, out_desc.layers); - EXPECT_EQ(desc.format, out_desc.format); - - auto id = NDKHelpers::AHardwareBuffer_getId(buffer); - if (android_get_device_api_level() >= 31) { - EXPECT_TRUE(id.has_value()); - } else { - EXPECT_FALSE(id.has_value()); - } - - NDKHelpers::AHardwareBuffer_release(buffer); -} - -TEST_F(NdkHelpersTest, SurfaceTransaction) { - if (android_get_device_api_level() < 29) { - GTEST_SKIP() << "Test requires at least API 29."; - } - EXPECT_TRUE(NDKHelpers::SurfaceControlAndTransactionSupported()); - - // Need ANativeWindow to create ASurfaceControl and set a buffer to the - // transaction. Just create/apply/delete as a smoke test. - - ASurfaceTransaction* transaction = NDKHelpers::ASurfaceTransaction_create(); - EXPECT_TRUE(transaction); - NDKHelpers::ASurfaceTransaction_apply(transaction); - NDKHelpers::ASurfaceTransaction_delete(transaction); -} +// } // namespace android } // namespace testing diff --git a/impeller/toolkit/android/proc_table.h b/impeller/toolkit/android/proc_table.h index 4ba9237f7afaf..b59563ec75747 100644 --- a/impeller/toolkit/android/proc_table.h +++ b/impeller/toolkit/android/proc_table.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -22,6 +23,7 @@ namespace impeller::android { INVOKE(AChoreographer_getInstance, 24) \ INVOKE(AChoreographer_postFrameCallback, 24) \ INVOKE(AChoreographer_postFrameCallback64, 29) \ + INVOKE(AHardwareBuffer_fromHardwareBuffer, 26) \ INVOKE(AHardwareBuffer_allocate, 26) \ INVOKE(AHardwareBuffer_acquire, 26) \ INVOKE(AHardwareBuffer_release, 26) \ diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 1500fb90e0639..3bacd1b42dd79 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -143,6 +143,7 @@ source_set("flutter_shell_native_src") { "//flutter/flow", "//flutter/fml", "//flutter/impeller", + "//flutter/impeller/toolkit/android", "//flutter/impeller/toolkit/egl", "//flutter/impeller/toolkit/gles", "//flutter/lib/ui", diff --git a/shell/platform/android/flutter_main.cc b/shell/platform/android/flutter_main.cc index 298fd3aff870e..24a00c65ad354 100644 --- a/shell/platform/android/flutter_main.cc +++ b/shell/platform/android/flutter_main.cc @@ -27,6 +27,7 @@ #include "flutter/shell/platform/android/android_context_vulkan_impeller.h" #include "flutter/shell/platform/android/flutter_main.h" #include "impeller/base/validation.h" +#include "impeller/toolkit/android/proc_table.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "txt/platform.h" @@ -85,7 +86,8 @@ void FlutterMain::Init(JNIEnv* env, // Turn systracing on if ATrace_isEnabled is true and the user did not already // request systracing if (!settings.trace_systrace) { - settings.trace_systrace = NDKHelpers::ATrace_isEnabled(); + settings.trace_systrace = + impeller::android::GetProcTable().TraceIsEnabled(); if (settings.trace_systrace) { __android_log_print( ANDROID_LOG_INFO, "Flutter", From 6d118c75d1a19b72b6a6c6c9ffde0ef606e7df1d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Mar 2024 16:16:02 -0700 Subject: [PATCH 07/15] Port VSyncWaiterAndroid. --- .../platform/android/vsync_waiter_android.cc | 62 ++++++++----------- shell/platform/android/vsync_waiter_android.h | 3 - 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index d0a8db383a0ce..b41b14a708a73 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -14,6 +14,7 @@ #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" +#include "impeller/toolkit/android/choreographer.h" namespace flutter { @@ -28,47 +29,34 @@ VsyncWaiterAndroid::~VsyncWaiterAndroid() = default; // |VsyncWaiter| void VsyncWaiterAndroid::AwaitVSync() { - switch (NDKHelpers::ChoreographerSupported()) { - case ChoreographerSupportStatus::kSupported32: { - auto* weak_this = new std::weak_ptr(shared_from_this()); - fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetUITaskRunner(), [weak_this]() { - NDKHelpers::AChoreographer_postFrameCallback( - NDKHelpers::AChoreographer_getInstance(), &OnVsyncFromNDK32, - weak_this); + if (impeller::android::Choreographer::IsAvailableOnPlatform()) { + auto* weak_this = new std::weak_ptr(shared_from_this()); + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), [weak_this]() { + const auto& choreographer = + impeller::android::Choreographer::GetInstance(); + choreographer.PostFrameCallback([weak_this](auto time) { + auto time_ns = + std::chrono::time_point_cast(time) + .time_since_epoch() + .count(); + OnVsyncFromNDK(time_ns, weak_this); }); - } break; - case ChoreographerSupportStatus::kSupported64: { - auto* weak_this = new std::weak_ptr(shared_from_this()); - fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetUITaskRunner(), [weak_this]() { - NDKHelpers::AChoreographer_postFrameCallback64( - NDKHelpers::AChoreographer_getInstance(), &OnVsyncFromNDK, - weak_this); - }); - } break; - case ChoreographerSupportStatus::kUnsupported: { - // TODO(99798): Remove it when we drop support for API level < 29. - auto* weak_this = new std::weak_ptr(shared_from_this()); - jlong java_baton = reinterpret_cast(weak_this); - task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() { - JNIEnv* env = fml::jni::AttachCurrentThread(); - env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), // - g_async_wait_for_vsync_method_, // - java_baton // - ); - }); - } break; + }); + } else { + // TODO(99798): Remove it when we drop support for API level < 29. + auto* weak_this = new std::weak_ptr(shared_from_this()); + jlong java_baton = reinterpret_cast(weak_this); + task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), // + g_async_wait_for_vsync_method_, // + java_baton // + ); + }); } } -// static -void VsyncWaiterAndroid::OnVsyncFromNDK32( - long frame_nanos, // NOLINT to match a deprecated NDK interface. - void* data) { - OnVsyncFromNDK(frame_nanos, data); -} - // static void VsyncWaiterAndroid::OnVsyncFromNDK(int64_t frame_nanos, void* data) { auto frame_time = fml::TimePoint::FromEpochDelta( diff --git a/shell/platform/android/vsync_waiter_android.h b/shell/platform/android/vsync_waiter_android.h index 69c3ad912839c..7129bb94305e2 100644 --- a/shell/platform/android/vsync_waiter_android.h +++ b/shell/platform/android/vsync_waiter_android.h @@ -29,9 +29,6 @@ class VsyncWaiterAndroid final : public VsyncWaiter { void AwaitVSync() override; static void OnVsyncFromNDK(int64_t frame_nanos, void* data); - // This needs to match a deprecated NDK interface. - static void OnVsyncFromNDK32(long frame_nanos, // NOLINT - void* data); static void OnVsyncFromJava(JNIEnv* env, jclass jcaller, From e925912930dd2aca6b07f4a1a74b9f7fb6399cd6 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Mar 2024 16:22:50 -0700 Subject: [PATCH 08/15] Port external textures VK. --- shell/platform/android/image_external_texture.cc | 6 ++++-- shell/platform/android/image_external_texture_vk.cc | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/shell/platform/android/image_external_texture.cc b/shell/platform/android/image_external_texture.cc index 90d78291c988f..494aae256dc34 100644 --- a/shell/platform/android/image_external_texture.cc +++ b/shell/platform/android/image_external_texture.cc @@ -6,6 +6,7 @@ #include "flutter/fml/platform/android/jni_util.h" #include "flutter/fml/platform/android/ndk_helpers.h" +#include "flutter/impeller/toolkit/android/proc_table.h" #include "flutter/shell/platform/android/jni/platform_view_android_jni.h" namespace flutter { @@ -107,8 +108,9 @@ AHardwareBuffer* ImageExternalTexture::AHardwareBufferFor( const fml::jni::JavaRef& hardware_buffer) { JNIEnv* env = fml::jni::AttachCurrentThread(); FML_CHECK(env != nullptr); - return NDKHelpers::AHardwareBuffer_fromHardwareBuffer(env, - hardware_buffer.obj()); + const auto& proc = + impeller::android::GetProcTable().AHardwareBuffer_fromHardwareBuffer; + return proc ? proc(env, hardware_buffer.obj()) : nullptr; } } // namespace flutter diff --git a/shell/platform/android/image_external_texture_vk.cc b/shell/platform/android/image_external_texture_vk.cc index ad00687b555ba..c16bd49841ece 100644 --- a/shell/platform/android/image_external_texture_vk.cc +++ b/shell/platform/android/image_external_texture_vk.cc @@ -10,6 +10,7 @@ #include "flutter/impeller/renderer/backend/vulkan/command_buffer_vk.h" #include "flutter/impeller/renderer/backend/vulkan/command_encoder_vk.h" #include "flutter/impeller/renderer/backend/vulkan/texture_vk.h" +#include "flutter/impeller/toolkit/android/hardware_buffer.h" namespace flutter { @@ -41,9 +42,8 @@ void ImageExternalTextureVK::ProcessFrame(PaintContext& context, JavaLocalRef hardware_buffer = HardwareBufferFor(image); AHardwareBuffer* latest_hardware_buffer = AHardwareBufferFor(hardware_buffer); - AHardwareBuffer_Desc hb_desc = {}; - flutter::NDKHelpers::AHardwareBuffer_describe(latest_hardware_buffer, - &hb_desc); + auto hb_desc = + impeller::android::HardwareBuffer::Describe(latest_hardware_buffer); std::optional key = flutter::NDKHelpers::AHardwareBuffer_getId(latest_hardware_buffer); auto existing_image = image_lru_.FindImage(key); @@ -55,7 +55,7 @@ void ImageExternalTextureVK::ProcessFrame(PaintContext& context, } auto texture_source = std::make_shared( - impeller_context_, latest_hardware_buffer, hb_desc); + impeller_context_, latest_hardware_buffer, hb_desc.value()); if (!texture_source->IsValid()) { CloseHardwareBuffer(hardware_buffer); return; From 11a7e64a4b5baead1b34e503134916885d57fc51 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 14 Mar 2024 16:26:56 -0700 Subject: [PATCH 09/15] Nuke the helpers and gauge the fallout. --- fml/BUILD.gn | 2 - fml/platform/android/ndk_helpers.cc | 264 ------------------ fml/platform/android/ndk_helpers.h | 125 --------- fml/platform/android/ndk_helpers_unittests.cc | 18 -- shell/platform/android/BUILD.gn | 1 - shell/platform/android/flutter_main.cc | 1 - .../android/image_external_texture.cc | 1 - .../android/image_external_texture_gl.cc | 1 - .../android/image_external_texture_gl.h | 2 - .../android/image_external_texture_vk.cc | 5 +- shell/platform/android/image_lru.h | 3 +- shell/platform/android/library_loader.cc | 4 - .../android/platform_view_android_jni_impl.cc | 1 - .../platform/android/vsync_waiter_android.cc | 1 - 14 files changed, 6 insertions(+), 423 deletions(-) delete mode 100644 fml/platform/android/ndk_helpers.cc delete mode 100644 fml/platform/android/ndk_helpers.h delete mode 100644 fml/platform/android/ndk_helpers_unittests.cc diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 2ca165612b0dd..ee43fc4b35584 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -185,8 +185,6 @@ source_set("fml") { "platform/android/jni_weak_ref.h", "platform/android/message_loop_android.cc", "platform/android/message_loop_android.h", - "platform/android/ndk_helpers.cc", - "platform/android/ndk_helpers.h", "platform/android/paths_android.cc", "platform/android/paths_android.h", "platform/android/scoped_java_ref.cc", diff --git a/fml/platform/android/ndk_helpers.cc b/fml/platform/android/ndk_helpers.cc deleted file mode 100644 index 1fcc49b0146b9..0000000000000 --- a/fml/platform/android/ndk_helpers.cc +++ /dev/null @@ -1,264 +0,0 @@ -// 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 "fml/platform/android/ndk_helpers.h" - -#include "fml/logging.h" -#include "fml/native_library.h" - -#include -#include - -namespace flutter { - -namespace { - -#define DECLARE_TYPES(ret, name, args) \ - typedef ret(*fp_##name) args; \ - ret(*_##name) args = nullptr - -DECLARE_TYPES(int, - AHardwareBuffer_allocate, - (const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer)); -DECLARE_TYPES(int, - AHardwareBuffer_isSupported, - (const AHardwareBuffer_Desc* desc)); -DECLARE_TYPES(AHardwareBuffer*, - AHardwareBuffer_fromHardwareBuffer, - (JNIEnv * env, jobject hardwareBufferObj)); -DECLARE_TYPES(void, AHardwareBuffer_release, (AHardwareBuffer * buffer)); -DECLARE_TYPES(void, - AHardwareBuffer_describe, - (AHardwareBuffer * buffer, AHardwareBuffer_Desc* desc)); -DECLARE_TYPES(int, - AHardwareBuffer_getId, - (AHardwareBuffer * buffer, uint64_t* outId)); - -DECLARE_TYPES(bool, ATrace_isEnabled, (void)); - -DECLARE_TYPES(ASurfaceControl*, - ASurfaceControl_createFromWindow, - (ANativeWindow * parent, const char* debug_name)); -DECLARE_TYPES(void, - ASurfaceControl_release, - (ASurfaceControl * surface_control)); -DECLARE_TYPES(ASurfaceTransaction*, ASurfaceTransaction_create, (void)); -DECLARE_TYPES(void, - ASurfaceTransaction_delete, - (ASurfaceTransaction * surface_transaction)); -DECLARE_TYPES(void, - ASurfaceTransaction_apply, - (ASurfaceTransaction * surface_transaction)); -DECLARE_TYPES(void, - ASurfaceTransaction_setBuffer, - (ASurfaceTransaction * transaction, - ASurfaceControl* surface_control, - AHardwareBuffer* buffer, - int acquire_fence_fd)); - -DECLARE_TYPES(AChoreographer*, AChoreographer_getInstance, (void)); -DECLARE_TYPES(void, - AChoreographer_postFrameCallback, - (AChoreographer * choreographer, - AChoreographer_frameCallback callbackk, - void* data)); -DECLARE_TYPES(void, - AChoreographer_postFrameCallback64, - (AChoreographer * choreographer, - AChoreographer_frameCallback64 callbackk, - void* data)); - -DECLARE_TYPES(EGLClientBuffer, - eglGetNativeClientBufferANDROID, - (AHardwareBuffer * buffer)); - -#undef DECLARE_TYPES - -std::once_flag init_once; - -void InitOnceCallback() { - static fml::RefPtr android = - fml::NativeLibrary::Create("libandroid.so"); - FML_CHECK(android.get() != nullptr); - static fml::RefPtr egl = - fml::NativeLibrary::Create("libEGL.so"); - FML_CHECK(egl.get() != nullptr); - -#define LOOKUP(lib, func) \ - _##func = lib->ResolveFunction(#func).value_or(nullptr) - - LOOKUP(egl, eglGetNativeClientBufferANDROID); - - LOOKUP(android, AHardwareBuffer_fromHardwareBuffer); - LOOKUP(android, AHardwareBuffer_release); - LOOKUP(android, AHardwareBuffer_getId); - LOOKUP(android, AHardwareBuffer_describe); - LOOKUP(android, AHardwareBuffer_allocate); - LOOKUP(android, AHardwareBuffer_isSupported); - LOOKUP(android, ATrace_isEnabled); - LOOKUP(android, AChoreographer_getInstance); - if (_AChoreographer_getInstance) { - LOOKUP(android, AChoreographer_postFrameCallback64); -// See discussion at -// https://github.com/flutter/engine/pull/31859#discussion_r822072987 -// This method is not suitable for Flutter's use cases on 32 bit architectures, -// and we should fall back to the Java based Choreographer. -#if FML_ARCH_CPU_64_BITS - if (!_AChoreographer_postFrameCallback64) { - LOOKUP(android, AChoreographer_postFrameCallback); - } -#endif - } - - LOOKUP(android, ASurfaceControl_createFromWindow); - LOOKUP(android, ASurfaceControl_release); - LOOKUP(android, ASurfaceTransaction_apply); - LOOKUP(android, ASurfaceTransaction_create); - LOOKUP(android, ASurfaceTransaction_delete); - LOOKUP(android, ASurfaceTransaction_setBuffer); -#undef LOOKUP -} - -} // namespace - -void NDKHelpers::Init() { - std::call_once(init_once, InitOnceCallback); -} - -bool NDKHelpers::ATrace_isEnabled() { - if (_ATrace_isEnabled) { - return _ATrace_isEnabled(); - } - return false; -} - -ChoreographerSupportStatus NDKHelpers::ChoreographerSupported() { - if (_AChoreographer_postFrameCallback64) { - return ChoreographerSupportStatus::kSupported64; - } - if (_AChoreographer_postFrameCallback) { - return ChoreographerSupportStatus::kSupported32; - } - return ChoreographerSupportStatus::kUnsupported; -} - -AChoreographer* NDKHelpers::AChoreographer_getInstance() { - FML_CHECK(_AChoreographer_getInstance); - return _AChoreographer_getInstance(); -} - -void NDKHelpers::AChoreographer_postFrameCallback( - AChoreographer* choreographer, - AChoreographer_frameCallback callback, - void* data) { - FML_CHECK(_AChoreographer_postFrameCallback); - return _AChoreographer_postFrameCallback(choreographer, callback, data); -} - -void NDKHelpers::AChoreographer_postFrameCallback64( - AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, - void* data) { - FML_CHECK(_AChoreographer_postFrameCallback64); - return _AChoreographer_postFrameCallback64(choreographer, callback, data); -} - -bool NDKHelpers::HardwareBufferSupported() { - const bool r = _AHardwareBuffer_fromHardwareBuffer != nullptr; - return r; -} - -AHardwareBuffer* NDKHelpers::AHardwareBuffer_fromHardwareBuffer( - JNIEnv* env, - jobject hardwareBufferObj) { - FML_CHECK(_AHardwareBuffer_fromHardwareBuffer != nullptr); - return _AHardwareBuffer_fromHardwareBuffer(env, hardwareBufferObj); -} - -void NDKHelpers::AHardwareBuffer_release(AHardwareBuffer* buffer) { - FML_CHECK(_AHardwareBuffer_release != nullptr); - _AHardwareBuffer_release(buffer); -} - -void NDKHelpers::AHardwareBuffer_describe(AHardwareBuffer* buffer, - AHardwareBuffer_Desc* desc) { - FML_CHECK(_AHardwareBuffer_describe != nullptr); - _AHardwareBuffer_describe(buffer, desc); -} - -std::optional NDKHelpers::AHardwareBuffer_getId( - AHardwareBuffer* buffer) { - if (_AHardwareBuffer_getId == nullptr) { - return std::nullopt; - } - HardwareBufferKey outId; - int result = _AHardwareBuffer_getId(buffer, &outId); - if (result == 0) { - return outId; - } - return std::nullopt; -} - -EGLClientBuffer NDKHelpers::eglGetNativeClientBufferANDROID( - AHardwareBuffer* buffer) { - FML_CHECK(_eglGetNativeClientBufferANDROID != nullptr); - return _eglGetNativeClientBufferANDROID(buffer); -} - -bool NDKHelpers::SurfaceControlAndTransactionSupported() { - return _ASurfaceControl_createFromWindow && _ASurfaceControl_release && - _ASurfaceTransaction_create && _ASurfaceTransaction_apply && - _ASurfaceTransaction_delete && _ASurfaceTransaction_setBuffer; -} - -ASurfaceControl* NDKHelpers::ASurfaceControl_createFromWindow( - ANativeWindow* parent, - const char* debug_name) { - FML_CHECK(_ASurfaceControl_createFromWindow); - return _ASurfaceControl_createFromWindow(parent, debug_name); -} - -void NDKHelpers::ASurfaceControl_release(ASurfaceControl* surface_control) { - FML_CHECK(_ASurfaceControl_release); - return _ASurfaceControl_release(surface_control); -} - -ASurfaceTransaction* NDKHelpers::ASurfaceTransaction_create() { - FML_CHECK(_ASurfaceTransaction_create); - return _ASurfaceTransaction_create(); -} - -void NDKHelpers::ASurfaceTransaction_delete( - ASurfaceTransaction* surface_transaction) { - FML_CHECK(_ASurfaceTransaction_delete); - _ASurfaceTransaction_delete(surface_transaction); -} - -void NDKHelpers::ASurfaceTransaction_apply( - ASurfaceTransaction* surface_transaction) { - FML_CHECK(_ASurfaceTransaction_apply); - _ASurfaceTransaction_apply(surface_transaction); -} - -void NDKHelpers::ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, - ASurfaceControl* surface_control, - AHardwareBuffer* buffer, - int acquire_fence_fd) { - FML_CHECK(_ASurfaceTransaction_setBuffer); - _ASurfaceTransaction_setBuffer(transaction, surface_control, buffer, - acquire_fence_fd); -} - -int NDKHelpers::AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) { - FML_CHECK(_AHardwareBuffer_isSupported); - return _AHardwareBuffer_isSupported(desc); -} - -int NDKHelpers::AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, - AHardwareBuffer** outBuffer) { - FML_CHECK(_AHardwareBuffer_allocate); - return _AHardwareBuffer_allocate(desc, outBuffer); -} - -} // namespace flutter diff --git a/fml/platform/android/ndk_helpers.h b/fml/platform/android/ndk_helpers.h deleted file mode 100644 index b834cc18bce6e..0000000000000 --- a/fml/platform/android/ndk_helpers.h +++ /dev/null @@ -1,125 +0,0 @@ -// 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_FML_PLATFORM_ANDROID_NDK_HELPERS_H_ -#define FLUTTER_FML_PLATFORM_ANDROID_NDK_HELPERS_H_ - -#include -#include -#include -#include -#include -#include -#include - -namespace flutter { - -using HardwareBufferKey = uint64_t; - -enum class ChoreographerSupportStatus { - // Unavailable, API level < 24. - kUnsupported, - // Available, but only with postFrameCallback. - kSupported32, - // Available, but only with postFrameCallback64. - kSupported64, -}; - -// A collection of NDK functions that are available depending on the version of -// the Android SDK we are linked with at runtime. -class NDKHelpers { - public: - // Safe to call multiple times. - // Normally called from JNI_OnLoad. - static void Init(); - - // API Version 23 - [[deprecated]] - static bool ATrace_isEnabled(); - - // API Version 24 - [[deprecated]] - static ChoreographerSupportStatus ChoreographerSupported(); - - [[deprecated]] static AChoreographer* _Nullable AChoreographer_getInstance(); - // Deprecated in 29, available since 24. - - [[deprecated]] - static void AChoreographer_postFrameCallback( - AChoreographer* _Nonnull choreographer, - AChoreographer_frameCallback _Nonnull callback, - void* _Nullable data); - - // API Version 26 - [[deprecated]] - static bool HardwareBufferSupported(); - - [[deprecated]] static AHardwareBuffer* _Nonnull AHardwareBuffer_fromHardwareBuffer( - JNIEnv* _Nonnull env, - jobject _Nonnull hardwareBufferObj); - - [[deprecated]] - static void AHardwareBuffer_release(AHardwareBuffer* _Nonnull buffer); - - [[deprecated]] - static void AHardwareBuffer_describe(AHardwareBuffer* _Nonnull buffer, - AHardwareBuffer_Desc* _Nullable desc); - - [[deprecated]] - static int AHardwareBuffer_allocate( - const AHardwareBuffer_Desc* _Nonnull desc, - AHardwareBuffer* _Nullable* _Nullable outBuffer); - - static EGLClientBuffer _Nonnull eglGetNativeClientBufferANDROID( - AHardwareBuffer* _Nonnull buffer); - - // API Version 29 - [[deprecated]] - static int AHardwareBuffer_isSupported( - const AHardwareBuffer_Desc* _Nonnull desc); - - [[deprecated]] - static void AChoreographer_postFrameCallback64( - AChoreographer* _Nonnull choreographer, - AChoreographer_frameCallback64 _Nonnull callback, - void* _Nullable data); - - [[deprecated]] - static bool SurfaceControlAndTransactionSupported(); - - [[deprecated]] static ASurfaceControl* _Nonnull ASurfaceControl_createFromWindow( - ANativeWindow* _Nonnull parent, - const char* _Nullable debug_name); - - [[deprecated]] - static void ASurfaceControl_release( - ASurfaceControl* _Nonnull surface_control); - - [[deprecated]] static ASurfaceTransaction* _Nonnull ASurfaceTransaction_create(); - - [[deprecated]] - static void ASurfaceTransaction_delete( - ASurfaceTransaction* _Nonnull surface_transaction); - - [[deprecated]] - static void ASurfaceTransaction_apply( - ASurfaceTransaction* _Nonnull surface_transaction); - - [[deprecated]] - static void ASurfaceTransaction_setBuffer( - ASurfaceTransaction* _Nonnull transaction, - ASurfaceControl* _Nonnull surface_control, - AHardwareBuffer* _Nonnull buffer, - int acquire_fence_fd); - - // API Version 31 - - // Returns std::nullopt on API version 26 - 30. - static std::optional AHardwareBuffer_getId( - AHardwareBuffer* _Nonnull buffer); -}; - -} // namespace flutter - -#endif // FLUTTER_FML_PLATFORM_ANDROID_NDK_HELPERS_H_ diff --git a/fml/platform/android/ndk_helpers_unittests.cc b/fml/platform/android/ndk_helpers_unittests.cc deleted file mode 100644 index 33895ecdfda59..0000000000000 --- a/fml/platform/android/ndk_helpers_unittests.cc +++ /dev/null @@ -1,18 +0,0 @@ -// 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 "fml/message_loop.h" -#include "fml/platform/android/ndk_helpers.h" - -#include "gtest/gtest.h" - -namespace flutter { -namespace testing { -namespace android { - -// - -} // namespace android -} // namespace testing -} // namespace flutter diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 3bacd1b42dd79..3025fabbbbbec 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -42,7 +42,6 @@ executable("flutter_shell_native_unittests") { visibility = [ "*" ] testonly = true sources = [ - "//flutter/fml/platform/android/ndk_helpers_unittests.cc", "android_context_gl_impeller_unittests.cc", "android_context_gl_unittests.cc", "android_shell_holder_unittests.cc", diff --git a/shell/platform/android/flutter_main.cc b/shell/platform/android/flutter_main.cc index 24a00c65ad354..9c9c58e66abe6 100644 --- a/shell/platform/android/flutter_main.cc +++ b/shell/platform/android/flutter_main.cc @@ -17,7 +17,6 @@ #include "flutter/fml/native_library.h" #include "flutter/fml/paths.h" #include "flutter/fml/platform/android/jni_util.h" -#include "flutter/fml/platform/android/ndk_helpers.h" #include "flutter/fml/platform/android/paths_android.h" #include "flutter/fml/size.h" #include "flutter/lib/ui/plugins/callback_cache.h" diff --git a/shell/platform/android/image_external_texture.cc b/shell/platform/android/image_external_texture.cc index 494aae256dc34..b2df3c91de3de 100644 --- a/shell/platform/android/image_external_texture.cc +++ b/shell/platform/android/image_external_texture.cc @@ -5,7 +5,6 @@ #include #include "flutter/fml/platform/android/jni_util.h" -#include "flutter/fml/platform/android/ndk_helpers.h" #include "flutter/impeller/toolkit/android/proc_table.h" #include "flutter/shell/platform/android/jni/platform_view_android_jni.h" diff --git a/shell/platform/android/image_external_texture_gl.cc b/shell/platform/android/image_external_texture_gl.cc index 8ac28e46a7470..c6dfbc7fb4ab0 100644 --- a/shell/platform/android/image_external_texture_gl.cc +++ b/shell/platform/android/image_external_texture_gl.cc @@ -8,7 +8,6 @@ #include #include "flutter/common/graphics/texture.h" -#include "flutter/fml/platform/android/ndk_helpers.h" #include "flutter/impeller/core/formats.h" #include "flutter/impeller/display_list/dl_image_impeller.h" #include "flutter/impeller/toolkit/egl/image.h" diff --git a/shell/platform/android/image_external_texture_gl.h b/shell/platform/android/image_external_texture_gl.h index 6a132237dbe3d..995592c7787f5 100644 --- a/shell/platform/android/image_external_texture_gl.h +++ b/shell/platform/android/image_external_texture_gl.h @@ -16,8 +16,6 @@ #include "flutter/impeller/toolkit/egl/egl.h" #include "flutter/impeller/toolkit/egl/image.h" #include "flutter/impeller/toolkit/gles/texture.h" - -#include "flutter/fml/platform/android/ndk_helpers.h" #include "flutter/shell/platform/android/android_context_gl_skia.h" namespace flutter { diff --git a/shell/platform/android/image_external_texture_vk.cc b/shell/platform/android/image_external_texture_vk.cc index c16bd49841ece..9f7016848e25a 100644 --- a/shell/platform/android/image_external_texture_vk.cc +++ b/shell/platform/android/image_external_texture_vk.cc @@ -1,8 +1,11 @@ +// 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 "flutter/shell/platform/android/image_external_texture_vk.h" + #include -#include "flutter/fml/platform/android/ndk_helpers.h" #include "flutter/impeller/core/formats.h" #include "flutter/impeller/core/texture_descriptor.h" #include "flutter/impeller/display_list/dl_image_impeller.h" diff --git a/shell/platform/android/image_lru.h b/shell/platform/android/image_lru.h index 28a31cd2bf842..63829efb970ac 100644 --- a/shell/platform/android/image_lru.h +++ b/shell/platform/android/image_lru.h @@ -9,7 +9,6 @@ #include #include "display_list/image/dl_image.h" -#include "fml/platform/android/ndk_helpers.h" namespace flutter { @@ -20,6 +19,8 @@ namespace flutter { // necessary. static constexpr size_t kImageReaderSwapchainSize = 6u; +using HardwareBufferKey = uint64_t; + class ImageLRU { public: ImageLRU() = default; diff --git a/shell/platform/android/library_loader.cc b/shell/platform/android/library_loader.cc index 9f3691d818512..644bd5de3b8ec 100644 --- a/shell/platform/android/library_loader.cc +++ b/shell/platform/android/library_loader.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "flutter/fml/platform/android/jni_util.h" -#include "flutter/fml/platform/android/ndk_helpers.h" #include "flutter/shell/platform/android/android_image_generator.h" #include "flutter/shell/platform/android/flutter_main.h" #include "flutter/shell/platform/android/platform_view_android.h" @@ -14,9 +13,6 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { // Initialize the Java VM. fml::jni::InitJavaVM(vm); - // Registery dlsym lookups for NDK functions - flutter::NDKHelpers::Init(); - JNIEnv* env = fml::jni::AttachCurrentThread(); bool result = false; diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index b6d5dfc2f504f..0b4ed2ca1fead 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -12,7 +12,6 @@ #include #include -#include "flutter/fml/platform/android/ndk_helpers.h" #include "include/android/SkImageAndroid.h" #include "unicode/uchar.h" diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index b41b14a708a73..77a24cfc74dfe 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -10,7 +10,6 @@ #include "flutter/common/task_runners.h" #include "flutter/fml/logging.h" #include "flutter/fml/platform/android/jni_util.h" -#include "flutter/fml/platform/android/ndk_helpers.h" #include "flutter/fml/platform/android/scoped_java_ref.h" #include "flutter/fml/size.h" #include "flutter/fml/trace_event.h" From 317830f935bc53682468816a094655960f79693d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 15 Mar 2024 10:33:47 -0700 Subject: [PATCH 10/15] Static system unique ID. --- impeller/toolkit/android/hardware_buffer.cc | 9 +++++++-- impeller/toolkit/android/hardware_buffer.h | 9 +++++++++ shell/platform/android/image_external_texture_gl.cc | 4 +++- shell/platform/android/image_external_texture_vk.cc | 3 ++- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/impeller/toolkit/android/hardware_buffer.cc b/impeller/toolkit/android/hardware_buffer.cc index 08526e61bc3be..bfa52a2063cb6 100644 --- a/impeller/toolkit/android/hardware_buffer.cc +++ b/impeller/toolkit/android/hardware_buffer.cc @@ -103,11 +103,16 @@ bool HardwareBuffer::IsAvailableOnPlatform() { } std::optional HardwareBuffer::GetSystemUniqueID() const { - if (!IsValid() || !GetProcTable().AHardwareBuffer_getId) { + return GetSystemUniqueID(GetHandle()); +} + +std::optional HardwareBuffer::GetSystemUniqueID( + AHardwareBuffer* buffer) { + if (!GetProcTable().AHardwareBuffer_getId) { return false; } uint64_t out_id = 0u; - if (GetProcTable().AHardwareBuffer_getId(GetHandle(), &out_id) != 0) { + if (GetProcTable().AHardwareBuffer_getId(buffer, &out_id) != 0) { return std::nullopt; } return out_id; diff --git a/impeller/toolkit/android/hardware_buffer.h b/impeller/toolkit/android/hardware_buffer.h index c49b00cc13ccc..02b99b1013674 100644 --- a/impeller/toolkit/android/hardware_buffer.h +++ b/impeller/toolkit/android/hardware_buffer.h @@ -116,6 +116,15 @@ class HardwareBuffer { /// std::optional GetSystemUniqueID() const; + //---------------------------------------------------------------------------- + /// @brief Get the system wide unique ID of the hardware buffer if + /// possible. This is only available on Android API 31 and above. + /// Within the process, the handle are unique. + /// + /// @return The system unique id if one can be obtained. + /// + static std::optional GetSystemUniqueID(AHardwareBuffer* buffer); + private: struct UniqueAHardwareBufferTraits { static AHardwareBuffer* InvalidValue() { return nullptr; } diff --git a/shell/platform/android/image_external_texture_gl.cc b/shell/platform/android/image_external_texture_gl.cc index c6dfbc7fb4ab0..dc6a15c1fa08b 100644 --- a/shell/platform/android/image_external_texture_gl.cc +++ b/shell/platform/android/image_external_texture_gl.cc @@ -10,6 +10,7 @@ #include "flutter/common/graphics/texture.h" #include "flutter/impeller/core/formats.h" #include "flutter/impeller/display_list/dl_image_impeller.h" +#include "flutter/impeller/toolkit/android/hardware_buffer.h" #include "flutter/impeller/toolkit/egl/image.h" #include "flutter/impeller/toolkit/gles/texture.h" #include "third_party/skia/include/core/SkAlphaType.h" @@ -44,7 +45,8 @@ void ImageExternalTextureGL::UpdateImage(JavaLocalRef& hardware_buffer, PaintContext& context) { AHardwareBuffer* latest_hardware_buffer = AHardwareBufferFor(hardware_buffer); std::optional key = - flutter::NDKHelpers::AHardwareBuffer_getId(latest_hardware_buffer); + impeller::android::HardwareBuffer::GetSystemUniqueID( + latest_hardware_buffer); auto existing_image = image_lru_.FindImage(key); if (existing_image != nullptr) { dl_image_ = existing_image; diff --git a/shell/platform/android/image_external_texture_vk.cc b/shell/platform/android/image_external_texture_vk.cc index 9f7016848e25a..fbf804badb759 100644 --- a/shell/platform/android/image_external_texture_vk.cc +++ b/shell/platform/android/image_external_texture_vk.cc @@ -48,7 +48,8 @@ void ImageExternalTextureVK::ProcessFrame(PaintContext& context, auto hb_desc = impeller::android::HardwareBuffer::Describe(latest_hardware_buffer); std::optional key = - flutter::NDKHelpers::AHardwareBuffer_getId(latest_hardware_buffer); + impeller::android::HardwareBuffer::GetSystemUniqueID( + latest_hardware_buffer); auto existing_image = image_lru_.FindImage(key); if (existing_image != nullptr) { dl_image_ = existing_image; From e4d6822709d2751580300b4d1bd85499f9b7df02 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 15 Mar 2024 11:23:51 -0700 Subject: [PATCH 11/15] Allow resolving EGL procs. --- impeller/toolkit/android/proc_table.cc | 59 +++++++++++++------ impeller/toolkit/android/proc_table.h | 26 ++++---- .../android/image_external_texture_gl.cc | 3 +- 3 files changed, 57 insertions(+), 31 deletions(-) diff --git a/impeller/toolkit/android/proc_table.cc b/impeller/toolkit/android/proc_table.cc index 55791b238ceee..ba9cac2fb3a39 100644 --- a/impeller/toolkit/android/proc_table.cc +++ b/impeller/toolkit/android/proc_table.cc @@ -13,15 +13,45 @@ const ProcTable& GetProcTable() { return gProcTable; } -ProcTable::ProcTable() - : lib_android_(fml::NativeLibrary::Create("libandroid.so")) { - if (!lib_android_) { - VALIDATION_LOG << "Could not open libandroid.so"; +template +bool ResolveAndroidProc( + AndroidProc& proc, + uint32_t device_api_level, + const std::vector>& libs) { + if (device_api_level < proc.api_availability) { + // Nothing to do. We don't expect this proc. to be available on this device. + return true; + } + std::optional proc_value; + for (const auto& lib : libs) { + proc_value = lib->ResolveFunction(proc.proc_name); + if (proc_value) { + break; + } + } + if (!proc_value.has_value()) { + VALIDATION_LOG << "Could not find proc in any of the Android libraries: " + << proc.proc_name; + return false; + } + proc.proc = proc_value.value(); + return true; +} + +ProcTable::ProcTable() { + auto lib_android = fml::NativeLibrary::Create("libandroid.so"); + auto lib_egl = fml::NativeLibrary::Create("libEGL.so"); + + if (!lib_android || !lib_egl) { + VALIDATION_LOG << "Could not open Android libraries."; return; } + libraries_.push_back(lib_android); + libraries_.push_back(lib_egl); + auto proc = - lib_android_->ResolveFunction( + lib_android->ResolveFunction( "android_get_device_api_level"); if (!proc.has_value()) { return; @@ -35,20 +65,11 @@ ProcTable::ProcTable() device_api_level_ = api; -#define RESOLVE_PROC(table_member, api) \ - { \ - if (device_api_level_ >= table_member.api_availability) { \ - if (auto resolved = \ - lib_android_->ResolveFunction( \ - table_member.proc_name); \ - resolved.has_value()) { \ - table_member.proc = resolved.value(); \ - } else { \ - VALIDATION_LOG << "Could not resolve function: " \ - << table_member.proc_name; \ - return; \ - } \ - } \ +#define RESOLVE_PROC(table_member, api) \ + { \ + if (!ResolveAndroidProc(table_member, device_api_level_, libraries_)) { \ + return; \ + } \ } FOR_EACH_ANDROID_PROC(RESOLVE_PROC); #undef RESOLVE_PROC diff --git a/impeller/toolkit/android/proc_table.h b/impeller/toolkit/android/proc_table.h index b59563ec75747..d67ed6ea3f446 100644 --- a/impeller/toolkit/android/proc_table.h +++ b/impeller/toolkit/android/proc_table.h @@ -5,6 +5,9 @@ #ifndef FLUTTER_IMPELLER_TOOLKIT_ANDROID_PROC_TABLE_H_ #define FLUTTER_IMPELLER_TOOLKIT_ANDROID_PROC_TABLE_H_ +#include +#define EGL_EGLEXT_PROTOTYPES +#include #include #include #include @@ -19,30 +22,31 @@ namespace impeller::android { #define FOR_EACH_ANDROID_PROC(INVOKE) \ - INVOKE(ATrace_isEnabled, 23) \ INVOKE(AChoreographer_getInstance, 24) \ INVOKE(AChoreographer_postFrameCallback, 24) \ INVOKE(AChoreographer_postFrameCallback64, 29) \ - INVOKE(AHardwareBuffer_fromHardwareBuffer, 26) \ - INVOKE(AHardwareBuffer_allocate, 26) \ INVOKE(AHardwareBuffer_acquire, 26) \ - INVOKE(AHardwareBuffer_release, 26) \ - INVOKE(AHardwareBuffer_isSupported, 29) \ + INVOKE(AHardwareBuffer_allocate, 26) \ INVOKE(AHardwareBuffer_describe, 26) \ + INVOKE(AHardwareBuffer_fromHardwareBuffer, 26) \ INVOKE(AHardwareBuffer_getId, 31) \ + INVOKE(AHardwareBuffer_isSupported, 29) \ + INVOKE(AHardwareBuffer_release, 26) \ INVOKE(ANativeWindow_acquire, 0) \ - INVOKE(ANativeWindow_release, 0) \ - INVOKE(ANativeWindow_getWidth, 0) \ INVOKE(ANativeWindow_getHeight, 0) \ + INVOKE(ANativeWindow_getWidth, 0) \ + INVOKE(ANativeWindow_release, 0) \ INVOKE(ASurfaceControl_createFromWindow, 29) \ INVOKE(ASurfaceControl_release, 29) \ + INVOKE(ASurfaceTransaction_apply, 29) \ INVOKE(ASurfaceTransaction_create, 29) \ INVOKE(ASurfaceTransaction_delete, 29) \ - INVOKE(ASurfaceTransaction_apply, 29) \ - INVOKE(ASurfaceTransaction_setOnComplete, 29) \ INVOKE(ASurfaceTransaction_reparent, 29) \ INVOKE(ASurfaceTransaction_setBuffer, 29) \ - INVOKE(ASurfaceTransaction_setColor, 29) + INVOKE(ASurfaceTransaction_setColor, 29) \ + INVOKE(ASurfaceTransaction_setOnComplete, 29) \ + INVOKE(ATrace_isEnabled, 23) \ + INVOKE(eglGetNativeClientBufferANDROID, 0) template struct AndroidProc { @@ -113,7 +117,7 @@ struct ProcTable { #undef DEFINE_PROC private: - fml::RefPtr lib_android_; + std::vector> libraries_; uint32_t device_api_level_ = 0u; bool is_valid_ = false; }; diff --git a/shell/platform/android/image_external_texture_gl.cc b/shell/platform/android/image_external_texture_gl.cc index dc6a15c1fa08b..92743654d0289 100644 --- a/shell/platform/android/image_external_texture_gl.cc +++ b/shell/platform/android/image_external_texture_gl.cc @@ -90,7 +90,8 @@ impeller::UniqueEGLImageKHR ImageExternalTextureGL::CreateEGLImage( FML_CHECK(display != EGL_NO_DISPLAY); EGLClientBuffer client_buffer = - NDKHelpers::eglGetNativeClientBufferANDROID(hardware_buffer); + impeller::android::GetProcTable().eglGetNativeClientBufferANDROID( + hardware_buffer); FML_DCHECK(client_buffer != nullptr); if (client_buffer == nullptr) { FML_LOG(ERROR) << "eglGetNativeClientBufferAndroid returned null."; From ca8c4f7f8750a242f81a0ad12e60aecce7726c74 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 15 Mar 2024 11:45:29 -0700 Subject: [PATCH 12/15] Don't do any availability checks while setting up the proc table. --- impeller/toolkit/android/choreographer.cc | 19 ++---- impeller/toolkit/android/proc_table.cc | 58 ++++++------------- impeller/toolkit/android/proc_table.h | 36 ++++++------ .../android/toolkit_android_unittests.cc | 5 -- .../platform/android/vsync_waiter_android.cc | 3 +- 5 files changed, 43 insertions(+), 78 deletions(-) diff --git a/impeller/toolkit/android/choreographer.cc b/impeller/toolkit/android/choreographer.cc index f4e7eebab6f96..f723e3e62fc07 100644 --- a/impeller/toolkit/android/choreographer.cc +++ b/impeller/toolkit/android/choreographer.cc @@ -14,16 +14,7 @@ Choreographer& Choreographer::GetInstance() { } Choreographer::Choreographer() { - if (!GetProcTable().AChoreographer_getInstance) { - return; - } - - const auto api_level = GetProcTable().GetAndroidDeviceAPILevel(); - if (api_level >= 29 && !GetProcTable().AChoreographer_postFrameCallback64) { - return; - } - - if (api_level >= 24 && !GetProcTable().AChoreographer_postFrameCallback) { + if (!IsAvailableOnPlatform()) { return; } @@ -57,8 +48,7 @@ bool Choreographer::PostFrameCallback(FrameCallback callback) const { data->callback = std::move(callback); const auto& table = GetProcTable(); - const auto api_level = table.GetAndroidDeviceAPILevel(); - if (api_level >= 29) { + if (table.AChoreographer_postFrameCallback64) { table.AChoreographer_postFrameCallback64( const_cast(instance_), [](int64_t nanos, void* p_data) { @@ -68,7 +58,7 @@ bool Choreographer::PostFrameCallback(FrameCallback callback) const { }, data.release()); return true; - } else if (api_level >= 24) { + } else if (table.AChoreographer_postFrameCallback) { table.AChoreographer_postFrameCallback( const_cast(instance_), [](long /*NOLINT*/ nanos, void* p_data) { @@ -86,7 +76,8 @@ bool Choreographer::PostFrameCallback(FrameCallback callback) const { } bool Choreographer::IsAvailableOnPlatform() { - return GetProcTable().AChoreographer_postFrameCallback64 || + return GetProcTable().AChoreographer_getInstance || + GetProcTable().AChoreographer_postFrameCallback64 || GetProcTable().AChoreographer_postFrameCallback; } diff --git a/impeller/toolkit/android/proc_table.cc b/impeller/toolkit/android/proc_table.cc index ba9cac2fb3a39..71e6ce5c4d14d 100644 --- a/impeller/toolkit/android/proc_table.cc +++ b/impeller/toolkit/android/proc_table.cc @@ -4,6 +4,7 @@ #include "flutter/impeller/toolkit/android/proc_table.h" +#include "flutter/fml/build_config.h" #include "impeller/base/validation.h" namespace impeller::android { @@ -14,28 +15,15 @@ const ProcTable& GetProcTable() { } template -bool ResolveAndroidProc( +void ResolveAndroidProc( AndroidProc& proc, - uint32_t device_api_level, const std::vector>& libs) { - if (device_api_level < proc.api_availability) { - // Nothing to do. We don't expect this proc. to be available on this device. - return true; - } - std::optional proc_value; for (const auto& lib : libs) { - proc_value = lib->ResolveFunction(proc.proc_name); - if (proc_value) { + proc.proc = lib->ResolveFunction(proc.proc_name).value_or(nullptr); + if (proc.proc) { break; } } - if (!proc_value.has_value()) { - VALIDATION_LOG << "Could not find proc in any of the Android libraries: " - << proc.proc_name; - return false; - } - proc.proc = proc_value.value(); - return true; } ProcTable::ProcTable() { @@ -47,32 +35,22 @@ ProcTable::ProcTable() { return; } - libraries_.push_back(lib_android); - libraries_.push_back(lib_egl); + libraries_.push_back(std::move(lib_android)); + libraries_.push_back(std::move(lib_egl)); - auto proc = - lib_android->ResolveFunction( - "android_get_device_api_level"); - if (!proc.has_value()) { - return; - } +#define RESOLVE_PROC(proc, api) ResolveAndroidProc(proc, libraries_); + FOR_EACH_ANDROID_PROC(RESOLVE_PROC); +#undef RESOLVE_PROC - int api = proc.value()(); - if (api < 0) { - VALIDATION_LOG << "Could not get Android API level."; - return; + if (AChoreographer_postFrameCallback64) { + AChoreographer_postFrameCallback.Reset(); } - device_api_level_ = api; - -#define RESOLVE_PROC(table_member, api) \ - { \ - if (!ResolveAndroidProc(table_member, device_api_level_, libraries_)) { \ - return; \ - } \ - } - FOR_EACH_ANDROID_PROC(RESOLVE_PROC); -#undef RESOLVE_PROC +#if FML_ARCH_CPU_32_BITS + // On 32-bit platforms, the nanosecond resolution timestamp causes overflow on + // the argument in the callback. Don't use it on those platforms. + AChoreographer_postFrameCallback.Reset(); +#endif // FML_ARCH_CPU_32_BITS is_valid_ = true; } @@ -83,8 +61,8 @@ bool ProcTable::IsValid() const { return is_valid_; } -uint32_t ProcTable::GetAndroidDeviceAPILevel() const { - return device_api_level_; +bool ProcTable::TraceIsEnabled() const { + return this->ATrace_isEnabled ? this->ATrace_isEnabled() : false; } } // namespace impeller::android diff --git a/impeller/toolkit/android/proc_table.h b/impeller/toolkit/android/proc_table.h index d67ed6ea3f446..83b38a1293c79 100644 --- a/impeller/toolkit/android/proc_table.h +++ b/impeller/toolkit/android/proc_table.h @@ -21,6 +21,19 @@ namespace impeller::android { +//------------------------------------------------------------------------------ +/// @brief The Android procs along with the device API level on which these +/// will be available. There is no checking of the actual API level +/// however (because getting the API level is itself only possible +/// on API levels 29 and above). +/// +/// Take care to explicitly check for the availability of these APIs +/// at runtime before invoking them. +/// +/// Typically, you'll never have to deal with the proc. table +/// directly. Instead, rely on the handle wrappers (`Choreographer`, +/// `HardwareBuffer`, etc..). +/// #define FOR_EACH_ANDROID_PROC(INVOKE) \ INVOKE(AChoreographer_getInstance, 24) \ INVOKE(AChoreographer_postFrameCallback, 24) \ @@ -54,8 +67,6 @@ struct AndroidProc { const char* proc_name = nullptr; - size_t api_availability = 0; - AndroidProcType* proc = nullptr; constexpr bool IsAvailable() const { return proc != nullptr; } @@ -69,6 +80,8 @@ struct AndroidProc { << " is not available on this device. Missing check."; return proc(std::forward(args)...); } + + void Reset() { proc = nullptr; } }; //------------------------------------------------------------------------------ @@ -91,34 +104,21 @@ struct ProcTable { /// bool IsValid() const; - //---------------------------------------------------------------------------- - /// @brief Get the Android device API level. Due to the overall - /// availability restrictions of this class, this may only be at - /// or above 29 on a valid proc table. - /// - /// @return The Android device api level. - /// - uint32_t GetAndroidDeviceAPILevel() const; - //---------------------------------------------------------------------------- /// @brief Check if tracing in enabled in the process. This call can be /// made at any API level. /// /// @return If tracing is enabled. /// - bool TraceIsEnabled() const { - return this->ATrace_isEnabled ? this->ATrace_isEnabled() : false; - } + bool TraceIsEnabled() const; -#define DEFINE_PROC(name, api) \ - AndroidProc name = {.proc_name = #name, \ - .api_availability = api}; +#define DEFINE_PROC(name, api) \ + AndroidProc name = {.proc_name = #name}; FOR_EACH_ANDROID_PROC(DEFINE_PROC); #undef DEFINE_PROC private: std::vector> libraries_; - uint32_t device_api_level_ = 0u; bool is_valid_ = false; }; diff --git a/impeller/toolkit/android/toolkit_android_unittests.cc b/impeller/toolkit/android/toolkit_android_unittests.cc index fc0297fb973ed..d9aa1db8ca0e0 100644 --- a/impeller/toolkit/android/toolkit_android_unittests.cc +++ b/impeller/toolkit/android/toolkit_android_unittests.cc @@ -80,11 +80,6 @@ TEST_F(ToolkitAndroidTest, SurfacControlsAreAvailable) { ASSERT_TRUE(SurfaceControl::IsAvailableOnPlatform()); } -TEST_F(ToolkitAndroidTest, CanAccessDeviceAPILevel) { - // This test will be skipped otherwise. - ASSERT_GE(GetProcTable().GetAndroidDeviceAPILevel(), 29u); -} - TEST_F(ToolkitAndroidTest, ChoreographerIsAvailable) { ASSERT_TRUE(Choreographer::IsAvailableOnPlatform()); } diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index 77a24cfc74dfe..bfced030f5fe5 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -43,7 +43,8 @@ void VsyncWaiterAndroid::AwaitVSync() { }); }); } else { - // TODO(99798): Remove it when we drop support for API level < 29. + // TODO(99798): Remove it when we drop support for API level < 29 and 32-bit + // devices. auto* weak_this = new std::weak_ptr(shared_from_this()); jlong java_baton = reinterpret_cast(weak_this); task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() { From 64237403bf2f69a82e338f0c91e4a53816e8e13c Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 15 Mar 2024 11:46:32 -0700 Subject: [PATCH 13/15] Patch licenses. --- ci/licenses_golden/excluded_files | 1 - ci/licenses_golden/licenses_flutter | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index a4a4944b86efa..dd2c355727afa 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -101,7 +101,6 @@ ../../../flutter/fml/message_loop_task_queues_unittests.cc ../../../flutter/fml/message_loop_unittests.cc ../../../flutter/fml/paths_unittests.cc -../../../flutter/fml/platform/android/ndk_helpers_unittests.cc ../../../flutter/fml/platform/darwin/cf_utils_unittests.mm ../../../flutter/fml/platform/darwin/scoped_nsobject_arc_unittests.mm ../../../flutter/fml/platform/darwin/scoped_nsobject_unittests.mm diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 6bb2d982e5969..477bd20564f91 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -39364,8 +39364,6 @@ ORIGIN: ../../../flutter/fml/platform/android/jni_weak_ref.cc + ../../../flutter ORIGIN: ../../../flutter/fml/platform/android/jni_weak_ref.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/platform/android/message_loop_android.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/platform/android/message_loop_android.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/fml/platform/android/ndk_helpers.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/fml/platform/android/ndk_helpers.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/platform/android/paths_android.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/platform/android/paths_android.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/fml/platform/android/scoped_java_ref.cc + ../../../flutter/LICENSE @@ -40151,6 +40149,8 @@ ORIGIN: ../../../flutter/impeller/tessellator/c/tessellator.h + ../../../flutter ORIGIN: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/tessellator/tessellator.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/tessellator/tessellator.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/choreographer.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/toolkit/android/choreographer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/toolkit/android/hardware_buffer.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/toolkit/android/hardware_buffer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/toolkit/android/native_window.cc + ../../../flutter/LICENSE @@ -40884,6 +40884,7 @@ ORIGIN: ../../../flutter/shell/platform/android/flutter_main.h + ../../../flutte ORIGIN: ../../../flutter/shell/platform/android/image_external_texture.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_gl.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_gl.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_vk.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/image_external_texture_vk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/image_lru.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/image_lru.h + ../../../flutter/LICENSE @@ -42230,8 +42231,6 @@ FILE: ../../../flutter/fml/platform/android/jni_weak_ref.cc FILE: ../../../flutter/fml/platform/android/jni_weak_ref.h FILE: ../../../flutter/fml/platform/android/message_loop_android.cc FILE: ../../../flutter/fml/platform/android/message_loop_android.h -FILE: ../../../flutter/fml/platform/android/ndk_helpers.cc -FILE: ../../../flutter/fml/platform/android/ndk_helpers.h FILE: ../../../flutter/fml/platform/android/paths_android.cc FILE: ../../../flutter/fml/platform/android/paths_android.h FILE: ../../../flutter/fml/platform/android/scoped_java_ref.cc @@ -43018,6 +43017,8 @@ FILE: ../../../flutter/impeller/tessellator/c/tessellator.h FILE: ../../../flutter/impeller/tessellator/dart/lib/tessellator.dart FILE: ../../../flutter/impeller/tessellator/tessellator.cc FILE: ../../../flutter/impeller/tessellator/tessellator.h +FILE: ../../../flutter/impeller/toolkit/android/choreographer.cc +FILE: ../../../flutter/impeller/toolkit/android/choreographer.h FILE: ../../../flutter/impeller/toolkit/android/hardware_buffer.cc FILE: ../../../flutter/impeller/toolkit/android/hardware_buffer.h FILE: ../../../flutter/impeller/toolkit/android/native_window.cc From 994e5f639d30f3075b1df569363c7c45bcccf179 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 15 Mar 2024 12:15:22 -0700 Subject: [PATCH 14/15] Lint. --- impeller/toolkit/android/toolkit_android_unittests.cc | 4 ++-- shell/platform/android/image_external_texture_vk.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/impeller/toolkit/android/toolkit_android_unittests.cc b/impeller/toolkit/android/toolkit_android_unittests.cc index d9aa1db8ca0e0..ff3a7df9daae2 100644 --- a/impeller/toolkit/android/toolkit_android_unittests.cc +++ b/impeller/toolkit/android/toolkit_android_unittests.cc @@ -63,8 +63,8 @@ TEST_F(ToolkitAndroidTest, CanDescribeHardwareBufferHandles) { ASSERT_TRUE(buffer.IsValid()); auto a_desc = HardwareBuffer::Describe(buffer.GetHandle()); ASSERT_TRUE(a_desc.has_value()); - ASSERT_EQ(a_desc->width, 100u); - ASSERT_EQ(a_desc->height, 100u); + ASSERT_EQ(a_desc->width, 100u); // NOLINT + ASSERT_EQ(a_desc->height, 100u); // NOLINT } TEST_F(ToolkitAndroidTest, CanApplySurfaceTransaction) { diff --git a/shell/platform/android/image_external_texture_vk.cc b/shell/platform/android/image_external_texture_vk.cc index fbf804badb759..88574548d6c11 100644 --- a/shell/platform/android/image_external_texture_vk.cc +++ b/shell/platform/android/image_external_texture_vk.cc @@ -51,7 +51,7 @@ void ImageExternalTextureVK::ProcessFrame(PaintContext& context, impeller::android::HardwareBuffer::GetSystemUniqueID( latest_hardware_buffer); auto existing_image = image_lru_.FindImage(key); - if (existing_image != nullptr) { + if (existing_image != nullptr || !hb_desc.has_value()) { dl_image_ = existing_image; CloseHardwareBuffer(hardware_buffer); From 4f572047d2c3e15d5363ada532430453fc0f09e7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 15 Mar 2024 13:01:15 -0700 Subject: [PATCH 15/15] Build the unittests on CI. --- ci/builders/linux_android_emulator.json | 2 ++ ci/builders/linux_android_emulator_api_33.json | 1 + ci/builders/linux_android_emulator_skia.json | 1 + 3 files changed, 4 insertions(+) diff --git a/ci/builders/linux_android_emulator.json b/ci/builders/linux_android_emulator.json index 918c8e6178b16..2340fe3921b64 100644 --- a/ci/builders/linux_android_emulator.json +++ b/ci/builders/linux_android_emulator.json @@ -29,6 +29,7 @@ "ninja": { "config": "android_emulator_debug_x64", "targets": [ + "flutter/impeller/toolkit/android:unittests", "flutter/shell/platform/android:flutter_shell_native_unittests", "flutter/testing/scenario_app" ] @@ -120,6 +121,7 @@ "ninja": { "config": "android_emulator_debug_x86", "targets": [ + "flutter/impeller/toolkit/android:unittests", "flutter/shell/platform/android:flutter_shell_native_unittests", "flutter/testing/scenario_app" ] diff --git a/ci/builders/linux_android_emulator_api_33.json b/ci/builders/linux_android_emulator_api_33.json index dfb70d49ce2d3..b955dd19e4d6f 100644 --- a/ci/builders/linux_android_emulator_api_33.json +++ b/ci/builders/linux_android_emulator_api_33.json @@ -29,6 +29,7 @@ "ninja": { "config": "android_debug_api33_x64", "targets": [ + "flutter/impeller/toolkit/android:unittests", "flutter/shell/platform/android:flutter_shell_native_unittests", "flutter/testing/scenario_app" ] diff --git a/ci/builders/linux_android_emulator_skia.json b/ci/builders/linux_android_emulator_skia.json index 32fcbc96ac174..f2a5f97aec2c8 100644 --- a/ci/builders/linux_android_emulator_skia.json +++ b/ci/builders/linux_android_emulator_skia.json @@ -29,6 +29,7 @@ "ninja": { "config": "android_emulator_skia_debug_x64", "targets": [ + "flutter/impeller/toolkit/android:unittests", "flutter/shell/platform/android:flutter_shell_native_unittests", "flutter/testing/scenario_app" ]