66
77#include < EGL/eglext.h>
88
9+ #include < list>
910#include < utility>
1011
12+ // required to get API level
13+ #include < sys/system_properties.h>
14+
1115#include " flutter/fml/trace_event.h"
1216
1317namespace flutter {
@@ -105,10 +109,135 @@ static bool TeardownContext(EGLDisplay display, EGLContext context) {
105109 return true ;
106110}
107111
112+ class AndroidEGLSurfaceDamage {
113+ public:
114+ void init (EGLDisplay display, EGLContext context) {
115+ if (GetAPILevel () < 28 ) {
116+ // Disable partial repaint for devices older than Android 9. There
117+ // are old devices that have extensions below available but the
118+ // implementation causes glitches (i.e. Xperia Z3 with Android 6).
119+ partial_redraw_supported_ = false ;
120+ return ;
121+ }
122+
123+ const char * extensions = eglQueryString (display, EGL_EXTENSIONS);
124+
125+ if (HasExtension (extensions, " EGL_KHR_partial_update" )) {
126+ set_damage_region_ = reinterpret_cast <PFNEGLSETDAMAGEREGIONKHRPROC>(
127+ eglGetProcAddress (" eglSetDamageRegionKHR" ));
128+ }
129+
130+ if (HasExtension (extensions, " EGL_EXT_swap_buffers_with_damage" )) {
131+ swap_buffers_with_damage_ =
132+ reinterpret_cast <PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(
133+ eglGetProcAddress (" eglSwapBuffersWithDamageEXT" ));
134+ } else if (HasExtension (extensions, " EGL_KHR_swap_buffers_with_damage" )) {
135+ swap_buffers_with_damage_ =
136+ reinterpret_cast <PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(
137+ eglGetProcAddress (" eglSwapBuffersWithDamageKHR" ));
138+ }
139+
140+ partial_redraw_supported_ =
141+ set_damage_region_ != nullptr && swap_buffers_with_damage_ != nullptr ;
142+ }
143+
144+ static int GetAPILevel () {
145+ char sdk_version_string[PROP_VALUE_MAX];
146+ if (__system_property_get (" ro.build.version.sdk" , sdk_version_string)) {
147+ return atoi (sdk_version_string);
148+ } else {
149+ return -1 ;
150+ }
151+ }
152+
153+ void SetDamageRegion (EGLDisplay display,
154+ EGLSurface surface,
155+ const std::optional<SkIRect>& region) {
156+ if (set_damage_region_ && region) {
157+ auto rects = RectToInts (display, surface, *region);
158+ set_damage_region_ (display, surface, rects.data (), 1 );
159+ }
160+ }
161+
162+ // Maximum damage history - for triple buffering we need to store damage for
163+ // last two frames; Some Android devices (Pixel 4) use quad buffering.
164+ static const int kMaxHistorySize = 10 ;
165+
166+ bool SupportsPartialRepaint () const { return partial_redraw_supported_; }
167+
168+ std::optional<SkIRect> InitialDamage (EGLDisplay display, EGLSurface surface) {
169+ if (!partial_redraw_supported_) {
170+ return std::nullopt ;
171+ }
172+
173+ EGLint age;
174+ eglQuerySurface (display, surface, EGL_BUFFER_AGE_EXT, &age);
175+
176+ if (age == 0 ) { // full repaint
177+ return std::nullopt ;
178+ } else {
179+ // join up to (age - 1) last rects from damage history
180+ --age;
181+ auto res = SkIRect::MakeEmpty ();
182+ for (auto i = damage_history_.rbegin ();
183+ i != damage_history_.rend () && age > 0 ; ++i, --age) {
184+ res.join (*i);
185+ }
186+ return res;
187+ }
188+ }
189+
190+ bool SwapBuffersWithDamage (EGLDisplay display,
191+ EGLSurface surface,
192+ const std::optional<SkIRect>& damage) {
193+ if (swap_buffers_with_damage_ && damage) {
194+ damage_history_.push_back (*damage);
195+ if (damage_history_.size () > kMaxHistorySize ) {
196+ damage_history_.pop_front ();
197+ }
198+ auto rects = RectToInts (display, surface, *damage);
199+ return swap_buffers_with_damage_ (display, surface, rects.data (), 1 );
200+ } else {
201+ return eglSwapBuffers (display, surface);
202+ }
203+ }
204+
205+ private:
206+ std::array<EGLint, 4 > static RectToInts (EGLDisplay display,
207+ EGLSurface surface,
208+ const SkIRect& rect) {
209+ EGLint height;
210+ eglQuerySurface (display, surface, EGL_HEIGHT, &height);
211+
212+ std::array<EGLint, 4 > res{rect.left (), height - rect.bottom (), rect.width (),
213+ rect.height ()};
214+ return res;
215+ }
216+
217+ PFNEGLSETDAMAGEREGIONKHRPROC set_damage_region_ = nullptr ;
218+ PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage_ = nullptr ;
219+
220+ bool partial_redraw_supported_;
221+
222+ bool HasExtension (const char * extensions, const char * name) {
223+ const char * r = strstr (extensions, name);
224+ auto len = strlen (name);
225+ // check that the extension name is terminated by space or null terminator
226+ return r != nullptr && (r[len] == ' ' || r[len] == 0 );
227+ }
228+
229+ std::list<SkIRect> damage_history_;
230+ };
231+
108232AndroidEGLSurface::AndroidEGLSurface (EGLSurface surface,
109233 EGLDisplay display,
110234 EGLContext context)
111- : surface_(surface), display_(display), context_(context) {}
235+ : surface_(surface),
236+ display_ (display),
237+ context_(context),
238+ damage_(std::make_unique<AndroidEGLSurfaceDamage>()) {
239+ damage_->init (display_, context);
240+ }
112241
113242AndroidEGLSurface::~AndroidEGLSurface () {
114243 auto result = eglDestroySurface (display_, surface_);
@@ -128,9 +257,23 @@ bool AndroidEGLSurface::MakeCurrent() const {
128257 return true ;
129258}
130259
131- bool AndroidEGLSurface::SwapBuffers () {
260+ void AndroidEGLSurface::SetDamageRegion (
261+ const std::optional<SkIRect>& buffer_damage) {
262+ damage_->SetDamageRegion (display_, surface_, buffer_damage);
263+ }
264+
265+ bool AndroidEGLSurface::SwapBuffers (
266+ const std::optional<SkIRect>& surface_damage) {
132267 TRACE_EVENT0 (" flutter" , " AndroidContextGL::SwapBuffers" );
133- return eglSwapBuffers (display_, surface_);
268+ return damage_->SwapBuffersWithDamage (display_, surface_, surface_damage);
269+ }
270+
271+ bool AndroidEGLSurface::SupportsPartialRepaint () const {
272+ return damage_->SupportsPartialRepaint ();
273+ }
274+
275+ std::optional<SkIRect> AndroidEGLSurface::InitialDamage () {
276+ return damage_->InitialDamage (display_, surface_);
134277}
135278
136279SkISize AndroidEGLSurface::GetSize () const {
0 commit comments