diff --git a/runtime/service_protocol.cc b/runtime/service_protocol.cc index ea93a120facc7..42e4eb7528d17 100644 --- a/runtime/service_protocol.cc +++ b/runtime/service_protocol.cc @@ -30,6 +30,8 @@ const fml::StringView ServiceProtocol::kFlushUIThreadTasksExtensionName = "_flutter.flushUIThreadTasks"; const fml::StringView ServiceProtocol::kSetAssetBundlePathExtensionName = "_flutter.setAssetBundlePath"; +const fml::StringView ServiceProtocol::kGetDisplayRefreshRateExtensionName = + "_flutter.getDisplayRefreshRate"; static constexpr fml::StringView kViewIdPrefx = "_flutterView/"; static constexpr fml::StringView kListViewsExtensionName = "_flutter.listViews"; @@ -45,6 +47,7 @@ ServiceProtocol::ServiceProtocol() kRunInViewExtensionName, kFlushUIThreadTasksExtensionName, kSetAssetBundlePathExtensionName, + kGetDisplayRefreshRateExtensionName, }), handlers_mutex_(fml::SharedMutex::Create()) {} diff --git a/runtime/service_protocol.h b/runtime/service_protocol.h index 1ab1f9183d208..b514beaf55fcf 100644 --- a/runtime/service_protocol.h +++ b/runtime/service_protocol.h @@ -27,6 +27,7 @@ class ServiceProtocol { static const fml::StringView kRunInViewExtensionName; static const fml::StringView kFlushUIThreadTasksExtensionName; static const fml::StringView kSetAssetBundlePathExtensionName; + static const fml::StringView kGetDisplayRefreshRateExtensionName; class Handler { public: diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 4c281e1eceba7..19aaad5394f60 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -39,6 +39,10 @@ Animator::Animator(Delegate& delegate, Animator::~Animator() = default; +float Animator::GetDisplayRefreshRate() const { + return waiter_->GetDisplayRefreshRate(); +} + void Animator::Stop() { paused_ = true; } diff --git a/shell/common/animator.h b/shell/common/animator.h index 2d60475621562..4f75b8fb3cb4d 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -36,6 +36,8 @@ class Animator final { ~Animator(); + float GetDisplayRefreshRate() const; + void RequestFrame(bool regenerate_layer_tree = true); void Render(std::unique_ptr layer_tree); diff --git a/shell/common/engine.cc b/shell/common/engine.cc index dbc794dc0a443..56c55310e7f8b 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -69,6 +69,10 @@ Engine::Engine(Delegate& delegate, Engine::~Engine() = default; +float Engine::GetDisplayRefreshRate() const { + return animator_->GetDisplayRefreshRate(); +} + fml::WeakPtr Engine::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } diff --git a/shell/common/engine.h b/shell/common/engine.h index fd5671bc36648..2b72592575526 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -66,6 +66,8 @@ class Engine final : public blink::RuntimeDelegate { ~Engine() override; + float GetDisplayRefreshRate() const; + fml::WeakPtr GetWeakPtr() const; FML_WARN_UNUSED_RESULT diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 4adf55a3f6c74..1847c115f51bb 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -293,6 +293,11 @@ Shell::Shell(blink::TaskRunners task_runners, blink::Settings settings) task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolSetAssetBundlePath, this, std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_ + [blink::ServiceProtocol::kGetDisplayRefreshRateExtensionName.ToString()] = + {task_runners_.GetUITaskRunner(), + std::bind(&Shell::OnServiceProtocolGetDisplayRefreshRate, this, + std::placeholders::_1, std::placeholders::_2)}; } Shell::~Shell() { @@ -939,6 +944,16 @@ bool Shell::OnServiceProtocolFlushUIThreadTasks( return true; } +bool Shell::OnServiceProtocolGetDisplayRefreshRate( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + response.SetObject(); + response.AddMember("fps", engine_->GetDisplayRefreshRate(), + response.GetAllocator()); + return true; +} + // Service protocol handler bool Shell::OnServiceProtocolSetAssetBundlePath( const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, diff --git a/shell/common/shell.h b/shell/common/shell.h index 241823038c6a5..955d9e86c4c90 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -225,6 +225,11 @@ class Shell final : public PlatformView::Delegate, const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response); + // Service protocol handler + bool OnServiceProtocolGetDisplayRefreshRate( + const blink::ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + FML_DISALLOW_COPY_AND_ASSIGN(Shell); }; diff --git a/shell/common/vsync_waiter.h b/shell/common/vsync_waiter.h index 0e3012c2de95e..8af5b05abfda0 100644 --- a/shell/common/vsync_waiter.h +++ b/shell/common/vsync_waiter.h @@ -14,6 +14,8 @@ namespace shell { +constexpr float kUnknownRefreshRateFPS = 0.0; + class VsyncWaiter : public std::enable_shared_from_this { public: using Callback = std::function { void FireCallback(fml::TimePoint frame_start_time, fml::TimePoint frame_target_time); + // Get the display's maximum refresh rate in the unit of frame per second. + // Return 0.0 if the refresh rate is unkonwn. + virtual float GetDisplayRefreshRate() const { return 0.0; } + protected: const blink::TaskRunners task_runners_; std::mutex callback_mutex_; diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 759b2744cdd6a..a8fb3ff483450 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -806,6 +806,7 @@ private void updateViewportMetrics() { WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); float fps = wm.getDefaultDisplay().getRefreshRate(); VsyncWaiter.refreshPeriodNanos = (long) (1000000000.0 / fps); + VsyncWaiter.refreshRateFPS = fps; } // Called by native to update the semantics/accessibility tree. diff --git a/shell/platform/android/io/flutter/view/VsyncWaiter.java b/shell/platform/android/io/flutter/view/VsyncWaiter.java index 526d836167efd..cce4a96f868da 100644 --- a/shell/platform/android/io/flutter/view/VsyncWaiter.java +++ b/shell/platform/android/io/flutter/view/VsyncWaiter.java @@ -10,6 +10,10 @@ public class VsyncWaiter { // This estimate will be updated by FlutterView when it is attached to a Display. public static long refreshPeriodNanos = 1000000000 / 60; + // This should also be updated by FlutterView when it is attached to a Display. + // The initial value of 0.0 indicates unkonwn refresh rate. + public static float refreshRateFPS = 0.0f; + public static void asyncWaitForVsync(final long cookie) { Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() { @Override diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index 7d551c71143a8..bd8cea3600cb2 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -81,6 +81,22 @@ bool VsyncWaiterAndroid::Register(JNIEnv* env) { return env->RegisterNatives(clazz, methods, arraysize(methods)) == 0; } +float VsyncWaiterAndroid::GetDisplayRefreshRate() const { + JNIEnv* env = fml::jni::AttachCurrentThread(); + if (g_vsync_waiter_class == nullptr) { + return kUnknownRefreshRateFPS; + } + jclass clazz = g_vsync_waiter_class->obj(); + if (clazz == nullptr) { + return kUnknownRefreshRateFPS; + } + jfieldID fid = env->GetStaticFieldID(clazz, "refreshRateFPS", "F"); + // We can safely read this 32-bit float from Java in any thread because + // 32-bits read and write are guaranteed to be atomic: + // https://stackoverflow.com/questions/11459543/should-getters-and-setters-be-synchronized/11459616#11459616 + return env->GetStaticFloatField(clazz, fid); +} + static void ConsumePendingCallback(jlong java_baton, fml::TimePoint frame_start_time, fml::TimePoint frame_target_time) { diff --git a/shell/platform/android/vsync_waiter_android.h b/shell/platform/android/vsync_waiter_android.h index 46fa28fc48f2e..76cfbd73667f2 100644 --- a/shell/platform/android/vsync_waiter_android.h +++ b/shell/platform/android/vsync_waiter_android.h @@ -21,6 +21,8 @@ class VsyncWaiterAndroid final : public VsyncWaiter { ~VsyncWaiterAndroid() override; + float GetDisplayRefreshRate() const override; + private: // |shell::VsyncWaiter| void AwaitVSync() override;