Skip to content

Commit 88948de

Browse files
authored
Use eglPresentationTimeANDROID to avoid bogging down the GPU (flutter#29727)
Fixes flutter#93352 Improves Android benchmarks on both Pixel 4 and a lower end Android Go device for 99th percentile and average raster times. This works by telling the system compositor what timestamp we intended to show this frame for. This way, if we end up with a frame that gets submitted right at the beginning of a vsync and then a second frame submitted on the same vsync, the compositor will only try to show the second frame on the screen and save the GPU some work. Without this, a situation like that results in an "avalanche" of calls where the GPU is behind the CPU and keeps delaying CPU work until we finally stop submitting frames. This can be observed as a lengthy dequeuBuffer in a systrace enabled trace, as shown in the linked issue. This avalanche is often triggered by a frame that does a shader compile through a couple vsyncs and then is followed by a bunch of very fast frames that take less than a vsync to render - the first of those fast frames gets delivered before the end of the vsync that the slow frame ended in. We cannot implement this ourselves because we don't know how long the swap buffers call will take on the system side, and if we try to guess we can very well get it wrong. I've filed issues to look into adding this for Vulkan and Metal, although we should also first take traces there to make sure it's warranted. See also: https://android-developers.googleblog.com/2020/04/high-refresh-rate-rendering-on-android.html
1 parent 3d64644 commit 88948de

21 files changed

+187
-96
lines changed

flow/surface_frame.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "flutter/common/graphics/gl_context_switch.h"
1212
#include "flutter/fml/macros.h"
13+
#include "flutter/fml/time/time_point.h"
1314
#include "third_party/skia/include/core/SkCanvas.h"
1415
#include "third_party/skia/include/core/SkSurface.h"
1516

@@ -66,6 +67,12 @@ class SurfaceFrame {
6667
//
6768
// Corresponds to EGL_KHR_partial_update
6869
std::optional<SkIRect> buffer_damage;
70+
71+
// The vsync target time.
72+
//
73+
// Backends may use this information to avoid overloading the GPU with
74+
// multiple frames per vsync.
75+
fml::TimePoint target_time;
6976
};
7077

7178
bool Submit();

shell/common/rasterizer.cc

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -537,60 +537,60 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe(
537537
.supports_readback, // surface supports pixel reads
538538
raster_thread_merger_ // thread merger
539539
);
540-
if (compositor_frame) {
541-
compositor_context_->raster_cache().PrepareNewFrame();
542-
frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now());
543-
544-
// Disable partial repaint if external_view_embedder_ SubmitFrame is
545-
// involved - ExternalViewEmbedder unconditionally clears the entire
546-
// surface and also partial repaint with platform view present is something
547-
// that still need to be figured out.
548-
bool disable_partial_repaint =
549-
external_view_embedder_ &&
550-
(!raster_thread_merger_ || raster_thread_merger_->IsMerged());
551-
552-
FrameDamage damage;
553-
if (!disable_partial_repaint && frame->framebuffer_info().existing_damage) {
554-
damage.SetPreviousLayerTree(last_layer_tree_.get());
555-
damage.AddAdditonalDamage(*frame->framebuffer_info().existing_damage);
556-
}
540+
if (!compositor_frame) {
541+
return RasterStatus::kFailed;
542+
}
557543

558-
RasterStatus raster_status =
559-
compositor_frame->Raster(layer_tree, false, &damage);
560-
if (raster_status == RasterStatus::kFailed ||
561-
raster_status == RasterStatus::kSkipAndRetry) {
562-
return raster_status;
563-
}
544+
compositor_context_->raster_cache().PrepareNewFrame();
545+
frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now());
564546

565-
SurfaceFrame::SubmitInfo submit_info;
566-
submit_info.frame_damage = damage.GetFrameDamage();
567-
submit_info.buffer_damage = damage.GetBufferDamage();
547+
// Disable partial repaint if external_view_embedder_ SubmitFrame is
548+
// involved - ExternalViewEmbedder unconditionally clears the entire
549+
// surface and also partial repaint with platform view present is something
550+
// that still need to be figured out.
551+
bool disable_partial_repaint =
552+
external_view_embedder_ &&
553+
(!raster_thread_merger_ || raster_thread_merger_->IsMerged());
568554

569-
frame->set_submit_info(submit_info);
555+
FrameDamage damage;
556+
if (!disable_partial_repaint && frame->framebuffer_info().existing_damage) {
557+
damage.SetPreviousLayerTree(last_layer_tree_.get());
558+
damage.AddAdditonalDamage(*frame->framebuffer_info().existing_damage);
559+
}
570560

571-
if (external_view_embedder_ &&
572-
(!raster_thread_merger_ || raster_thread_merger_->IsMerged())) {
573-
FML_DCHECK(!frame->IsSubmitted());
574-
external_view_embedder_->SubmitFrame(surface_->GetContext(),
575-
std::move(frame));
576-
} else {
577-
frame->Submit();
578-
}
561+
RasterStatus raster_status =
562+
compositor_frame->Raster(layer_tree, false, &damage);
563+
if (raster_status == RasterStatus::kFailed ||
564+
raster_status == RasterStatus::kSkipAndRetry) {
565+
return raster_status;
566+
}
579567

580-
compositor_context_->raster_cache().CleanupAfterFrame();
581-
frame_timings_recorder.RecordRasterEnd(
582-
&compositor_context_->raster_cache());
583-
FireNextFrameCallbackIfPresent();
568+
SurfaceFrame::SubmitInfo submit_info;
569+
submit_info.frame_damage = damage.GetFrameDamage();
570+
submit_info.buffer_damage = damage.GetBufferDamage();
571+
submit_info.target_time = frame_timings_recorder.GetVsyncTargetTime();
584572

585-
if (surface_->GetContext()) {
586-
TRACE_EVENT0("flutter", "PerformDeferredSkiaCleanup");
587-
surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);
588-
}
573+
frame->set_submit_info(submit_info);
589574

590-
return raster_status;
575+
if (external_view_embedder_ &&
576+
(!raster_thread_merger_ || raster_thread_merger_->IsMerged())) {
577+
FML_DCHECK(!frame->IsSubmitted());
578+
external_view_embedder_->SubmitFrame(surface_->GetContext(),
579+
std::move(frame));
580+
} else {
581+
frame->Submit();
591582
}
592583

593-
return RasterStatus::kFailed;
584+
compositor_context_->raster_cache().CleanupAfterFrame();
585+
frame_timings_recorder.RecordRasterEnd(&compositor_context_->raster_cache());
586+
FireNextFrameCallbackIfPresent();
587+
588+
if (surface_->GetContext()) {
589+
TRACE_EVENT0("flutter", "PerformDeferredSkiaCleanup");
590+
surface_->GetContext()->performDeferredCleanup(kSkiaCleanupExpiration);
591+
}
592+
593+
return raster_status;
594594
}
595595

596596
static sk_sp<SkData> ScreenshotLayerTreeAsPicture(

shell/common/shell_test_platform_view_gl.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ bool ShellTestPlatformViewGL::GLContextClearCurrent() {
6363
}
6464

6565
// |GPUSurfaceGLDelegate|
66-
bool ShellTestPlatformViewGL::GLContextPresent(uint32_t fbo_id) {
66+
bool ShellTestPlatformViewGL::GLContextPresent(fml::TimePoint target_time,
67+
uint32_t fbo_id) {
6768
return gl_surface_.Present();
6869
}
6970

shell/common/shell_test_platform_view_gl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView,
5858
bool GLContextClearCurrent() override;
5959

6060
// |GPUSurfaceGLDelegate|
61-
bool GLContextPresent(uint32_t fbo_id) override;
61+
bool GLContextPresent(fml::TimePoint target_time, uint32_t fbo_id) override;
6262

6363
// |GPUSurfaceGLDelegate|
6464
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;

shell/gpu/gpu_surface_gl.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,9 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
242242
SurfaceFrame::SubmitCallback submit_callback =
243243
[weak = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame,
244244
SkCanvas* canvas) {
245-
return weak ? weak->PresentSurface(canvas) : false;
245+
return weak ? weak->PresentSurface(
246+
surface_frame.submit_info().target_time, canvas)
247+
: false;
246248
};
247249

248250
framebuffer_info = delegate_->GLContextFramebufferInfo();
@@ -251,7 +253,8 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
251253
std::move(context_switch));
252254
}
253255

254-
bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
256+
bool GPUSurfaceGL::PresentSurface(fml::TimePoint target_time,
257+
SkCanvas* canvas) {
255258
if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
256259
return false;
257260
}
@@ -261,7 +264,7 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
261264
onscreen_surface_->getCanvas()->flush();
262265
}
263266

264-
if (!delegate_->GLContextPresent(fbo_id_)) {
267+
if (!delegate_->GLContextPresent(target_time, fbo_id_)) {
265268
return false;
266269
}
267270

shell/gpu/gpu_surface_gl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class GPUSurfaceGL : public Surface {
6060
const SkISize& untransformed_size,
6161
const SkMatrix& root_surface_transformation);
6262

63-
bool PresentSurface(SkCanvas* canvas);
63+
bool PresentSurface(fml::TimePoint target_time, SkCanvas* canvas);
6464

6565
GPUSurfaceGLDelegate* delegate_;
6666
sk_sp<GrDirectContext> context_;
@@ -77,6 +77,7 @@ class GPUSurfaceGL : public Surface {
7777

7878
// WeakPtrFactory must be the last member.
7979
fml::TaskRunnerAffineWeakPtrFactory<GPUSurfaceGL> weak_factory_;
80+
8081
FML_DISALLOW_COPY_AND_ASSIGN(GPUSurfaceGL);
8182
};
8283

shell/gpu/gpu_surface_gl_delegate.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class GPUSurfaceGLDelegate {
3333

3434
// Called to present the main GL surface. This is only called for the main GL
3535
// context and not any of the contexts dedicated for IO.
36-
virtual bool GLContextPresent(uint32_t fbo_id) = 0;
36+
virtual bool GLContextPresent(fml::TimePoint target_time,
37+
uint32_t fbo_id) = 0;
3738

3839
// The ID of the main window bound framebuffer. Typically FBO0.
3940
virtual intptr_t GLContextFBO(GLFrameInfo frame_info) const = 0;

shell/platform/android/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ executable("flutter_shell_native_unittests") {
4646
public_configs = [ "//flutter:config" ]
4747
deps = [
4848
":flutter_shell_native_src",
49+
"//flutter/fml",
4950
"//third_party/googletest:gmock",
5051
"//third_party/googletest:gtest",
5152
]

shell/platform/android/android_context_gl.cc

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <utility>
1010

11+
#include "android_environment_gl.h"
1112
#include "flutter/fml/trace_event.h"
1213

1314
namespace flutter {
@@ -105,13 +106,14 @@ static bool TeardownContext(EGLDisplay display, EGLContext context) {
105106
return true;
106107
}
107108

108-
AndroidEGLSurface::AndroidEGLSurface(EGLSurface surface,
109-
EGLDisplay display,
110-
EGLContext context)
111-
: surface_(surface), display_(display), context_(context) {}
109+
AndroidEGLSurface::AndroidEGLSurface(
110+
EGLSurface surface,
111+
fml::RefPtr<AndroidEnvironmentGL> environment,
112+
EGLContext context)
113+
: surface_(surface), environment_(environment), context_(context) {}
112114

113115
AndroidEGLSurface::~AndroidEGLSurface() {
114-
auto result = eglDestroySurface(display_, surface_);
116+
auto result = eglDestroySurface(environment_->Display(), surface_);
115117
FML_DCHECK(result == EGL_TRUE);
116118
}
117119

@@ -120,25 +122,28 @@ bool AndroidEGLSurface::IsValid() const {
120122
}
121123

122124
bool AndroidEGLSurface::MakeCurrent() const {
123-
if (eglMakeCurrent(display_, surface_, surface_, context_) != EGL_TRUE) {
125+
if (eglMakeCurrent(environment_->Display(), surface_, surface_, context_) !=
126+
EGL_TRUE) {
124127
FML_LOG(ERROR) << "Could not make the context current";
125128
LogLastEGLError();
126129
return false;
127130
}
128131
return true;
129132
}
130133

131-
bool AndroidEGLSurface::SwapBuffers() {
134+
bool AndroidEGLSurface::SwapBuffers(fml::TimePoint target_time) {
132135
TRACE_EVENT0("flutter", "AndroidContextGL::SwapBuffers");
133-
return eglSwapBuffers(display_, surface_);
136+
environment_->SetPresentationTime(surface_, target_time);
137+
return eglSwapBuffers(environment_->Display(), surface_);
134138
}
135139

136140
SkISize AndroidEGLSurface::GetSize() const {
137141
EGLint width = 0;
138142
EGLint height = 0;
139143

140-
if (!eglQuerySurface(display_, surface_, EGL_WIDTH, &width) ||
141-
!eglQuerySurface(display_, surface_, EGL_HEIGHT, &height)) {
144+
if (!eglQuerySurface(environment_->Display(), surface_, EGL_WIDTH, &width) ||
145+
!eglQuerySurface(environment_->Display(), surface_, EGL_HEIGHT,
146+
&height)) {
142147
FML_LOG(ERROR) << "Unable to query EGL surface size";
143148
LogLastEGLError();
144149
return SkISize::Make(0, 0);
@@ -229,27 +234,24 @@ std::unique_ptr<AndroidEGLSurface> AndroidContextGL::CreateOnscreenSurface(
229234
if (window->IsFakeWindow()) {
230235
return CreatePbufferSurface();
231236
} else {
232-
EGLDisplay display = environment_->Display();
233-
234237
const EGLint attribs[] = {EGL_NONE};
235238

236239
EGLSurface surface = eglCreateWindowSurface(
237-
display, config_,
240+
environment_->Display(), config_,
238241
reinterpret_cast<EGLNativeWindowType>(window->handle()), attribs);
239-
return std::make_unique<AndroidEGLSurface>(surface, display, context_);
242+
return std::make_unique<AndroidEGLSurface>(surface, environment_, context_);
240243
}
241244
}
242245

243246
std::unique_ptr<AndroidEGLSurface> AndroidContextGL::CreateOffscreenSurface()
244247
const {
245248
// We only ever create pbuffer surfaces for background resource loading
246249
// contexts. We never bind the pbuffer to anything.
247-
EGLDisplay display = environment_->Display();
248-
249250
const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
250251

251-
EGLSurface surface = eglCreatePbufferSurface(display, config_, attribs);
252-
return std::make_unique<AndroidEGLSurface>(surface, display,
252+
EGLSurface surface =
253+
eglCreatePbufferSurface(environment_->Display(), config_, attribs);
254+
return std::make_unique<AndroidEGLSurface>(surface, environment_,
253255
resource_context_);
254256
}
255257

@@ -260,7 +262,7 @@ std::unique_ptr<AndroidEGLSurface> AndroidContextGL::CreatePbufferSurface()
260262
const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
261263

262264
EGLSurface surface = eglCreatePbufferSurface(display, config_, attribs);
263-
return std::make_unique<AndroidEGLSurface>(surface, display, context_);
265+
return std::make_unique<AndroidEGLSurface>(surface, environment_, context_);
264266
}
265267

266268
fml::RefPtr<AndroidEnvironmentGL> AndroidContextGL::Environment() const {

shell/platform/android/android_context_gl.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
#ifndef FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_CONTEXT_GL_H_
66
#define FLUTTER_SHELL_PLATFORM_ANDROID_ANDROID_CONTEXT_GL_H_
77

8+
#include <EGL/egl.h>
9+
#define EGL_EGLEXT_PROTOTYPES
10+
#include <EGL/eglext.h>
11+
812
#include "flutter/fml/macros.h"
913
#include "flutter/fml/memory/ref_counted.h"
1014
#include "flutter/fml/memory/ref_ptr.h"
@@ -25,7 +29,9 @@ namespace flutter {
2529
///
2630
class AndroidEGLSurface {
2731
public:
28-
AndroidEGLSurface(EGLSurface surface, EGLDisplay display, EGLContext context);
32+
AndroidEGLSurface(EGLSurface surface,
33+
fml::RefPtr<AndroidEnvironmentGL> environment,
34+
EGLContext context);
2935
~AndroidEGLSurface();
3036

3137
//----------------------------------------------------------------------------
@@ -47,9 +53,11 @@ class AndroidEGLSurface {
4753
/// @brief This only applies to on-screen surfaces such as those created
4854
/// by `AndroidContextGL::CreateOnscreenSurface`.
4955
///
56+
/// @param target_time The vsync target time for the buffer.
57+
///
5058
/// @return Whether the EGL surface color buffer was swapped.
5159
///
52-
bool SwapBuffers();
60+
bool SwapBuffers(fml::TimePoint target_time);
5361

5462
//----------------------------------------------------------------------------
5563
/// @return The size of an `EGLSurface`.
@@ -58,7 +66,7 @@ class AndroidEGLSurface {
5866

5967
private:
6068
const EGLSurface surface_;
61-
const EGLDisplay display_;
69+
const fml::RefPtr<AndroidEnvironmentGL> environment_;
6270
const EGLContext context_;
6371
};
6472

0 commit comments

Comments
 (0)