Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,8 @@ FILE: ../../../flutter/shell/platform/android/AndroidManifest.xml
FILE: ../../../flutter/shell/platform/android/android_context_gl.cc
FILE: ../../../flutter/shell/platform/android/android_context_gl.h
FILE: ../../../flutter/shell/platform/android/android_context_gl_unittests.cc
FILE: ../../../flutter/shell/platform/android/android_display.cc
FILE: ../../../flutter/shell/platform/android/android_display.h
FILE: ../../../flutter/shell/platform/android/android_environment_gl.cc
FILE: ../../../flutter/shell/platform/android/android_environment_gl.h
FILE: ../../../flutter/shell/platform/android/android_exports.lst
Expand All @@ -791,6 +793,8 @@ FILE: ../../../flutter/shell/platform/android/apk_asset_provider.cc
FILE: ../../../flutter/shell/platform/android/apk_asset_provider.h
FILE: ../../../flutter/shell/platform/android/context/android_context.cc
FILE: ../../../flutter/shell/platform/android/context/android_context.h
FILE: ../../../flutter/shell/platform/android/display_refresh_listener.cc
FILE: ../../../flutter/shell/platform/android/display_refresh_listener.h
FILE: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder.cc
FILE: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder.h
FILE: ../../../flutter/shell/platform/android/external_view_embedder/external_view_embedder_unittests.cc
Expand Down
4 changes: 2 additions & 2 deletions shell/common/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ class Display {
explicit Display(double refresh_rate)
: display_id_({}), refresh_rate_(refresh_rate) {}

~Display() = default;
virtual ~Display() = default;

// Get the display's maximum refresh rate in the unit of frame per second.
// Return `kUnknownDisplayRefreshRate` if the refresh rate is unknown.
double GetRefreshRate() const { return refresh_rate_; }
virtual double GetRefreshRate() const { return refresh_rate_; }

/// Returns the `DisplayId` of the display.
std::optional<DisplayId> GetDisplayId() const { return display_id_; }
Expand Down
4 changes: 4 additions & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ source_set("flutter_shell_native_src") {
"$root_build_dir/flutter_icu/icudtl.o",
"android_context_gl.cc",
"android_context_gl.h",
"android_display.cc",
"android_display.h",
"android_environment_gl.cc",
"android_environment_gl.h",
"android_external_texture_gl.cc",
Expand All @@ -76,6 +78,8 @@ source_set("flutter_shell_native_src") {
"android_surface_software.h",
"apk_asset_provider.cc",
"apk_asset_provider.h",
"display_refresh_listener.cc",
"display_refresh_listener.h",
"flutter_main.cc",
"flutter_main.h",
"library_loader.cc",
Expand Down
16 changes: 16 additions & 0 deletions shell/platform/android/android_display.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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/android_display.h"

namespace flutter {

AndroidDisplay::AndroidDisplay(double refresh_rate)
: Display(refresh_rate), listener_(*this), refresh_rate_(refresh_rate) {}

void AndroidDisplay::OnDisplayRefreshUpdated(int64_t vsync_period_nanos) {
refresh_rate_ = 1000000000 / vsync_period_nanos;
}

} // namespace flutter
37 changes: 37 additions & 0 deletions shell/platform/android/android_display.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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_DISPLAY_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_DISPLAY_H_

#include <cstdint>

#include "flutter/fml/macros.h"
#include "flutter/shell/common/display.h"
#include "flutter/shell/platform/android/display_refresh_listener.h"

namespace flutter {

/// A |Display| that listens to refresh rate changes.
class AndroidDisplay : public Display, public DisplayRefreshListener::Delegate {
public:
explicit AndroidDisplay(double refresh_rate);
~AndroidDisplay() = default;

// |Display|
double GetRefreshRate() const override { return refresh_rate_; };

private:
// |DisplayRefreshListener::Delegate|
void OnDisplayRefreshUpdated(int64_t vsync_period_nanos) override;

DisplayRefreshListener listener_;
double refresh_rate_;

FML_DISALLOW_COPY_AND_ASSIGN(AndroidDisplay);
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_ANDROID_DISPLAY_H_
5 changes: 3 additions & 2 deletions shell/platform/android/android_shell_holder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "flutter/shell/common/rasterizer.h"
#include "flutter/shell/common/run_configuration.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/shell/platform/android/android_display.h"
#include "flutter/shell/platform/android/android_image_generator.h"
#include "flutter/shell/platform/android/context/android_context.h"
#include "flutter/shell/platform/android/platform_view_android.h"
Expand Down Expand Up @@ -61,7 +62,7 @@ AndroidShellHolder::AndroidShellHolder(
.enable_software_rendering // use software rendering
);
weak_platform_view = platform_view_android->GetWeakPtr();
auto display = Display(jni_facade->GetDisplayRefreshRate());
auto display = AndroidDisplay(jni_facade->GetDisplayRefreshRate());
shell.OnDisplayUpdates(DisplayUpdateType::kStartup, {display});
return platform_view_android;
};
Expand Down Expand Up @@ -209,7 +210,7 @@ std::unique_ptr<AndroidShellHolder> AndroidShellHolder::Spawn(
android_context // Android context
);
weak_platform_view = platform_view_android->GetWeakPtr();
auto display = Display(jni_facade->GetDisplayRefreshRate());
auto display = AndroidDisplay(jni_facade->GetDisplayRefreshRate());
shell.OnDisplayUpdates(DisplayUpdateType::kStartup, {display});
return platform_view_android;
};
Expand Down
79 changes: 79 additions & 0 deletions shell/platform/android/display_refresh_listener.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// 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 "display_refresh_listener.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/native_library.h"

namespace flutter {
namespace {
// Only avialalbe on API 24+
typedef void AChoreographer;

// Must be called from the Platform thread.
AChoreographer* GetAChoreographer() {
auto libandroid = fml::NativeLibrary::Create("libandroid.so");
FML_CHECK(libandroid);
auto get_instance_fn = libandroid->ResolveFunction<AChoreographer* (*)(void)>(
"AChoreographer_getInstance");
if (get_instance_fn) {
return get_instance_fn.value()();
}
return nullptr;
}

// Only available on API 30+
typedef void (*AChoreographer_refreshRateCallback)(int64_t vsyncPeriodNanos,
void* data);
void OnDisplayRefreshUpdated(int64_t vsyncPeriodNanos, void* data) {
auto* delegate = reinterpret_cast<DisplayRefreshListener::Delegate*>(data);
delegate->OnDisplayRefreshUpdated(vsyncPeriodNanos);
}

// The data parameter will not be accessed, but will serve as an identifier for
// unregistering.
void ListenToRefreshRateChanges(DisplayRefreshListener::Delegate* delegate) {
auto libandroid = fml::NativeLibrary::Create("libandroid.so");
FML_CHECK(libandroid);
auto register_refresh_rate_callback_fn = libandroid->ResolveFunction<void (*)(
AChoreographer*, AChoreographer_refreshRateCallback, void*)>(
"AChoreographer_registerRefreshRateCallback");

if (register_refresh_rate_callback_fn) {
auto choreographer = GetAChoreographer();
FML_CHECK(choreographer);
register_refresh_rate_callback_fn.value()(
choreographer, &OnDisplayRefreshUpdated, delegate);
}
}

// The data parmaeter must be the same as the pointer passed to
// ListenToRefreshRateChanges.
void StopListeningToRefreshRateChanges(
DisplayRefreshListener::Delegate* delegate) {
auto libandroid = fml::NativeLibrary::Create("libandroid.so");
FML_CHECK(libandroid);
auto unregister_refresh_rate_callback_fn =
libandroid->ResolveFunction<void (*)(
AChoreographer*, AChoreographer_refreshRateCallback, void*)>(
"AChoreographer_unregisterRefreshRateCallback");

if (unregister_refresh_rate_callback_fn) {
auto choreographer = GetAChoreographer();
FML_CHECK(choreographer);
unregister_refresh_rate_callback_fn.value()(
choreographer, &OnDisplayRefreshUpdated, delegate);
}
}
} // namespace

DisplayRefreshListener::DisplayRefreshListener(Delegate& delegate)
: delegate_(delegate) {
ListenToRefreshRateChanges(&delegate_);
}

DisplayRefreshListener::~DisplayRefreshListener() {
StopListeningToRefreshRateChanges(&delegate_);
}
} // namespace flutter
44 changes: 44 additions & 0 deletions shell/platform/android/display_refresh_listener.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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_DISPLAY_REFRESH_LISTENER_H_
#define FLUTTER_SHELL_PLATFORM_ANDROID_DISPLAY_REFRESH_LISTENER_H_

#include <cstdint>

#include "flutter/fml/macros.h"

namespace flutter {

/// A means to recieve updates when the display refresh rate updates on Android.
///
/// This class only works on API30+. On older versions, the
/// Delegate::OnDisplayRefreshUpdated will never be called. Instead, the
/// FlutterJNI#refreshRateFPS should be used for lower API versions. That field
/// has the disadvantage being out of sync with the current refresh rate, as it
/// is typically only updated on initialization.
///
/// This class must be constructed on the Platform task runner. Callbacks on the
/// delegate are safe to recieve on any task runner.
class DisplayRefreshListener {
public:
class Delegate {
public:
/// This method may be called on any task runner.
virtual void OnDisplayRefreshUpdated(int64_t vsync_period_nanos) = 0;
};

/// This class must be constructed on the Platform task runner.
explicit DisplayRefreshListener(Delegate& delegate);
~DisplayRefreshListener();

private:
Delegate& delegate_;

FML_DISALLOW_COPY_AND_ASSIGN(DisplayRefreshListener);
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_ANDROID_DISPLAY_REFRESH_LISTENER_H_
36 changes: 25 additions & 11 deletions shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
Original file line number Diff line number Diff line change
Expand Up @@ -216,17 +216,21 @@ public static String getObservatoryUri() {
return observatoryUri;
}

/**
* Notifies the engine about the refresh rate of the display when the API level is below 30.
*
* <p>For API 30 and above, this value is ignored.
*
* <p>Calling this method multiple times will update the refresh rate for the next vsync period.
* However, callers should avoid calling {@link android.view.Display#getRefreshRate} frequently,
* since it is expensive on some vendor implementations.
*
* @param refreshRateFPS The refresh rate in nanoseconds.
*/
public static void setRefreshRateFPS(float refreshRateFPS) {
if (FlutterJNI.setRefreshRateFPSCalled) {
Log.w(TAG, "FlutterJNI.setRefreshRateFPS called more than once");
}

FlutterJNI.refreshRateFPS = refreshRateFPS;
FlutterJNI.setRefreshRateFPSCalled = true;
}

private static boolean setRefreshRateFPSCalled = false;

// TODO(mattcarroll): add javadocs
public static void setAsyncWaitForVsyncDelegate(@Nullable AsyncWaitForVsyncDelegate delegate) {
asyncWaitForVsyncDelegate = delegate;
Expand All @@ -243,9 +247,20 @@ private static void asyncWaitForVsync(final long cookie) {
}
}

// TODO(mattcarroll): add javadocs
/**
* Notifies the engine that the Choreographer has signaled a vsync.
*
* @param frameDelayNanos The time in nanoseconds when the frame started being rendered,
* subtracted from the {@link System#nanoTime} timebase.
* @param fallbackRefreshPeriodNanos The display refresh period in nanoseconds. On API30 and
* above, this parameter will be ignored. For other API levels, a best estimate must be
* provided, e.g. the refresh rate at initialization of the application. Callers should avoid
* calling {@link android.view.Display#getRefreshRate} frequently, since it is expensive on
* some vendor implementations.
* @param cookie An opaque handle to the C++ VSyncWaiter object.
*/
public static native void nativeOnVsync(
long frameDelayNanos, long refreshPeriodNanos, long cookie);
long frameDelayNanos, long fallbackRefreshPeriodNanos, long cookie);

// TODO(mattcarroll): add javadocs
@NonNull
Expand Down Expand Up @@ -337,8 +352,7 @@ public long performNativeAttach(@NonNull FlutterJNI flutterJNI) {
* #attachToNative()}.
*
* <p>Static methods that should be only called once such as {@link #init(Context, String[],
* String, String, String, long)} or {@link #setRefreshRateFPS(float)} shouldn't be called again
* on the spawned FlutterJNI instance.
* String, String, String, long)} shouldn't be called again on the spawned FlutterJNI instance.
*/
@UiThread
@NonNull
Expand Down
2 changes: 0 additions & 2 deletions shell/platform/android/io/flutter/view/VsyncWaiter.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ private VsyncWaiter(float fps) {

public void init() {
FlutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);

// TODO(mattcarroll): look into moving FPS reporting to a plugin
FlutterJNI.setRefreshRateFPS(fps);
}
}
16 changes: 14 additions & 2 deletions shell/platform/android/vsync_waiter_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,23 @@

#include "flutter/common/task_runners.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/platform/android/jni_util.h"
#include "flutter/fml/platform/android/scoped_java_ref.h"
#include "flutter/fml/size.h"
#include "flutter/fml/trace_event.h"
#include "vsync_waiter_android.h"

namespace flutter {

static fml::jni::ScopedJavaGlobalRef<jclass>* g_vsync_waiter_class = nullptr;
static jmethodID g_async_wait_for_vsync_method_ = nullptr;

// static
std::optional<int64_t> VsyncWaiterAndroid::current_vsync_period_nanos_ = {};

VsyncWaiterAndroid::VsyncWaiterAndroid(flutter::TaskRunners task_runners)
: VsyncWaiter(std::move(task_runners)) {}
: VsyncWaiter(std::move(task_runners)), display_refresh_listener_(*this) {}

VsyncWaiterAndroid::~VsyncWaiterAndroid() = default;

Expand All @@ -38,6 +43,11 @@ void VsyncWaiterAndroid::AwaitVSync() {
});
}

// |DisplayRefreshListener::Delegate|
void VsyncWaiterAndroid::OnDisplayRefreshUpdated(int64_t vsync_period_nanos) {
current_vsync_period_nanos_ = vsync_period_nanos;
}

// static
void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env,
jclass jcaller,
Expand All @@ -49,7 +59,9 @@ void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env,
auto frame_time =
fml::TimePoint::Now() - fml::TimeDelta::FromNanoseconds(frameDelayNanos);
auto target_time =
frame_time + fml::TimeDelta::FromNanoseconds(refreshPeriodNanos);
frame_time +
fml::TimeDelta::FromNanoseconds(
current_vsync_period_nanos_.value_or(refreshPeriodNanos));

ConsumePendingCallback(java_baton, frame_time, target_time);
}
Expand Down
Loading