diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 078e1ee3d0..ef13edbe47 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -44834,6 +44834,8 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registrar.cc + ../../../ ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registrar_test.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/linux/fl_renderable.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/linux/fl_renderable.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_gdk.cc + ../../../flutter/LICENSE @@ -47739,6 +47741,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar.cc FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_private.h FILE: ../../../flutter/shell/platform/linux/fl_plugin_registrar_test.cc FILE: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc +FILE: ../../../flutter/shell/platform/linux/fl_renderable.cc +FILE: ../../../flutter/shell/platform/linux/fl_renderable.h FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer.h FILE: ../../../flutter/shell/platform/linux/fl_renderer_gdk.cc diff --git a/engine/src/flutter/shell/platform/linux/BUILD.gn b/engine/src/flutter/shell/platform/linux/BUILD.gn index 36045a9fd4..9cbcc9f8c9 100644 --- a/engine/src/flutter/shell/platform/linux/BUILD.gn +++ b/engine/src/flutter/shell/platform/linux/BUILD.gn @@ -129,6 +129,7 @@ source_set("flutter_linux_sources") { "fl_platform_handler.cc", "fl_plugin_registrar.cc", "fl_plugin_registry.cc", + "fl_renderable.cc", "fl_renderer.cc", "fl_renderer_gdk.cc", "fl_renderer_headless.cc", diff --git a/engine/src/flutter/shell/platform/linux/fl_renderable.cc b/engine/src/flutter/shell/platform/linux/fl_renderable.cc new file mode 100644 index 0000000000..b8a44a5948 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_renderable.cc @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/linux/fl_renderable.h" + +G_DEFINE_INTERFACE(FlRenderable, fl_renderable, G_TYPE_OBJECT) + +static void fl_renderable_default_init(FlRenderableInterface* iface) {} + +void fl_renderable_redraw(FlRenderable* self) { + g_return_if_fail(FL_IS_RENDERABLE(self)); + + FL_RENDERABLE_GET_IFACE(self)->redraw(self); +} diff --git a/engine/src/flutter/shell/platform/linux/fl_renderable.h b/engine/src/flutter/shell/platform/linux/fl_renderable.h new file mode 100644 index 0000000000..8a17caa658 --- /dev/null +++ b/engine/src/flutter/shell/platform/linux/fl_renderable.h @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERABLE_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERABLE_H_ + +#include + +#include "flutter/shell/platform/embedder/embedder.h" + +G_BEGIN_DECLS + +G_DECLARE_INTERFACE(FlRenderable, fl_renderable, FL, RENDERABLE, GObject); + +/** + * FlRenderable: + * + * An interface for a class that can render views from #FlRenderer. + * + * This interface is typically implemented by #FlView and is provided to make + * #FlRenderer easier to test. + */ + +struct _FlRenderableInterface { + GTypeInterface g_iface; + + void (*redraw)(FlRenderable* renderable); +}; + +/** + * fl_renderable_redraw: + * @renderable: an #FlRenderable + * + * Indicate the renderable needs to redraw. When ready, the renderable should + * call fl_renderer_draw(). + */ +void fl_renderable_redraw(FlRenderable* renderable); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERABLE_H_ diff --git a/engine/src/flutter/shell/platform/linux/fl_renderer.cc b/engine/src/flutter/shell/platform/linux/fl_renderer.cc index 52aa9dee90..64a891b417 100644 --- a/engine/src/flutter/shell/platform/linux/fl_renderer.cc +++ b/engine/src/flutter/shell/platform/linux/fl_renderer.cc @@ -10,7 +10,6 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/fl_framebuffer.h" -#include "flutter/shell/platform/linux/fl_view_private.h" // Vertex shader to draw Flutter window contents. static const char* vertex_shader_src = @@ -77,6 +76,12 @@ typedef struct { G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT) +static void free_weak_ref(gpointer value) { + GWeakRef* ref = static_cast(value); + g_weak_ref_clear(ref); + free(ref); +} + // Check if running on an NVIDIA driver. static gboolean is_nvidia() { const gchar* vendor = reinterpret_cast(glGetString(GL_VENDOR)); @@ -295,8 +300,8 @@ static void fl_renderer_class_init(FlRendererClass* klass) { static void fl_renderer_init(FlRenderer* self) { FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); - priv->views = - g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, nullptr); + priv->views = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, + free_weak_ref); priv->framebuffers_by_view_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, nullptr, (GDestroyNotify)g_ptr_array_unref); @@ -311,15 +316,17 @@ void fl_renderer_set_engine(FlRenderer* self, FlEngine* engine) { g_weak_ref_init(&priv->engine, engine); } -void fl_renderer_add_view(FlRenderer* self, - FlutterViewId view_id, - FlView* view) { +void fl_renderer_add_renderable(FlRenderer* self, + FlutterViewId view_id, + FlRenderable* renderable) { FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); g_return_if_fail(FL_IS_RENDERER(self)); - g_hash_table_insert(priv->views, GINT_TO_POINTER(view_id), view); + GWeakRef* ref = g_new(GWeakRef, 1); + g_weak_ref_init(ref, G_OBJECT(renderable)); + g_hash_table_insert(priv->views, GINT_TO_POINTER(view_id), ref); } void fl_renderer_remove_view(FlRenderer* self, FlutterViewId view_id) { @@ -472,10 +479,12 @@ gboolean fl_renderer_present_layers(FlRenderer* self, } } - FlView* view = - FL_VIEW(g_hash_table_lookup(priv->views, GINT_TO_POINTER(view_id))); - if (view != nullptr) { - fl_view_redraw(view); + GWeakRef* ref = static_cast( + g_hash_table_lookup(priv->views, GINT_TO_POINTER(view_id))); + g_autoptr(FlRenderable) renderable = + ref != nullptr ? FL_RENDERABLE(g_weak_ref_get(ref)) : nullptr; + if (renderable != nullptr) { + fl_renderable_redraw(renderable); } return TRUE; diff --git a/engine/src/flutter/shell/platform/linux/fl_renderer.h b/engine/src/flutter/shell/platform/linux/fl_renderer.h index 35d3669913..b107442ea4 100644 --- a/engine/src/flutter/shell/platform/linux/fl_renderer.h +++ b/engine/src/flutter/shell/platform/linux/fl_renderer.h @@ -7,11 +7,11 @@ #include -#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h" -#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" - #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/linux/fl_renderable.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h" + G_BEGIN_DECLS /** @@ -79,16 +79,16 @@ struct _FlRendererClass { void fl_renderer_set_engine(FlRenderer* renderer, FlEngine* engine); /** - * fl_renderer_add_view: + * fl_renderer_add_renderable: * @renderer: an #FlRenderer. * @view_id: the ID of the view. - * @view: the view Flutter is renderering to. + * @renderable: object that is to be rendered on. * * Add a view to render on. */ -void fl_renderer_add_view(FlRenderer* renderer, - FlutterViewId view_id, - FlView* view); +void fl_renderer_add_renderable(FlRenderer* renderer, + FlutterViewId view_id, + FlRenderable* renderable); /** * fl_renderer_remove_view: diff --git a/engine/src/flutter/shell/platform/linux/fl_renderer_test.cc b/engine/src/flutter/shell/platform/linux/fl_renderer_test.cc index b9336a141c..7c4d83cea3 100644 --- a/engine/src/flutter/shell/platform/linux/fl_renderer_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_renderer_test.cc @@ -6,7 +6,6 @@ #include "flutter/fml/logging.h" #include "flutter/shell/platform/linux/fl_framebuffer.h" -#include "flutter/shell/platform/linux/testing/fl_test_gtk_logs.h" #include "flutter/shell/platform/linux/testing/mock_epoxy.h" #include "flutter/shell/platform/linux/testing/mock_renderer.h" @@ -48,14 +47,12 @@ TEST(FlRendererTest, RestoresGLState) { constexpr int kWidth = 100; constexpr int kHeight = 100; - flutter::testing::fl_ensure_gtk_init(); - g_autoptr(FlDartProject) project = fl_dart_project_new(); - g_autoptr(FlView) view = fl_view_new(project); + g_autoptr(FlRenderable) renderable = FL_RENDERABLE(fl_mock_renderable_new()); g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); g_autoptr(FlFramebuffer) framebuffer = fl_framebuffer_new(GL_RGB, kWidth, kHeight); - fl_renderer_add_view(FL_RENDERER(renderer), 0, view); + fl_renderer_add_renderable(FL_RENDERER(renderer), 0, renderable); fl_renderer_wait_for_frame(FL_RENDERER(renderer), kWidth, kHeight); FlutterBackingStore backing_store; @@ -84,8 +81,6 @@ TEST(FlRendererTest, RestoresGLState) { glGetIntegerv(GL_TEXTURE_BINDING_2D, reinterpret_cast(&texture_2d_binding)); EXPECT_EQ(texture_2d_binding, kFakeTextureName); - - g_object_ref_sink(view); } static constexpr double kExpectedRefreshRate = 120.0; @@ -94,8 +89,6 @@ static gdouble renderer_get_refresh_rate(FlRenderer* renderer) { } TEST(FlRendererTest, RefreshRate) { - flutter::testing::fl_ensure_gtk_init(); - g_autoptr(FlDartProject) project = fl_dart_project_new(); g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(&renderer_get_refresh_rate); diff --git a/engine/src/flutter/shell/platform/linux/fl_view.cc b/engine/src/flutter/shell/platform/linux/fl_view.cc index e84b144142..5a68647cd7 100644 --- a/engine/src/flutter/shell/platform/linux/fl_view.cc +++ b/engine/src/flutter/shell/platform/linux/fl_view.cc @@ -91,6 +91,8 @@ enum { kSignalFirstFrame, kSignalLastSignal }; static guint fl_view_signals[kSignalLastSignal]; +static void fl_renderable_iface_init(FlRenderableInterface* iface); + static void fl_view_plugin_registry_iface_init( FlPluginRegistryInterface* iface); @@ -107,14 +109,16 @@ G_DEFINE_TYPE_WITH_CODE( FlView, fl_view, GTK_TYPE_BOX, - G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(), - fl_view_plugin_registry_iface_init) - G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(), - fl_view_keyboard_delegate_iface_init) - G_IMPLEMENT_INTERFACE(fl_scrolling_view_delegate_get_type(), - fl_view_scrolling_delegate_iface_init) - G_IMPLEMENT_INTERFACE(fl_text_input_view_delegate_get_type(), - fl_view_text_input_delegate_iface_init)) + G_IMPLEMENT_INTERFACE(fl_renderable_get_type(), fl_renderable_iface_init) + G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(), + fl_view_plugin_registry_iface_init) + G_IMPLEMENT_INTERFACE(fl_keyboard_view_delegate_get_type(), + fl_view_keyboard_delegate_iface_init) + G_IMPLEMENT_INTERFACE(fl_scrolling_view_delegate_get_type(), + fl_view_scrolling_delegate_iface_init) + G_IMPLEMENT_INTERFACE( + fl_text_input_view_delegate_get_type(), + fl_view_text_input_delegate_iface_init)) // Emit the first frame signal in the main thread. static gboolean first_frame_idle_cb(gpointer user_data) { @@ -317,6 +321,20 @@ static void on_pre_engine_restart_cb(FlView* self) { init_scrolling(self); } +// Implements FlRenderable::redraw +static void fl_view_redraw(FlRenderable* renderable) { + FlView* self = FL_VIEW(renderable); + + gtk_widget_queue_draw(GTK_WIDGET(self->gl_area)); + + if (!self->have_first_frame) { + self->have_first_frame = TRUE; + // This is not the main thread, so the signal needs to be done via an idle + // callback. + g_idle_add(first_frame_idle_cb, self); + } +} + // Implements FlPluginRegistry::get_registrar_for_plugin. static FlPluginRegistrar* fl_view_get_registrar_for_plugin( FlPluginRegistry* registry, @@ -328,6 +346,10 @@ static FlPluginRegistrar* fl_view_get_registrar_for_plugin( fl_engine_get_texture_registrar(self->engine)); } +static void fl_renderable_iface_init(FlRenderableInterface* iface) { + iface->redraw = fl_view_redraw; +} + static void fl_view_plugin_registry_iface_init( FlPluginRegistryInterface* iface) { iface->get_registrar_for_plugin = fl_view_get_registrar_for_plugin; @@ -621,7 +643,8 @@ static void realize_cb(FlView* self) { init_keyboard(self); - fl_renderer_add_view(FL_RENDERER(self->renderer), self->view_id, self); + fl_renderer_add_renderable(FL_RENDERER(self->renderer), self->view_id, + FL_RENDERABLE(self)); if (!fl_engine_start(self->engine, &error)) { g_warning("Failed to start Flutter engine: %s", error->message); @@ -866,7 +889,8 @@ G_MODULE_EXPORT FlView* fl_view_new_for_engine(FlEngine* engine) { self->view_id = fl_engine_add_view(self->engine, 1, 1, 1.0, self->cancellable, view_added_cb, self); - fl_renderer_add_view(FL_RENDERER(self->renderer), self->view_id, self); + fl_renderer_add_renderable(FL_RENDERER(self->renderer), self->view_id, + FL_RENDERABLE(self)); return self; } @@ -889,19 +913,6 @@ G_MODULE_EXPORT void fl_view_set_background_color(FlView* self, self->background_color = gdk_rgba_copy(color); } -void fl_view_redraw(FlView* self) { - g_return_if_fail(FL_IS_VIEW(self)); - - gtk_widget_queue_draw(GTK_WIDGET(self->gl_area)); - - if (!self->have_first_frame) { - self->have_first_frame = TRUE; - // This is not the main thread, so the signal needs to be done via an idle - // callback. - g_idle_add(first_frame_idle_cb, self); - } -} - GHashTable* fl_view_get_keyboard_state(FlView* self) { g_return_val_if_fail(FL_IS_VIEW(self), nullptr); return fl_keyboard_handler_get_pressed_state(self->keyboard_handler); diff --git a/engine/src/flutter/shell/platform/linux/fl_view_private.h b/engine/src/flutter/shell/platform/linux/fl_view_private.h index de3fc9c911..e23b178cad 100644 --- a/engine/src/flutter/shell/platform/linux/fl_view_private.h +++ b/engine/src/flutter/shell/platform/linux/fl_view_private.h @@ -7,14 +7,6 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" -/** - * fl_view_redraw: - * @view: an #FlView. - * - * Indicate the view needs to redraw. - */ -void fl_view_redraw(FlView* view); - /** * fl_view_get_keyboard_state: * @view: an #FlView. diff --git a/engine/src/flutter/shell/platform/linux/fl_view_test.cc b/engine/src/flutter/shell/platform/linux/fl_view_test.cc index 8abc8a1b58..acf1bca7fd 100644 --- a/engine/src/flutter/shell/platform/linux/fl_view_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_view_test.cc @@ -50,7 +50,7 @@ TEST(FlViewTest, FirstFrameSignal) { EXPECT_FALSE(first_frame_emitted); - fl_view_redraw(view); + fl_renderable_redraw(FL_RENDERABLE(view)); // Signal is emitted in idle, clear the main loop. while (g_main_context_iteration(g_main_context_default(), FALSE)) { diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_renderer.cc b/engine/src/flutter/shell/platform/linux/testing/mock_renderer.cc index c70980b710..1106554271 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_renderer.cc +++ b/engine/src/flutter/shell/platform/linux/testing/mock_renderer.cc @@ -9,8 +9,20 @@ struct _FlMockRenderer { FlMockRendererGetRefreshRate get_refresh_rate; }; +struct _FlMockRenderable { + GObject parent_instance; +}; + G_DEFINE_TYPE(FlMockRenderer, fl_mock_renderer, fl_renderer_get_type()) +static void mock_renderable_iface_init(FlRenderableInterface* iface); + +G_DEFINE_TYPE_WITH_CODE(FlMockRenderable, + fl_mock_renderable, + g_object_get_type(), + G_IMPLEMENT_INTERFACE(fl_renderable_get_type(), + mock_renderable_iface_init)) + // Implements FlRenderer::make_current. static void fl_mock_renderer_make_current(FlRenderer* renderer) {} @@ -40,11 +52,27 @@ static void fl_mock_renderer_class_init(FlMockRendererClass* klass) { static void fl_mock_renderer_init(FlMockRenderer* self) {} +static void mock_renderable_redraw(FlRenderable* renderable) {} + +static void mock_renderable_iface_init(FlRenderableInterface* iface) { + iface->redraw = mock_renderable_redraw; +} + +static void fl_mock_renderable_class_init(FlMockRenderableClass* klass) {} + +static void fl_mock_renderable_init(FlMockRenderable* self) {} + // Creates a stub renderer FlMockRenderer* fl_mock_renderer_new( FlMockRendererGetRefreshRate get_refresh_rate) { - FlMockRenderer* fl_mock_renderer = FL_MOCK_RENDERER( - g_object_new_valist(fl_mock_renderer_get_type(), nullptr, nullptr)); - fl_mock_renderer->get_refresh_rate = get_refresh_rate; - return fl_mock_renderer; + FlMockRenderer* self = + FL_MOCK_RENDERER(g_object_new(fl_mock_renderer_get_type(), nullptr)); + self->get_refresh_rate = get_refresh_rate; + return self; +} + +// Creates a sub renderable. +FlMockRenderable* fl_mock_renderable_new() { + return FL_MOCK_RENDERABLE( + g_object_new(fl_mock_renderable_get_type(), nullptr)); } diff --git a/engine/src/flutter/shell/platform/linux/testing/mock_renderer.h b/engine/src/flutter/shell/platform/linux/testing/mock_renderer.h index 2dbbe08255..0838708c4f 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_renderer.h +++ b/engine/src/flutter/shell/platform/linux/testing/mock_renderer.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_RENDERER_H_ #define FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_RENDERER_H_ +#include "flutter/shell/platform/linux/fl_renderable.h" #include "flutter/shell/platform/linux/fl_renderer.h" G_BEGIN_DECLS @@ -15,11 +16,19 @@ G_DECLARE_FINAL_TYPE(FlMockRenderer, MOCK_RENDERER, FlRenderer) +G_DECLARE_FINAL_TYPE(FlMockRenderable, + fl_mock_renderable, + FL, + MOCK_RENDERABLE, + GObject) + typedef gdouble (*FlMockRendererGetRefreshRate)(FlRenderer* renderer); FlMockRenderer* fl_mock_renderer_new( FlMockRendererGetRefreshRate get_refresh_rate = nullptr); +FlMockRenderable* fl_mock_renderable_new(); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_RENDERER_H_