Skip to content

Commit

Permalink
[Impeller] Gate Vulkan selection on API 29 (#48089)
Browse files Browse the repository at this point in the history
Platform Views on Android require API 29 when using Vulkan.

~~I'm not sure if I did the CI changes right, want to run it here and see. The added test needs to run on a device or emulator with API 29 or above and API 28 or below to be valid.~~

Makes the following changes to CI:

- Runs the emulator tests on presubmit, but only if `shell/platform/android/**` or `lib/ui/**` or `.ci.yaml` changes.
- Changes the emulator tests to be a regular engine_v2 test that spawns an x86 build. Unfortunately, the older API level emulators are only available for x86, not x86_64, and you cannot run the binary we create unless it matches the ABI.
- Runs the new test on both API 28 and API 34.

Fixes flutter/flutter#132984

/cc @johnmccutchan fyi
  • Loading branch information
dnfield authored Nov 16, 2023
1 parent 3b7621b commit 27fe2c3
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 62 deletions.
8 changes: 6 additions & 2 deletions .ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
111 changes: 111 additions & 0 deletions ci/builders/linux_android_emulator.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
]
}
]
}
60 changes: 0 additions & 60 deletions ci/builders/standalone/linux_android_emulator.json

This file was deleted.

1 change: 1 addition & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
24 changes: 24 additions & 0 deletions shell/platform/android/platform_view_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "flutter/shell/platform/android/platform_view_android.h"

#include <android/api-level.h>
#include <memory>
#include <utility>

Expand Down Expand Up @@ -33,6 +34,8 @@

namespace flutter {

constexpr int kMinimumAndroidApiLevelForVulkan = 29;

AndroidSurfaceFactoryImpl::AndroidSurfaceFactoryImpl(
const std::shared_ptr<AndroidContext>& context,
bool enable_impeller)
Expand Down Expand Up @@ -73,9 +76,30 @@ static std::shared_ptr<flutter::AndroidContext> CreateAndroidContext(
bool enable_vulkan_validation,
bool enable_opengl_gpu_tracing) {
if (use_software_rendering) {
FML_DCHECK(!enable_impeller);
return std::make_shared<AndroidContext>(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<AndroidContextGLImpeller>(
std::make_unique<impeller::egl::Display>(),
enable_opengl_gpu_tracing);
}

// Default value is Vulkan with GLES fallback.
AndroidRenderingAPI backend = AndroidRenderingAPI::kAutoselect;
if (impeller_backend.has_value()) {
Expand Down
133 changes: 133 additions & 0 deletions shell/platform/android/platform_view_android_unittests.cc
Original file line number Diff line number Diff line change
@@ -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> 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<PlatformMessage> message),
(override));

MOCK_METHOD(void,
OnPlatformViewDispatchPointerDataPacket,
(std::unique_ptr<PointerDataPacket> 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> 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<const fml::Mapping> snapshot_data,
std::unique_ptr<const fml::Mapping> 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<AssetResolver> 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<MockPlatformViewDelegate> 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

0 comments on commit 27fe2c3

Please sign in to comment.