diff --git a/.ci.yaml b/.ci.yaml index 1e0a19ec8790d..cb313b499ce7f 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -88,11 +88,15 @@ targets: - name: Linux linux_android_emulator_tests enabled_branches: - main - recipe: engine_v2/builder - presubmit: false + recipe: engine_v2/engine_v2 + bringup: true properties: config_name: linux_android_emulator timeout: 60 + runIf: + - .ci.yaml + - lib/ui/** + - shell/platform/android/** - name: Linux builder_cache enabled_branches: diff --git a/ci/builders/linux_android_emulator.json b/ci/builders/linux_android_emulator.json new file mode 100644 index 0000000000000..735ee538747c4 --- /dev/null +++ b/ci/builders/linux_android_emulator.json @@ -0,0 +1,111 @@ +{ + "builds": [ + { + "drone_dimensions": [ + "device_type=none", + "os=Linux", + "kvm=1", + "cores=8" + ], + "gn": [ + "--android", + "--android-cpu=x64", + "--no-lto", + "--rbe", + "--no-goma" + ], + "name": "android_debug_x64", + "ninja": { + "config": "android_debug_x64", + "targets": [ + "flutter/shell/platform/android:flutter_shell_native_unittests", + "flutter/testing/scenario_app" + ] + }, + "tests": [ + { + "language": "python3", + "name": "Android Unit Tests", + "test_dependencies": [ + { + "dependency": "android_virtual_device", + "version": "34" + } + ], + "contexts": [ + "android_virtual_device" + ], + "script": "flutter/testing/run_tests.py", + "parameters": [ + "--android-variant", + "android_debug_x64", + "--type", + "android" + ] + }, + { + "language": "bash", + "name": "Scenario App Integration Tests", + "test_dependencies": [ + { + "dependency": "android_virtual_device", + "version": "34" + } + ], + "contexts": [ + "android_virtual_device" + ], + "script": "flutter/testing/scenario_app/run_android_tests.sh", + "parameters": [ + "android_debug_x64" + ] + } + ] + }, + { + "drone_dimensions": [ + "device_type=none", + "os=Linux", + "kvm=1", + "cores=8" + ], + "gn": [ + "--android", + "--android-cpu=x86", + "--no-lto", + "--rbe", + "--no-goma" + ], + "name": "android_debug_x86", + "ninja": { + "config": "android_debug_x86", + "targets": [ + "flutter/shell/platform/android:flutter_shell_native_unittests", + "flutter/testing/scenario_app" + ] + }, + "tests": [ + { + "language": "python3", + "name": "Android Unit Tests (API 28)", + "test_dependencies": [ + { + "dependency": "android_virtual_device", + "version": "28" + } + ], + "contexts": [ + "android_virtual_device" + ], + "script": "flutter/testing/run_tests.py", + "parameters": [ + "--android-variant", + "android_debug_x86", + "--type", + "android" + ] + } + ] + } + ] +} diff --git a/ci/builders/standalone/linux_android_emulator.json b/ci/builders/standalone/linux_android_emulator.json deleted file mode 100644 index 307e47905fae9..0000000000000 --- a/ci/builders/standalone/linux_android_emulator.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "drone_dimensions": [ - "device_type=none", - "os=Linux" - ], - "gn": [ - "--android", - "--android-cpu=x64", - "--no-lto", - "--rbe", - "--no-goma" - ], - "name": "android_debug_x64", - "ninja": { - "config": "android_debug_x64", - "targets": [ - "flutter/shell/platform/android:flutter_shell_native_unittests", - "flutter/testing/scenario_app" - ] - }, - "tests": [ - { - "language": "python3", - "name": "Android Unit Tests", - "test_dependencies": [ - { - "dependency": "android_virtual_device", - "version": "34" - } - ], - "contexts": [ - "android_virtual_device" - ], - "script": "flutter/testing/run_tests.py", - "parameters": [ - "--android-variant", - "android_debug_x64", - "--type", - "android" - ] - }, - { - "language": "bash", - "name": "Scenario App Integration Tests", - "test_dependencies": [ - { - "dependency": "android_virtual_device", - "version": "34" - } - ], - "contexts": [ - "android_virtual_device" - ], - "script": "flutter/testing/scenario_app/run_android_tests.sh", - "parameters": [ - "android_debug_x64" - ] - } - ] -} diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 148ff2d6aedc6..04a4be99a0e27 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -269,6 +269,7 @@ ../../../flutter/shell/platform/android/flutter_shell_native_unittests.cc ../../../flutter/shell/platform/android/jni/jni_mock_unittest.cc ../../../flutter/shell/platform/android/platform_view_android_delegate/platform_view_android_delegate_unittests.cc +../../../flutter/shell/platform/android/platform_view_android_unittests.cc ../../../flutter/shell/platform/android/test ../../../flutter/shell/platform/android/test_runner ../../../flutter/shell/platform/common/accessibility_bridge_unittests.cc diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 5d8458973481f..10aa924ccca41 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -47,6 +47,7 @@ executable("flutter_shell_native_unittests") { "android_shell_holder_unittests.cc", "apk_asset_provider_unittests.cc", "flutter_shell_native_unittests.cc", + "platform_view_android_unittests.cc", ] public_configs = [ "//flutter:config" ] deps = [ diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index d5854c530f02b..ba979963e51e9 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -4,6 +4,7 @@ #include "flutter/shell/platform/android/platform_view_android.h" +#include #include #include @@ -33,6 +34,8 @@ namespace flutter { +constexpr int kMinimumAndroidApiLevelForVulkan = 29; + AndroidSurfaceFactoryImpl::AndroidSurfaceFactoryImpl( const std::shared_ptr& context, bool enable_impeller) @@ -73,9 +76,30 @@ static std::shared_ptr CreateAndroidContext( bool enable_vulkan_validation, bool enable_opengl_gpu_tracing) { if (use_software_rendering) { + FML_DCHECK(!enable_impeller); return std::make_shared(AndroidRenderingAPI::kSoftware); } if (enable_impeller) { + // Vulkan must only be used on API level 29+, as older API levels do not + // have requisite features to support platform views. + // + // Even if this check returns true, Impeller may determine it cannot use + // Vulkan for some other reason, such as a missing required extension or + // feature. + int api_level = android_get_device_api_level(); + if (api_level < kMinimumAndroidApiLevelForVulkan) { + if (impeller_backend.value_or("") == "vulkan") { + FML_LOG(WARNING) + << "Impeller Vulkan requested, but detected Android API level " + << api_level + << " does not support required features for Vulkan with platform " + "views. Falling back to OpenGLES."; + } + return std::make_unique( + std::make_unique(), + enable_opengl_gpu_tracing); + } + // Default value is Vulkan with GLES fallback. AndroidRenderingAPI backend = AndroidRenderingAPI::kAutoselect; if (impeller_backend.has_value()) { diff --git a/shell/platform/android/platform_view_android_unittests.cc b/shell/platform/android/platform_view_android_unittests.cc new file mode 100644 index 0000000000000..c757c71001508 --- /dev/null +++ b/shell/platform/android/platform_view_android_unittests.cc @@ -0,0 +1,133 @@ +// 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/platform_view_android.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "shell/platform/android/context/android_context.h" +#include "third_party/googletest/googlemock/include/gmock/gmock-nice-strict.h" + +namespace flutter { +namespace testing { + +using ::testing::NiceMock; +using ::testing::ReturnRef; + +namespace { +class MockPlatformViewDelegate : public PlatformView::Delegate { + public: + MOCK_METHOD(void, + OnPlatformViewCreated, + (std::unique_ptr surface), + (override)); + + MOCK_METHOD(void, OnPlatformViewDestroyed, (), (override)); + + MOCK_METHOD(void, OnPlatformViewScheduleFrame, (), (override)); + + MOCK_METHOD(void, + OnPlatformViewSetNextFrameCallback, + (const fml::closure& closure), + (override)); + + MOCK_METHOD(void, + OnPlatformViewSetViewportMetrics, + (int64_t view_id, const ViewportMetrics& metrics), + (override)); + + MOCK_METHOD(void, + OnPlatformViewDispatchPlatformMessage, + (std::unique_ptr message), + (override)); + + MOCK_METHOD(void, + OnPlatformViewDispatchPointerDataPacket, + (std::unique_ptr packet), + (override)); + + MOCK_METHOD(void, + OnPlatformViewDispatchSemanticsAction, + (int32_t id, SemanticsAction action, fml::MallocMapping args), + (override)); + + MOCK_METHOD(void, + OnPlatformViewSetSemanticsEnabled, + (bool enabled), + (override)); + + MOCK_METHOD(void, + OnPlatformViewSetAccessibilityFeatures, + (int32_t flags), + (override)); + + MOCK_METHOD(void, + OnPlatformViewRegisterTexture, + (std::shared_ptr texture), + (override)); + + MOCK_METHOD(void, + OnPlatformViewUnregisterTexture, + (int64_t texture_id), + (override)); + + MOCK_METHOD(void, + OnPlatformViewMarkTextureFrameAvailable, + (int64_t texture_id), + (override)); + + MOCK_METHOD(const Settings&, + OnPlatformViewGetSettings, + (), + (const, override)); + + MOCK_METHOD(void, + LoadDartDeferredLibrary, + (intptr_t loading_unit_id, + std::unique_ptr snapshot_data, + std::unique_ptr snapshot_instructions), + (override)); + + MOCK_METHOD(void, + LoadDartDeferredLibraryError, + (intptr_t loading_unit_id, + const std::string error_message, + bool transient), + (override)); + + MOCK_METHOD(void, + UpdateAssetResolverByType, + (std::unique_ptr updated_asset_resolver, + AssetResolver::AssetResolverType type), + (override)); +}; +} // namespace + +TEST(AndroidPlatformView, SelectsVulkanBasedOnApiLevel) { + Settings settings; + settings.enable_software_rendering = false; + settings.enable_impeller = true; + settings.impeller_backend = "vulkan"; + NiceMock mock_delegate; + EXPECT_CALL(mock_delegate, OnPlatformViewGetSettings) + .WillRepeatedly(ReturnRef(settings)); + + TaskRunners task_runners("test", nullptr, nullptr, nullptr, nullptr); + PlatformViewAndroid platform_view(/*delegate=*/mock_delegate, + /*task_runners=*/task_runners, + /*jni_facade=*/nullptr, + /*use_software_rendering=*/false, + /*msaa_samples=*/1); + auto context = platform_view.GetAndroidContext(); + EXPECT_TRUE(context); + int api_level = android_get_device_api_level(); + EXPECT_GT(api_level, 0); + if (api_level >= 29) { + EXPECT_TRUE(context->RenderingApi() == AndroidRenderingAPI::kVulkan); + } else { + EXPECT_TRUE(context->RenderingApi() == AndroidRenderingAPI::kOpenGLES); + } +} + +} // namespace testing +} // namespace flutter