blob: bfced030f5fe558e3bc657241423e6b820d02228 [file] [log] [blame]
// 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/vsync_waiter_android.h"
#include <cmath>
#include <utility>
#include "flutter/common/task_runners.h"
#include "flutter/fml/logging.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 "impeller/toolkit/android/choreographer.h"
namespace flutter {
static fml::jni::ScopedJavaGlobalRef<jclass>* g_vsync_waiter_class = nullptr;
static jmethodID g_async_wait_for_vsync_method_ = nullptr;
static std::atomic_uint g_refresh_rate_ = 60;
VsyncWaiterAndroid::VsyncWaiterAndroid(const flutter::TaskRunners& task_runners)
: VsyncWaiter(task_runners) {}
VsyncWaiterAndroid::~VsyncWaiterAndroid() = default;
// |VsyncWaiter|
void VsyncWaiterAndroid::AwaitVSync() {
if (impeller::android::Choreographer::IsAvailableOnPlatform()) {
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(), [weak_this]() {
const auto& choreographer =
impeller::android::Choreographer::GetInstance();
choreographer.PostFrameCallback([weak_this](auto time) {
auto time_ns =
std::chrono::time_point_cast<std::chrono::nanoseconds>(time)
.time_since_epoch()
.count();
OnVsyncFromNDK(time_ns, weak_this);
});
});
} else {
// TODO(99798): Remove it when we drop support for API level < 29 and 32-bit
// devices.
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
jlong java_baton = reinterpret_cast<jlong>(weak_this);
task_runners_.GetPlatformTaskRunner()->PostTask([java_baton]() {
JNIEnv* env = fml::jni::AttachCurrentThread();
env->CallStaticVoidMethod(g_vsync_waiter_class->obj(), //
g_async_wait_for_vsync_method_, //
java_baton //
);
});
}
}
// static
void VsyncWaiterAndroid::OnVsyncFromNDK(int64_t frame_nanos, void* data) {
auto frame_time = fml::TimePoint::FromEpochDelta(
fml::TimeDelta::FromNanoseconds(frame_nanos));
auto now = fml::TimePoint::Now();
if (frame_time > now) {
frame_time = now;
}
auto target_time = frame_time + fml::TimeDelta::FromNanoseconds(
1000000000.0 / g_refresh_rate_);
TRACE_EVENT2_INT("flutter", "PlatformVsync", "frame_start_time",
frame_time.ToEpochDelta().ToMicroseconds(),
"frame_target_time",
target_time.ToEpochDelta().ToMicroseconds());
auto* weak_this = reinterpret_cast<std::weak_ptr<VsyncWaiter>*>(data);
ConsumePendingCallback(weak_this, frame_time, target_time);
}
// static
void VsyncWaiterAndroid::OnVsyncFromJava(JNIEnv* env,
jclass jcaller,
jlong frameDelayNanos,
jlong refreshPeriodNanos,
jlong java_baton) {
auto frame_time =
fml::TimePoint::Now() - fml::TimeDelta::FromNanoseconds(frameDelayNanos);
auto target_time =
frame_time + fml::TimeDelta::FromNanoseconds(refreshPeriodNanos);
TRACE_EVENT2_INT("flutter", "PlatformVsync", "frame_start_time",
frame_time.ToEpochDelta().ToMicroseconds(),
"frame_target_time",
target_time.ToEpochDelta().ToMicroseconds());
auto* weak_this = reinterpret_cast<std::weak_ptr<VsyncWaiter>*>(java_baton);
ConsumePendingCallback(weak_this, frame_time, target_time);
}
// static
void VsyncWaiterAndroid::ConsumePendingCallback(
std::weak_ptr<VsyncWaiter>* weak_this,
fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time) {
auto shared_this = weak_this->lock();
delete weak_this;
if (shared_this) {
shared_this->FireCallback(frame_start_time, frame_target_time);
}
}
// static
void VsyncWaiterAndroid::OnUpdateRefreshRate(JNIEnv* env,
jclass jcaller,
jfloat refresh_rate) {
FML_DCHECK(refresh_rate > 0);
g_refresh_rate_ = static_cast<uint>(refresh_rate);
}
// static
bool VsyncWaiterAndroid::Register(JNIEnv* env) {
static const JNINativeMethod methods[] = {
{
.name = "nativeOnVsync",
.signature = "(JJJ)V",
.fnPtr = reinterpret_cast<void*>(&OnVsyncFromJava),
},
{
.name = "nativeUpdateRefreshRate",
.signature = "(F)V",
.fnPtr = reinterpret_cast<void*>(&OnUpdateRefreshRate),
}};
jclass clazz = env->FindClass("io/flutter/embedding/engine/FlutterJNI");
if (clazz == nullptr) {
return false;
}
g_vsync_waiter_class = new fml::jni::ScopedJavaGlobalRef<jclass>(env, clazz);
FML_CHECK(!g_vsync_waiter_class->is_null());
g_async_wait_for_vsync_method_ = env->GetStaticMethodID(
g_vsync_waiter_class->obj(), "asyncWaitForVsync", "(J)V");
FML_CHECK(g_async_wait_for_vsync_method_ != nullptr);
return env->RegisterNatives(clazz, methods, fml::size(methods)) == 0;
}
} // namespace flutter