diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 0b7b1f588c71d38e396fdd58ccf1a6de394dc6fc..2adcbe6b5dfda7850d4d9816fd718a7ac14faa93 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -60,7 +60,7 @@ bool ShellTestPlatformViewGL::GLContextPresent() { } // |GPUSurfaceGLDelegate| -intptr_t ShellTestPlatformViewGL::GLContextFBO() const { +intptr_t ShellTestPlatformViewGL::GLContextFBO(GLFrameInfo frame_info) const { return gl_surface_.GetFramebuffer(); } diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index e33b84fed7814f792bac0d6a13adbd980bd5d99a..5ca969531751d1d7d420ea854cb27dd6b38ae6f1 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -56,7 +56,7 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, bool GLContextPresent() override; // |GPUSurfaceGLDelegate| - intptr_t GLContextFBO() const override; + intptr_t GLContextFBO(GLFrameInfo frame_info) const override; // |GPUSurfaceGLDelegate| GLProcResolver GetGLProcResolver() const override; diff --git a/shell/gpu/gpu_surface_gl.cc b/shell/gpu/gpu_surface_gl.cc index c492a456bb8129ce3db094093a7c493b82476950..5386683a27474c7bfb0d0fa457e3c739b720e0f0 100644 --- a/shell/gpu/gpu_surface_gl.cc +++ b/shell/gpu/gpu_surface_gl.cc @@ -204,10 +204,12 @@ bool GPUSurfaceGL::CreateOrUpdateSurfaces(const SkISize& size) { sk_sp onscreen_surface; + GLFrameInfo frame_info = {static_cast(size.width()), + static_cast(size.height())}; onscreen_surface = - WrapOnscreenSurface(context_.get(), // GL context - size, // root surface size - delegate_->GLContextFBO() // window FBO ID + WrapOnscreenSurface(context_.get(), // GL context + size, // root surface size + delegate_->GLContextFBO(frame_info) // window FBO ID ); if (onscreen_surface == nullptr) { @@ -287,13 +289,16 @@ bool GPUSurfaceGL::PresentSurface(SkCanvas* canvas) { auto current_size = SkISize::Make(onscreen_surface_->width(), onscreen_surface_->height()); + GLFrameInfo frame_info = {static_cast(current_size.width()), + static_cast(current_size.height())}; + // 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() // window FBO ID - ); + auto new_onscreen_surface = WrapOnscreenSurface( + context_.get(), // GL context + current_size, // root surface size + delegate_->GLContextFBO(frame_info) // window FBO ID + ); if (!new_onscreen_surface) { return false; diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index 9fb9765ac9be289332867a80067b97023c27f774..7fd111dd3ebe5e5288ccc76a3ab4f356cc73bda2 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -14,6 +14,13 @@ namespace flutter { +// A structure to represent the frame information which is passed to the +// embedder when requesting a frame buffer object. +struct GLFrameInfo { + uint32_t width; + uint32_t height; +}; + class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { public: ~GPUSurfaceGLDelegate() override; @@ -33,13 +40,13 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { virtual bool GLContextPresent() = 0; // The ID of the main window bound framebuffer. Typically FBO0. - virtual intptr_t GLContextFBO() const = 0; + virtual intptr_t 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, // embedders are required to return true from this method. In such cases, - // GLContextFBO() will be called again to acquire the new FBO ID for rendering - // subsequent frames. + // GLContextFBO(frame_info) will be called again to acquire the new FBO ID for + // rendering subsequent frames. virtual bool GLContextFBOResetAfterPresent() const; // Indicates whether or not the surface supports pixel readback as used in diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index a3356653df9a8de28997809acbf0e6ce51b5b933..7f692f007634a3a4d39facb281837f494692e0d0 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -115,7 +115,7 @@ bool AndroidSurfaceGL::GLContextPresent() { return onscreen_surface_->SwapBuffers(); } -intptr_t AndroidSurfaceGL::GLContextFBO() const { +intptr_t AndroidSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const { FML_DCHECK(IsValid()); // The default window bound framebuffer on Android. return 0; diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index 281a273351908822450d1c10a3f9a395f982babc..1ebbf8f60b1a5c54d1de413beb6cd5274704201d 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -59,7 +59,7 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, bool GLContextPresent() override; // |GPUSurfaceGLDelegate| - intptr_t GLContextFBO() const override; + intptr_t GLContextFBO(GLFrameInfo frame_info) const override; // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; diff --git a/shell/platform/android/surface/android_surface_mock.cc b/shell/platform/android/surface/android_surface_mock.cc index 5b0e7adad8cdd72eeb4578c4c5318feb0c8be93f..f7098f7c56b747ff5eb12bc9a3ac0ca7d7dbd8d2 100644 --- a/shell/platform/android/surface/android_surface_mock.cc +++ b/shell/platform/android/surface/android_surface_mock.cc @@ -18,7 +18,7 @@ bool AndroidSurfaceMock::GLContextPresent() { return true; } -intptr_t AndroidSurfaceMock::GLContextFBO() const { +intptr_t AndroidSurfaceMock::GLContextFBO(GLFrameInfo frame_info) const { return 0; } diff --git a/shell/platform/android/surface/android_surface_mock.h b/shell/platform/android/surface/android_surface_mock.h index 688681e01c8c09516ecd8a63feb059f4b0e2d50f..7d5fddbf3d922fe40e24dc20b9f624633786426e 100644 --- a/shell/platform/android/surface/android_surface_mock.h +++ b/shell/platform/android/surface/android_surface_mock.h @@ -48,7 +48,7 @@ class AndroidSurfaceMock final : public GPUSurfaceGLDelegate, bool GLContextPresent() override; // |GPUSurfaceGLDelegate| - intptr_t GLContextFBO() const override; + intptr_t GLContextFBO(GLFrameInfo frame_info) const override; // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.h b/shell/platform/darwin/ios/ios_surface_gl.h index e6433eb83ef56a7fb23083dad294ad9f527a3ae2..745b5d0070dc4f4afea060f881f8daa089aa5158 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.h +++ b/shell/platform/darwin/ios/ios_surface_gl.h @@ -43,7 +43,7 @@ class IOSSurfaceGL final : public IOSSurface, public GPUSurfaceGLDelegate { bool GLContextPresent() override; // |GPUSurfaceGLDelegate| - intptr_t GLContextFBO() const override; + intptr_t GLContextFBO(GLFrameInfo frame_info) const override; // |GPUSurfaceGLDelegate| bool SurfaceSupportsReadback() const override; diff --git a/shell/platform/darwin/ios/ios_surface_gl.mm b/shell/platform/darwin/ios/ios_surface_gl.mm index 3531f61c3c0dfcbb5dd073683cb6df81557c123d..9da8d9ea5008889e9f781823557ace4147bde0c4 100644 --- a/shell/platform/darwin/ios/ios_surface_gl.mm +++ b/shell/platform/darwin/ios/ios_surface_gl.mm @@ -44,7 +44,7 @@ std::unique_ptr IOSSurfaceGL::CreateGPUSurface(GrDirectContext* gr_cont } // |GPUSurfaceGLDelegate| -intptr_t IOSSurfaceGL::GLContextFBO() const { +intptr_t IOSSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const { return IsValid() ? render_target_->GetFramebuffer() : GL_NONE; } diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index e260c0b1bc71fb2e0321f68579a88ec2a16a3fd0..8a94ee5eb2dc972204b67591c8da88f7ab51de64 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -93,8 +93,17 @@ static bool IsOpenGLRendererConfigValid(const FlutterRendererConfig* config) { 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 || - SAFE_ACCESS(open_gl_config, fbo_callback, 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) { return false; } @@ -168,8 +177,20 @@ InferOpenGLPlatformViewCreationCallback( return ptr(user_data); }; - auto gl_fbo_callback = [ptr = config->open_gl.fbo_callback, - user_data]() -> intptr_t { return ptr(user_data); }; + auto gl_fbo_callback = + [fbo_callback = config->open_gl.fbo_callback, + fbo_with_frame_info_callback = + config->open_gl.fbo_with_frame_info_callback, + user_data](flutter::GLFrameInfo gl_frame_info) -> intptr_t { + if (fbo_callback) { + return fbo_callback(user_data); + } else { + FlutterFrameInfo frame_info = {}; + frame_info.struct_size = sizeof(FlutterFrameInfo); + frame_info.size = {gl_frame_info.width, gl_frame_info.height}; + return fbo_with_frame_info_callback(user_data, &frame_info); + } + }; const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; std::function gl_make_resource_current_callback = nullptr; diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 3a6126300f8b794248abaa0bac2ce4f0881bf4e2..885926eb122cc8c24715c44a6ba463bd2e717aa7 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -306,12 +306,46 @@ typedef bool (*TextureFrameCallback)(void* /* user data */, FlutterOpenGLTexture* /* texture out */); typedef void (*VsyncCallback)(void* /* user data */, intptr_t /* baton */); +/// A structure to represent the width and height. +typedef struct { + double width; + double height; +} FlutterSize; + +/// A structure to represent the width and height. +/// +/// See: \ref FlutterSize when the value are not integers. +typedef struct { + uint32_t width; + uint32_t height; +} FlutterUIntSize; + +/// This information is passed to the embedder when requesting a frame buffer +/// object. +/// +/// See: \ref FlutterSoftwareRendererConfig.fbo_with_frame_info_callback. +typedef struct { + /// The size of this struct. Must be sizeof(FlutterFrameInfo). + size_t struct_size; + /// The size of the surface that will be backed by the fbo. + FlutterUIntSize size; +} FlutterFrameInfo; + +typedef uint32_t (*UIntFrameInfoCallback)( + void* /* user data */, + const FlutterFrameInfo* /* frame info */); + typedef struct { /// The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig). size_t struct_size; BoolCallback make_current; BoolCallback clear_current; BoolCallback present; + /// Specifying one (and only one) of the `fbo_callback` or + /// `fbo_with_frame_info_callback` is required. Specifying both is an error + /// and engine intialization will be terminated. The return value indicates + /// the id of the frame buffer object that flutter will obtain the gl surface + /// from. UIntCallback fbo_callback; /// This is an optional callback. Flutter will ask the emebdder to create a GL /// context current on a background thread. If the embedder is able to do so, @@ -342,6 +376,14 @@ typedef struct { /// that external texture details can be supplied to the engine for subsequent /// composition. TextureFrameCallback gl_external_texture_frame_callback; + /// Specifying one (and only one) of the `fbo_callback` or + /// `fbo_with_frame_info_callback` is required. Specifying both is an error + /// and engine intialization will be terminated. The return value indicates + /// the id of the frame buffer object (fbo) that flutter will obtain the gl + /// surface from. When using this variant, the embedder is passed a + /// `FlutterFrameInfo` struct that indicates the properties of the surface + /// that flutter will acquire from the returned fbo. + UIntFrameInfoCallback fbo_with_frame_info_callback; } FlutterOpenGLRendererConfig; typedef struct { @@ -502,11 +544,6 @@ typedef struct { double y; } FlutterPoint; -typedef struct { - double width; - double height; -} FlutterSize; - typedef struct { FlutterRect rect; FlutterSize upper_left_corner_radius; diff --git a/shell/platform/embedder/embedder_surface_gl.cc b/shell/platform/embedder/embedder_surface_gl.cc index 3deb7b5f3032c85fb587f7e7a627e9e4d4efb3df..4a28d68ecb2958b728ac97533116ab0b3710c28e 100644 --- a/shell/platform/embedder/embedder_surface_gl.cc +++ b/shell/platform/embedder/embedder_surface_gl.cc @@ -50,8 +50,8 @@ bool EmbedderSurfaceGL::GLContextPresent() { } // |GPUSurfaceGLDelegate| -intptr_t EmbedderSurfaceGL::GLContextFBO() const { - return gl_dispatch_table_.gl_fbo_callback(); +intptr_t EmbedderSurfaceGL::GLContextFBO(GLFrameInfo frame_info) const { + return gl_dispatch_table_.gl_fbo_callback(frame_info); } // |GPUSurfaceGLDelegate| diff --git a/shell/platform/embedder/embedder_surface_gl.h b/shell/platform/embedder/embedder_surface_gl.h index 2a3a76748db1719b6cc259a75f9a4b6e929017de..6a4b8ed94703d12e25923301aaf3c147a411245b 100644 --- a/shell/platform/embedder/embedder_surface_gl.h +++ b/shell/platform/embedder/embedder_surface_gl.h @@ -19,7 +19,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, std::function gl_make_current_callback; // required std::function gl_clear_current_callback; // required std::function gl_present_callback; // required - std::function gl_fbo_callback; // required + std::function gl_fbo_callback; // required std::function gl_make_resource_current_callback; // optional std::function gl_surface_transformation_callback; // optional @@ -59,7 +59,7 @@ class EmbedderSurfaceGL final : public EmbedderSurface, bool GLContextPresent() override; // |GPUSurfaceGLDelegate| - intptr_t GLContextFBO() const override; + intptr_t GLContextFBO(GLFrameInfo frame_info) const override; // |GPUSurfaceGLDelegate| bool GLContextFBOResetAfterPresent() const override; diff --git a/shell/platform/embedder/tests/embedder_config_builder.cc b/shell/platform/embedder/tests/embedder_config_builder.cc index 1c08b98fde1f9f6f26bc17a10a82a5b7e15c60c1..f69161e6e23cc1a666d4cc4b7274760e5a5c9d28 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.cc +++ b/shell/platform/embedder/tests/embedder_config_builder.cc @@ -35,8 +35,10 @@ EmbedderConfigBuilder::EmbedderConfigBuilder( opengl_renderer_config_.present = [](void* context) -> bool { return reinterpret_cast(context)->GLPresent(); }; - opengl_renderer_config_.fbo_callback = [](void* context) -> uint32_t { - return reinterpret_cast(context)->GLGetFramebuffer(); + opengl_renderer_config_.fbo_with_frame_info_callback = + [](void* context, const FlutterFrameInfo* frame_info) -> uint32_t { + return reinterpret_cast(context)->GLGetFramebuffer( + *frame_info); }; opengl_renderer_config_.make_resource_current = [](void* context) -> bool { return reinterpret_cast(context) @@ -110,6 +112,21 @@ void EmbedderConfigBuilder::SetSoftwareRendererConfig(SkISize surface_size) { context_.SetupOpenGLSurface(surface_size); } +void EmbedderConfigBuilder::SetOpenGLFBOCallBack() { + // SetOpenGLRendererConfig must be called before this. + FML_CHECK(renderer_config_.type == FlutterRendererType::kOpenGL); + renderer_config_.open_gl.fbo_callback = [](void* context) -> uint32_t { + FlutterFrameInfo frame_info = {}; + // fbo_callback doesn't use the frame size information, only + // fbo_callback_with_frame_info does. + frame_info.struct_size = sizeof(FlutterFrameInfo); + frame_info.size.width = 0; + frame_info.size.height = 0; + return reinterpret_cast(context)->GLGetFramebuffer( + frame_info); + }; +} + 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 a386cd91d0c6d1811dc057c21d4ee4a3efeb253c..e6f001691877695bba2ce3f1e883a93403a1528d 100644 --- a/shell/platform/embedder/tests/embedder_config_builder.h +++ b/shell/platform/embedder/tests/embedder_config_builder.h @@ -49,6 +49,12 @@ class EmbedderConfigBuilder { void SetOpenGLRendererConfig(SkISize surface_size); + // Used to explicitly set an `open_gl.fbo_callback`. Using this method will + // cause your test to fail since the ctor for this class sets + // `open_gl.fbo_callback_with_frame_info`. This method exists as a utility to + // explicitly test this behavior. + void SetOpenGLFBOCallBack(); + 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 be7f7641cc58693e024158a280fe603827f1b733..048d5d9c141116f01a70488e257b086196284286 100644 --- a/shell/platform/embedder/tests/embedder_test_context.cc +++ b/shell/platform/embedder/tests/embedder_test_context.cc @@ -197,11 +197,16 @@ bool EmbedderTestContext::GLPresent() { return true; } -uint32_t EmbedderTestContext::GLGetFramebuffer() { +uint32_t EmbedderTestContext::GLGetFramebuffer(FlutterFrameInfo frame_info) { FML_CHECK(gl_surface_) << "GL surface must be initialized."; + gl_surface_fbo_frame_infos_.push_back(frame_info); return gl_surface_->GetFramebuffer(); } +std::vector EmbedderTestContext::GetGLFBOFrameInfos() { + return gl_surface_fbo_frame_infos_; +} + bool EmbedderTestContext::GLMakeResourceCurrent() { FML_CHECK(gl_surface_) << "GL surface must be initialized."; return gl_surface_->MakeResourceCurrent(); diff --git a/shell/platform/embedder/tests/embedder_test_context.h b/shell/platform/embedder/tests/embedder_test_context.h index 56a44f6b5efe63a18fff7ea8c71cdab61c2a739b..f4135c07d7f7c76a2396f0127336078f8f9eb482 100644 --- a/shell/platform/embedder/tests/embedder_test_context.h +++ b/shell/platform/embedder/tests/embedder_test_context.h @@ -79,6 +79,9 @@ class EmbedderTestContext { size_t GetSoftwareSurfacePresentCount() const; + // Returns the frame information for all the frames that were rendered. + std::vector GetGLFBOFrameInfos(); + private: // This allows the builder to access the hooks. friend class EmbedderConfigBuilder; @@ -101,6 +104,7 @@ class EmbedderTestContext { std::unique_ptr compositor_; NextSceneCallback next_scene_callback_; SkMatrix root_surface_transformation_; + std::vector gl_surface_fbo_frame_infos_; size_t gl_surface_present_count_ = 0; size_t software_surface_present_count_ = 0; @@ -133,7 +137,7 @@ class EmbedderTestContext { bool GLPresent(); - uint32_t GLGetFramebuffer(); + uint32_t GLGetFramebuffer(FlutterFrameInfo frame_info); bool GLMakeResourceCurrent(); diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 558669ddb550cb7dd730b8c85caddd3339d6fd36..f235613698f6a948c0b5ccffe8258a39e86d02b2 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -4352,5 +4352,60 @@ TEST_F(EmbedderTest, CanLaunchAndShutdownWithAValidElfSource) { engine.reset(); } +TEST_F(EmbedderTest, FrameInfoContainsValidWidthAndHeight) { + 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()); + + constexpr size_t frames_expected = 10; + fml::CountDownLatch frame_latch(frames_expected); + size_t frames_seen = 0; + context.AddNativeCallback("SignalNativeTest", + CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + frames_seen++; + frame_latch.CountDown(); + })); + frame_latch.Wait(); + + ASSERT_EQ(frames_expected, frames_seen); + ASSERT_EQ(context.GetGLFBOFrameInfos().size(), frames_seen); + + for (FlutterFrameInfo frame_info : context.GetGLFBOFrameInfos()) { + // width and height are rotated by 90 deg + ASSERT_EQ(frame_info.size.width, event.height); + ASSERT_EQ(frame_info.size.height, event.width); + } +} + +TEST_F(EmbedderTest, MustNotRunWithBothFBOCallbacksSet) { + auto& context = GetEmbedderContext(); + + EmbedderConfigBuilder builder(context); + builder.SetOpenGLRendererConfig(SkISize::Make(600, 1024)); + builder.SetOpenGLFBOCallBack(); + + auto engine = builder.LaunchEngine(); + ASSERT_FALSE(engine.is_valid()); +} + } // namespace testing } // namespace flutter