mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Enable dirty region management within the Embedder API (flutter/engine#35022)
This commit is contained in:
parent
aa5f5cecc6
commit
d9da8e0d2e
@ -69,8 +69,10 @@ bool ShellTestPlatformViewGL::GLContextPresent(
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
intptr_t ShellTestPlatformViewGL::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
return gl_surface_.GetFramebuffer(frame_info.width, frame_info.height);
|
||||
GLFBOInfo ShellTestPlatformViewGL::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
return GLFBOInfo{
|
||||
.fbo_id = gl_surface_.GetFramebuffer(frame_info.width, frame_info.height),
|
||||
};
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
|
||||
@ -61,7 +61,7 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView,
|
||||
bool GLContextPresent(const GLPresentInfo& present_info) override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
GLProcResolver GetGLProcResolver() const override;
|
||||
|
||||
@ -22,17 +22,32 @@ struct GLFrameInfo {
|
||||
uint32_t height;
|
||||
};
|
||||
|
||||
// A structure to represent the frame buffer information which is returned to
|
||||
// the rendering backend after requesting a frame buffer object.
|
||||
struct GLFBOInfo {
|
||||
// The frame buffer's ID.
|
||||
uint32_t fbo_id;
|
||||
// This boolean flags whether the returned FBO supports partial repaint.
|
||||
const bool partial_repaint_enabled;
|
||||
// The frame buffer's existing damage (i.e. damage since it was last used).
|
||||
const SkIRect existing_damage;
|
||||
};
|
||||
|
||||
// Information passed during presentation of a frame.
|
||||
struct GLPresentInfo {
|
||||
uint32_t fbo_id;
|
||||
|
||||
// Damage is a hint to compositor telling it which parts of front buffer
|
||||
// need to be updated
|
||||
const std::optional<SkIRect>& damage;
|
||||
// The frame damage is a hint to compositor telling it which parts of front
|
||||
// buffer need to be updated.
|
||||
const std::optional<SkIRect>& frame_damage;
|
||||
|
||||
// Time at which this frame is scheduled to be presented. This is a hint
|
||||
// that can be passed to the platform to drop queued frames.
|
||||
std::optional<fml::TimePoint> presentation_time = std::nullopt;
|
||||
|
||||
// The buffer damage refers to the region that needs to be set as damaged
|
||||
// within the frame buffer.
|
||||
const std::optional<SkIRect>& buffer_damage;
|
||||
};
|
||||
|
||||
class GPUSurfaceGLDelegate {
|
||||
@ -54,8 +69,9 @@ class GPUSurfaceGLDelegate {
|
||||
// context and not any of the contexts dedicated for IO.
|
||||
virtual bool GLContextPresent(const GLPresentInfo& present_info) = 0;
|
||||
|
||||
// The ID of the main window bound framebuffer. Typically FBO0.
|
||||
virtual intptr_t GLContextFBO(GLFrameInfo frame_info) const = 0;
|
||||
// The information about the main window bound framebuffer. ID is Typically
|
||||
// FBO0.
|
||||
virtual GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const = 0;
|
||||
|
||||
// The rendering subsystem assumes that the ID of the main window bound
|
||||
// framebuffer remains constant throughout. If this assumption in incorrect,
|
||||
|
||||
@ -62,10 +62,11 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGLImpeller::AcquireFrame(
|
||||
if (weak) {
|
||||
GLPresentInfo present_info = {
|
||||
.fbo_id = 0,
|
||||
.damage = std::nullopt,
|
||||
.frame_damage = std::nullopt,
|
||||
// TODO (https://github.com/flutter/flutter/issues/105597): wire-up
|
||||
// presentation time to impeller backend.
|
||||
.presentation_time = std::nullopt,
|
||||
.buffer_damage = std::nullopt,
|
||||
};
|
||||
delegate->GLContextPresent(present_info);
|
||||
}
|
||||
|
||||
@ -181,10 +181,10 @@ bool GPUSurfaceGLSkia::CreateOrUpdateSurfaces(const SkISize& size) {
|
||||
|
||||
GLFrameInfo frame_info = {static_cast<uint32_t>(size.width()),
|
||||
static_cast<uint32_t>(size.height())};
|
||||
const uint32_t fbo_id = delegate_->GLContextFBO(frame_info);
|
||||
const GLFBOInfo fbo_info = delegate_->GLContextFBO(frame_info);
|
||||
onscreen_surface = WrapOnscreenSurface(context_.get(), // GL context
|
||||
size, // root surface size
|
||||
fbo_id // window FBO ID
|
||||
fbo_info.fbo_id // window FBO ID
|
||||
);
|
||||
|
||||
if (onscreen_surface == nullptr) {
|
||||
@ -195,7 +195,9 @@ bool GPUSurfaceGLSkia::CreateOrUpdateSurfaces(const SkISize& size) {
|
||||
}
|
||||
|
||||
onscreen_surface_ = std::move(onscreen_surface);
|
||||
fbo_id_ = fbo_id;
|
||||
fbo_id_ = fbo_info.fbo_id;
|
||||
supports_partial_repaint_ = fbo_info.partial_repaint_enabled;
|
||||
existing_damage_ = fbo_info.existing_damage;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -248,6 +250,9 @@ std::unique_ptr<SurfaceFrame> GPUSurfaceGLSkia::AcquireFrame(
|
||||
};
|
||||
|
||||
framebuffer_info = delegate_->GLContextFramebufferInfo();
|
||||
// Partial repaint is enabled by default
|
||||
framebuffer_info.supports_partial_repaint = supports_partial_repaint_;
|
||||
framebuffer_info.existing_damage = existing_damage_;
|
||||
return std::make_unique<SurfaceFrame>(surface, std::move(framebuffer_info),
|
||||
submit_callback,
|
||||
std::move(context_switch));
|
||||
@ -268,8 +273,9 @@ bool GPUSurfaceGLSkia::PresentSurface(const SurfaceFrame& frame,
|
||||
|
||||
GLPresentInfo present_info = {
|
||||
.fbo_id = fbo_id_,
|
||||
.damage = frame.submit_info().frame_damage,
|
||||
.frame_damage = frame.submit_info().frame_damage,
|
||||
.presentation_time = frame.submit_info().presentation_time,
|
||||
.buffer_damage = frame.submit_info().buffer_damage,
|
||||
};
|
||||
if (!delegate_->GLContextPresent(present_info)) {
|
||||
return false;
|
||||
@ -284,11 +290,11 @@ bool GPUSurfaceGLSkia::PresentSurface(const SurfaceFrame& frame,
|
||||
|
||||
// The FBO has changed, ask the delegate for the new FBO and do a surface
|
||||
// re-wrap.
|
||||
const uint32_t fbo_id = delegate_->GLContextFBO(frame_info);
|
||||
const GLFBOInfo fbo_info = delegate_->GLContextFBO(frame_info);
|
||||
auto new_onscreen_surface =
|
||||
WrapOnscreenSurface(context_.get(), // GL context
|
||||
current_size, // root surface size
|
||||
fbo_id // window FBO ID
|
||||
fbo_info.fbo_id // window FBO ID
|
||||
);
|
||||
|
||||
if (!new_onscreen_surface) {
|
||||
@ -296,7 +302,9 @@ bool GPUSurfaceGLSkia::PresentSurface(const SurfaceFrame& frame,
|
||||
}
|
||||
|
||||
onscreen_surface_ = std::move(new_onscreen_surface);
|
||||
fbo_id_ = fbo_id;
|
||||
fbo_id_ = fbo_info.fbo_id;
|
||||
supports_partial_repaint_ = fbo_info.partial_repaint_enabled;
|
||||
existing_damage_ = fbo_info.existing_damage;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@ -67,6 +67,8 @@ class GPUSurfaceGLSkia : public Surface {
|
||||
sk_sp<SkSurface> onscreen_surface_;
|
||||
/// FBO backing the current `onscreen_surface_`.
|
||||
uint32_t fbo_id_ = 0;
|
||||
// Private variable used to keep track of the current FBO's existing damage.
|
||||
SkIRect existing_damage_ = SkIRect::MakeEmpty();
|
||||
bool context_owner_ = false;
|
||||
// TODO(38466): Refactor GPU surface APIs take into account the fact that an
|
||||
// external view embedder may want to render to the root surface. This is a
|
||||
@ -74,6 +76,8 @@ class GPUSurfaceGLSkia : public Surface {
|
||||
// external view embedder is present.
|
||||
const bool render_to_surface_ = true;
|
||||
bool valid_ = false;
|
||||
// Partial repaint is on by default.
|
||||
bool supports_partial_repaint_ = true;
|
||||
|
||||
// WeakPtrFactory must be the last member.
|
||||
fml::TaskRunnerAffineWeakPtrFactory<GPUSurfaceGLSkia> weak_factory_;
|
||||
|
||||
@ -295,9 +295,11 @@ bool AndroidSurfaceGLImpeller::GLContextPresent(
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
intptr_t AndroidSurfaceGLImpeller::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
GLFBOInfo AndroidSurfaceGLImpeller::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
// FBO0 is the default window bound framebuffer in EGL environments.
|
||||
return 0;
|
||||
return GLFBOInfo{
|
||||
.fbo_id = 0,
|
||||
};
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
|
||||
@ -68,7 +68,7 @@ class AndroidSurfaceGLImpeller final : public GPUSurfaceGLDelegate,
|
||||
bool GLContextPresent(const GLPresentInfo& present_info) override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
sk_sp<const GrGLInterface> GetGLInterface() const override;
|
||||
|
||||
@ -159,13 +159,15 @@ bool AndroidSurfaceGLSkia::GLContextPresent(const GLPresentInfo& present_info) {
|
||||
if (present_info.presentation_time) {
|
||||
onscreen_surface_->SetPresentationTime(*present_info.presentation_time);
|
||||
}
|
||||
return onscreen_surface_->SwapBuffers(present_info.damage);
|
||||
return onscreen_surface_->SwapBuffers(present_info.frame_damage);
|
||||
}
|
||||
|
||||
intptr_t AndroidSurfaceGLSkia::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
GLFBOInfo AndroidSurfaceGLSkia::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
FML_DCHECK(IsValid());
|
||||
// The default window bound framebuffer on Android.
|
||||
return 0;
|
||||
return GLFBOInfo{
|
||||
.fbo_id = 0,
|
||||
};
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
|
||||
@ -67,7 +67,7 @@ class AndroidSurfaceGLSkia final : public GPUSurfaceGLDelegate,
|
||||
bool GLContextPresent(const GLPresentInfo& present_info) override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
sk_sp<const GrGLInterface> GetGLInterface() const override;
|
||||
|
||||
@ -22,8 +22,10 @@ bool AndroidSurfaceMock::GLContextPresent(const GLPresentInfo& present_info) {
|
||||
return true;
|
||||
}
|
||||
|
||||
intptr_t AndroidSurfaceMock::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
return 0;
|
||||
GLFBOInfo AndroidSurfaceMock::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
return GLFBOInfo{
|
||||
.fbo_id = 0,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -51,7 +51,7 @@ class AndroidSurfaceMock final : public GPUSurfaceGLDelegate,
|
||||
bool GLContextPresent(const GLPresentInfo& present_info) override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
};
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -132,6 +132,12 @@ static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SAFE_EXISTS(open_gl_config, populate_existing_damage)) {
|
||||
FML_LOG(INFO) << "populate_existing_damage was not defined, disabling "
|
||||
"partial repaint. If you wish to enable partial repaint, "
|
||||
"please define this callback.";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -222,7 +228,29 @@ static void* DefaultGLProcResolver(const char* name) {
|
||||
}
|
||||
#endif // FML_OS_LINUX || FML_OS_WIN
|
||||
|
||||
static flutter::Shell::CreateCallback<flutter::PlatformView>
|
||||
#ifdef SHELL_ENABLE_GL
|
||||
// Auxiliary function used to translate rectangles of type SkIRect to
|
||||
// FlutterRect.
|
||||
static FlutterRect SkIRectToFlutterRect(const SkIRect sk_rect) {
|
||||
FlutterRect flutter_rect = {static_cast<double>(sk_rect.fLeft),
|
||||
static_cast<double>(sk_rect.fTop),
|
||||
static_cast<double>(sk_rect.fRight),
|
||||
static_cast<double>(sk_rect.fBottom)};
|
||||
return flutter_rect;
|
||||
}
|
||||
|
||||
// Auxiliary function used to translate rectangles of type FlutterRect to
|
||||
// SkIRect.
|
||||
static const SkIRect FlutterRectToSkIRect(FlutterRect flutter_rect) {
|
||||
SkIRect rect = {static_cast<int32_t>(flutter_rect.left),
|
||||
static_cast<int32_t>(flutter_rect.top),
|
||||
static_cast<int32_t>(flutter_rect.right),
|
||||
static_cast<int32_t>(flutter_rect.bottom)};
|
||||
return rect;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline flutter::Shell::CreateCallback<flutter::PlatformView>
|
||||
InferOpenGLPlatformViewCreationCallback(
|
||||
const FlutterRendererConfig* config,
|
||||
void* user_data,
|
||||
@ -241,15 +269,45 @@ InferOpenGLPlatformViewCreationCallback(
|
||||
auto gl_clear_current = [ptr = config->open_gl.clear_current,
|
||||
user_data]() -> bool { return ptr(user_data); };
|
||||
|
||||
auto gl_present = [present = config->open_gl.present,
|
||||
present_with_info = config->open_gl.present_with_info,
|
||||
user_data](uint32_t fbo_id) -> bool {
|
||||
auto gl_present =
|
||||
[present = config->open_gl.present,
|
||||
present_with_info = config->open_gl.present_with_info,
|
||||
user_data](flutter::GLPresentInfo gl_present_info) -> bool {
|
||||
if (present) {
|
||||
return present(user_data);
|
||||
} else {
|
||||
FlutterPresentInfo present_info = {};
|
||||
present_info.struct_size = sizeof(FlutterPresentInfo);
|
||||
present_info.fbo_id = fbo_id;
|
||||
// Format the frame and buffer damages accordingly. Note that, since the
|
||||
// current compute damage algorithm only returns one rectangle for damage
|
||||
// we are assuming the number of rectangles provided in frame and buffer
|
||||
// damage are always 1. Once the function that computes damage implements
|
||||
// support for multiple damage rectangles, GLPresentInfo should also
|
||||
// contain the number of damage rectangles.
|
||||
const size_t num_rects = 1;
|
||||
|
||||
std::array<FlutterRect, num_rects> frame_damage_rect = {
|
||||
SkIRectToFlutterRect(*(gl_present_info.frame_damage))};
|
||||
std::array<FlutterRect, num_rects> buffer_damage_rect = {
|
||||
SkIRectToFlutterRect(*(gl_present_info.buffer_damage))};
|
||||
|
||||
FlutterDamage frame_damage{
|
||||
.struct_size = sizeof(FlutterDamage),
|
||||
.num_rects = frame_damage_rect.size(),
|
||||
.damage = frame_damage_rect.data(),
|
||||
};
|
||||
FlutterDamage buffer_damage{
|
||||
.struct_size = sizeof(FlutterDamage),
|
||||
.num_rects = buffer_damage_rect.size(),
|
||||
.damage = buffer_damage_rect.data(),
|
||||
};
|
||||
|
||||
// Construct the present information concerning the frame being rendered.
|
||||
FlutterPresentInfo present_info = {
|
||||
.struct_size = sizeof(FlutterPresentInfo),
|
||||
.fbo_id = gl_present_info.fbo_id,
|
||||
.frame_damage = frame_damage,
|
||||
.buffer_damage = buffer_damage,
|
||||
};
|
||||
|
||||
return present_with_info(user_data, &present_info);
|
||||
}
|
||||
};
|
||||
@ -269,6 +327,50 @@ InferOpenGLPlatformViewCreationCallback(
|
||||
}
|
||||
};
|
||||
|
||||
auto gl_populate_existing_damage =
|
||||
[populate_existing_damage = config->open_gl.populate_existing_damage,
|
||||
user_data](intptr_t id) -> flutter::GLFBOInfo {
|
||||
// If no populate_existing_damage was provided, disable partial
|
||||
// repaint.
|
||||
if (!populate_existing_damage) {
|
||||
return flutter::GLFBOInfo{
|
||||
.fbo_id = static_cast<uint32_t>(id),
|
||||
.partial_repaint_enabled = false,
|
||||
.existing_damage = SkIRect::MakeEmpty(),
|
||||
};
|
||||
}
|
||||
|
||||
// Given the FBO's ID, get its existing damage.
|
||||
FlutterDamage existing_damage;
|
||||
populate_existing_damage(user_data, id, &existing_damage);
|
||||
|
||||
bool partial_repaint_enabled = true;
|
||||
SkIRect existing_damage_rect;
|
||||
|
||||
// Verify that at least one damage rectangle was provided.
|
||||
if (existing_damage.num_rects <= 0 || existing_damage.damage == nullptr) {
|
||||
FML_LOG(INFO) << "No damage was provided. Forcing full repaint.";
|
||||
existing_damage_rect = SkIRect::MakeEmpty();
|
||||
partial_repaint_enabled = false;
|
||||
} else if (existing_damage.num_rects > 1) {
|
||||
// Log message notifying users that multi-damage is not yet available in
|
||||
// case they try to make use of it.
|
||||
FML_LOG(INFO) << "Damage with multiple rectangles not yet supported. "
|
||||
"Repainting the whole frame.";
|
||||
existing_damage_rect = SkIRect::MakeEmpty();
|
||||
partial_repaint_enabled = false;
|
||||
} else {
|
||||
existing_damage_rect = FlutterRectToSkIRect(*(existing_damage.damage));
|
||||
}
|
||||
|
||||
// Pass the information about this FBO to the rendering backend.
|
||||
return flutter::GLFBOInfo{
|
||||
.fbo_id = static_cast<uint32_t>(id),
|
||||
.partial_repaint_enabled = partial_repaint_enabled,
|
||||
.existing_damage = existing_damage_rect,
|
||||
};
|
||||
};
|
||||
|
||||
const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl;
|
||||
std::function<bool()> gl_make_resource_current_callback = nullptr;
|
||||
if (SAFE_ACCESS(open_gl_config, make_resource_current, nullptr) != nullptr) {
|
||||
@ -326,6 +428,7 @@ InferOpenGLPlatformViewCreationCallback(
|
||||
gl_make_resource_current_callback, // gl_make_resource_current_callback
|
||||
gl_surface_transformation_callback, // gl_surface_transformation_callback
|
||||
gl_proc_resolver, // gl_proc_resolver
|
||||
gl_populate_existing_damage, // gl_populate_existing_damage
|
||||
};
|
||||
|
||||
return fml::MakeCopyable(
|
||||
|
||||
@ -433,6 +433,16 @@ typedef struct {
|
||||
FlutterSize lower_left_corner_radius;
|
||||
} FlutterRoundedRect;
|
||||
|
||||
/// A structure to represent a damage region.
|
||||
typedef struct {
|
||||
/// The size of this struct. Must be sizeof(FlutterDamage).
|
||||
size_t struct_size;
|
||||
/// The number of rectangles within the damage region.
|
||||
size_t num_rects;
|
||||
/// The actual damage region(s) in question.
|
||||
FlutterRect* damage;
|
||||
} FlutterDamage;
|
||||
|
||||
/// This information is passed to the embedder when requesting a frame buffer
|
||||
/// object.
|
||||
///
|
||||
@ -449,6 +459,13 @@ typedef uint32_t (*UIntFrameInfoCallback)(
|
||||
void* /* user data */,
|
||||
const FlutterFrameInfo* /* frame info */);
|
||||
|
||||
/// Callback for when a frame buffer object is requested with necessary
|
||||
/// information for partial repaint.
|
||||
typedef void (*FlutterFrameBufferWithDamageCallback)(
|
||||
void* /* user data */,
|
||||
const intptr_t /* fbo id */,
|
||||
FlutterDamage* /* existing damage */);
|
||||
|
||||
/// This information is passed to the embedder when a surface is presented.
|
||||
///
|
||||
/// See: \ref FlutterOpenGLRendererConfig.present_with_info.
|
||||
@ -457,6 +474,10 @@ typedef struct {
|
||||
size_t struct_size;
|
||||
/// Id of the fbo backing the surface that was presented.
|
||||
uint32_t fbo_id;
|
||||
/// Damage representing the area that the compositor needs to render.
|
||||
FlutterDamage frame_damage;
|
||||
/// Damage used to set the buffer's damage region.
|
||||
FlutterDamage buffer_damage;
|
||||
} FlutterPresentInfo;
|
||||
|
||||
/// Callback for when a surface is presented.
|
||||
@ -471,7 +492,10 @@ typedef struct {
|
||||
BoolCallback clear_current;
|
||||
/// Specifying one (and only one) of `present` or `present_with_info` is
|
||||
/// required. Specifying both is an error and engine initialization will be
|
||||
/// terminated. The return value indicates success of the present call.
|
||||
/// terminated. The return value indicates success of the present call. If
|
||||
/// the intent is to use dirty region management, present_with_info must be
|
||||
/// defined as present will not succeed in communicating information about
|
||||
/// damage.
|
||||
BoolCallback present;
|
||||
/// Specifying one (and only one) of the `fbo_callback` or
|
||||
/// `fbo_with_frame_info_callback` is required. Specifying both is an error
|
||||
@ -520,8 +544,27 @@ typedef struct {
|
||||
/// required. Specifying both is an error and engine initialization will be
|
||||
/// terminated. When using this variant, the embedder is passed a
|
||||
/// `FlutterPresentInfo` struct that the embedder can use to release any
|
||||
/// resources. The return value indicates success of the present call.
|
||||
/// resources. The return value indicates success of the present call. This
|
||||
/// callback is essential for dirty region management. If not defined, all the
|
||||
/// pixels on the screen will be rendered at every frame (regardless of
|
||||
/// whether damage is actually being computed or not). This is because the
|
||||
/// information that is passed along to the callback contains the frame and
|
||||
/// buffer damage that are essential for dirty region management.
|
||||
BoolPresentInfoCallback present_with_info;
|
||||
/// Specifying this callback is a requirement for dirty region management.
|
||||
/// Dirty region management will only render the areas of the screen that have
|
||||
/// changed in between frames, greatly reducing rendering times and energy
|
||||
/// consumption. To take advantage of these benefits, it is necessary to
|
||||
/// define populate_existing_damage as a callback that takes user
|
||||
/// data, an FBO ID, and an existing damage FlutterDamage. The callback should
|
||||
/// use the given FBO ID to identify the FBO's exisiting damage (i.e. areas
|
||||
/// that have changed since the FBO was last used) and use it to populate the
|
||||
/// given existing damage variable. This callback is dependent on either
|
||||
/// fbo_callback or fbo_with_frame_info_callback being defined as they are
|
||||
/// responsible for providing populate_existing_damage with the FBO's
|
||||
/// ID. Not specifying populate_existing_damage will result in full
|
||||
/// repaint (i.e. rendering all the pixels on the screen at every frame).
|
||||
FlutterFrameBufferWithDamageCallback populate_existing_damage;
|
||||
} FlutterOpenGLRendererConfig;
|
||||
|
||||
/// Alias for id<MTLDevice>.
|
||||
|
||||
@ -19,7 +19,8 @@ EmbedderSurfaceGL::EmbedderSurfaceGL(
|
||||
if (!gl_dispatch_table_.gl_make_current_callback ||
|
||||
!gl_dispatch_table_.gl_clear_current_callback ||
|
||||
!gl_dispatch_table_.gl_present_callback ||
|
||||
!gl_dispatch_table_.gl_fbo_callback) {
|
||||
!gl_dispatch_table_.gl_fbo_callback ||
|
||||
!gl_dispatch_table_.gl_populate_existing_damage) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -46,12 +47,16 @@ bool EmbedderSurfaceGL::GLContextClearCurrent() {
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
bool EmbedderSurfaceGL::GLContextPresent(const GLPresentInfo& present_info) {
|
||||
return gl_dispatch_table_.gl_present_callback(present_info.fbo_id);
|
||||
// Pass the present information to the embedder present callback.
|
||||
return gl_dispatch_table_.gl_present_callback(present_info);
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
intptr_t EmbedderSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
return gl_dispatch_table_.gl_fbo_callback(frame_info);
|
||||
GLFBOInfo EmbedderSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const {
|
||||
// Get the FBO ID using the gl_fbo_callback and then get exiting damage by
|
||||
// passing that ID to the gl_populate_existing_damage.
|
||||
return gl_dispatch_table_.gl_populate_existing_damage(
|
||||
gl_dispatch_table_.gl_fbo_callback(frame_info));
|
||||
}
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
|
||||
@ -18,12 +18,13 @@ class EmbedderSurfaceGL final : public EmbedderSurface,
|
||||
struct GLDispatchTable {
|
||||
std::function<bool(void)> gl_make_current_callback; // required
|
||||
std::function<bool(void)> gl_clear_current_callback; // required
|
||||
std::function<bool(uint32_t)> gl_present_callback; // required
|
||||
std::function<bool(GLPresentInfo)> gl_present_callback; // required
|
||||
std::function<intptr_t(GLFrameInfo)> gl_fbo_callback; // required
|
||||
std::function<bool(void)> gl_make_resource_current_callback; // optional
|
||||
std::function<SkMatrix(void)>
|
||||
gl_surface_transformation_callback; // optional
|
||||
std::function<void*(const char*)> gl_proc_resolver; // optional
|
||||
gl_surface_transformation_callback; // optional
|
||||
std::function<void*(const char*)> gl_proc_resolver; // optional
|
||||
std::function<GLFBOInfo(intptr_t)> gl_populate_existing_damage; // required
|
||||
};
|
||||
|
||||
EmbedderSurfaceGL(
|
||||
@ -59,7 +60,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface,
|
||||
bool GLContextPresent(const GLPresentInfo& present_info) override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
intptr_t GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
GLFBOInfo GLContextFBO(GLFrameInfo frame_info) const override;
|
||||
|
||||
// |GPUSurfaceGLDelegate|
|
||||
bool GLContextFBOResetAfterPresent() const override;
|
||||
|
||||
@ -52,13 +52,14 @@ EmbedderConfigBuilder::EmbedderConfigBuilder(
|
||||
opengl_renderer_config_.present_with_info =
|
||||
[](void* context, const FlutterPresentInfo* present_info) -> bool {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)->GLPresent(
|
||||
present_info->fbo_id);
|
||||
*present_info);
|
||||
};
|
||||
opengl_renderer_config_.fbo_with_frame_info_callback =
|
||||
[](void* context, const FlutterFrameInfo* frame_info) -> uint32_t {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)->GLGetFramebuffer(
|
||||
*frame_info);
|
||||
};
|
||||
opengl_renderer_config_.populate_existing_damage = nullptr;
|
||||
opengl_renderer_config_.make_resource_current = [](void* context) -> bool {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLMakeResourceCurrent();
|
||||
@ -160,7 +161,10 @@ void EmbedderConfigBuilder::SetOpenGLPresentCallBack() {
|
||||
FML_CHECK(renderer_config_.type == FlutterRendererType::kOpenGL);
|
||||
renderer_config_.open_gl.present = [](void* context) -> bool {
|
||||
// passing a placeholder fbo_id.
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)->GLPresent(0);
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)->GLPresent(
|
||||
FlutterPresentInfo{
|
||||
.fbo_id = 0,
|
||||
});
|
||||
};
|
||||
#endif
|
||||
}
|
||||
@ -312,6 +316,10 @@ void EmbedderConfigBuilder::SetupVsyncCallback() {
|
||||
};
|
||||
}
|
||||
|
||||
FlutterRendererConfig& EmbedderConfigBuilder::GetRendererConfig() {
|
||||
return renderer_config_;
|
||||
}
|
||||
|
||||
void EmbedderConfigBuilder::SetRenderTaskRunner(
|
||||
const FlutterTaskRunnerDescription* runner) {
|
||||
if (runner == nullptr) {
|
||||
|
||||
@ -104,6 +104,8 @@ class EmbedderConfigBuilder {
|
||||
|
||||
FlutterCompositor& GetCompositor();
|
||||
|
||||
FlutterRendererConfig& GetRendererConfig();
|
||||
|
||||
void SetRenderTargetType(
|
||||
EmbedderTestBackingStoreProducer::RenderTargetType type,
|
||||
FlutterSoftwarePixelFormat software_pixfmt = kNative32);
|
||||
|
||||
@ -39,7 +39,7 @@ bool EmbedderTestContextGL::GLClearCurrent() {
|
||||
return gl_surface_->ClearCurrent();
|
||||
}
|
||||
|
||||
bool EmbedderTestContextGL::GLPresent(uint32_t fbo_id) {
|
||||
bool EmbedderTestContextGL::GLPresent(FlutterPresentInfo present_info) {
|
||||
FML_CHECK(gl_surface_) << "GL surface must be initialized.";
|
||||
gl_surface_present_count_++;
|
||||
|
||||
@ -50,7 +50,7 @@ bool EmbedderTestContextGL::GLPresent(uint32_t fbo_id) {
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(fbo_id);
|
||||
callback(present_info);
|
||||
}
|
||||
|
||||
FireRootSurfacePresentCallbackIfPresent(
|
||||
@ -64,6 +64,12 @@ void EmbedderTestContextGL::SetGLGetFBOCallback(GLGetFBOCallback callback) {
|
||||
gl_get_fbo_callback_ = callback;
|
||||
}
|
||||
|
||||
void EmbedderTestContextGL::SetGLPopulateExistingDamageCallback(
|
||||
GLPopulateExistingDamageCallback callback) {
|
||||
std::scoped_lock lock(gl_callback_mutex_);
|
||||
gl_populate_existing_damage_callback_ = callback;
|
||||
}
|
||||
|
||||
void EmbedderTestContextGL::SetGLPresentCallback(GLPresentCallback callback) {
|
||||
std::scoped_lock lock(gl_callback_mutex_);
|
||||
gl_present_callback_ = callback;
|
||||
@ -86,6 +92,22 @@ uint32_t EmbedderTestContextGL::GLGetFramebuffer(FlutterFrameInfo frame_info) {
|
||||
return gl_surface_->GetFramebuffer(size.width, size.height);
|
||||
}
|
||||
|
||||
void EmbedderTestContextGL::GLPopulateExistingDamage(
|
||||
const intptr_t id,
|
||||
FlutterDamage* existing_damage) {
|
||||
FML_CHECK(gl_surface_) << "GL surface must be initialized.";
|
||||
|
||||
GLPopulateExistingDamageCallback callback;
|
||||
{
|
||||
std::scoped_lock lock(gl_callback_mutex_);
|
||||
callback = gl_populate_existing_damage_callback_;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(id, existing_damage);
|
||||
}
|
||||
}
|
||||
|
||||
bool EmbedderTestContextGL::GLMakeResourceCurrent() {
|
||||
FML_CHECK(gl_surface_) << "GL surface must be initialized.";
|
||||
return gl_surface_->MakeResourceCurrent();
|
||||
|
||||
@ -14,7 +14,10 @@ namespace testing {
|
||||
class EmbedderTestContextGL : public EmbedderTestContext {
|
||||
public:
|
||||
using GLGetFBOCallback = std::function<void(FlutterFrameInfo frame_info)>;
|
||||
using GLPresentCallback = std::function<void(uint32_t fbo_id)>;
|
||||
using GLPopulateExistingDamageCallback =
|
||||
std::function<void(intptr_t id, FlutterDamage* existing_damage)>;
|
||||
using GLPresentCallback =
|
||||
std::function<void(FlutterPresentInfo present_info)>;
|
||||
|
||||
explicit EmbedderTestContextGL(std::string assets_path = "");
|
||||
|
||||
@ -38,6 +41,9 @@ class EmbedderTestContextGL : public EmbedderTestContext {
|
||||
///
|
||||
void SetGLGetFBOCallback(GLGetFBOCallback callback);
|
||||
|
||||
void SetGLPopulateExistingDamageCallback(
|
||||
GLPopulateExistingDamageCallback callback);
|
||||
|
||||
uint32_t GetWindowFBOId() const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
@ -53,6 +59,9 @@ class EmbedderTestContextGL : public EmbedderTestContext {
|
||||
///
|
||||
void SetGLPresentCallback(GLPresentCallback callback);
|
||||
|
||||
void GLPopulateExistingDamage(const intptr_t id,
|
||||
FlutterDamage* existing_damage);
|
||||
|
||||
protected:
|
||||
virtual void SetupCompositor() override;
|
||||
|
||||
@ -65,6 +74,7 @@ class EmbedderTestContextGL : public EmbedderTestContext {
|
||||
std::mutex gl_callback_mutex_;
|
||||
GLGetFBOCallback gl_get_fbo_callback_;
|
||||
GLPresentCallback gl_present_callback_;
|
||||
GLPopulateExistingDamageCallback gl_populate_existing_damage_callback_;
|
||||
|
||||
void SetupSurface(SkISize surface_size) override;
|
||||
|
||||
@ -72,7 +82,7 @@ class EmbedderTestContextGL : public EmbedderTestContext {
|
||||
|
||||
bool GLClearCurrent();
|
||||
|
||||
bool GLPresent(uint32_t fbo_id);
|
||||
bool GLPresent(FlutterPresentInfo present_info);
|
||||
|
||||
uint32_t GLGetFramebuffer(FlutterFrameInfo frame_info);
|
||||
|
||||
|
||||
@ -3106,6 +3106,72 @@ TEST_F(EmbedderTest, MustNotRunWithBothPresentCallbacksSet) {
|
||||
ASSERT_FALSE(engine.is_valid());
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, MustStillRunWhenPopulateExistingDamageIsNotProvided) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(1, 1));
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage = nullptr;
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, MustRunWhenPopulateExistingDamageIsProvided) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(1, 1));
|
||||
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage =
|
||||
[](void* context, const intptr_t id,
|
||||
FlutterDamage* existing_damage) -> void {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLPopulateExistingDamage(id, existing_damage);
|
||||
};
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, MustRunWithPopulateExistingDamageAndFBOCallback) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(1, 1));
|
||||
builder.GetRendererConfig().open_gl.fbo_callback =
|
||||
[](void* context) -> uint32_t { return 0; };
|
||||
builder.GetRendererConfig().open_gl.fbo_with_frame_info_callback = nullptr;
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage =
|
||||
[](void* context, const intptr_t id,
|
||||
FlutterDamage* existing_damage) -> void {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLPopulateExistingDamage(id, existing_damage);
|
||||
};
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest,
|
||||
MustNotRunWhenPopulateExistingDamageButNoOtherFBOCallback) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(1, 1));
|
||||
builder.GetRendererConfig().open_gl.fbo_callback = nullptr;
|
||||
builder.GetRendererConfig().open_gl.fbo_with_frame_info_callback = nullptr;
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage =
|
||||
[](void* context, const intptr_t id,
|
||||
FlutterDamage* existing_damage) -> void {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLPopulateExistingDamage(id, existing_damage);
|
||||
};
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_FALSE(engine.is_valid());
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, PresentInfoContainsValidFBOId) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
@ -3140,8 +3206,8 @@ TEST_F(EmbedderTest, PresentInfoContainsValidFBOId) {
|
||||
const uint32_t window_fbo_id =
|
||||
static_cast<EmbedderTestContextGL&>(context).GetWindowFBOId();
|
||||
static_cast<EmbedderTestContextGL&>(context).SetGLPresentCallback(
|
||||
[window_fbo_id = window_fbo_id](uint32_t fbo_id) {
|
||||
ASSERT_EQ(fbo_id, window_fbo_id);
|
||||
[window_fbo_id = window_fbo_id](FlutterPresentInfo present_info) {
|
||||
ASSERT_EQ(present_info.fbo_id, window_fbo_id);
|
||||
|
||||
frame_latch.CountDown();
|
||||
});
|
||||
@ -3149,6 +3215,315 @@ TEST_F(EmbedderTest, PresentInfoContainsValidFBOId) {
|
||||
frame_latch.Wait();
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest,
|
||||
PresentInfoReceivesFullDamageWhenExistingDamageIsWholeScreen) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetDartEntrypoint("render_gradient");
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage =
|
||||
[](void* context, const intptr_t id,
|
||||
FlutterDamage* existing_damage) -> void {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLPopulateExistingDamage(id, existing_damage);
|
||||
};
|
||||
|
||||
// Return existing damage as the entire screen on purpose.
|
||||
static_cast<EmbedderTestContextGL&>(context)
|
||||
.SetGLPopulateExistingDamageCallback(
|
||||
[](const intptr_t id, FlutterDamage* existing_damage_ptr) {
|
||||
const size_t num_rects = 1;
|
||||
FlutterRect existing_damage_rects[num_rects] = {
|
||||
FlutterRect{0, 0, 800, 600}};
|
||||
existing_damage_ptr->num_rects = num_rects;
|
||||
existing_damage_ptr->damage = existing_damage_rects;
|
||||
});
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
|
||||
// First frame should be entirely rerendered.
|
||||
static_cast<EmbedderTestContextGL&>(context).SetGLPresentCallback(
|
||||
[](FlutterPresentInfo present_info) {
|
||||
const size_t num_rects = 1;
|
||||
ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->right, 800);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
|
||||
|
||||
ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
|
||||
});
|
||||
|
||||
// Send a window metrics events so frames may be scheduled.
|
||||
FlutterWindowMetricsEvent event = {};
|
||||
event.struct_size = sizeof(event);
|
||||
event.width = 800;
|
||||
event.height = 600;
|
||||
event.pixel_ratio = 1.0;
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
|
||||
// Because it's the same as the first frame, the second frame damage should
|
||||
// be empty but, because there was a full existing buffer damage, the buffer
|
||||
// damage should be the entire screen.
|
||||
static_cast<EmbedderTestContextGL&>(context).SetGLPresentCallback(
|
||||
[](FlutterPresentInfo present_info) {
|
||||
const size_t num_rects = 1;
|
||||
ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->right, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->bottom, 0);
|
||||
|
||||
ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
|
||||
});
|
||||
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, PresentInfoReceivesEmptyDamage) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetDartEntrypoint("render_gradient");
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage =
|
||||
[](void* context, const intptr_t id,
|
||||
FlutterDamage* existing_damage) -> void {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLPopulateExistingDamage(id, existing_damage);
|
||||
};
|
||||
|
||||
// Return no existing damage on purpose.
|
||||
static_cast<EmbedderTestContextGL&>(context)
|
||||
.SetGLPopulateExistingDamageCallback(
|
||||
[](const intptr_t id, FlutterDamage* existing_damage_ptr) {
|
||||
const size_t num_rects = 1;
|
||||
FlutterRect existing_damage_rects[num_rects] = {
|
||||
FlutterRect{0, 0, 0, 0}};
|
||||
existing_damage_ptr->num_rects = num_rects;
|
||||
existing_damage_ptr->damage = existing_damage_rects;
|
||||
});
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
|
||||
// First frame should be entirely rerendered.
|
||||
static_cast<EmbedderTestContextGL&>(context).SetGLPresentCallback(
|
||||
[](FlutterPresentInfo present_info) {
|
||||
const size_t num_rects = 1;
|
||||
ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->right, 800);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
|
||||
|
||||
ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
|
||||
});
|
||||
|
||||
// Send a window metrics events so frames may be scheduled.
|
||||
FlutterWindowMetricsEvent event = {};
|
||||
event.struct_size = sizeof(event);
|
||||
event.width = 800;
|
||||
event.height = 600;
|
||||
event.pixel_ratio = 1.0;
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
|
||||
// Because it's the same as the first frame, the second frame should not be
|
||||
// rerendered assuming there is no existing damage.
|
||||
static_cast<EmbedderTestContextGL&>(context).SetGLPresentCallback(
|
||||
[](FlutterPresentInfo present_info) {
|
||||
const size_t num_rects = 1;
|
||||
ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->right, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->bottom, 0);
|
||||
|
||||
ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->right, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->bottom, 0);
|
||||
});
|
||||
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, PresentInfoReceivesPartialDamage) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetDartEntrypoint("render_gradient");
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage =
|
||||
[](void* context, const intptr_t id,
|
||||
FlutterDamage* existing_damage) -> void {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLPopulateExistingDamage(id, existing_damage);
|
||||
};
|
||||
|
||||
// Return existing damage as only part of the screen on purpose.
|
||||
static_cast<EmbedderTestContextGL&>(context)
|
||||
.SetGLPopulateExistingDamageCallback(
|
||||
[](const intptr_t id, FlutterDamage* existing_damage_ptr) {
|
||||
const size_t num_rects = 1;
|
||||
FlutterRect existing_damage_rects[num_rects] = {
|
||||
FlutterRect{200, 150, 400, 300}};
|
||||
existing_damage_ptr->num_rects = num_rects;
|
||||
existing_damage_ptr->damage = existing_damage_rects;
|
||||
});
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
|
||||
// First frame should be entirely rerendered.
|
||||
static_cast<EmbedderTestContextGL&>(context).SetGLPresentCallback(
|
||||
[](FlutterPresentInfo present_info) {
|
||||
const size_t num_rects = 1;
|
||||
ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->right, 800);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->bottom, 600);
|
||||
|
||||
ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->right, 800);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->bottom, 600);
|
||||
});
|
||||
|
||||
// Send a window metrics events so frames may be scheduled.
|
||||
FlutterWindowMetricsEvent event = {};
|
||||
event.struct_size = sizeof(event);
|
||||
event.width = 800;
|
||||
event.height = 600;
|
||||
event.pixel_ratio = 1.0;
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
|
||||
// Because it's the same as the first frame, the second frame damage should be
|
||||
// empty but, because there was a partial existing damage, the buffer damage
|
||||
// should represent that partial damage area.
|
||||
static_cast<EmbedderTestContextGL&>(context).SetGLPresentCallback(
|
||||
[](FlutterPresentInfo present_info) {
|
||||
const size_t num_rects = 1;
|
||||
ASSERT_EQ(present_info.frame_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->left, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->top, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->right, 0);
|
||||
ASSERT_EQ(present_info.frame_damage.damage->bottom, 0);
|
||||
|
||||
ASSERT_EQ(present_info.buffer_damage.num_rects, num_rects);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->left, 200);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->top, 150);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->right, 400);
|
||||
ASSERT_EQ(present_info.buffer_damage.damage->bottom, 300);
|
||||
});
|
||||
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, PopulateExistingDamageReceivesValidID) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetDartEntrypoint("render_gradient");
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage =
|
||||
[](void* context, const intptr_t id,
|
||||
FlutterDamage* existing_damage) -> void {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLPopulateExistingDamage(id, existing_damage);
|
||||
};
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
|
||||
const uint32_t window_fbo_id =
|
||||
static_cast<EmbedderTestContextGL&>(context).GetWindowFBOId();
|
||||
static_cast<EmbedderTestContextGL&>(context)
|
||||
.SetGLPopulateExistingDamageCallback(
|
||||
[window_fbo_id = window_fbo_id](intptr_t id,
|
||||
FlutterDamage* existing_damage) {
|
||||
ASSERT_EQ(id, window_fbo_id);
|
||||
});
|
||||
|
||||
// Send a window metrics events so frames may be scheduled.
|
||||
FlutterWindowMetricsEvent event = {};
|
||||
event.struct_size = sizeof(event);
|
||||
event.width = 800;
|
||||
event.height = 600;
|
||||
event.pixel_ratio = 1.0;
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, PopulateExistingDamageReceivesInvalidID) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
EmbedderConfigBuilder builder(context);
|
||||
builder.SetOpenGLRendererConfig(SkISize::Make(800, 600));
|
||||
builder.SetDartEntrypoint("render_gradient");
|
||||
builder.GetRendererConfig().open_gl.populate_existing_damage =
|
||||
[](void* context, const intptr_t id,
|
||||
FlutterDamage* existing_damage) -> void {
|
||||
return reinterpret_cast<EmbedderTestContextGL*>(context)
|
||||
->GLPopulateExistingDamage(id, existing_damage);
|
||||
};
|
||||
|
||||
// Return a bad FBO ID on purpose.
|
||||
builder.GetRendererConfig().open_gl.fbo_with_frame_info_callback =
|
||||
[](void* context, const FlutterFrameInfo* frame_info) -> uint32_t {
|
||||
return 123;
|
||||
};
|
||||
|
||||
auto engine = builder.LaunchEngine();
|
||||
ASSERT_TRUE(engine.is_valid());
|
||||
|
||||
context.AddNativeCallback("SignalNativeTest",
|
||||
CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
|
||||
/* Nothing to do. */
|
||||
}));
|
||||
|
||||
const uint32_t window_fbo_id =
|
||||
static_cast<EmbedderTestContextGL&>(context).GetWindowFBOId();
|
||||
static_cast<EmbedderTestContextGL&>(context)
|
||||
.SetGLPopulateExistingDamageCallback(
|
||||
[window_fbo_id = window_fbo_id](intptr_t id,
|
||||
FlutterDamage* existing_damage) {
|
||||
ASSERT_NE(id, window_fbo_id);
|
||||
});
|
||||
|
||||
// Send a window metrics events so frames may be scheduled.
|
||||
FlutterWindowMetricsEvent event = {};
|
||||
event.struct_size = sizeof(event);
|
||||
event.width = 800;
|
||||
event.height = 600;
|
||||
event.pixel_ratio = 1.0;
|
||||
ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event),
|
||||
kSuccess);
|
||||
}
|
||||
|
||||
TEST_F(EmbedderTest, SetSingleDisplayConfigurationWithDisplayId) {
|
||||
auto& context = GetEmbedderContext(EmbedderTestContextType::kOpenGLContext);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user