[Impeller] A toolkit for managed handles to Android NDK vended objects. (flutter/engine#51334)

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.
This commit is contained in:
Chinmay Garde 2024-03-15 13:45:15 -07:00 committed by GitHub
parent 3eb357342b
commit 38ec9b7328
37 changed files with 1381 additions and 577 deletions

View File

@ -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) {

View File

@ -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"
]

View File

@ -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"
]

View File

@ -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"
]

View File

@ -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
@ -200,6 +199,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

View File

@ -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,18 @@ 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
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
@ -40874,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
@ -42220,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
@ -43008,6 +43017,18 @@ 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
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

View File

@ -188,8 +188,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",

View File

@ -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 <android/hardware_buffer.h>
#include <dlfcn.h>
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<fml::NativeLibrary> android =
fml::NativeLibrary::Create("libandroid.so");
FML_CHECK(android.get() != nullptr);
static fml::RefPtr<fml::NativeLibrary> egl =
fml::NativeLibrary::Create("libEGL.so");
FML_CHECK(egl.get() != nullptr);
#define LOOKUP(lib, func) \
_##func = lib->ResolveFunction<fp_##func>(#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<HardwareBufferKey> 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

View File

@ -1,100 +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 <EGL/egl.h>
#include <android/choreographer.h>
#include <android/hardware_buffer.h>
#include <android/surface_control.h>
#include <android/trace.h>
#include <jni.h>
#include <optional>
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
static bool ATrace_isEnabled();
// API Version 24
static ChoreographerSupportStatus ChoreographerSupported();
static AChoreographer* _Nullable AChoreographer_getInstance();
// Deprecated in 29, available since 24.
static void AChoreographer_postFrameCallback(
AChoreographer* _Nonnull choreographer,
AChoreographer_frameCallback _Nonnull callback,
void* _Nullable data);
// API Version 26
static bool HardwareBufferSupported();
static AHardwareBuffer* _Nonnull AHardwareBuffer_fromHardwareBuffer(
JNIEnv* _Nonnull env,
jobject _Nonnull hardwareBufferObj);
static void AHardwareBuffer_release(AHardwareBuffer* _Nonnull buffer);
static void AHardwareBuffer_describe(AHardwareBuffer* _Nonnull buffer,
AHardwareBuffer_Desc* _Nullable desc);
static int AHardwareBuffer_allocate(
const AHardwareBuffer_Desc* _Nonnull desc,
AHardwareBuffer* _Nullable* _Nullable outBuffer);
static EGLClientBuffer _Nonnull eglGetNativeClientBufferANDROID(
AHardwareBuffer* _Nonnull buffer);
// API Version 29
static int AHardwareBuffer_isSupported(
const AHardwareBuffer_Desc* _Nonnull desc);
static void AChoreographer_postFrameCallback64(
AChoreographer* _Nonnull choreographer,
AChoreographer_frameCallback64 _Nonnull callback,
void* _Nullable data);
static bool SurfaceControlAndTransactionSupported();
static ASurfaceControl* _Nonnull ASurfaceControl_createFromWindow(
ANativeWindow* _Nonnull parent,
const char* _Nullable debug_name);
static void ASurfaceControl_release(
ASurfaceControl* _Nonnull surface_control);
static ASurfaceTransaction* _Nonnull ASurfaceTransaction_create();
static void ASurfaceTransaction_delete(
ASurfaceTransaction* _Nonnull surface_transaction);
static void ASurfaceTransaction_apply(
ASurfaceTransaction* _Nonnull surface_transaction);
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<HardwareBufferKey> AHardwareBuffer_getId(
AHardwareBuffer* _Nonnull buffer);
};
} // namespace flutter
#endif // FLUTTER_FML_PLATFORM_ANDROID_NDK_HELPERS_H_

View File

@ -1,135 +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 {
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
} // namespace flutter

View File

@ -0,0 +1,54 @@
# 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 = [
"choreographer.cc",
"choreographer.h",
"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",
]
}

View File

@ -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.

View File

@ -0,0 +1,84 @@
// 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 (!IsAvailableOnPlatform()) {
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<InFlightData>();
data->callback = std::move(callback);
const auto& table = GetProcTable();
if (table.AChoreographer_postFrameCallback64) {
table.AChoreographer_postFrameCallback64(
const_cast<AChoreographer*>(instance_),
[](int64_t nanos, void* p_data) {
auto data = reinterpret_cast<InFlightData*>(p_data);
data->callback(ClockMonotonicNanosToFrameTimePoint(nanos));
delete data;
},
data.release());
return true;
} else if (table.AChoreographer_postFrameCallback) {
table.AChoreographer_postFrameCallback(
const_cast<AChoreographer*>(instance_),
[](long /*NOLINT*/ nanos, void* p_data) {
auto data = reinterpret_cast<InFlightData*>(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_getInstance ||
GetProcTable().AChoreographer_postFrameCallback64 ||
GetProcTable().AChoreographer_postFrameCallback;
}
} // namespace impeller::android

View File

@ -0,0 +1,84 @@
// 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 <chrono>
#include <memory>
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();
//----------------------------------------------------------------------------
/// @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();
Choreographer(const Choreographer&) = delete;
Choreographer& operator=(const Choreographer&) = delete;
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<FrameClock>;
using FrameCallback = std::function<void(FrameTimePoint)>;
//----------------------------------------------------------------------------
/// @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:
AChoreographer* instance_ = nullptr;
explicit Choreographer();
};
} // namespace impeller::android
#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_CHOREOGRAPHER_H_

View File

@ -0,0 +1,131 @@
// 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<HardwareBufferUsage>(
HardwareBufferUsageFlags::kFrameBufferAttachment)) {
ahb_desc.usage |= AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
}
if (desc.usage & static_cast<HardwareBufferUsage>(
HardwareBufferUsageFlags::kCompositorOverlay)) {
ahb_desc.usage |= AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY;
}
if (desc.usage & static_cast<HardwareBufferUsage>(
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<HardwareBufferUsage>(
HardwareBufferUsageFlags::kFrameBufferAttachment) |
static_cast<HardwareBufferUsage>(
HardwareBufferUsageFlags::kCompositorOverlay) |
static_cast<HardwareBufferUsage>(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();
}
std::optional<uint64_t> HardwareBuffer::GetSystemUniqueID() const {
return GetSystemUniqueID(GetHandle());
}
std::optional<uint64_t> HardwareBuffer::GetSystemUniqueID(
AHardwareBuffer* buffer) {
if (!GetProcTable().AHardwareBuffer_getId) {
return false;
}
uint64_t out_id = 0u;
if (GetProcTable().AHardwareBuffer_getId(buffer, &out_id) != 0) {
return std::nullopt;
}
return out_id;
}
std::optional<AHardwareBuffer_Desc> 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

View File

@ -0,0 +1,149 @@
// 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 <optional>
#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;
static std::optional<AHardwareBuffer_Desc> 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<uint64_t> 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<uint64_t> GetSystemUniqueID(AHardwareBuffer* buffer);
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<AHardwareBuffer*, UniqueAHardwareBufferTraits> buffer_;
bool is_valid_ = false;
};
} // namespace impeller::android
#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_HARDWARE_BUFFER_H_

View File

@ -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

View File

@ -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<ANativeWindow*, UniqueANativeWindowTraits> window_;
};
} // namespace impeller::android
#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_NATIVE_WINDOW_H_

View File

@ -0,0 +1,68 @@
// 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 "flutter/fml/build_config.h"
#include "impeller/base/validation.h"
namespace impeller::android {
const ProcTable& GetProcTable() {
static ProcTable gProcTable;
return gProcTable;
}
template <class T>
void ResolveAndroidProc(
AndroidProc<T>& proc,
const std::vector<fml::RefPtr<fml::NativeLibrary>>& libs) {
for (const auto& lib : libs) {
proc.proc = lib->ResolveFunction<T*>(proc.proc_name).value_or(nullptr);
if (proc.proc) {
break;
}
}
}
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(std::move(lib_android));
libraries_.push_back(std::move(lib_egl));
#define RESOLVE_PROC(proc, api) ResolveAndroidProc(proc, libraries_);
FOR_EACH_ANDROID_PROC(RESOLVE_PROC);
#undef RESOLVE_PROC
if (AChoreographer_postFrameCallback64) {
AChoreographer_postFrameCallback.Reset();
}
#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;
}
ProcTable::~ProcTable() = default;
bool ProcTable::IsValid() const {
return is_valid_;
}
bool ProcTable::TraceIsEnabled() const {
return this->ATrace_isEnabled ? this->ATrace_isEnabled() : false;
}
} // namespace impeller::android

View File

@ -0,0 +1,129 @@
// 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 <EGL/egl.h>
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/eglext.h>
#include <android/api-level.h>
#include <android/hardware_buffer.h>
#include <android/hardware_buffer_jni.h>
#include <android/surface_control.h>
#include <android/trace.h>
#include <functional>
#include "flutter/fml/logging.h"
#include "flutter/fml/native_library.h"
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) \
INVOKE(AChoreographer_postFrameCallback64, 29) \
INVOKE(AHardwareBuffer_acquire, 26) \
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_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_reparent, 29) \
INVOKE(ASurfaceTransaction_setBuffer, 29) \
INVOKE(ASurfaceTransaction_setColor, 29) \
INVOKE(ASurfaceTransaction_setOnComplete, 29) \
INVOKE(ATrace_isEnabled, 23) \
INVOKE(eglGetNativeClientBufferANDROID, 0)
template <class T>
struct AndroidProc {
using AndroidProcType = T;
const char* proc_name = nullptr;
AndroidProcType* proc = nullptr;
constexpr bool IsAvailable() const { return proc != nullptr; }
explicit constexpr operator bool() const { return IsAvailable(); }
template <class... Args>
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>(args)...);
}
void Reset() { proc = nullptr; }
};
//------------------------------------------------------------------------------
/// @brief The table of Android procs that are resolved dynamically.
///
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 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;
#define DEFINE_PROC(name, api) \
AndroidProc<decltype(name)> name = {.proc_name = #name};
FOR_EACH_ANDROID_PROC(DEFINE_PROC);
#undef DEFINE_PROC
private:
std::vector<fml::RefPtr<fml::NativeLibrary>> libraries_;
bool is_valid_ = false;
};
const ProcTable& GetProcTable();
} // namespace impeller::android
#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_PROC_TABLE_H_

View File

@ -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

View File

@ -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<ASurfaceControl*, UniqueASurfaceControlTraits> control_;
};
} // namespace impeller::android
#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_SURFACE_CONTROL_H_

View File

@ -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<TransactionInFlightData>();
data->callback = callback;
proc_table.ASurfaceTransaction_setOnComplete(
transaction_.get(), //
data.release(), //
[](void* context, ASurfaceTransactionStats* stats) -> void {
auto data = reinterpret_cast<TransactionInFlightData*>(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

View File

@ -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 <functional>
#include <map>
#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<void(void)>;
//----------------------------------------------------------------------------
/// @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<ASurfaceTransaction*, UniqueASurfaceTransactionTraits>
transaction_;
};
} // namespace impeller::android
#endif // FLUTTER_IMPELLER_TOOLKIT_ANDROID_SURFACE_TRANSACTION_H_

View File

@ -0,0 +1,108 @@
// 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/choreographer.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, 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, 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); // NOLINT
ASSERT_EQ(a_desc->height, 100u); // NOLINT
}
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, 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

View File

@ -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",
@ -143,6 +142,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",

View File

@ -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"
@ -27,6 +26,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 +85,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",

View File

@ -5,7 +5,7 @@
#include <android/sensor.h>
#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 +107,9 @@ AHardwareBuffer* ImageExternalTexture::AHardwareBufferFor(
const fml::jni::JavaRef<jobject>& 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

View File

@ -8,9 +8,9 @@
#include <android/sensor.h>
#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/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"
@ -45,7 +45,8 @@ void ImageExternalTextureGL::UpdateImage(JavaLocalRef& hardware_buffer,
PaintContext& context) {
AHardwareBuffer* latest_hardware_buffer = AHardwareBufferFor(hardware_buffer);
std::optional<HardwareBufferKey> 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;
@ -89,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.";

View File

@ -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 {

View File

@ -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 <cstdint>
#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"
@ -10,6 +13,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,13 +45,13 @@ 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<HardwareBufferKey> 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) {
if (existing_image != nullptr || !hb_desc.has_value()) {
dl_image_ = existing_image;
CloseHardwareBuffer(hardware_buffer);
@ -55,7 +59,7 @@ void ImageExternalTextureVK::ProcessFrame(PaintContext& context,
}
auto texture_source = std::make_shared<impeller::AHBTextureSourceVK>(
impeller_context_, latest_hardware_buffer, hb_desc);
impeller_context_, latest_hardware_buffer, hb_desc.value());
if (!texture_source->IsValid()) {
CloseHardwareBuffer(hardware_buffer);
return;

View File

@ -9,7 +9,6 @@
#include <cstddef>
#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;

View File

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

View File

@ -12,7 +12,6 @@
#include <sstream>
#include <utility>
#include "flutter/fml/platform/android/ndk_helpers.h"
#include "include/android/SkImageAndroid.h"
#include "unicode/uchar.h"

View File

@ -10,10 +10,10 @@
#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"
#include "impeller/toolkit/android/choreographer.h"
namespace flutter {
@ -28,47 +28,35 @@ VsyncWaiterAndroid::~VsyncWaiterAndroid() = default;
// |VsyncWaiter|
void VsyncWaiterAndroid::AwaitVSync() {
switch (NDKHelpers::ChoreographerSupported()) {
case ChoreographerSupportStatus::kSupported32: {
auto* weak_this = new std::weak_ptr<VsyncWaiter>(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<VsyncWaiter>(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<std::chrono::nanoseconds>(time)
.time_since_epoch()
.count();
OnVsyncFromNDK(time_ns, weak_this);
});
} break;
case ChoreographerSupportStatus::kSupported64: {
auto* weak_this = new std::weak_ptr<VsyncWaiter>(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<VsyncWaiter>(shared_from_this());
jlong java_baton = reinterpret_cast<jlong>(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 and 32-bit
// devices.
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
jlong java_baton = reinterpret_cast<jlong>(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(

View File

@ -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,

View File

@ -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([