From 1892e031f2c5d7040ea7db00fc8eb59bba43ba55 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 26 Aug 2020 15:53:01 -0700 Subject: [PATCH] [embedder] Add gl present callback that takes present info (#20672) --- shell/common/shell_test_platform_view_gl.cc | 4 +- shell/common/shell_test_platform_view_gl.h | 2 +- shell/gpu/gpu_surface_gl.cc | 27 ++++++---- shell/gpu/gpu_surface_gl.h | 2 + shell/gpu/gpu_surface_gl_delegate.h | 2 +- shell/platform/android/android_surface_gl.cc | 2 +- shell/platform/android/android_surface_gl.h | 2 +- .../android/surface/android_surface_mock.cc | 2 +- .../android/surface/android_surface_mock.h | 2 +- shell/platform/darwin/ios/ios_surface_gl.h | 2 +- shell/platform/darwin/ios/ios_surface_gl.mm | 2 +- shell/platform/embedder/embedder.cc | 31 +++++------ shell/platform/embedder/embedder.h | 27 +++++++++- .../platform/embedder/embedder_safe_access.h | 8 +++ .../platform/embedder/embedder_surface_gl.cc | 4 +- shell/platform/embedder/embedder_surface_gl.h | 4 +- .../embedder/tests/embedder_config_builder.cc | 15 +++++- .../embedder/tests/embedder_config_builder.h | 6 +++ .../embedder/tests/embedder_test_context.cc | 29 ++++++++-- .../embedder/tests/embedder_test_context.h | 22 +++++++- .../embedder/tests/embedder_unittests.cc | 53 +++++++++++++++++++ testing/test_gl_surface.cc | 23 ++++---- testing/test_gl_surface.h | 4 +- 23 files changed, 216 insertions(+), 59 deletions(-) diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 2adcbe6b5..2a7e7d9bf 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -55,13 +55,13 @@ bool ShellTestPlatformViewGL::GLContextClearCurrent() { } // |GPUSurfaceGLDelegate| -bool ShellTestPlatformViewGL::GLContextPresent() { +bool ShellTestPlatformViewGL::GLContextPresent(uint32_t fbo_id) { return gl_surface_.Present(); } // |GPUSurfaceGLDelegate| intptr_t ShellTestPlatformViewGL::GLContextFBO(GLFrameInfo frame_info) const { - return gl_surface_.GetFramebuffer(); + return gl_surface_.GetFramebuffer(frame_info.width, frame_info.height); } // |GPUSurfaceGLDelegate| diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index 5ca969531..a4e020915 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -53,7 +53,7 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, bool GLContextClearCurrent() override; // |GPUSurfaceGLDelegate| - bool GLContextPresent() override; + bool GLContextPresent(uint32_t fbo_id) override; // |GPUSurfaceGLDelegate| intptr_t GLContextFBO(GLFrameInfo frame_info) const override; diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index 5386683a2..b7086654e 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -125,6 +125,7 @@ GPUSurfaceGL::~GPUSurfaceGL() { } onscreen_surface_ = nullptr; + fbo_id_ = 0; if (context_owner_) { context_->releaseResourcesAndAbandonContext(); } @@ -196,6 +197,7 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { // Either way, we need to get rid of previous surface. onscreen_surface_ = nullptr; + fbo_id_ = 0; if (size.isEmpty()) { FML_LOG(ERROR) << "Cannot create surfaces of empty size."; @@ -206,11 +208,11 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { GLFrameInfo frame_info = {static_cast(size.width()), static_cast(size.height())}; - onscreen_surface = - WrapOnscreenSurface(context_.get(), // GL context - size, // root surface size - delegate_->GLContextFBO(frame_info) // window FBO ID - ); + const uint32_t fbo_id = delegate_->GLContextFBO(frame_info); + onscreen_surface = WrapOnscreenSurface(context_.get(), // GL context + size, // root surface size + fbo_id // window FBO ID + ); if (onscreen_surface == nullptr) { // If the onscreen surface could not be wrapped. There is absolutely no @@ -220,6 +222,7 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { } onscreen_surface_ = std::move(onscreen_surface); + fbo_id_ = fbo_id; return true; } @@ -281,7 +284,7 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { onscreen_surface_->getCanvas()->flush(); } - if (!delegate_->GLContextPresent()) { + if (!delegate_->GLContextPresent(fbo_id_)) { return false; } @@ -294,17 +297,19 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { // The FBO has changed, ask the delegate for the new FBO and do a surface // re-wrap. - auto new_onscreen_surface = WrapOnscreenSurface( - context_.get(), // GL context - current_size, // root surface size - delegate_->GLContextFBO(frame_info) // window FBO ID - ); + const uint32_t fbo_id = delegate_->GLContextFBO(frame_info); + auto new_onscreen_surface = + WrapOnscreenSurface(context_.get(), // GL context + current_size, // root surface size + fbo_id // window FBO ID + ); if (!new_onscreen_surface) { return false; } onscreen_surface_ = std::move(new_onscreen_surface); + fbo_id_ = fbo_id; } return true; diff --git a/shell/gpu/gpu_surface_gl.h b/shell/gpu/gpu_surface_gl.h index 11ecfb9ee..aab085c92 100644 --- a/shell/gpu/gpu_surface_gl.h +++ b/shell/gpu/gpu_surface_gl.h @@ -52,6 +52,8 @@ class GPUSurfaceGL : public Surface { GPUSurfaceGLDelegate* delegate_; sk_sp context_; sk_sp onscreen_surface_; + /// FBO backing the current `onscreen_surface_`. + uint32_t fbo_id_; bool context_owner_; // 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 diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index 7fd111dd3..8f2c3df04 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -37,7 +37,7 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // 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() = 0; + virtual bool GLContextPresent(uint32_t fbo_id) = 0; // The ID of the main window bound framebuffer. Typically FBO0. virtual intptr_t GLContextFBO(GLFrameInfo frame_info) const = 0; diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index b1bd5eec6..97ba6186f 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -113,7 +113,7 @@ bool AndroidSurfaceGL::GLContextClearCurrent() { return android_context_->ClearCurrent(); } -bool AndroidSurfaceGL::GLContextPresent() { +bool AndroidSurfaceGL::GLContextPresent(uint32_t fbo_id) { FML_DCHECK(IsValid()); FML_DCHECK(onscreen_surface_); return onscreen_surface_->SwapBuffers(); diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index 1ebbf8f60..33132191d 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -56,7 +56,7 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool GLContextClearCurrent() override; // |GPUSurfaceGLDelegate| - bool GLContextPresent() override; + bool GLContextPresent(uint32_t fbo_id) override; // |GPUSurfaceGLDelegate| intptr_t GLContextFBO(GLFrameInfo frame_info) const override; diff --git a/shell/platform/android/surface/android_surface_mock.cc b/shell/platform/android/surface/android_surface_mock.cc index f7098f7c5..8e76b5ed7 100644 --- a/shell/platform/android/surface/android_surface_mock.cc +++ b/shell/platform/android/surface/android_surface_mock.cc @@ -14,7 +14,7 @@ bool AndroidSurfaceMock::GLContextClearCurrent() { return true; } -bool AndroidSurfaceMock::GLContextPresent() { +bool AndroidSurfaceMock::GLContextPresent(uint32_t fbo_id) { return true; } diff --git a/shell/platform/android/surface/android_surface_mock.h b/shell/platform/android/surface/android_surface_mock.h index 7d5fddbf3..359d22833 100644 --- a/shell/platform/android/surface/android_surface_mock.h +++ b/shell/platform/android/surface/android_surface_mock.h @@ -45,7 +45,7 @@ class AndroidSurfaceMock final : public GPUSurfaceGLDelegate, bool GLContextClearCurrent() override; // |GPUSurfaceGLDelegate| - bool GLContextPresent() override; + bool GLContextPresent(uint32_t fbo_id) override; // |GPUSurfaceGLDelegate| intptr_t GLContextFBO(GLFrameInfo frame_info) const override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index 745b5d007..79e453445 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -40,7 +40,7 @@ class IOSSurfaceGL final : public IOSSurface, public GPUSurfaceGLDelegate { bool GLContextClearCurrent() override; // |GPUSurfaceGLDelegate| - bool GLContextPresent() override; + bool GLContextPresent(uint32_t fbo_id) override; // |GPUSurfaceGLDelegate| intptr_t GLContextFBO(GLFrameInfo frame_info) const override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 05d2d3135..6178e73f0 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -77,7 +77,7 @@ bool IOSSurfaceGL::GLContextClearCurrent() { } // |GPUSurfaceGLDelegate| -bool IOSSurfaceGL::GLContextPresent() { +bool IOSSurfaceGL::GLContextPresent(uint32_t fbo_id) { TRACE_EVENT0("flutter", "IOSSurfaceGL::GLContextPresent"); return IsValid() && render_target_->PresentRenderBuffer(); } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 8a94ee5eb..aae3ccd4e 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -91,19 +91,11 @@ static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) { const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; - if (SAFE_ACCESS(open_gl_config, make_current, nullptr) == nullptr || - SAFE_ACCESS(open_gl_config, clear_current, nullptr) == nullptr || - SAFE_ACCESS(open_gl_config, present, nullptr) == nullptr) { - return false; - } - - bool fbo_callback_exists = - SAFE_ACCESS(open_gl_config, fbo_callback, nullptr) != nullptr; - bool fbo_with_frame_info_callback_exists = - SAFE_ACCESS(open_gl_config, fbo_with_frame_info_callback, nullptr) != - nullptr; - // only one of these callbacks must exist. - if (fbo_callback_exists == fbo_with_frame_info_callback_exists) { + if (!SAFE_EXISTS(open_gl_config, make_current) || + !SAFE_EXISTS(open_gl_config, clear_current) || + !SAFE_EXISTS_ONE_OF(open_gl_config, fbo_callback, + fbo_with_frame_info_callback) || + !SAFE_EXISTS_ONE_OF(open_gl_config, present, present_with_info)) { return false; } @@ -173,8 +165,17 @@ InferOpenGLPlatformViewCreationCallback( auto gl_clear_current = [ptr = config->open_gl.clear_current, user_data]() -> bool { return ptr(user_data); }; - auto gl_present = [ptr = config->open_gl.present, 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 { + if (present) { + return present(user_data); + } else { + FlutterPresentInfo present_info = {}; + present_info.struct_size = sizeof(FlutterPresentInfo); + present_info.fbo_id = fbo_id; + return present_with_info(user_data, &present_info); + } }; auto gl_fbo_callback = diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 9e468e484..97df3d246 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -346,7 +346,7 @@ typedef struct { /// This information is passed to the embedder when requesting a frame buffer /// object. /// -/// See: \ref FlutterSoftwareRendererConfig.fbo_with_frame_info_callback. +/// See: \ref FlutterOpenGLRendererConfig.fbo_with_frame_info_callback. typedef struct { /// The size of this struct. Must be sizeof(FlutterFrameInfo). size_t struct_size; @@ -354,15 +354,34 @@ typedef struct { FlutterUIntSize size; } FlutterFrameInfo; +/// Callback for when a frame buffer object is requested. typedef uint32_t (*UIntFrameInfoCallback)( void* /* user data */, const FlutterFrameInfo* /* frame info */); +/// This information is passed to the embedder when a surface is presented. +/// +/// See: \ref FlutterOpenGLRendererConfig.present_with_info. +typedef struct { + /// The size of this struct. Must be sizeof(FlutterFrameInfo). + size_t struct_size; + /// Id of the fbo backing the surface that was presented. + uint32_t fbo_id; +} FlutterPresentInfo; + +/// Callback for when a surface is presented. +typedef bool (*BoolPresentInfoCallback)( + void* /* user data */, + const FlutterPresentInfo* /* present info */); + typedef struct { /// The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig). size_t struct_size; BoolCallback make_current; 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. BoolCallback present; /// Specifying one (and only one) of the `fbo_callback` or /// `fbo_with_frame_info_callback` is required. Specifying both is an error @@ -407,6 +426,12 @@ typedef struct { /// `FlutterFrameInfo` struct that indicates the properties of the surface /// that flutter will acquire from the returned fbo. UIntFrameInfoCallback fbo_with_frame_info_callback; + /// Specifying one (and only one) of `present` or `present_with_info` is + /// 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. + BoolPresentInfoCallback present_with_info; } FlutterOpenGLRendererConfig; typedef struct { diff --git a/shell/platform/embedder/embedder_safe_access.h b/shell/platform/embedder/embedder_safe_access.h index 783eb44a8..e9673a04a 100644 --- a/shell/platform/embedder/embedder_safe_access.h +++ b/shell/platform/embedder/embedder_safe_access.h @@ -17,4 +17,12 @@ return static_castmember)>((default_value)); \ })() +/// Checks if the member exists. +#define SAFE_EXISTS(pointer, member) \ + (SAFE_ACCESS(pointer, member, nullptr) != nullptr) + +/// Checks if exactly one of member1 or member2 exists. +#define SAFE_EXISTS_ONE_OF(pointer, member1, member2) \ + (SAFE_EXISTS(pointer, member1) != SAFE_EXISTS(pointer, member2)) + #endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_SAFE_ACCESS_H_ diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 4a28d68ec..6b88fcb9d 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -45,8 +45,8 @@ bool EmbedderSurfaceGL::GLContextClearCurrent() { } // |GPUSurfaceGLDelegate| -bool EmbedderSurfaceGL::GLContextPresent() { - return gl_dispatch_table_.gl_present_callback(); +bool EmbedderSurfaceGL::GLContextPresent(uint32_t fbo_id) { + return gl_dispatch_table_.gl_present_callback(fbo_id); } // |GPUSurfaceGLDelegate| diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index 6a4b8ed94..42141d767 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -18,7 +18,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, struct GLDispatchTable { std::function gl_make_current_callback; // required std::function gl_clear_current_callback; // required - std::function gl_present_callback; // required + std::function gl_present_callback; // required std::function gl_fbo_callback; // required std::function gl_make_resource_current_callback; // optional std::function @@ -56,7 +56,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, bool GLContextClearCurrent() override; // |GPUSurfaceGLDelegate| - bool GLContextPresent() override; + bool GLContextPresent(uint32_t fbo_id) override; // |GPUSurfaceGLDelegate| intptr_t GLContextFBO(GLFrameInfo frame_info) const override; diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc index f69161e6e..ee432e059 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.cc +++ b/shell/platform/embedder/tests/embedder_config_builder.cc @@ -32,8 +32,10 @@ EmbedderConfigBuilder::EmbedderConfigBuilder( opengl_renderer_config_.clear_current = [](void* context) -> bool { return reinterpret_cast(context)->GLClearCurrent(); }; - opengl_renderer_config_.present = [](void* context) -> bool { - return reinterpret_cast(context)->GLPresent(); + opengl_renderer_config_.present_with_info = + [](void* context, const FlutterPresentInfo* present_info) -> bool { + return reinterpret_cast(context)->GLPresent( + present_info->fbo_id); }; opengl_renderer_config_.fbo_with_frame_info_callback = [](void* context, const FlutterFrameInfo* frame_info) -> uint32_t { @@ -127,6 +129,15 @@ void EmbedderConfigBuilder::SetOpenGLFBOCallBack() { }; } +void EmbedderConfigBuilder::SetOpenGLPresentCallBack() { + // SetOpenGLRendererConfig must be called before this. + FML_CHECK(renderer_config_.type == FlutterRendererType::kOpenGL); + renderer_config_.open_gl.present = [](void* context) -> bool { + // passing a placeholder fbo_id. + return reinterpret_cast(context)->GLPresent(0); + }; +} + void EmbedderConfigBuilder::SetOpenGLRendererConfig(SkISize surface_size) { renderer_config_.type = FlutterRendererType::kOpenGL; renderer_config_.open_gl = opengl_renderer_config_; diff --git a/shell/platform/embedder/tests/embedder_config_builder.h b/shell/platform/embedder/tests/embedder_config_builder.h index e6f001691..e7f819fed 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.h +++ b/shell/platform/embedder/tests/embedder_config_builder.h @@ -55,6 +55,12 @@ class EmbedderConfigBuilder { // explicitly test this behavior. void SetOpenGLFBOCallBack(); + // Used to explicitly set an `open_gl.present`. Using this method will cause + // your test to fail since the ctor for this class sets + // `open_gl.present_with_info`. This method exists as a utility to explicitly + // test this behavior. + void SetOpenGLPresentCallBack(); + void SetAssetsPath(); void SetSnapshots(); diff --git a/shell/platform/embedder/tests/embedder_test_context.cc b/shell/platform/embedder/tests/embedder_test_context.cc index 9394f9f60..4c1ec7f9b 100644 --- a/shell/platform/embedder/tests/embedder_test_context.cc +++ b/shell/platform/embedder/tests/embedder_test_context.cc @@ -185,10 +185,20 @@ bool EmbedderTestContext::GLClearCurrent() { return gl_surface_->ClearCurrent(); } -bool EmbedderTestContext::GLPresent() { +bool EmbedderTestContext::GLPresent(uint32_t fbo_id) { FML_CHECK(gl_surface_) << "GL surface must be initialized."; gl_surface_present_count_++; + GLPresentCallback callback; + { + std::scoped_lock lock(gl_callback_mutex_); + callback = gl_present_callback_; + } + + if (callback) { + callback(fbo_id); + } + FireRootSurfacePresentCallbackIfPresent( [&]() { return gl_surface_->GetRasterSurfaceSnapshot(); }); @@ -200,16 +210,21 @@ bool EmbedderTestContext::GLPresent() { } void EmbedderTestContext::SetGLGetFBOCallback(GLGetFBOCallback callback) { - std::scoped_lock lock(gl_get_fbo_callback_mutex_); + std::scoped_lock lock(gl_callback_mutex_); gl_get_fbo_callback_ = callback; } +void EmbedderTestContext::SetGLPresentCallback(GLPresentCallback callback) { + std::scoped_lock lock(gl_callback_mutex_); + gl_present_callback_ = callback; +} + uint32_t EmbedderTestContext::GLGetFramebuffer(FlutterFrameInfo frame_info) { FML_CHECK(gl_surface_) << "GL surface must be initialized."; GLGetFBOCallback callback; { - std::scoped_lock lock(gl_get_fbo_callback_mutex_); + std::scoped_lock lock(gl_callback_mutex_); callback = gl_get_fbo_callback_; } @@ -217,7 +232,8 @@ uint32_t EmbedderTestContext::GLGetFramebuffer(FlutterFrameInfo frame_info) { callback(frame_info); } - return gl_surface_->GetFramebuffer(); + const auto size = frame_info.size; + return gl_surface_->GetFramebuffer(size.width, size.height); } bool EmbedderTestContext::GLMakeResourceCurrent() { @@ -295,5 +311,10 @@ void EmbedderTestContext::FireRootSurfacePresentCallbackIfPresent( callback(image_callback()); } +uint32_t EmbedderTestContext::GetWindowFBOId() const { + FML_CHECK(gl_surface_); + return gl_surface_->GetWindowFBOId(); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/embedder/tests/embedder_test_context.h b/shell/platform/embedder/tests/embedder_test_context.h index c10bb9a67..14dc0b0c2 100644 --- a/shell/platform/embedder/tests/embedder_test_context.h +++ b/shell/platform/embedder/tests/embedder_test_context.h @@ -95,6 +95,23 @@ class EmbedderTestContext { /// void SetGLGetFBOCallback(GLGetFBOCallback callback); + uint32_t GetWindowFBOId() const; + + using GLPresentCallback = std::function; + + //---------------------------------------------------------------------------- + /// @brief Sets a callback that will be invoked (on the raster task + /// runner) when the engine presents an fbo that was given by the + /// embedder. + /// + /// @attention The callback will be invoked on the raster task runner. The + /// callback can be set on the tests host thread. + /// + /// @param[in] callback The callback to set. The previous callback will be + /// un-registered. + /// + void SetGLPresentCallback(GLPresentCallback callback); + private: // This allows the builder to access the hooks. friend class EmbedderConfigBuilder; @@ -119,8 +136,9 @@ class EmbedderTestContext { SkMatrix root_surface_transformation_; size_t gl_surface_present_count_ = 0; size_t software_surface_present_count_ = 0; - std::mutex gl_get_fbo_callback_mutex_; + std::mutex gl_callback_mutex_; GLGetFBOCallback gl_get_fbo_callback_; + GLPresentCallback gl_present_callback_; static VoidCallback GetIsolateCreateCallbackHook(); @@ -149,7 +167,7 @@ class EmbedderTestContext { bool GLClearCurrent(); - bool GLPresent(); + bool GLPresent(uint32_t fbo_id); uint32_t GLGetFramebuffer(FlutterFrameInfo frame_info); diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 18305b0ee..d990bdef6 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -4406,5 +4406,58 @@ TEST_F(EmbedderTest, MustNotRunWithBothFBOCallbacksSet) { ASSERT_FALSE(engine.is_valid()); } +TEST_F(EmbedderTest, MustNotRunWithBothPresentCallbacksSet) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(600, 1024)); + builder.SetOpenGLPresentCallBack(); + + auto engine = builder.LaunchEngine(); + ASSERT_FALSE(engine.is_valid()); +} + +TEST_F(EmbedderTest, PresentInfoContainsValidFBOId) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(600, 1024)); + builder.SetDartEntrypoint("push_frames_over_and_over"); + + const auto root_surface_transformation = + SkMatrix().preTranslate(0, 1024).preRotate(-90, 0, 0); + + context.SetRootSurfaceTransformation(root_surface_transformation); + + auto engine = builder.LaunchEngine(); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 1024; + event.height = 600; + event.pixel_ratio = 1.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + ASSERT_TRUE(engine.is_valid()); + + fml::CountDownLatch frame_latch(10); + + context.AddNativeCallback("SignalNativeTest", + CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + /* Nothing to do. */ + })); + + const uint32_t window_fbo_id = context.GetWindowFBOId(); + context.SetGLPresentCallback( + [window_fbo_id = window_fbo_id, &frame_latch](uint32_t fbo_id) { + ASSERT_EQ(fbo_id, window_fbo_id); + + frame_latch.CountDown(); + }); + + frame_latch.Wait(); +} + } // namespace testing } // namespace flutter diff --git a/testing/test_gl_surface.cc b/testing/test_gl_surface.cc index 2b5f40b12..db837495e 100644 --- a/testing/test_gl_surface.cc +++ b/testing/test_gl_surface.cc @@ -220,9 +220,8 @@ bool TestGLSurface::Present() { return result == EGL_TRUE; } -uint32_t TestGLSurface::GetFramebuffer() const { - // Return FBO0 - return 0; +uint32_t TestGLSurface::GetFramebuffer(uint32_t width, uint32_t height) const { + return GetWindowFBOId(); } bool TestGLSurface::MakeResourceCurrent() { @@ -296,7 +295,9 @@ sk_sp TestGLSurface::GetOnscreenSurface() { FML_CHECK(::eglGetCurrentContext() != EGL_NO_CONTEXT); GrGLFramebufferInfo framebuffer_info = {}; - framebuffer_info.fFBOID = GetFramebuffer(); + const uint32_t width = surface_size_.width(); + const uint32_t height = surface_size_.height(); + framebuffer_info.fFBOID = GetFramebuffer(width, height); #if OS_MACOSX framebuffer_info.fFormat = GR_GL_RGBA8; #else @@ -304,11 +305,11 @@ sk_sp TestGLSurface::GetOnscreenSurface() { #endif GrBackendRenderTarget backend_render_target( - surface_size_.width(), // width - surface_size_.height(), // height - 1, // sample count - 8, // stencil bits - framebuffer_info // framebuffer info + width, // width + height, // height + 1, // sample count + 8, // stencil bits + framebuffer_info // framebuffer info ); SkSurfaceProps surface_properties( @@ -362,5 +363,9 @@ sk_sp TestGLSurface::GetRasterSurfaceSnapshot() { return host_snapshot; } +uint32_t TestGLSurface::GetWindowFBOId() const { + return 0u; +} + } // namespace testing } // namespace flutter diff --git a/testing/test_gl_surface.h b/testing/test_gl_surface.h index c7ceac403..ee339eb45 100644 --- a/testing/test_gl_surface.h +++ b/testing/test_gl_surface.h @@ -27,7 +27,7 @@ class TestGLSurface { bool Present(); - uint32_t GetFramebuffer() const; + uint32_t GetFramebuffer(uint32_t width, uint32_t height) const; bool MakeResourceCurrent(); @@ -41,6 +41,8 @@ class TestGLSurface { sk_sp GetRasterSurfaceSnapshot(); + uint32_t GetWindowFBOId() const; + private: // Importing the EGL.h pulls in platform headers which are problematic // (especially X11 which #defineds types like Bool). Any TUs importing -- GitLab