Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable partial repaint for Android/OpenGL #29591

Merged
merged 6 commits into from
Jan 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion shell/common/shell_test_platform_view_gl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ bool ShellTestPlatformViewGL::GLContextClearCurrent() {
}

// |GPUSurfaceGLDelegate|
bool ShellTestPlatformViewGL::GLContextPresent(uint32_t fbo_id) {
bool ShellTestPlatformViewGL::GLContextPresent(
uint32_t fbo_id,
const std::optional<SkIRect>& damage) {
return gl_surface_.Present();
}

Expand Down
3 changes: 2 additions & 1 deletion shell/common/shell_test_platform_view_gl.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView,
bool GLContextClearCurrent() override;

// |GPUSurfaceGLDelegate|
bool GLContextPresent(uint32_t fbo_id) override;
bool GLContextPresent(uint32_t fbo_id,
const std::optional<SkIRect>& damage) override;

// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
Expand Down
8 changes: 5 additions & 3 deletions shell/gpu/gpu_surface_gl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
SurfaceFrame::SubmitCallback submit_callback =
[weak = weak_factory_.GetWeakPtr()](const SurfaceFrame& surface_frame,
SkCanvas* canvas) {
return weak ? weak->PresentSurface(canvas) : false;
return weak ? weak->PresentSurface(surface_frame, canvas) : false;
};

framebuffer_info = delegate_->GLContextFramebufferInfo();
Expand All @@ -251,17 +251,19 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGL::AcquireFrame(const SkISize& size) {
std::move(context_switch));
}

bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) {
bool GPUSurfaceGL::PresentSurface(const SurfaceFrame& frame, SkCanvas* canvas) {
if (delegate_ == nullptr || canvas == nullptr || context_ == nullptr) {
return false;
}

delegate_->GLContextSetDamageRegion(frame.submit_info().buffer_damage);

{
TRACE_EVENT0("flutter", "SkCanvas::Flush");
onscreen_surface_->getCanvas()->flush();
}

if (!delegate_->GLContextPresent(fbo_id_)) {
if (!delegate_->GLContextPresent(fbo_id_, frame.submit_info().frame_damage)) {
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion shell/gpu/gpu_surface_gl.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class GPUSurfaceGL : public Surface {
const SkISize& untransformed_size,
const SkMatrix& root_surface_transformation);

bool PresentSurface(SkCanvas* canvas);
bool PresentSurface(const SurfaceFrame& frame, SkCanvas* canvas);

GPUSurfaceGLDelegate* delegate_;
sk_sp<GrDirectContext> context_;
Expand Down
12 changes: 11 additions & 1 deletion shell/gpu/gpu_surface_gl_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#ifndef FLUTTER_SHELL_GPU_GPU_SURFACE_GL_DELEGATE_H_
#define FLUTTER_SHELL_GPU_GPU_SURFACE_GL_DELEGATE_H_

#include <optional>

#include "flutter/common/graphics/gl_context_switch.h"
#include "flutter/flow/embedded_views.h"
#include "flutter/fml/macros.h"
Expand All @@ -31,9 +33,17 @@ class GPUSurfaceGLDelegate {
// either the GPU or IO threads.
virtual bool GLContextClearCurrent() = 0;

// Inform the GL Context that there's going to be no writing beyond
// the specified region
virtual void GLContextSetDamageRegion(const std::optional<SkIRect>& region) {}

// Called to present the main GL surface. This is only called for the main GL
// context and not any of the contexts dedicated for IO.
virtual bool GLContextPresent(uint32_t fbo_id) = 0;
//
// Damage is a hint to compositor telling it which parts of front buffer
// need to be updated
virtual bool GLContextPresent(uint32_t fbo_id,
const std::optional<SkIRect>& damage) = 0;

// The ID of the main window bound framebuffer. Typically FBO0.
virtual intptr_t GLContextFBO(GLFrameInfo frame_info) const = 0;
Expand Down
149 changes: 146 additions & 3 deletions shell/platform/android/android_context_gl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@

#include <EGL/eglext.h>

#include <list>
#include <utility>

// required to get API level
#include <sys/system_properties.h>

#include "flutter/fml/trace_event.h"

namespace flutter {
Expand Down Expand Up @@ -105,10 +109,135 @@ static bool TeardownContext(EGLDisplay display, EGLContext context) {
return true;
}

class AndroidEGLSurfaceDamage {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason this class is forward declared rather than have the definition in android_context_gl.h?

Copy link
Member Author

@knopp knopp Dec 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer not having to split it into header/implementation but don't have strong feelings about it, if you prefer the definition in header I can certainly do it.

public:
void init(EGLDisplay display, EGLContext context) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

context isn't used below. was the plan to use it in the future?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. I think I just overlooked it.

if (GetAPILevel() < 28) {
// Disable partial repaint for devices older than Android 9. There
// are old devices that have extensions below available but the
// implementation causes glitches (i.e. Xperia Z3 with Android 6).
partial_redraw_supported_ = false;
return;
}

const char* extensions = eglQueryString(display, EGL_EXTENSIONS);

if (HasExtension(extensions, "EGL_KHR_partial_update")) {
set_damage_region_ = reinterpret_cast<PFNEGLSETDAMAGEREGIONKHRPROC>(
eglGetProcAddress("eglSetDamageRegionKHR"));
}

if (HasExtension(extensions, "EGL_EXT_swap_buffers_with_damage")) {
swap_buffers_with_damage_ =
reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(
eglGetProcAddress("eglSwapBuffersWithDamageEXT"));
} else if (HasExtension(extensions, "EGL_KHR_swap_buffers_with_damage")) {
swap_buffers_with_damage_ =
reinterpret_cast<PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC>(
eglGetProcAddress("eglSwapBuffersWithDamageKHR"));
}

partial_redraw_supported_ =
set_damage_region_ != nullptr && swap_buffers_with_damage_ != nullptr;
}

static int GetAPILevel() {
char sdk_version_string[PROP_VALUE_MAX];
if (__system_property_get("ro.build.version.sdk", sdk_version_string)) {
return atoi(sdk_version_string);
} else {
return -1;
}
}

void SetDamageRegion(EGLDisplay display,
EGLSurface surface,
const std::optional<SkIRect>& region) {
if (set_damage_region_ && region) {
auto rects = RectToInts(display, surface, *region);
set_damage_region_(display, surface, rects.data(), 1);
}
}

// Maximum damage history - for triple buffering we need to store damage for
// last two frames; Some Android devices (Pixel 4) use quad buffering.
static const int kMaxHistorySize = 10;

bool SupportsPartialRepaint() const { return partial_redraw_supported_; }

std::optional<SkIRect> InitialDamage(EGLDisplay display, EGLSurface surface) {
if (!partial_redraw_supported_) {
return std::nullopt;
}

EGLint age;
eglQuerySurface(display, surface, EGL_BUFFER_AGE_EXT, &age);

if (age == 0) { // full repaint
return std::nullopt;
} else {
// join up to (age - 1) last rects from damage history
--age;
auto res = SkIRect::MakeEmpty();
for (auto i = damage_history_.rbegin();
i != damage_history_.rend() && age > 0; ++i, --age) {
res.join(*i);
}
return res;
}
}

bool SwapBuffersWithDamage(EGLDisplay display,
EGLSurface surface,
const std::optional<SkIRect>& damage) {
if (swap_buffers_with_damage_ && damage) {
damage_history_.push_back(*damage);
if (damage_history_.size() > kMaxHistorySize) {
damage_history_.pop_front();
}
auto rects = RectToInts(display, surface, *damage);
return swap_buffers_with_damage_(display, surface, rects.data(), 1);
} else {
return eglSwapBuffers(display, surface);
}
}

private:
std::array<EGLint, 4> static RectToInts(EGLDisplay display,
EGLSurface surface,
const SkIRect& rect) {
EGLint height;
eglQuerySurface(display, surface, EGL_HEIGHT, &height);

std::array<EGLint, 4> res{rect.left(), height - rect.bottom(), rect.width(),
rect.height()};
return res;
}

PFNEGLSETDAMAGEREGIONKHRPROC set_damage_region_ = nullptr;
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage_ = nullptr;

bool partial_redraw_supported_;

bool HasExtension(const char* extensions, const char* name) {
const char* r = strstr(extensions, name);
auto len = strlen(name);
// check that the extension name is terminated by space or null terminator
return r != nullptr && (r[len] == ' ' || r[len] == 0);
}

std::list<SkIRect> damage_history_;
};

AndroidEGLSurface::AndroidEGLSurface(EGLSurface surface,
EGLDisplay display,
EGLContext context)
: surface_(surface), display_(display), context_(context) {}
: surface_(surface),
display_(display),
context_(context),
damage_(std::make_unique<AndroidEGLSurfaceDamage>()) {
damage_->init(display_, context);
}

AndroidEGLSurface::~AndroidEGLSurface() {
auto result = eglDestroySurface(display_, surface_);
Expand All @@ -128,9 +257,23 @@ bool AndroidEGLSurface::MakeCurrent() const {
return true;
}

bool AndroidEGLSurface::SwapBuffers() {
void AndroidEGLSurface::SetDamageRegion(
const std::optional<SkIRect>& buffer_damage) {
damage_->SetDamageRegion(display_, surface_, buffer_damage);
}

bool AndroidEGLSurface::SwapBuffers(
const std::optional<SkIRect>& surface_damage) {
TRACE_EVENT0("flutter", "AndroidContextGL::SwapBuffers");
return eglSwapBuffers(display_, surface_);
return damage_->SwapBuffersWithDamage(display_, surface_, surface_damage);
}

bool AndroidEGLSurface::SupportsPartialRepaint() const {
return damage_->SupportsPartialRepaint();
}

std::optional<SkIRect> AndroidEGLSurface::InitialDamage() {
return damage_->InitialDamage(display_, surface_);
}

SkISize AndroidEGLSurface::GetSize() const {
Expand Down
27 changes: 26 additions & 1 deletion shell/platform/android/android_context_gl.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace flutter {
/// This can be used in conjunction to unique_ptr to provide better guarantees
/// about the lifespan of the `EGLSurface` object.
///
class AndroidEGLSurfaceDamage;

class AndroidEGLSurface {
public:
AndroidEGLSurface(EGLSurface surface, EGLDisplay display, EGLContext context);
Expand All @@ -43,13 +45,35 @@ class AndroidEGLSurface {
///
bool MakeCurrent() const;

//----------------------------------------------------------------------------
///
/// @return Whether target surface supports partial repaint.
///
bool SupportsPartialRepaint() const;

//----------------------------------------------------------------------------
/// @brief This is the minimal area that needs to be repainted to get
knopp marked this conversation as resolved.
Show resolved Hide resolved
/// correct result.
///
/// With double or triple buffering this buffer content may lag behind
/// current front buffer and the rect accounts for accumulated damage.
///
/// @return The area of current surface where it is behind front buffer.
///
std::optional<SkIRect> InitialDamage();

//----------------------------------------------------------------------------
/// @brief Sets the damage region for current surface. Corresponds to
// eglSetDamageRegionKHR
void SetDamageRegion(const std::optional<SkIRect>& buffer_damage);

//----------------------------------------------------------------------------
/// @brief This only applies to on-screen surfaces such as those created
/// by `AndroidContextGL::CreateOnscreenSurface`.
///
/// @return Whether the EGL surface color buffer was swapped.
///
bool SwapBuffers();
bool SwapBuffers(const std::optional<SkIRect>& surface_damage);

//----------------------------------------------------------------------------
/// @return The size of an `EGLSurface`.
Expand All @@ -60,6 +84,7 @@ class AndroidEGLSurface {
const EGLSurface surface_;
const EGLDisplay display_;
const EGLContext context_;
std::unique_ptr<AndroidEGLSurfaceDamage> damage_;
};

//------------------------------------------------------------------------------
Expand Down
21 changes: 19 additions & 2 deletions shell/platform/android/android_surface_gl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,27 @@ bool AndroidSurfaceGL::GLContextClearCurrent() {
return GLContextPtr()->ClearCurrent();
}

bool AndroidSurfaceGL::GLContextPresent(uint32_t fbo_id) {
SurfaceFrame::FramebufferInfo AndroidSurfaceGL::GLContextFramebufferInfo()
const {
FML_DCHECK(IsValid());
SurfaceFrame::FramebufferInfo res;
res.supports_readback = true;
res.supports_partial_repaint = onscreen_surface_->SupportsPartialRepaint();
res.existing_damage = onscreen_surface_->InitialDamage();
return res;
}

void AndroidSurfaceGL::GLContextSetDamageRegion(
const std::optional<SkIRect>& region) {
FML_DCHECK(IsValid());
onscreen_surface_->SetDamageRegion(region);
}

bool AndroidSurfaceGL::GLContextPresent(uint32_t fbo_id,
const std::optional<SkIRect>& damage) {
FML_DCHECK(IsValid());
FML_DCHECK(onscreen_surface_);
return onscreen_surface_->SwapBuffers();
return onscreen_surface_->SwapBuffers(damage);
}

intptr_t AndroidSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const {
Expand Down
9 changes: 8 additions & 1 deletion shell/platform/android/android_surface_gl.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate,
bool GLContextClearCurrent() override;

// |GPUSurfaceGLDelegate|
bool GLContextPresent(uint32_t fbo_id) override;
SurfaceFrame::FramebufferInfo GLContextFramebufferInfo() const override;

// |GPUSurfaceGLDelegate|
void GLContextSetDamageRegion(const std::optional<SkIRect>& region) override;

// |GPUSurfaceGLDelegate|
bool GLContextPresent(uint32_t fbo_id,
const std::optional<SkIRect>& damage) override;

// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
Expand Down
4 changes: 3 additions & 1 deletion shell/platform/android/surface/android_surface_mock.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ bool AndroidSurfaceMock::GLContextClearCurrent() {
return true;
}

bool AndroidSurfaceMock::GLContextPresent(uint32_t fbo_id) {
bool AndroidSurfaceMock::GLContextPresent(
uint32_t fbo_id,
const std::optional<SkIRect>& damage) {
return true;
}

Expand Down
3 changes: 2 additions & 1 deletion shell/platform/android/surface/android_surface_mock.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class AndroidSurfaceMock final : public GPUSurfaceGLDelegate,
bool GLContextClearCurrent() override;

// |GPUSurfaceGLDelegate|
bool GLContextPresent(uint32_t fbo_id) override;
bool GLContextPresent(uint32_t fbo_id,
const std::optional<SkIRect>& damage) override;

// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
Expand Down
2 changes: 1 addition & 1 deletion shell/platform/darwin/ios/ios_surface_gl.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class IOSSurfaceGL final : public IOSSurface, public GPUSurfaceGLDelegate {
bool GLContextClearCurrent() override;

// |GPUSurfaceGLDelegate|
bool GLContextPresent(uint32_t fbo_id) override;
bool GLContextPresent(uint32_t fbo_id, const std::optional<SkIRect>& damage) override;

// |GPUSurfaceGLDelegate|
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
Expand Down
Loading