Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 10ff302

Browse files
authored
[android] set presentation time via eglPresentationTimeANDROID (#33881)
Attempt to reland: #29727 with some fixes.
1 parent c1cb617 commit 10ff302

9 files changed

+218
-9
lines changed

flow/surface_frame.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "flutter/common/graphics/gl_context_switch.h"
1212
#include "flutter/display_list/display_list_canvas_recorder.h"
1313
#include "flutter/fml/macros.h"
14+
#include "flutter/fml/time/time_point.h"
1415
#include "third_party/skia/include/core/SkCanvas.h"
1516
#include "third_party/skia/include/core/SkSurface.h"
1617

@@ -72,6 +73,10 @@ class SurfaceFrame {
7273
//
7374
// Corresponds to EGL_KHR_partial_update
7475
std::optional<SkIRect> buffer_damage;
76+
77+
// Time at which this frame is scheduled to be presented. This is a hint
78+
// that can be passed to the platform to drop queued frames.
79+
std::optional<fml::TimePoint> presentation_time;
7580
};
7681

7782
bool Submit();

shell/common/rasterizer.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,14 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe(
604604
}
605605

606606
SurfaceFrame::SubmitInfo submit_info;
607+
// TODO (https://github.com/flutter/flutter/issues/105596): this can be in
608+
// the past and might need to get snapped to future as this frame could have
609+
// been resubmitted. `presentation_time` on `submit_info` is not set in this
610+
// case.
611+
const auto presentation_time = frame_timings_recorder.GetVsyncTargetTime();
612+
if (presentation_time > fml::TimePoint::Now()) {
613+
submit_info.presentation_time = presentation_time;
614+
}
607615
if (damage) {
608616
submit_info.frame_damage = damage->GetFrameDamage();
609617
submit_info.buffer_damage = damage->GetBufferDamage();

shell/common/rasterizer_unittests.cc

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "flutter/shell/common/rasterizer.h"
88

99
#include <memory>
10+
#include <optional>
1011

1112
#include "flutter/flow/frame_timings.h"
1213
#include "flutter/fml/synchronization/count_down_latch.h"
@@ -777,7 +778,7 @@ TEST(RasterizerTest,
777778
framebuffer_info.supports_readback = true;
778779
return std::make_unique<SurfaceFrame>(
779780
/*surface=*/nullptr, framebuffer_info,
780-
/*submit_callback=*/[](const SurfaceFrame&, SkCanvas*) {
781+
/*submit_callback=*/[](const SurfaceFrame& frame, SkCanvas*) {
781782
return true;
782783
});
783784
}));
@@ -830,4 +831,152 @@ TEST(RasterizerTest,
830831
latch.Wait();
831832
}
832833

834+
TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) {
835+
std::string test_name =
836+
::testing::UnitTest::GetInstance()->current_test_info()->name();
837+
ThreadHost thread_host("io.flutter.test." + test_name + ".",
838+
ThreadHost::Type::Platform | ThreadHost::Type::RASTER |
839+
ThreadHost::Type::IO | ThreadHost::Type::UI);
840+
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
841+
thread_host.raster_thread->GetTaskRunner(),
842+
thread_host.ui_thread->GetTaskRunner(),
843+
thread_host.io_thread->GetTaskRunner());
844+
MockDelegate delegate;
845+
ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners));
846+
847+
fml::AutoResetWaitableEvent latch;
848+
std::unique_ptr<Rasterizer> rasterizer;
849+
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
850+
rasterizer = std::make_unique<Rasterizer>(delegate);
851+
latch.Signal();
852+
});
853+
latch.Wait();
854+
855+
const auto millis_16 = fml::TimeDelta::FromMilliseconds(16);
856+
const auto first_timestamp = fml::TimePoint::Now() + millis_16;
857+
auto second_timestamp = first_timestamp + millis_16;
858+
std::vector<fml::TimePoint> timestamps = {first_timestamp, second_timestamp};
859+
860+
int frames_submitted = 0;
861+
fml::CountDownLatch submit_latch(2);
862+
auto surface = std::make_unique<MockSurface>();
863+
ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true));
864+
ON_CALL(*surface, AcquireFrame(SkISize()))
865+
.WillByDefault(::testing::Invoke([&] {
866+
SurfaceFrame::FramebufferInfo framebuffer_info;
867+
framebuffer_info.supports_readback = true;
868+
return std::make_unique<SurfaceFrame>(
869+
/*surface=*/nullptr, framebuffer_info,
870+
/*submit_callback=*/[&](const SurfaceFrame& frame, SkCanvas*) {
871+
const auto pres_time = *frame.submit_info().presentation_time;
872+
const auto diff = pres_time - first_timestamp;
873+
int num_frames_submitted = frames_submitted++;
874+
EXPECT_EQ(diff.ToMilliseconds(),
875+
num_frames_submitted * millis_16.ToMilliseconds());
876+
submit_latch.CountDown();
877+
return true;
878+
});
879+
}));
880+
881+
ON_CALL(*surface, MakeRenderContextCurrent())
882+
.WillByDefault(::testing::Invoke(
883+
[] { return std::make_unique<GLContextDefaultResult>(true); }));
884+
885+
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
886+
rasterizer->Setup(std::move(surface));
887+
auto pipeline = std::make_shared<LayerTreePipeline>(/*depth=*/10);
888+
for (int i = 0; i < 2; i++) {
889+
auto layer_tree =
890+
std::make_unique<LayerTree>(/*frame_size=*/SkISize(),
891+
/*device_pixel_ratio=*/2.0f);
892+
auto layer_tree_item = std::make_unique<LayerTreeItem>(
893+
std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i]));
894+
PipelineProduceResult result =
895+
pipeline->Produce().Complete(std::move(layer_tree_item));
896+
EXPECT_TRUE(result.success);
897+
EXPECT_EQ(result.is_first_item, i == 0);
898+
}
899+
auto no_discard = [](LayerTree&) { return false; };
900+
// Although we only call 'Rasterizer::Draw' once, it will be called twice
901+
// finally because there are two items in the pipeline.
902+
rasterizer->Draw(pipeline, no_discard);
903+
});
904+
905+
submit_latch.Wait();
906+
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
907+
rasterizer.reset();
908+
latch.Signal();
909+
});
910+
latch.Wait();
911+
}
912+
913+
TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) {
914+
std::string test_name =
915+
::testing::UnitTest::GetInstance()->current_test_info()->name();
916+
ThreadHost thread_host("io.flutter.test." + test_name + ".",
917+
ThreadHost::Type::Platform | ThreadHost::Type::RASTER |
918+
ThreadHost::Type::IO | ThreadHost::Type::UI);
919+
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
920+
thread_host.raster_thread->GetTaskRunner(),
921+
thread_host.ui_thread->GetTaskRunner(),
922+
thread_host.io_thread->GetTaskRunner());
923+
MockDelegate delegate;
924+
ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners));
925+
926+
fml::AutoResetWaitableEvent latch;
927+
std::unique_ptr<Rasterizer> rasterizer;
928+
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
929+
rasterizer = std::make_unique<Rasterizer>(delegate);
930+
latch.Signal();
931+
});
932+
latch.Wait();
933+
934+
const auto millis_16 = fml::TimeDelta::FromMilliseconds(16);
935+
const auto first_timestamp = fml::TimePoint::Now() - millis_16;
936+
937+
fml::CountDownLatch submit_latch(1);
938+
auto surface = std::make_unique<MockSurface>();
939+
ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true));
940+
ON_CALL(*surface, AcquireFrame(SkISize()))
941+
.WillByDefault(::testing::Invoke([&] {
942+
SurfaceFrame::FramebufferInfo framebuffer_info;
943+
framebuffer_info.supports_readback = true;
944+
return std::make_unique<SurfaceFrame>(
945+
/*surface=*/nullptr, framebuffer_info,
946+
/*submit_callback=*/[&](const SurfaceFrame& frame, SkCanvas*) {
947+
const std::optional<fml::TimePoint> pres_time =
948+
frame.submit_info().presentation_time;
949+
EXPECT_EQ(pres_time, std::nullopt);
950+
submit_latch.CountDown();
951+
return true;
952+
});
953+
}));
954+
955+
ON_CALL(*surface, MakeRenderContextCurrent())
956+
.WillByDefault(::testing::Invoke(
957+
[] { return std::make_unique<GLContextDefaultResult>(true); }));
958+
959+
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
960+
rasterizer->Setup(std::move(surface));
961+
auto pipeline = std::make_shared<LayerTreePipeline>(/*depth=*/10);
962+
auto layer_tree = std::make_unique<LayerTree>(/*frame_size=*/SkISize(),
963+
/*device_pixel_ratio=*/2.0f);
964+
auto layer_tree_item = std::make_unique<LayerTreeItem>(
965+
std::move(layer_tree), CreateFinishedBuildRecorder(first_timestamp));
966+
PipelineProduceResult result =
967+
pipeline->Produce().Complete(std::move(layer_tree_item));
968+
EXPECT_TRUE(result.success);
969+
EXPECT_EQ(result.is_first_item, true);
970+
auto no_discard = [](LayerTree&) { return false; };
971+
rasterizer->Draw(pipeline, no_discard);
972+
});
973+
974+
submit_latch.Wait();
975+
thread_host.raster_thread->GetTaskRunner()->PostTask([&] {
976+
rasterizer.reset();
977+
latch.Signal();
978+
});
979+
latch.Wait();
980+
}
981+
833982
} // namespace flutter

shell/gpu/gpu_surface_gl_delegate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ struct GLPresentInfo {
2929
// Damage is a hint to compositor telling it which parts of front buffer
3030
// need to be updated
3131
const std::optional<SkIRect>& damage;
32+
33+
// Time at which this frame is scheduled to be presented. This is a hint
34+
// that can be passed to the platform to drop queued frames.
35+
std::optional<fml::TimePoint> presentation_time = std::nullopt;
3236
};
3337

3438
class GPUSurfaceGLDelegate {

shell/gpu/gpu_surface_gl_impeller.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGLImpeller::AcquireFrame(
6363
GLPresentInfo present_info = {
6464
.fbo_id = 0,
6565
.damage = std::nullopt,
66+
// TODO (https://github.com/flutter/flutter/issues/105597): wire-up
67+
// presentation time to impeller backend.
68+
.presentation_time = std::nullopt,
6669
};
6770
delegate->GLContextPresent(present_info);
6871
}

shell/gpu/gpu_surface_gl_skia.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,11 @@ bool GPUSurfaceGLSkia::PresentSurface(const SurfaceFrame& frame,
266266
onscreen_surface_->getCanvas()->flush();
267267
}
268268

269-
GLPresentInfo present_info = {fbo_id_, frame.submit_info().frame_damage};
269+
GLPresentInfo present_info = {
270+
.fbo_id = fbo_id_,
271+
.damage = frame.submit_info().frame_damage,
272+
.presentation_time = frame.submit_info().presentation_time,
273+
};
270274
if (!delegate_->GLContextPresent(present_info)) {
271275
return false;
272276
}

shell/platform/android/android_egl_surface.cc

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ void LogLastEGLError() {
6060
FML_LOG(ERROR) << "Unknown EGL Error";
6161
}
6262

63+
namespace {
64+
65+
static bool HasExtension(const char* extensions, const char* name) {
66+
const char* r = strstr(extensions, name);
67+
auto len = strlen(name);
68+
// check that the extension name is terminated by space or null terminator
69+
return r != nullptr && (r[len] == ' ' || r[len] == 0);
70+
}
71+
72+
} // namespace
73+
6374
class AndroidEGLSurfaceDamage {
6475
public:
6576
void init(EGLDisplay display, EGLContext context) {
@@ -170,13 +181,6 @@ class AndroidEGLSurfaceDamage {
170181

171182
bool partial_redraw_supported_;
172183

173-
bool HasExtension(const char* extensions, const char* name) {
174-
const char* r = strstr(extensions, name);
175-
auto len = strlen(name);
176-
// check that the extension name is terminated by space or null terminator
177-
return r != nullptr && (r[len] == ' ' || r[len] == 0);
178-
}
179-
180184
std::list<SkIRect> damage_history_;
181185
};
182186

@@ -188,6 +192,14 @@ AndroidEGLSurface::AndroidEGLSurface(EGLSurface surface,
188192
context_(context),
189193
damage_(std::make_unique<AndroidEGLSurfaceDamage>()) {
190194
damage_->init(display_, context);
195+
196+
const char* extensions = eglQueryString(display, EGL_EXTENSIONS);
197+
198+
if (HasExtension(extensions, "EGL_ANDROID_presentation_time")) {
199+
presentation_time_proc_ =
200+
reinterpret_cast<PFNEGLPRESENTATIONTIMEANDROIDPROC>(
201+
eglGetProcAddress("eglPresentationTimeANDROID"));
202+
}
191203
}
192204

193205
AndroidEGLSurface::~AndroidEGLSurface() {
@@ -240,6 +252,16 @@ void AndroidEGLSurface::SetDamageRegion(
240252
damage_->SetDamageRegion(display_, surface_, buffer_damage);
241253
}
242254

255+
bool AndroidEGLSurface::SetPresentationTime(
256+
const fml::TimePoint& presentation_time) {
257+
if (presentation_time_proc_) {
258+
const auto time_ns = presentation_time.ToEpochDelta().ToNanoseconds();
259+
return presentation_time_proc_(display_, surface_, time_ns);
260+
} else {
261+
return false;
262+
}
263+
}
264+
243265
bool AndroidEGLSurface::SwapBuffers(
244266
const std::optional<SkIRect>& surface_damage) {
245267
TRACE_EVENT0("flutter", "AndroidContextGL::SwapBuffers");

shell/platform/android/android_egl_surface.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_EGL_SURFACE_H_
66
#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_EGL_SURFACE_H_
77

8+
#include <EGL/egl.h>
9+
#include <EGL/eglext.h>
10+
#include <KHR/khrplatform.h>
811
#include <optional>
912

1013
#include "flutter/fml/macros.h"
14+
#include "flutter/fml/time/time_point.h"
1115
#include "flutter/shell/platform/android/android_environment_gl.h"
1216
#include "third_party/skia/include/core/SkRect.h"
1317

@@ -77,6 +81,12 @@ class AndroidEGLSurface {
7781
// eglSetDamageRegionKHR
7882
void SetDamageRegion(const std::optional<SkIRect>& buffer_damage);
7983

84+
//----------------------------------------------------------------------------
85+
/// @brief Sets the presentation time for the current surface. This
86+
// corresponds to calling eglPresentationTimeAndroid when
87+
// available.
88+
bool SetPresentationTime(const fml::TimePoint& presentation_time);
89+
8090
//----------------------------------------------------------------------------
8191
/// @brief This only applies to on-screen surfaces such as those created
8292
/// by `AndroidContextGL::CreateOnscreenSurface`.
@@ -98,6 +108,7 @@ class AndroidEGLSurface {
98108
const EGLDisplay display_;
99109
const EGLContext context_;
100110
std::unique_ptr<AndroidEGLSurfaceDamage> damage_;
111+
PFNEGLPRESENTATIONTIMEANDROIDPROC presentation_time_proc_;
101112
};
102113

103114
} // namespace flutter

shell/platform/android/android_surface_gl_skia.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ void AndroidSurfaceGLSkia::GLContextSetDamageRegion(
156156
bool AndroidSurfaceGLSkia::GLContextPresent(const GLPresentInfo& present_info) {
157157
FML_DCHECK(IsValid());
158158
FML_DCHECK(onscreen_surface_);
159+
if (present_info.presentation_time) {
160+
onscreen_surface_->SetPresentationTime(*present_info.presentation_time);
161+
}
159162
return onscreen_surface_->SwapBuffers(present_info.damage);
160163
}
161164

0 commit comments

Comments
 (0)