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

Commit 51d5652

Browse files
committed
Enable partial repaint for Android/OpenGL
1 parent ba38209 commit 51d5652

16 files changed

+214
-26
lines changed

shell/common/shell_test_platform_view_gl.cc

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

6565
// |GPUSurfaceGLDelegate|
66-
bool ShellTestPlatformViewGL::GLContextPresent(fml::TimePoint target_time,
67-
uint32_t fbo_id) {
66+
bool ShellTestPlatformViewGL::GLContextPresent(
67+
fml::TimePoint target_time,
68+
uint32_t fbo_id,
69+
const std::optional<SkIRect>& damage) {
6870
return gl_surface_.Present();
6971
}
7072

shell/common/shell_test_platform_view_gl.h

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

6060
// |GPUSurfaceGLDelegate|
61-
bool GLContextPresent(fml::TimePoint target_time, uint32_t fbo_id) override;
61+
bool GLContextPresent(fml::TimePoint target_time,
62+
uint32_t fbo_id,
63+
const std::optional<SkIRect>& damage) override;
6264

6365
// |GPUSurfaceGLDelegate|
6466
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;

shell/gpu/gpu_surface_gl.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,7 @@ 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(
246-
surface_frame.submit_info().target_time, canvas)
247-
: false;
245+
return weak ? weak->PresentSurface(surface_frame, canvas) : false;
248246
};
249247

250248
framebuffer_info = delegate_->GLContextFramebufferInfo();
@@ -253,18 +251,20 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
253251
std::move(context_switch));
254252
}
255253

256-
bool GPUSurfaceGL::PresentSurface(fml::TimePoint target_time,
257-
SkCanvas* canvas) {
254+
bool GPUSurfaceGL::PresentSurface(const SurfaceFrame& frame, SkCanvas* canvas) {
258255
if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
259256
return false;
260257
}
261258

259+
delegate_->GLContextSetDamageRegion(frame.submit_info().buffer_damage);
260+
262261
{
263262
TRACE_EVENT0("flutter", "SkCanvas::Flush");
264263
onscreen_surface_->getCanvas()->flush();
265264
}
266265

267-
if (!delegate_->GLContextPresent(target_time, fbo_id_)) {
266+
if (!delegate_->GLContextPresent(frame.submit_info().target_time, fbo_id_,
267+
frame.submit_info().frame_damage)) {
268268
return false;
269269
}
270270

shell/gpu/gpu_surface_gl.h

Lines changed: 1 addition & 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(fml::TimePoint target_time, SkCanvas* canvas);
63+
bool PresentSurface(const SurfaceFrame& frame, SkCanvas* canvas);
6464

6565
GPUSurfaceGLDelegate* delegate_;
6666
sk_sp<GrDirectContext> context_;

shell/gpu/gpu_surface_gl_delegate.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_GL_DELEGATE_H_
66
#define FLUTTER_SHELL_GPU_GPU_SURFACE_GL_DELEGATE_H_
77

8+
#include <optional>
9+
810
#include "flutter/common/graphics/gl_context_switch.h"
911
#include "flutter/flow/embedded_views.h"
1012
#include "flutter/fml/macros.h"
@@ -31,10 +33,18 @@ class GPUSurfaceGLDelegate {
3133
// either the GPU or IO threads.
3234
virtual bool GLContextClearCurrent() = 0;
3335

36+
// Inform the GL Context that there's going to be no writing beyond
37+
// the specified region
38+
virtual void GLContextSetDamageRegion(const std::optional<SkIRect>& region) {}
39+
3440
// Called to present the main GL surface. This is only called for the main GL
3541
// context and not any of the contexts dedicated for IO.
42+
//
43+
// Damage is a hint to compositor telling it which parts of front buffer
44+
// need to be updated
3645
virtual bool GLContextPresent(fml::TimePoint target_time,
37-
uint32_t fbo_id) = 0;
46+
uint32_t fbo_id,
47+
const std::optional<SkIRect>& damage) = 0;
3848

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

shell/platform/android/android_context_gl.cc

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <EGL/eglext.h>
88

9+
#include <list>
910
#include <utility>
1011

1112
#include "android_environment_gl.h"
@@ -106,11 +107,120 @@ static bool TeardownContext(EGLDisplay display, EGLContext context) {
106107
return true;
107108
}
108109

110+
class AndroidEGLSurfaceDamage {
111+
public:
112+
void init(EGLDisplay display, EGLContext context) {
113+
const char* extensions = eglQueryString(display, EGL_EXTENSIONS);
114+
115+
if (HasExtension(extensions, "EGL_KHR_partial_update")) {
116+
set_damage_region = reinterpret_cast<PFNEGLSETDAMAGEREGIONKHRPROC>(
117+
eglGetProcAddress("eglSetDamageRegionKHR"));
118+
}
119+
120+
if (HasExtension(extensions, "EGL_EXT_swap_buffers_with_damage")) {
121+
swap_buffers_with_damage =
122+
reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(
123+
eglGetProcAddress("eglSwapBuffersWithDamageEXT"));
124+
} else if (HasExtension(extensions, "EGL_KHR_swap_buffers_with_damage")) {
125+
swap_buffers_with_damage =
126+
reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(
127+
eglGetProcAddress("eglSwapBuffersWithDamageKHR"));
128+
}
129+
130+
_partial_redraw_supported =
131+
set_damage_region != nullptr && swap_buffers_with_damage != nullptr;
132+
}
133+
134+
void SetDamageRegion(EGLDisplay display,
135+
EGLSurface surface,
136+
const std::optional<SkIRect>& region) {
137+
if (set_damage_region && region) {
138+
auto rects = RectToInts(display, surface, *region);
139+
set_damage_region(display, surface, rects.data(), 1);
140+
}
141+
}
142+
143+
std::optional<SkIRect> InitialDamage(EGLDisplay display, EGLSurface surface) {
144+
if (!_partial_redraw_supported) {
145+
return std::nullopt;
146+
}
147+
148+
EGLint age;
149+
eglQuerySurface(display, surface, EGL_BUFFER_AGE_EXT, &age);
150+
151+
if (age == 0) { // full repaint
152+
return std::nullopt;
153+
} else if (age == 1) {
154+
// no initial damage
155+
return SkIRect::MakeEmpty();
156+
} else {
157+
age -= 2;
158+
SkIRect res;
159+
for (auto i = damage_history_.begin();
160+
i != damage_history_.end() && age >= 0; ++i, --age) {
161+
res.join(*i);
162+
}
163+
return res;
164+
}
165+
}
166+
167+
bool SwapBuffersWithDamage(EGLDisplay display,
168+
EGLSurface surface,
169+
const std::optional<SkIRect>& damage) {
170+
if (swap_buffers_with_damage && damage) {
171+
damage_history_.push_back(*damage);
172+
if (damage_history_.size() > 2) {
173+
damage_history_.pop_front();
174+
}
175+
auto rects = RectToInts(display, surface, *damage);
176+
return swap_buffers_with_damage(display, surface, rects.data(), 1);
177+
178+
} else {
179+
return eglSwapBuffers(display, surface);
180+
}
181+
}
182+
183+
private:
184+
std::vector<EGLint> RectToInts(EGLDisplay display,
185+
EGLSurface surface,
186+
const SkIRect& rect) {
187+
std::vector<EGLint> res;
188+
EGLint height;
189+
eglQuerySurface(display, surface, EGL_HEIGHT, &height);
190+
191+
res.push_back(rect.left());
192+
res.push_back(height - rect.bottom());
193+
res.push_back(rect.width());
194+
res.push_back(rect.height());
195+
196+
return res;
197+
}
198+
199+
PFNEGLSETDAMAGEREGIONKHRPROC set_damage_region = nullptr;
200+
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage = nullptr;
201+
202+
bool _partial_redraw_supported;
203+
204+
bool HasExtension(const char* extensions, const char* name) {
205+
const char* r = strstr(extensions, name);
206+
auto len = strlen(name);
207+
// check that the extension name is terminated by space or null terminator
208+
return r != nullptr && (r[len] == ' ' || r[len] == 0);
209+
}
210+
211+
std::list<SkIRect> damage_history_;
212+
};
213+
109214
AndroidEGLSurface::AndroidEGLSurface(
110215
EGLSurface surface,
111216
fml::RefPtr<AndroidEnvironmentGL> environment,
112217
EGLContext context)
113-
: surface_(surface), environment_(environment), context_(context) {}
218+
: surface_(surface),
219+
environment_(environment),
220+
context_(context),
221+
damage_(std::make_unique<AndroidEGLSurfaceDamage>()) {
222+
damage_->init(environment->Display(), context);
223+
}
114224

115225
AndroidEGLSurface::~AndroidEGLSurface() {
116226
auto result = eglDestroySurface(environment_->Display(), surface_);
@@ -131,10 +241,22 @@ bool AndroidEGLSurface::MakeCurrent() const {
131241
return true;
132242
}
133243

134-
bool AndroidEGLSurface::SwapBuffers(fml::TimePoint target_time) {
244+
void AndroidEGLSurface::SetDamageRegion(
245+
const std::optional<SkIRect>& buffer_damage) {
246+
damage_->SetDamageRegion(environment_->Display(), surface_, buffer_damage);
247+
}
248+
249+
bool AndroidEGLSurface::SwapBuffers(
250+
fml::TimePoint target_time,
251+
const std::optional<SkIRect>& surface_damage) {
135252
TRACE_EVENT0("flutter", "AndroidContextGL::SwapBuffers");
136253
environment_->SetPresentationTime(surface_, target_time);
137-
return eglSwapBuffers(environment_->Display(), surface_);
254+
return damage_->SwapBuffersWithDamage(environment_->Display(), surface_,
255+
surface_damage);
256+
}
257+
258+
std::optional<SkIRect> AndroidEGLSurface::InitialDamage() {
259+
return damage_->InitialDamage(environment_->Display(), surface_);
138260
}
139261

140262
SkISize AndroidEGLSurface::GetSize() const {

shell/platform/android/android_context_gl.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ namespace flutter {
2727
/// This can be used in conjunction to unique_ptr to provide better guarantees
2828
/// about the lifespan of the `EGLSurface` object.
2929
///
30+
class AndroidEGLSurfaceDamage;
31+
3032
class AndroidEGLSurface {
3133
public:
3234
AndroidEGLSurface(EGLSurface surface,
@@ -49,6 +51,19 @@ class AndroidEGLSurface {
4951
///
5052
bool MakeCurrent() const;
5153

54+
//----------------------------------------------------------------------------
55+
/// @brief This is the minimal area that needs to be repainted to get
56+
/// correct result
57+
///
58+
/// @return The area of current surface where it is behind front buffer.
59+
///
60+
std::optional<SkIRect> InitialDamage();
61+
62+
//----------------------------------------------------------------------------
63+
/// @brief Sets the damage region for current surface. Corresponds to
64+
// eglSetDamageRegionKHR
65+
void SetDamageRegion(const std::optional<SkIRect>& buffer_damage);
66+
5267
//----------------------------------------------------------------------------
5368
/// @brief This only applies to on-screen surfaces such as those created
5469
/// by `AndroidContextGL::CreateOnscreenSurface`.
@@ -57,7 +72,8 @@ class AndroidEGLSurface {
5772
///
5873
/// @return Whether the EGL surface color buffer was swapped.
5974
///
60-
bool SwapBuffers(fml::TimePoint target_time);
75+
bool SwapBuffers(fml::TimePoint target_time,
76+
const std::optional<SkIRect>& surface_damage);
6177

6278
//----------------------------------------------------------------------------
6379
/// @return The size of an `EGLSurface`.
@@ -68,6 +84,7 @@ class AndroidEGLSurface {
6884
const EGLSurface surface_;
6985
const fml::RefPtr<AndroidEnvironmentGL> environment_;
7086
const EGLContext context_;
87+
std::unique_ptr<AndroidEGLSurfaceDamage> damage_;
7188
};
7289

7390
//------------------------------------------------------------------------------

shell/platform/android/android_context_gl_unittests.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ TEST(AndroidEGLSurface, SwapBuffersNotifiesAboutPresentationTime) {
9898
EXPECT_TRUE(surface);
9999
EXPECT_FALSE(environment->presentation_time_.has_value());
100100
auto now = fml::TimePoint::Now();
101-
surface->SwapBuffers(now);
101+
surface->SwapBuffers(now, std::nullopt);
102102
EXPECT_EQ(environment->presentation_time_, now);
103103
}
104104
} // namespace android

shell/platform/android/android_surface_gl.cc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,27 @@ bool AndroidSurfaceGL::GLContextClearCurrent() {
122122
return GLContextPtr()->ClearCurrent();
123123
}
124124

125+
SurfaceFrame::FramebufferInfo AndroidSurfaceGL::GLContextFramebufferInfo()
126+
const {
127+
FML_DCHECK(IsValid());
128+
SurfaceFrame::FramebufferInfo res;
129+
res.supports_readback = true;
130+
res.existing_damage = onscreen_surface_->InitialDamage();
131+
return res;
132+
}
133+
134+
void AndroidSurfaceGL::GLContextSetDamageRegion(
135+
const std::optional<SkIRect>& region) {
136+
FML_DCHECK(IsValid());
137+
onscreen_surface_->SetDamageRegion(region);
138+
}
139+
125140
bool AndroidSurfaceGL::GLContextPresent(fml::TimePoint target_time,
126-
uint32_t fbo_id) {
141+
uint32_t fbo_id,
142+
const std::optional<SkIRect>& damage) {
127143
FML_DCHECK(IsValid());
128144
FML_DCHECK(onscreen_surface_);
129-
return onscreen_surface_->SwapBuffers(target_time);
145+
return onscreen_surface_->SwapBuffers(target_time, damage);
130146
}
131147

132148
intptr_t AndroidSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const {

shell/platform/android/android_surface_gl.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,15 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate,
5858
bool GLContextClearCurrent() override;
5959

6060
// |GPUSurfaceGLDelegate|
61-
bool GLContextPresent(fml::TimePoint target_time, uint32_t fbo_id) override;
61+
SurfaceFrame::FramebufferInfo GLContextFramebufferInfo() const override;
62+
63+
// |GPUSurfaceGLDelegate|
64+
void GLContextSetDamageRegion(const std::optional<SkIRect>& region) override;
65+
66+
// |GPUSurfaceGLDelegate|
67+
bool GLContextPresent(fml::TimePoint target_time,
68+
uint32_t fbo_id,
69+
const std::optional<SkIRect>& damage) override;
6270

6371
// |GPUSurfaceGLDelegate|
6472
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;

0 commit comments

Comments
 (0)