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
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public void loadLibrary() {

System.loadLibrary("flutter");
FlutterJNI.loadLibraryCalled = true;
nativeSetRefreshRateFPS(refreshRateFPS);
}

private static boolean loadLibraryCalled = false;
Expand Down Expand Up @@ -227,6 +228,8 @@ public static void setRefreshRateFPS(float refreshRateFPS) {

private static boolean setRefreshRateFPSCalled = false;

private static native void nativeSetRefreshRateFPS(float refreshRateFPS);

// TODO(mattcarroll): add javadocs
public static void setAsyncWaitForVsyncDelegate(@Nullable AsyncWaitForVsyncDelegate delegate) {
asyncWaitForVsyncDelegate = delegate;
Expand Down
89 changes: 76 additions & 13 deletions shell/platform/android/vsync_waiter_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,85 @@
#include "flutter/fml/size.h"
#include "flutter/fml/trace_event.h"

#include <dlfcn.h>
#include <time.h>
#include <chrono>
#include <ctime>

namespace flutter {

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

VsyncWaiterAndroid::VsyncWaiterAndroid(flutter::TaskRunners task_runners)
: VsyncWaiter(std::move(task_runners)) {}
: VsyncWaiter(std::move(task_runners)) {
void* libAndroid = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL);
if (libAndroid == nullptr) {
FML_LOG(ERROR) << "FATAL: cannot open libandroid.so: " << errno;
return;
}

mAChoreographer_getInstance =
reinterpret_cast<PFN_AChoreographer_getInstance>(
dlsym(libAndroid, "AChoreographer_getInstance"));

mAChoreographer_postFrameCallback =
reinterpret_cast<PFN_AChoreographer_postFrameCallback>(
dlsym(libAndroid, "AChoreographer_postFrameCallback"));

if (!mAChoreographer_getInstance || !mAChoreographer_postFrameCallback) {
FML_LOG(ERROR) << "FATAL: cannot get AChoreographer symbols";
return;
} else {
useAChoreographer_ = true;
}
}

VsyncWaiterAndroid::~VsyncWaiterAndroid() = default;

// |VsyncWaiter|
void VsyncWaiterAndroid::AwaitVSync() {
auto* weak_this = new std::weak_ptr<VsyncWaiter>(shared_from_this());
jlong java_baton = reinterpret_cast<jlong>(weak_this);
if (!useAChoreographer_) {
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 //
);
});
} else {
mAChoreographer_postFrameCallback(mAChoreographer_getInstance(),
VsyncWaiterAndroid::OnAChoreographerVsync,
reinterpret_cast<void*>(java_baton));
}
}

void VsyncWaiterAndroid::OnAChoreographerVsync(long frameTimeNanos,
void* java_baton) {
struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp);
auto now = tp.tv_sec * (1000 * 1000 * 1000) + tp.tv_nsec;
long delay = now - frameTimeNanos;
if (delay < 0) {
delay = 0;
}

auto frame_time =
fml::TimePoint::Now() - fml::TimeDelta::FromNanoseconds(delay);
auto target_time = frame_time + fml::TimeDelta::FromNanoseconds(
1000000000.0 / refreshRateFPS_);

ConsumePendingCallback(reinterpret_cast<jlong>(java_baton), frame_time,
target_time);
}

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 //
);
});
void VsyncWaiterAndroid::SetRefreshRateFPS(JNIEnv* env,
jobject jcaller,
jfloat refreshRateFPS) {
refreshRateFPS_ = static_cast<double>(refreshRateFPS);
}

// static
Expand Down Expand Up @@ -70,11 +127,17 @@ void VsyncWaiterAndroid::ConsumePendingCallback(

// static
bool VsyncWaiterAndroid::Register(JNIEnv* env) {
static const JNINativeMethod methods[] = {{
.name = "nativeOnVsync",
.signature = "(JJJ)V",
.fnPtr = reinterpret_cast<void*>(&OnNativeVsync),
}};
static const JNINativeMethod methods[] = {
{
.name = "nativeOnVsync",
.signature = "(JJJ)V",
.fnPtr = reinterpret_cast<void*>(&OnNativeVsync),
},
{
.name = "nativeSetRefreshRateFPS",
.signature = "(F)V",
.fnPtr = reinterpret_cast<void*>(&SetRefreshRateFPS),
}};

jclass clazz = env->FindClass("io/flutter/embedding/engine/FlutterJNI");

Expand Down
35 changes: 35 additions & 0 deletions shell/platform/android/vsync_waiter_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,29 @@

namespace flutter {

struct AChoreographer;
typedef struct AChoreographer AChoreographer;

/**
* Prototype of the function that is called when a new frame is being rendered.
* It's passed the time that the frame is being rendered as nanoseconds in the
* CLOCK_MONOTONIC time base, as well as the data pointer provided by the
* application that registered a callback. All callbacks that run as part of
* rendering a frame will observe the same frame time, so it should be used
* whenever events need to be synchronized (e.g. animations).
*/
typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);

// AChoreographer is supported from API 24. To allow compilation for minSDK < 24
// and still use AChoreographer for SDK >= 24 we need runtime support to call
// AChoreographer APIs.
using PFN_AChoreographer_getInstance = AChoreographer* (*)();

using PFN_AChoreographer_postFrameCallback =
void (*)(AChoreographer* choreographer,
AChoreographer_frameCallback callback,
void* data);

class VsyncWaiterAndroid final : public VsyncWaiter {
public:
static bool Register(JNIEnv* env);
Expand All @@ -32,11 +55,23 @@ class VsyncWaiterAndroid final : public VsyncWaiter {
jlong refreshPeriodNanos,
jlong java_baton);

static void OnAChoreographerVsync(long frameTimeNanos, void* java_baton);

static void SetRefreshRateFPS(JNIEnv* env,
jobject jcaller,
jfloat refreshRateFPS);

static void ConsumePendingCallback(jlong java_baton,
fml::TimePoint frame_start_time,
fml::TimePoint frame_target_time);

FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterAndroid);

private:
PFN_AChoreographer_getInstance mAChoreographer_getInstance = nullptr;
PFN_AChoreographer_postFrameCallback mAChoreographer_postFrameCallback =
nullptr;
bool useAChoreographer_ = false;
};

} // namespace flutter
Expand Down