Skip to content

Refactor rendering selection logic for Android Embedder #163792

Open

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_

Activity

  1. added
    c: tech-debtTechnical debt, code quality, testing, etc.
    engineflutter/engine related. See also e: labels.
    P2Important issues not at the top of the work list
    platform-androidAndroid applications specifically
    team-androidOwned by Android platform team
    on Feb 20, 2025
  2. self-assigned this
    on Feb 20, 2025
  3. matanlurey commented on Mar 19, 2025

    @matanlurey
    ContributorAuthor

    Unfortunately I'm focused elsewhere, maybe I'll come back to this at some point.

  4. removed their assignment
    on Mar 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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