Allow embedders to specify a vsync waiter. (flutter/engine#7914)
Fixes https://github.com/flutter/flutter/issues/28240
This commit is contained in:
@@ -653,6 +653,8 @@ FILE: ../../../flutter/shell/platform/embedder/fixtures/a11y_main.dart
|
||||
FILE: ../../../flutter/shell/platform/embedder/fixtures/simple_main.dart
|
||||
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.cc
|
||||
FILE: ../../../flutter/shell/platform/embedder/platform_view_embedder.h
|
||||
FILE: ../../../flutter/shell/platform/embedder/vsync_waiter_embedder.cc
|
||||
FILE: ../../../flutter/shell/platform/embedder/vsync_waiter_embedder.h
|
||||
FILE: ../../../flutter/sky/packages/flutter_services/lib/empty.dart
|
||||
FILE: ../../../flutter/sky/tools/roll/patches/chromium/android_build.patch
|
||||
FILE: ../../../flutter/synchronization/pipeline.cc
|
||||
|
||||
@@ -52,4 +52,8 @@ void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time,
|
||||
});
|
||||
}
|
||||
|
||||
float VsyncWaiter::GetDisplayRefreshRate() const {
|
||||
return kUnknownRefreshRateFPS;
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
namespace shell {
|
||||
|
||||
constexpr float kUnknownRefreshRateFPS = 0.0;
|
||||
|
||||
class VsyncWaiter : public std::enable_shared_from_this<VsyncWaiter> {
|
||||
public:
|
||||
using Callback = std::function<void(fml::TimePoint frame_start_time,
|
||||
@@ -25,22 +23,35 @@ class VsyncWaiter : public std::enable_shared_from_this<VsyncWaiter> {
|
||||
|
||||
void AsyncWaitForVsync(Callback callback);
|
||||
|
||||
void FireCallback(fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time);
|
||||
static constexpr float kUnknownRefreshRateFPS = 0.0;
|
||||
|
||||
// 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; }
|
||||
// Return kUnknownRefreshRateFPS if the refresh rate is unkonwn.
|
||||
virtual float GetDisplayRefreshRate() const;
|
||||
|
||||
protected:
|
||||
// On some backends, the |FireCallback| needs to be made from a static C
|
||||
// method.
|
||||
friend class VsyncWaiterAndroid;
|
||||
friend class VsyncWaiterEmbedder;
|
||||
|
||||
const blink::TaskRunners task_runners_;
|
||||
std::mutex callback_mutex_;
|
||||
Callback callback_;
|
||||
|
||||
VsyncWaiter(blink::TaskRunners task_runners);
|
||||
|
||||
// Implementations are meant to override this method and arm their vsync
|
||||
// latches when in response to this invocation. On vsync, they are meant to
|
||||
// invoke the |FireCallback| method once (and only once) with the appropriate
|
||||
// arguments.
|
||||
virtual void AwaitVSync() = 0;
|
||||
|
||||
void FireCallback(fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time);
|
||||
|
||||
private:
|
||||
std::mutex callback_mutex_;
|
||||
Callback callback_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiter);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
namespace shell {
|
||||
|
||||
static void ConsumePendingCallback(jlong java_baton,
|
||||
fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time);
|
||||
|
||||
static fml::jni::ScopedJavaGlobalRef<jclass>* g_vsync_waiter_class = nullptr;
|
||||
static jmethodID g_async_wait_for_vsync_method_ = nullptr;
|
||||
|
||||
@@ -30,8 +26,7 @@ VsyncWaiterAndroid::~VsyncWaiterAndroid() = default;
|
||||
|
||||
// |shell::VsyncWaiter|
|
||||
void VsyncWaiterAndroid::AwaitVSync() {
|
||||
std::weak_ptr<VsyncWaiter>* weak_this =
|
||||
new std::weak_ptr<VsyncWaiter>(shared_from_this());
|
||||
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]() {
|
||||
@@ -43,11 +38,25 @@ void VsyncWaiterAndroid::AwaitVSync() {
|
||||
});
|
||||
}
|
||||
|
||||
static void OnNativeVsync(JNIEnv* env,
|
||||
jclass jcaller,
|
||||
jlong frameTimeNanos,
|
||||
jlong frameTargetTimeNanos,
|
||||
jlong java_baton) {
|
||||
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");
|
||||
return env->GetStaticFloatField(clazz, fid);
|
||||
}
|
||||
|
||||
// static
|
||||
void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env,
|
||||
jclass jcaller,
|
||||
jlong frameTimeNanos,
|
||||
jlong frameTargetTimeNanos,
|
||||
jlong java_baton) {
|
||||
auto frame_time = fml::TimePoint::FromEpochDelta(
|
||||
fml::TimeDelta::FromNanoseconds(frameTimeNanos));
|
||||
auto target_time = fml::TimePoint::FromEpochDelta(
|
||||
@@ -56,6 +65,21 @@ static void OnNativeVsync(JNIEnv* env,
|
||||
ConsumePendingCallback(java_baton, frame_time, target_time);
|
||||
}
|
||||
|
||||
// static
|
||||
void VsyncWaiterAndroid::ConsumePendingCallback(
|
||||
jlong java_baton,
|
||||
fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time) {
|
||||
auto* weak_this = reinterpret_cast<std::weak_ptr<VsyncWaiter>*>(java_baton);
|
||||
auto shared_this = weak_this->lock();
|
||||
delete weak_this;
|
||||
|
||||
if (shared_this) {
|
||||
shared_this->FireCallback(frame_start_time, frame_target_time);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool VsyncWaiterAndroid::Register(JNIEnv* env) {
|
||||
static const JNINativeMethod methods[] = {{
|
||||
.name = "nativeOnVsync",
|
||||
@@ -81,32 +105,4 @@ 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) {
|
||||
auto* weak_this = reinterpret_cast<std::weak_ptr<VsyncWaiter>*>(java_baton);
|
||||
auto shared_this = weak_this->lock();
|
||||
delete weak_this;
|
||||
|
||||
if (shared_this) {
|
||||
shared_this->FireCallback(frame_start_time, frame_target_time);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#define SHELL_PLATFORM_ANDROID_VSYNC_WAITER_ANDROID_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/fml/memory/weak_ptr.h"
|
||||
#include "flutter/shell/common/vsync_waiter.h"
|
||||
|
||||
namespace shell {
|
||||
@@ -27,6 +28,16 @@ class VsyncWaiterAndroid final : public VsyncWaiter {
|
||||
// |shell::VsyncWaiter|
|
||||
void AwaitVSync() override;
|
||||
|
||||
static void OnNativeVsync(JNIEnv* env,
|
||||
jclass jcaller,
|
||||
jlong frameTimeNanos,
|
||||
jlong frameTargetTimeNanos,
|
||||
jlong java_baton);
|
||||
|
||||
static void ConsumePendingCallback(jlong java_baton,
|
||||
fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time);
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterAndroid);
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ source_set("embedder") {
|
||||
"embedder_surface_software.h",
|
||||
"platform_view_embedder.cc",
|
||||
"platform_view_embedder.h",
|
||||
"vsync_waiter_embedder.cc",
|
||||
"vsync_waiter_embedder.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
|
||||
@@ -478,9 +478,18 @@ FlutterEngineResult FlutterEngineRun(size_t version,
|
||||
};
|
||||
}
|
||||
|
||||
shell::VsyncWaiterEmbedder::VsyncCallback vsync_callback = nullptr;
|
||||
if (SAFE_ACCESS(args, vsync_callback, nullptr) != nullptr) {
|
||||
vsync_callback = [ptr = args->vsync_callback, user_data](intptr_t baton) {
|
||||
return ptr(user_data, baton);
|
||||
};
|
||||
}
|
||||
|
||||
shell::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table = {
|
||||
update_semantics_nodes_callback, update_semantics_custom_actions_callback,
|
||||
platform_message_response_callback, // platform_message_response_callback
|
||||
update_semantics_nodes_callback, //
|
||||
update_semantics_custom_actions_callback, //
|
||||
platform_message_response_callback, //
|
||||
vsync_callback, //
|
||||
};
|
||||
|
||||
auto on_create_platform_view = InferPlatformViewCreationCallback(
|
||||
@@ -810,6 +819,28 @@ FlutterEngineResult FlutterEngineDispatchSemanticsAction(
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
FlutterEngineResult FlutterEngineOnVsync(FlutterEngine engine,
|
||||
intptr_t baton,
|
||||
uint64_t frame_start_time_nanos,
|
||||
uint64_t frame_target_time_nanos) {
|
||||
if (engine == nullptr) {
|
||||
return kInvalidArguments;
|
||||
}
|
||||
|
||||
auto start_time = fml::TimePoint::FromEpochDelta(
|
||||
fml::TimeDelta::FromNanoseconds(frame_start_time_nanos));
|
||||
|
||||
auto target_time = fml::TimePoint::FromEpochDelta(
|
||||
fml::TimeDelta::FromNanoseconds(frame_target_time_nanos));
|
||||
|
||||
if (!reinterpret_cast<shell::EmbedderEngine*>(engine)->OnVsyncEvent(
|
||||
baton, start_time, target_time)) {
|
||||
return kInternalInconsistency;
|
||||
}
|
||||
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
void FlutterEngineTraceEventDurationBegin(const char* name) {
|
||||
fml::tracing::TraceEvent0("flutter", name);
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@ typedef bool (*TextureFrameCallback)(void* /* user data */,
|
||||
size_t /* width */,
|
||||
size_t /* height */,
|
||||
FlutterOpenGLTexture* /* texture out */);
|
||||
typedef void (*VsyncCallback)(void* /* user data */, intptr_t /* baton */);
|
||||
|
||||
typedef struct {
|
||||
// The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig).
|
||||
@@ -509,6 +510,14 @@ typedef struct {
|
||||
// Flutter application (such as compiled shader programs used by Skia).
|
||||
// This is optional. The string must be NULL terminated.
|
||||
const char* persistent_cache_path;
|
||||
// A callback that gets invoked by the engine when it attempts to wait for
|
||||
// a platform vsync event. The engine will give the platform a baton that
|
||||
// needs to be returned back to the engine via |FlutterEngineOnVsync|. All
|
||||
// vsync operations must occur on the thread that made the call to
|
||||
// |FlutterEngineRun|. All batons must be retured to the engine before
|
||||
// initializing a |FlutterEngineShutdown|. Not doing the same will result in a
|
||||
// memory leak.
|
||||
VsyncCallback vsync_callback;
|
||||
} FlutterProjectArgs;
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@@ -596,6 +605,14 @@ FlutterEngineResult FlutterEngineDispatchSemanticsAction(
|
||||
const uint8_t* data,
|
||||
size_t data_length);
|
||||
|
||||
// Notify the engine that a vsync event occured. A baton passed to the platform
|
||||
// via the vsync callback must be returned.
|
||||
FLUTTER_EXPORT
|
||||
FlutterEngineResult FlutterEngineOnVsync(FlutterEngine engine,
|
||||
intptr_t baton,
|
||||
uint64_t frame_start_time_nanos,
|
||||
uint64_t frame_target_time_nanos);
|
||||
|
||||
// A profiling utility. Logs a trace duration begin event to the timeline. If
|
||||
// the timeline is unavailable or disabled, this has no effect. Must be
|
||||
// balanced with an duration end event (via
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "flutter/shell/platform/embedder/embedder_engine.h"
|
||||
|
||||
#include "flutter/fml/make_copyable.h"
|
||||
#include "flutter/shell/platform/embedder/vsync_waiter_embedder.h"
|
||||
|
||||
namespace shell {
|
||||
|
||||
@@ -191,4 +192,15 @@ bool EmbedderEngine::DispatchSemanticsAction(int id,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmbedderEngine::OnVsyncEvent(intptr_t baton,
|
||||
fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time) {
|
||||
if (!IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return VsyncWaiterEmbedder::OnEmbedderVsync(baton, frame_start_time,
|
||||
frame_target_time);
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
|
||||
@@ -59,6 +59,10 @@ class EmbedderEngine {
|
||||
blink::SemanticsAction action,
|
||||
std::vector<uint8_t> args);
|
||||
|
||||
bool OnVsyncEvent(intptr_t baton,
|
||||
fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time);
|
||||
|
||||
private:
|
||||
const ThreadHost thread_host_;
|
||||
std::unique_ptr<Shell> shell_;
|
||||
|
||||
@@ -80,4 +80,15 @@ sk_sp<GrContext> PlatformViewEmbedder::CreateResourceContext() const {
|
||||
return embedder_surface_->CreateResourceContext();
|
||||
}
|
||||
|
||||
// |shell::PlatformView|
|
||||
std::unique_ptr<VsyncWaiter> PlatformViewEmbedder::CreateVSyncWaiter() {
|
||||
if (!platform_dispatch_table_.vsync_callback) {
|
||||
// Superclass implementation creates a timer based fallback.
|
||||
return PlatformView::CreateVSyncWaiter();
|
||||
}
|
||||
|
||||
return std::make_unique<VsyncWaiterEmbedder>(
|
||||
platform_dispatch_table_.vsync_callback, task_runners_);
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "flutter/shell/platform/embedder/embedder_surface.h"
|
||||
#include "flutter/shell/platform/embedder/embedder_surface_gl.h"
|
||||
#include "flutter/shell/platform/embedder/embedder_surface_software.h"
|
||||
#include "flutter/shell/platform/embedder/vsync_waiter_embedder.h"
|
||||
|
||||
namespace shell {
|
||||
|
||||
@@ -30,7 +31,8 @@ class PlatformViewEmbedder final : public PlatformView {
|
||||
UpdateSemanticsCustomActionsCallback
|
||||
update_semantics_custom_actions_callback; // optional
|
||||
PlatformMessageResponseCallback
|
||||
platform_message_response_callback; // optional
|
||||
platform_message_response_callback; // optional
|
||||
VsyncWaiterEmbedder::VsyncCallback vsync_callback; // optional
|
||||
};
|
||||
|
||||
// Creates a platform view that sets up an OpenGL rasterizer.
|
||||
@@ -68,6 +70,9 @@ class PlatformViewEmbedder final : public PlatformView {
|
||||
// |shell::PlatformView|
|
||||
sk_sp<GrContext> CreateResourceContext() const override;
|
||||
|
||||
// |shell::PlatformView|
|
||||
std::unique_ptr<VsyncWaiter> CreateVSyncWaiter() override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(PlatformViewEmbedder);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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/embedder/vsync_waiter_embedder.h"
|
||||
|
||||
namespace shell {
|
||||
|
||||
VsyncWaiterEmbedder::VsyncWaiterEmbedder(VsyncCallback vsync_callback,
|
||||
blink::TaskRunners task_runners)
|
||||
: VsyncWaiter(std::move(task_runners)), vsync_callback_(vsync_callback) {
|
||||
FML_DCHECK(vsync_callback_);
|
||||
}
|
||||
|
||||
VsyncWaiterEmbedder::~VsyncWaiterEmbedder() = default;
|
||||
|
||||
// |shell::VsyncWaiter|
|
||||
void VsyncWaiterEmbedder::AwaitVSync() {
|
||||
auto* weak_waiter = new std::weak_ptr<VsyncWaiter>(shared_from_this());
|
||||
vsync_callback_(reinterpret_cast<intptr_t>(weak_waiter));
|
||||
}
|
||||
|
||||
// static
|
||||
bool VsyncWaiterEmbedder::OnEmbedderVsync(intptr_t baton,
|
||||
fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time) {
|
||||
if (baton == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* weak_waiter = reinterpret_cast<std::weak_ptr<VsyncWaiter>*>(baton);
|
||||
auto strong_waiter = weak_waiter->lock();
|
||||
delete weak_waiter;
|
||||
|
||||
if (!strong_waiter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
strong_waiter->FireCallback(frame_start_time, frame_target_time);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace shell
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 SHELL_PLATFORM_EMBEDDER_VSYNC_WAITER_EMBEDDER_H_
|
||||
#define SHELL_PLATFORM_EMBEDDER_VSYNC_WAITER_EMBEDDER_H_
|
||||
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/shell/common/vsync_waiter.h"
|
||||
|
||||
namespace shell {
|
||||
|
||||
class VsyncWaiterEmbedder final : public VsyncWaiter {
|
||||
public:
|
||||
using VsyncCallback = std::function<void(intptr_t)>;
|
||||
|
||||
VsyncWaiterEmbedder(VsyncCallback callback, blink::TaskRunners task_runners);
|
||||
|
||||
~VsyncWaiterEmbedder() override;
|
||||
|
||||
static bool OnEmbedderVsync(intptr_t baton,
|
||||
fml::TimePoint frame_start_time,
|
||||
fml::TimePoint frame_target_time);
|
||||
|
||||
private:
|
||||
const VsyncCallback vsync_callback_;
|
||||
|
||||
// |shell::VsyncWaiter|
|
||||
void AwaitVSync() override;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterEmbedder);
|
||||
};
|
||||
|
||||
} // namespace shell
|
||||
|
||||
#endif // SHELL_PLATFORM_EMBEDDER_VSYNC_WAITER_EMBEDDER_H_
|
||||
Reference in New Issue
Block a user