mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Enable partial repaint for Android/OpenGL (flutter/engine#29591)
This commit is contained in:
parent
5553f427d9
commit
549e82fec6
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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_;
|
||||
|
||||
@ -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"
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
@ -105,10 +109,135 @@ static bool TeardownContext(EGLDisplay display, EGLContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
class AndroidEGLSurfaceDamage {
|
||||
public:
|
||||
void init(EGLDisplay display, EGLContext context) {
|
||||
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_);
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
@ -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
|
||||
/// 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`.
|
||||
@ -60,6 +84,7 @@ class AndroidEGLSurface {
|
||||
const EGLSurface surface_;
|
||||
const EGLDisplay display_;
|
||||
const EGLContext context_;
|
||||
std::unique_ptr<AndroidEGLSurfaceDamage> damage_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -86,7 +86,7 @@ bool IOSSurfaceGL::GLContextClearCurrent() {
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
bool IOSSurfaceGL::GLContextPresent(uint32_t fbo_id) {
|
||||
bool IOSSurfaceGL::GLContextPresent(uint32_t fbo_id, const std::optional<SkIRect>& damage) {
|
||||
TRACE_EVENT0("flutter", "IOSSurfaceGL::GLContextPresent");
|
||||
return IsValid() && render_target_->PresentRenderBuffer();
|
||||
}
|
||||
|
||||
@ -45,7 +45,8 @@ bool EmbedderSurfaceGL::GLContextClearCurrent() {
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
bool EmbedderSurfaceGL::GLContextPresent(uint32_t fbo_id) {
|
||||
bool EmbedderSurfaceGL::GLContextPresent(uint32_t fbo_id,
|
||||
const std::optional<SkIRect>& damage) {
|
||||
return gl_dispatch_table_.gl_present_callback(fbo_id);
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,8 @@ class EmbedderSurfaceGL final : public EmbedderSurface,
|
||||
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user