Skip to content

Refactor rendering selection logic for Android Embedder #163792

@matanlurey

Description

@matanlurey

It would be nice for our rendering selection logic for the Android embedder to be better tested/testable:

AndroidRenderingAPI FlutterMain::SelectedRenderingAPI(
const flutter::Settings& settings) {
if (settings.enable_software_rendering) {
FML_CHECK(!settings.enable_impeller)
<< "Impeller does not support software rendering. Either disable "
"software rendering or disable impeller.";
return AndroidRenderingAPI::kSoftware;
}
constexpr AndroidRenderingAPI kVulkanUnsupportedFallback =
AndroidRenderingAPI::kImpellerOpenGLES;
// Debug/Profile only functionality for testing a specific
// backend configuration.
#ifndef FLUTTER_RELEASE
if (settings.requested_rendering_backend == "opengles" &&
settings.enable_impeller) {
return AndroidRenderingAPI::kImpellerOpenGLES;
}
if (settings.requested_rendering_backend == "vulkan" &&
settings.enable_impeller) {
return AndroidRenderingAPI::kImpellerVulkan;
}
#endif
if (settings.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) {
return kVulkanUnsupportedFallback;
}
char product_model[PROP_VALUE_MAX];
__system_property_get("ro.product.model", product_model);
if (IsDeviceEmulator(product_model)) {
// Avoid using Vulkan on known emulators.
return kVulkanUnsupportedFallback;
}
__system_property_get("ro.product.board", product_model);
if (IsKnownBadSOC(product_model)) {
// Avoid using Vulkan on known bad SoCs.
return kVulkanUnsupportedFallback;
}
// Determine if Vulkan is supported by creating a Vulkan context and
// checking if it is valid.
impeller::ScopedValidationDisable disable_validation;
auto vulkan_backend = std::make_unique<AndroidContextVKImpeller>(
AndroidContext::ContextSettings{.enable_validation = false,
.enable_gpu_tracing = false,
.quiet = true});
if (!vulkan_backend->IsValid()) {
return kVulkanUnsupportedFallback;
}
return AndroidRenderingAPI::kImpellerVulkan;
}
return AndroidRenderingAPI::kSkiaOpenGLES;
}

A few possible changes:

  • Add a AndroidRenderingAPI, AndroidDeviceState and AndroidRenderingSelector types
  • Add tests for the different case
  • Refactor the surrounding code to use this code only

I ran into wanting this in #163561.

A snippet as a starting place:

// 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_SHELL_PLATFORM_ANDROID_ANDROID_RENDERING_SELECTOR_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_RENDERING_SELECTOR_H_

#include <string>

namespace flutter {

/**
 * @brief A combination of targeted graphics API and Impeller support for
 * Android.
 */
enum class AndroidRenderingAPI {
  /**
   * @brief Software rendering backend for Skia.
   *
   * This rendering API is deprecated, and there is no replacement for Impeller.
   * @deprecated
   */
  kSkiaSoftware [[deprecated]],

  /**
   * @brief OpenGLES rendering backend for Skia.
   *
   * This rendering API is deprecated; an Impeller-based backend is used by
   * default unless opted-out manually (either using `flutter run
   * --no-enable-impeller` or `AndroidManifest.xml`).
   *
   * @see https://docs.flutter.dev/perf/impeller#android
   * @deprecated
   */
  kSkiaOpenGLES [[deprecated]],

  /**
   * @brief Vulkan rendering backend for Impeller.
   *
   * This API should be used if:
   * - The current Android API level is at least 29+;
   * - Not running on a known emulator (there are performance problems);
   * - Not running on a known bad SoC (Vulkan is not working or working
   * properly);
   * - A Vulkan context can be successfully created.
   *
   * Or, if the requested rendering backend is explicitly Vulkan (a debug
   * and profile-mode only feature for testing and debugging) by editing
   * `AndroidManifest.xml`:
   * `<meta-data android:name="io.flutter.embedding.android.ImpellerBackend"
   * android:value="vulkan />`
   */
  kImpellerVulkan,

  /**
   * @brief OpenGLES rendering backend for Impeller.
   *
   * This API is used as a fallback when `kImpellerVulkan` cannot be used or if
   * the requested rendering backend is explicitly OpenGLES (a debug and
   * profile-mode only feature for testing and debugging) by editing
   * `AndroidManifest.xml`:
   * `<meta-data android:name="io.flutter.embedding.android.ImpellerBackend"
   * android:value="opengles />`
   */
  kImpellerOpenGLES,
};

/**
 * @brief Used by `AndroidRenderingAPI` to determine an appropriate rendering
 * API.
 *
 * There are two ways to use this struct:
 * 1. Create it with user-defined state (i.e., for testing or to adapt another
 * source).
 * 2. Use `fromNdk`, to extract the current state from the current Android
 * device's NDK.
 */
struct AndroidDeviceState {
  /**
   * @brief The API level of the Android device.
   */
  int apiLevel;

  /**
   * @brief The product model of the Android device.
   */
  std::string_view productModel;

  /**
   * @brief The System on a Chip (SOC) board of the Android device.
   */
  std::string_view productSocBoard;

  /**
   * @brief Extracts the Android device state from the NDK.
   *
   * @return An initialized AndroidDeviceState struct.
   */
  static AndroidDeviceState fromNdk();
};

/**
 * @brief Determines which `AndroidRenderingAPI` to use, based on a variety of
 * factors.
 */
class AndroidRenderingSelector {
 public:
  ~AndroidRenderingSelector();

  AndroidRenderingAPI select(AndroidDeviceState options);

 private:
  AndroidRenderingSelector(const AndroidRenderingSelector&) = delete;
  AndroidRenderingSelector& operator=(const AndroidRenderingSelector&) = delete;
};

}  // namespace flutter

#endif  // FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_RENDERING_SELECTOR_H_

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: tech-debtTechnical debt, code quality, testing, etc.engineflutter/engine related. See also e: labels.platform-androidAndroid applications specificallyteam-androidOwned by Android platform teamtriaged-androidTriaged by Android platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions