diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a0dc79bf98cd5..bb8fb1b329baa 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -36899,8 +36899,6 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_engine_private.h + ../../../flu ORIGIN: ../../../flutter/shell/platform/linux/fl_engine_test.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_event_channel.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_event_channel_test.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/linux/fl_gl_area.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/linux/fl_gl_area.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_gnome_settings.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_gnome_settings.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_gnome_settings_test.cc + ../../../flutter/LICENSE @@ -36950,8 +36948,8 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registrar_test.cc + ../. ORIGIN: ../../../flutter/shell/platform/linux/fl_plugin_registry.cc + ../../../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_gl.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_gl.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_gdk.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_gdk.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_headless.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_renderer_headless.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/linux/fl_scrolling_manager.cc + ../../../flutter/LICENSE @@ -39772,8 +39770,6 @@ FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h FILE: ../../../flutter/shell/platform/linux/fl_engine_test.cc FILE: ../../../flutter/shell/platform/linux/fl_event_channel.cc FILE: ../../../flutter/shell/platform/linux/fl_event_channel_test.cc -FILE: ../../../flutter/shell/platform/linux/fl_gl_area.cc -FILE: ../../../flutter/shell/platform/linux/fl_gl_area.h FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings.cc FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings.h FILE: ../../../flutter/shell/platform/linux/fl_gnome_settings_test.cc @@ -39823,8 +39819,8 @@ 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_renderer.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer.h -FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.cc -FILE: ../../../flutter/shell/platform/linux/fl_renderer_gl.h +FILE: ../../../flutter/shell/platform/linux/fl_renderer_gdk.cc +FILE: ../../../flutter/shell/platform/linux/fl_renderer_gdk.h FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.cc FILE: ../../../flutter/shell/platform/linux/fl_renderer_headless.h FILE: ../../../flutter/shell/platform/linux/fl_scrolling_manager.cc diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 2ca198a18dec0..b2a8e800f8b38 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -105,7 +105,6 @@ source_set("flutter_linux_sources") { "fl_dart_project.cc", "fl_engine.cc", "fl_event_channel.cc", - "fl_gl_area.cc", "fl_gnome_settings.cc", "fl_json_message_codec.cc", "fl_json_method_codec.cc", @@ -126,7 +125,7 @@ source_set("flutter_linux_sources") { "fl_plugin_registrar.cc", "fl_plugin_registry.cc", "fl_renderer.cc", - "fl_renderer_gl.cc", + "fl_renderer_gdk.cc", "fl_renderer_headless.cc", "fl_scrolling_manager.cc", "fl_scrolling_view_delegate.cc", @@ -246,7 +245,10 @@ executable("flutter_linux_unittests") { public_configs = [ "//flutter:config" ] - configs += [ "//flutter/shell/platform/linux/config:gtk" ] + configs += [ + "//flutter/shell/platform/linux/config:gtk", + "//flutter/shell/platform/linux/config:epoxy", + ] defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES", diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc index 30e02c7ecbb02..3069cd2b39710 100644 --- a/shell/platform/linux/fl_engine.cc +++ b/shell/platform/linux/fl_engine.cc @@ -217,22 +217,14 @@ static void* fl_engine_gl_proc_resolver(void* user_data, const char* name) { static bool fl_engine_gl_make_current(void* user_data) { FlEngine* self = static_cast(user_data); - g_autoptr(GError) error = nullptr; - gboolean result = fl_renderer_make_current(self->renderer, &error); - if (!result) { - g_warning("%s", error->message); - } - return result; + fl_renderer_make_current(self->renderer); + return true; } static bool fl_engine_gl_clear_current(void* user_data) { FlEngine* self = static_cast(user_data); - g_autoptr(GError) error = nullptr; - gboolean result = fl_renderer_clear_current(self->renderer, &error); - if (!result) { - g_warning("%s", error->message); - } - return result; + fl_renderer_clear_current(self->renderer); + return true; } static uint32_t fl_engine_gl_get_fbo(void* user_data) { @@ -248,12 +240,8 @@ static bool fl_engine_gl_present(void* user_data) { static bool fl_engine_gl_make_resource_current(void* user_data) { FlEngine* self = static_cast(user_data); - g_autoptr(GError) error = nullptr; - gboolean result = fl_renderer_make_resource_current(self->renderer, &error); - if (!result) { - g_warning("%s", error->message); - } - return result; + fl_renderer_make_resource_current(self->renderer); + return true; } // Called by the engine to retrieve an external texture. diff --git a/shell/platform/linux/fl_gl_area.cc b/shell/platform/linux/fl_gl_area.cc deleted file mode 100644 index b6c11bdd42fae..0000000000000 --- a/shell/platform/linux/fl_gl_area.cc +++ /dev/null @@ -1,136 +0,0 @@ -// 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_gl_area.h" - -#include - -struct _FlGLArea { - GtkWidget parent_instance; - - GdkGLContext* context; - - GPtrArray* textures; -}; - -G_DEFINE_TYPE(FlGLArea, fl_gl_area, GTK_TYPE_WIDGET) - -static void fl_gl_area_dispose(GObject* gobject) { - FlGLArea* self = FL_GL_AREA(gobject); - - g_clear_object(&self->context); - g_clear_pointer(&self->textures, g_ptr_array_unref); - - G_OBJECT_CLASS(fl_gl_area_parent_class)->dispose(gobject); -} - -// Implements GtkWidget::realize. -static void fl_gl_area_realize(GtkWidget* widget) { - GtkAllocation allocation; - gtk_widget_get_allocation(widget, &allocation); - - gtk_widget_set_realized(widget, TRUE); - - GdkWindowAttr attributes; - attributes.window_type = GDK_WINDOW_CHILD; - attributes.x = allocation.x; - attributes.y = allocation.y; - attributes.width = allocation.width; - attributes.height = allocation.height; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gtk_widget_get_visual(widget); - attributes.event_mask = gtk_widget_get_events(widget); - gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; - GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), - &attributes, attributes_mask); - gtk_widget_set_window(widget, window); - gtk_widget_register_window(widget, window); -} - -// Implements GtkWidget::unrealize. -static void fl_gl_area_unrealize(GtkWidget* widget) { - FlGLArea* self = FL_GL_AREA(widget); - gdk_gl_context_make_current(self->context); - g_clear_pointer(&self->textures, g_ptr_array_unref); - - /* Make sure to unset the context if current */ - if (self->context == gdk_gl_context_get_current()) { - gdk_gl_context_clear_current(); - } - - GTK_WIDGET_CLASS(fl_gl_area_parent_class)->unrealize(widget); -} - -// Implements GtkWidget::size_allocate. -static void fl_gl_area_size_allocate(GtkWidget* widget, - GtkAllocation* allocation) { - gtk_widget_set_allocation(widget, allocation); - - if (gtk_widget_get_has_window(widget)) { - if (gtk_widget_get_realized(widget)) { - gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, - allocation->y, allocation->width, - allocation->height); - } - } -} - -// Implements GtkWidget::draw. -static gboolean fl_gl_area_draw(GtkWidget* widget, cairo_t* cr) { - FlGLArea* self = FL_GL_AREA(widget); - - gdk_gl_context_make_current(self->context); - - gint scale = gtk_widget_get_scale_factor(widget); - - if (self->textures == nullptr) { - return TRUE; - } - - for (guint i = 0; i < self->textures->len; i++) { - FlBackingStoreProvider* texture = - FL_BACKING_STORE_PROVIDER(g_ptr_array_index(self->textures, i)); - uint32_t texture_id = fl_backing_store_provider_get_gl_texture_id(texture); - GdkRectangle geometry = fl_backing_store_provider_get_geometry(texture); - gdk_cairo_draw_from_gl(cr, gtk_widget_get_window(widget), texture_id, - GL_TEXTURE, scale, geometry.x, geometry.y, - geometry.width, geometry.height); - } - - return TRUE; -} - -static void fl_gl_area_class_init(FlGLAreaClass* klass) { - GObjectClass* gobject_class = G_OBJECT_CLASS(klass); - gobject_class->dispose = fl_gl_area_dispose; - - GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); - widget_class->realize = fl_gl_area_realize; - widget_class->unrealize = fl_gl_area_unrealize; - widget_class->size_allocate = fl_gl_area_size_allocate; - widget_class->draw = fl_gl_area_draw; - - gtk_widget_class_set_accessible_role(widget_class, ATK_ROLE_DRAWING_AREA); -} - -static void fl_gl_area_init(FlGLArea* self) { - gtk_widget_set_app_paintable(GTK_WIDGET(self), TRUE); -} - -GtkWidget* fl_gl_area_new(GdkGLContext* context) { - g_return_val_if_fail(GDK_IS_GL_CONTEXT(context), nullptr); - FlGLArea* area = - reinterpret_cast(g_object_new(fl_gl_area_get_type(), nullptr)); - area->context = GDK_GL_CONTEXT(g_object_ref(context)); - return GTK_WIDGET(area); -} - -void fl_gl_area_queue_render(FlGLArea* self, GPtrArray* textures) { - g_return_if_fail(FL_IS_GL_AREA(self)); - - g_clear_pointer(&self->textures, g_ptr_array_unref); - self->textures = g_ptr_array_ref(textures); - - gtk_widget_queue_draw(GTK_WIDGET(self)); -} diff --git a/shell/platform/linux/fl_gl_area.h b/shell/platform/linux/fl_gl_area.h deleted file mode 100644 index 72bb32d2c06e9..0000000000000 --- a/shell/platform/linux/fl_gl_area.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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_GL_AREA_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_FL_GL_AREA_H_ - -#include - -#include "flutter/shell/platform/linux/fl_backing_store_provider.h" - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE(FlGLArea, fl_gl_area, FL, GL_AREA, GtkWidget) - -/** - * FlGLArea: - * - * #FlGLArea is a OpenGL drawing area that shows Flutter backing store Layer. - */ - -/** - * fl_gl_area_new: - * @context: an #GdkGLContext. - * - * Creates a new #FlGLArea widget. - * - * Returns: the newly created #FlGLArea widget. - */ -GtkWidget* fl_gl_area_new(GdkGLContext* context); - -/** - * fl_gl_area_queue_render: - * @area: an #FlGLArea. - * @textures: (transfer none) (element-type FlBackingStoreProvider): a list of - * #FlBackingStoreProvider. - * - * Queues textures to be drawn later. - */ -void fl_gl_area_queue_render(FlGLArea* area, GPtrArray* textures); - -G_END_DECLS - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_GL_AREA_H_ diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index f32215dd38ecc..c6d20f5a1f308 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -10,6 +10,27 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/linux/fl_backing_store_provider.h" #include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/fl_view_private.h" + +// Vertex shader to draw Flutter window contents. +static const char* vertex_shader_src = + "attribute vec2 position;\n" + "attribute vec2 in_texcoord;\n" + "varying vec2 texcoord;\n" + "\n" + "void main() {\n" + " gl_Position = vec4(position, 0, 1);\n" + " texcoord = in_texcoord;\n" + "}\n"; + +// Fragment shader to draw Flutter window contents. +static const char* fragment_shader_src = + "uniform sampler2D texture;\n" + "varying vec2 texcoord;\n" + "\n" + "void main() {\n" + " gl_FragColor = texture2D(texture, texcoord);\n" + "}\n"; G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error) @@ -27,12 +48,46 @@ typedef struct { // was rendered bool had_first_frame; - GdkGLContext* main_context; - GdkGLContext* resource_context; + // Shader program. + GLuint program; + + // Textures to render. + GPtrArray* textures; } FlRendererPrivate; G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT) +// Returns the log for the given OpenGL shader. Must be freed by the caller. +static gchar* get_shader_log(GLuint shader) { + int log_length; + gchar* log; + + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + + log = static_cast(g_malloc(log_length + 1)); + glGetShaderInfoLog(shader, log_length, nullptr, log); + + return log; +} + +// Returns the log for the given OpenGL program. Must be freed by the caller. +static gchar* get_program_log(GLuint program) { + int log_length; + gchar* log; + + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); + + log = static_cast(g_malloc(log_length + 1)); + glGetProgramInfoLog(program, log_length, nullptr, log); + + return log; +} + +/// Converts a pixel co-ordinate from 0..pixels to OpenGL -1..1. +static GLfloat pixels_to_gl_coords(GLfloat position, GLfloat pixels) { + return (2.0 * position / pixels) - 1.0; +} + static void fl_renderer_unblock_main_thread(FlRenderer* self) { FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); @@ -45,96 +100,103 @@ static void fl_renderer_unblock_main_thread(FlRenderer* self) { } } -static void fl_renderer_dispose(GObject* self) { - fl_renderer_unblock_main_thread(FL_RENDERER(self)); - G_OBJECT_CLASS(fl_renderer_parent_class)->dispose(self); +static void fl_renderer_dispose(GObject* object) { + FlRenderer* self = FL_RENDERER(object); + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + + fl_renderer_unblock_main_thread(self); + + g_clear_pointer(&priv->textures, g_ptr_array_unref); + + G_OBJECT_CLASS(fl_renderer_parent_class)->dispose(object); } static void fl_renderer_class_init(FlRendererClass* klass) { G_OBJECT_CLASS(klass)->dispose = fl_renderer_dispose; } -static void fl_renderer_init(FlRenderer* self) {} - -gboolean fl_renderer_start(FlRenderer* self, FlView* view, GError** error) { - g_return_val_if_fail(FL_IS_RENDERER(self), FALSE); +static void fl_renderer_init(FlRenderer* self) { FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); - priv->view = view; - gboolean result = FL_RENDERER_GET_CLASS(self)->create_contexts( - self, GTK_WIDGET(view), &priv->main_context, &priv->resource_context, - error); - - if (result) { - gdk_gl_context_realize(priv->main_context, error); - gdk_gl_context_realize(priv->resource_context, error); - } - - if (*error != nullptr) { - return FALSE; - } - return TRUE; + priv->textures = g_ptr_array_new_with_free_func(g_object_unref); } -FlView* fl_renderer_get_view(FlRenderer* self) { +gboolean fl_renderer_start(FlRenderer* self, FlView* view) { FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); - return priv->view; -} -GdkGLContext* fl_renderer_get_context(FlRenderer* self) { - FlRendererPrivate* priv = reinterpret_cast( - fl_renderer_get_instance_private(self)); - return priv->main_context; + g_return_val_if_fail(FL_IS_RENDERER(self), FALSE); + + priv->view = view; + return TRUE; } void* fl_renderer_get_proc_address(FlRenderer* self, const char* name) { + g_return_val_if_fail(FL_IS_RENDERER(self), NULL); + return reinterpret_cast(eglGetProcAddress(name)); } -gboolean fl_renderer_make_current(FlRenderer* self, GError** error) { - FlRendererPrivate* priv = reinterpret_cast( - fl_renderer_get_instance_private(self)); - if (priv->main_context) { - gdk_gl_context_make_current(priv->main_context); - } - - return TRUE; +void fl_renderer_make_current(FlRenderer* self) { + g_return_if_fail(FL_IS_RENDERER(self)); + FL_RENDERER_GET_CLASS(self)->make_current(self); } -gboolean fl_renderer_make_resource_current(FlRenderer* self, GError** error) { - FlRendererPrivate* priv = reinterpret_cast( - fl_renderer_get_instance_private(self)); - if (priv->resource_context) { - gdk_gl_context_make_current(priv->resource_context); - } - - return TRUE; +void fl_renderer_make_resource_current(FlRenderer* self) { + g_return_if_fail(FL_IS_RENDERER(self)); + FL_RENDERER_GET_CLASS(self)->make_resource_current(self); } -gboolean fl_renderer_clear_current(FlRenderer* self, GError** error) { - gdk_gl_context_clear_current(); - return TRUE; +void fl_renderer_clear_current(FlRenderer* self) { + g_return_if_fail(FL_IS_RENDERER(self)); + FL_RENDERER_GET_CLASS(self)->clear_current(self); } guint32 fl_renderer_get_fbo(FlRenderer* self) { + g_return_val_if_fail(FL_IS_RENDERER(self), 0); + // There is only one frame buffer object - always return that. return 0; } gboolean fl_renderer_create_backing_store( - FlRenderer* self, + FlRenderer* renderer, const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out) { - return FL_RENDERER_GET_CLASS(self)->create_backing_store(self, config, - backing_store_out); + fl_renderer_make_current(renderer); + + FlBackingStoreProvider* provider = + fl_backing_store_provider_new(config->size.width, config->size.height); + if (!provider) { + g_warning("Failed to create backing store"); + return FALSE; + } + + uint32_t name = fl_backing_store_provider_get_gl_framebuffer_id(provider); + uint32_t format = fl_backing_store_provider_get_gl_format(provider); + + backing_store_out->type = kFlutterBackingStoreTypeOpenGL; + backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; + backing_store_out->open_gl.framebuffer.user_data = provider; + backing_store_out->open_gl.framebuffer.name = name; + backing_store_out->open_gl.framebuffer.target = format; + backing_store_out->open_gl.framebuffer.destruction_callback = [](void* p) { + // Backing store destroyed in fl_renderer_collect_backing_store(), set + // on FlutterCompositor.collect_backing_store_callback during engine start. + }; + + return TRUE; } gboolean fl_renderer_collect_backing_store( FlRenderer* self, const FlutterBackingStore* backing_store) { - return FL_RENDERER_GET_CLASS(self)->collect_backing_store(self, - backing_store); + fl_renderer_make_current(self); + + // OpenGL context is required when destroying #FlBackingStoreProvider. + g_object_unref(backing_store->open_gl.framebuffer.user_data); + return TRUE; } void fl_renderer_wait_for_frame(FlRenderer* self, @@ -143,6 +205,8 @@ void fl_renderer_wait_for_frame(FlRenderer* self, FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); + g_return_if_fail(FL_IS_RENDERER(self)); + priv->target_width = target_width; priv->target_height = target_height; @@ -160,6 +224,8 @@ gboolean fl_renderer_present_layers(FlRenderer* self, FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); + g_return_val_if_fail(FL_IS_RENDERER(self), FALSE); + // ignore incoming frame with wrong dimensions in trivial case with just one // layer if (priv->blocking_main_thread && layers_count == 1 && @@ -173,6 +239,138 @@ gboolean fl_renderer_present_layers(FlRenderer* self, fl_renderer_unblock_main_thread(self); - return FL_RENDERER_GET_CLASS(self)->present_layers(self, layers, - layers_count); + if (!priv->view) { + return FALSE; + } + + g_ptr_array_set_size(priv->textures, 0); + for (size_t i = 0; i < layers_count; ++i) { + const FlutterLayer* layer = layers[i]; + switch (layer->type) { + case kFlutterLayerContentTypeBackingStore: { + const FlutterBackingStore* backing_store = layer->backing_store; + auto framebuffer = &backing_store->open_gl.framebuffer; + FlBackingStoreProvider* provider = + FL_BACKING_STORE_PROVIDER(framebuffer->user_data); + g_ptr_array_add(priv->textures, g_object_ref(provider)); + } break; + case kFlutterLayerContentTypePlatformView: { + // TODO(robert-ancell) Not implemented - + // https://github.com/flutter/flutter/issues/41724 + } break; + } + } + + fl_view_redraw(priv->view); + + return TRUE; +} + +void fl_renderer_setup(FlRenderer* self) { + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + + g_return_if_fail(FL_IS_RENDERER(self)); + + GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_src, nullptr); + glCompileShader(vertex_shader); + int vertex_compile_status; + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &vertex_compile_status); + if (vertex_compile_status == GL_FALSE) { + g_autofree gchar* shader_log = get_shader_log(vertex_shader); + g_warning("Failed to compile vertex shader: %s", shader_log); + } + + GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_src, nullptr); + glCompileShader(fragment_shader); + int fragment_compile_status; + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &fragment_compile_status); + if (fragment_compile_status == GL_FALSE) { + g_autofree gchar* shader_log = get_shader_log(fragment_shader); + g_warning("Failed to compile fragment shader: %s", shader_log); + } + + priv->program = glCreateProgram(); + glAttachShader(priv->program, vertex_shader); + glAttachShader(priv->program, fragment_shader); + glLinkProgram(priv->program); + + int link_status; + glGetProgramiv(priv->program, GL_LINK_STATUS, &link_status); + if (link_status == GL_FALSE) { + g_autofree gchar* program_log = get_program_log(priv->program); + g_warning("Failed to link program: %s", program_log); + } + + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); +} + +void fl_renderer_render(FlRenderer* self, int width, int height) { + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + + g_return_if_fail(FL_IS_RENDERER(self)); + + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(priv->program); + + for (guint i = 0; i < priv->textures->len; i++) { + FlBackingStoreProvider* texture = + FL_BACKING_STORE_PROVIDER(g_ptr_array_index(priv->textures, i)); + + uint32_t texture_id = fl_backing_store_provider_get_gl_texture_id(texture); + glBindTexture(GL_TEXTURE_2D, texture_id); + + // Translate into OpenGL co-ordinates + GdkRectangle texture_geometry = + fl_backing_store_provider_get_geometry(texture); + GLfloat texture_x = texture_geometry.x; + GLfloat texture_y = texture_geometry.y; + GLfloat texture_width = texture_geometry.width; + GLfloat texture_height = texture_geometry.height; + GLfloat x0 = pixels_to_gl_coords(texture_x, width); + GLfloat y0 = + pixels_to_gl_coords(height - (texture_y + texture_height), height); + GLfloat x1 = pixels_to_gl_coords(texture_x + texture_width, width); + GLfloat y1 = pixels_to_gl_coords(height - texture_y, height); + GLfloat vertex_data[] = {x0, y0, 0, 0, x1, y1, 1, 1, x0, y1, 0, 1, + x0, y0, 0, 0, x1, y0, 1, 0, x1, y1, 1, 1}; + + GLuint vao, vertex_buffer; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, + GL_STATIC_DRAW); + GLint position_index = glGetAttribLocation(priv->program, "position"); + glEnableVertexAttribArray(position_index); + glVertexAttribPointer(position_index, 2, GL_FLOAT, GL_FALSE, + sizeof(GLfloat) * 4, 0); + GLint texcoord_index = glGetAttribLocation(priv->program, "in_texcoord"); + glEnableVertexAttribArray(texcoord_index); + glVertexAttribPointer(texcoord_index, 2, GL_FLOAT, GL_FALSE, + sizeof(GLfloat) * 4, (void*)(sizeof(GLfloat) * 2)); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vertex_buffer); + } + + glFlush(); +} + +void fl_renderer_cleanup(FlRenderer* self) { + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + + g_return_if_fail(FL_IS_RENDERER(self)); + + glDeleteProgram(priv->program); } diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h index 16f2cbf540ac7..e2145fdbaed7b 100644 --- a/shell/platform/linux/fl_renderer.h +++ b/shell/platform/linux/fl_renderer.h @@ -39,30 +39,24 @@ struct _FlRendererClass { GObjectClass parent_class; /** - * Virtual method called when Flutter needs #GdkGLContext to render. + * Virtual method called when Flutter needs to make the OpenGL context + * current. * @renderer: an #FlRenderer. - * @widget: the widget being rendered on. - * @visible: (out): the GL context for visible surface. - * @resource: (out): the GL context for resource loading. - * @error: (allow-none): #GError location to store the error occurring, or - * %NULL to ignore. - * - * Returns: %TRUE if both contexts were created, %FALSE if there was an error. */ - gboolean (*create_contexts)(FlRenderer* renderer, - GtkWidget* widget, - GdkGLContext** visible, - GdkGLContext** resource, - GError** error); + void (*make_current)(FlRenderer* renderer); /** - * Virtual method called when Flutter needs OpenGL proc address. + * Virtual method called when Flutter needs to make the OpenGL resource + * context current. + * @renderer: an #FlRenderer. + */ + void (*make_resource_current)(FlRenderer* renderer); + + /** + * Virtual method called when Flutter needs to clear the OpenGL context. * @renderer: an #FlRenderer. - * @name: proc name. - * - * Returns: OpenGL proc address. */ - void* (*get_proc_address)(); + void (*clear_current)(FlRenderer* renderer); /** * Virtual method called when Flutter needs a backing store for a specific @@ -86,49 +80,18 @@ struct _FlRendererClass { */ gboolean (*collect_backing_store)(FlRenderer* renderer, const FlutterBackingStore* backing_store); - - /** - * Virtual method called when Flutter wants to composite layers onto the - * screen. - * @renderer: an #FlRenderer. - * @layers: layers to be composited. - * @layers_count: number of layers. - * - * Returns %TRUE if successful. - */ - gboolean (*present_layers)(FlRenderer* renderer, - const FlutterLayer** layers, - size_t layers_count); }; /** * fl_renderer_start: * @renderer: an #FlRenderer. * @view: the view Flutter is renderering to. - * @error: (allow-none): #GError location to store the error occurring, or %NULL - * to ignore. * * Start the renderer. * * Returns: %TRUE if successfully started. */ -gboolean fl_renderer_start(FlRenderer* renderer, FlView* view, GError** error); - -/** - * fl_renderer_get_view: - * @renderer: an #FlRenderer. - * - * Returns: targeted #FlView or %NULL if headless. - */ -FlView* fl_renderer_get_view(FlRenderer* renderer); - -/** - * fl_renderer_get_context: - * @renderer: an #FlRenderer. - * - * Returns: GL context for GLAreas or %NULL if headless. - */ -GdkGLContext* fl_renderer_get_context(FlRenderer* renderer); +gboolean fl_renderer_start(FlRenderer* renderer, FlView* view); /** * fl_renderer_get_proc_address: @@ -144,39 +107,26 @@ void* fl_renderer_get_proc_address(FlRenderer* renderer, const char* name); /** * fl_renderer_make_current: * @renderer: an #FlRenderer. - * @error: (allow-none): #GError location to store the error occurring, or %NULL - * to ignore. * * Makes the rendering context current. - * - * Returns %TRUE if successful. */ -gboolean fl_renderer_make_current(FlRenderer* renderer, GError** error); +void fl_renderer_make_current(FlRenderer* renderer); /** * fl_renderer_make_resource_current: * @renderer: an #FlRenderer. - * @error: (allow-none): #GError location to store the error occurring, or %NULL - * to ignore. * * Makes the resource rendering context current. - * - * Returns %TRUE if successful. */ -gboolean fl_renderer_make_resource_current(FlRenderer* renderer, - GError** error); +void fl_renderer_make_resource_current(FlRenderer* renderer); /** * fl_renderer_clear_current: * @renderer: an #FlRenderer. - * @error: (allow-none): #GError location to store the error occurring, or %NULL - * to ignore. * * Clears the current rendering context. - * - * Returns %TRUE if successful. */ -gboolean fl_renderer_clear_current(FlRenderer* renderer, GError** error); +void fl_renderer_clear_current(FlRenderer* renderer); /** * fl_renderer_get_fbo: @@ -246,6 +196,33 @@ void fl_renderer_wait_for_frame(FlRenderer* renderer, int target_width, int target_height); +/** + * fl_renderer_setup: + * @renderer: an #FlRenderer. + * + * Creates OpenGL resources required before rendering. Requires an active OpenGL + * context. + */ +void fl_renderer_setup(FlRenderer* renderer); + +/** + * fl_renderer_render: + * @renderer: an #FlRenderer. + * @width: width of the window in pixels. + * @height: height of the window in pixels. + * + * Performs OpenGL commands to render current Flutter view. + */ +void fl_renderer_render(FlRenderer* renderer, int width, int height); + +/** + * fl_renderer_cleanup: + * + * Removes OpenGL resources used for rendering. Requires an active OpenGL + * context. + */ +void fl_renderer_cleanup(FlRenderer* renderer); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_ diff --git a/shell/platform/linux/fl_renderer_gdk.cc b/shell/platform/linux/fl_renderer_gdk.cc new file mode 100644 index 0000000000000..f5d4990efcd82 --- /dev/null +++ b/shell/platform/linux/fl_renderer_gdk.cc @@ -0,0 +1,89 @@ +// 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_renderer_gdk.h" + +struct _FlRendererGdk { + FlRenderer parent_instance; + + // Window being rendered on. + GdkWindow* window; + + // Main OpenGL rendering context. + GdkGLContext* main_context; + + // Secondary OpenGL rendering context. + GdkGLContext* resource_context; +}; + +G_DEFINE_TYPE(FlRendererGdk, fl_renderer_gdk, fl_renderer_get_type()) + +// Implements FlRenderer::make_current. +static void fl_renderer_gdk_make_current(FlRenderer* renderer) { + FlRendererGdk* self = FL_RENDERER_GDK(renderer); + gdk_gl_context_make_current(self->main_context); +} + +// Implements FlRenderer::make_resource_current. +static void fl_renderer_gdk_make_resource_current(FlRenderer* renderer) { + FlRendererGdk* self = FL_RENDERER_GDK(renderer); + gdk_gl_context_make_current(self->resource_context); +} + +// Implements FlRenderer::clear_current. +static void fl_renderer_gdk_clear_current(FlRenderer* renderer) { + gdk_gl_context_clear_current(); +} + +static void fl_renderer_gdk_dispose(GObject* object) { + FlRendererGdk* self = FL_RENDERER_GDK(object); + + g_clear_object(&self->main_context); + g_clear_object(&self->resource_context); + + G_OBJECT_CLASS(fl_renderer_gdk_parent_class)->dispose(object); +} + +static void fl_renderer_gdk_class_init(FlRendererGdkClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_renderer_gdk_dispose; + + FL_RENDERER_CLASS(klass)->make_current = fl_renderer_gdk_make_current; + FL_RENDERER_CLASS(klass)->make_resource_current = + fl_renderer_gdk_make_resource_current; + FL_RENDERER_CLASS(klass)->clear_current = fl_renderer_gdk_clear_current; +} + +static void fl_renderer_gdk_init(FlRendererGdk* self) {} + +FlRendererGdk* fl_renderer_gdk_new(GdkWindow* window) { + FlRendererGdk* self = + FL_RENDERER_GDK(g_object_new(fl_renderer_gdk_get_type(), nullptr)); + self->window = window; + return self; +} + +gboolean fl_renderer_gdk_create_contexts(FlRendererGdk* self, GError** error) { + self->main_context = gdk_window_create_gl_context(self->window, error); + if (self->main_context == nullptr) { + return FALSE; + } + if (!gdk_gl_context_realize(self->main_context, error)) { + return FALSE; + } + + self->resource_context = gdk_window_create_gl_context(self->window, error); + if (self->resource_context == nullptr) { + return FALSE; + } + if (!gdk_gl_context_realize(self->resource_context, error)) { + return FALSE; + } + + return TRUE; +} + +GdkGLContext* fl_renderer_gdk_get_context(FlRendererGdk* self) { + g_return_val_if_fail(FL_IS_RENDERER_GDK(self), nullptr); + return self->main_context; +} diff --git a/shell/platform/linux/fl_renderer_gdk.h b/shell/platform/linux/fl_renderer_gdk.h new file mode 100644 index 0000000000000..e2b3f7bb1f961 --- /dev/null +++ b/shell/platform/linux/fl_renderer_gdk.h @@ -0,0 +1,57 @@ +// 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_RENDERER_GDK_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_GDK_H_ + +#include "flutter/shell/platform/linux/fl_renderer.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlRendererGdk, + fl_renderer_gdk, + FL, + RENDERER_GDK, + FlRenderer) + +/** + * FlRendererGdk: + * + * #FlRendererGdk is an implementation of #FlRenderer that renders by OpenGL ES. + */ + +/** + * fl_renderer_gdk_new: + * @window: the window that is being rendered on. + * + * Creates an object that allows Flutter to render by OpenGL ES. + * + * Returns: a new #FlRendererGdk. + */ +FlRendererGdk* fl_renderer_gdk_new(GdkWindow* window); + +/** + * fl_renderer_gdk_create_contexts: + * @renderer: an #FlRendererGdk. + * @error: (allow-none): #GError location to store the error occurring, or + * %NULL to ignore. + * + * Create rendering contexts. + * + * Returns: %TRUE if contexts were created, %FALSE if there was an error. + */ +gboolean fl_renderer_gdk_create_contexts(FlRendererGdk* renderer, + GError** error); + +/** + * fl_renderer_gdk_get_context: + * @renderer: an #FlRendererGdk. + * + * Returns: the main context used for rendering. + */ +GdkGLContext* fl_renderer_gdk_get_context(FlRendererGdk* renderer); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_GDK_H_ diff --git a/shell/platform/linux/fl_renderer_gl.cc b/shell/platform/linux/fl_renderer_gl.cc deleted file mode 100644 index 7210e95954950..0000000000000 --- a/shell/platform/linux/fl_renderer_gl.cc +++ /dev/null @@ -1,136 +0,0 @@ -// 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_renderer_gl.h" - -#include "flutter/shell/platform/linux/fl_backing_store_provider.h" -#include "flutter/shell/platform/linux/fl_view_private.h" - -struct _FlRendererGL { - FlRenderer parent_instance; -}; - -G_DEFINE_TYPE(FlRendererGL, fl_renderer_gl, fl_renderer_get_type()) - -// Implements FlRenderer::create_contexts. -static gboolean fl_renderer_gl_create_contexts(FlRenderer* renderer, - GtkWidget* widget, - GdkGLContext** visible, - GdkGLContext** resource, - GError** error) { - GdkWindow* window = gtk_widget_get_parent_window(widget); - - *visible = gdk_window_create_gl_context(window, error); - - if (*error != nullptr) { - return FALSE; - } - - *resource = gdk_window_create_gl_context(window, error); - - if (*error != nullptr) { - return FALSE; - } - return TRUE; -} - -// Implements FlRenderer::create_backing_store. -static gboolean fl_renderer_gl_create_backing_store( - FlRenderer* renderer, - const FlutterBackingStoreConfig* config, - FlutterBackingStore* backing_store_out) { - g_autoptr(GError) error = nullptr; - gboolean result = fl_renderer_make_current(renderer, &error); - if (!result) { - g_warning("Failed to make renderer current when creating backing store: %s", - error->message); - return FALSE; - } - - FlBackingStoreProvider* provider = - fl_backing_store_provider_new(config->size.width, config->size.height); - if (!provider) { - g_warning("Failed to create backing store"); - return FALSE; - } - - uint32_t name = fl_backing_store_provider_get_gl_framebuffer_id(provider); - uint32_t format = fl_backing_store_provider_get_gl_format(provider); - - backing_store_out->type = kFlutterBackingStoreTypeOpenGL; - backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; - backing_store_out->open_gl.framebuffer.user_data = provider; - backing_store_out->open_gl.framebuffer.name = name; - backing_store_out->open_gl.framebuffer.target = format; - backing_store_out->open_gl.framebuffer.destruction_callback = [](void* p) { - // Backing store destroyed in fl_renderer_gl_collect_backing_store(), set - // on FlutterCompositor.collect_backing_store_callback during engine start. - }; - - return TRUE; -} - -// Implements FlRenderer::collect_backing_store. -static gboolean fl_renderer_gl_collect_backing_store( - FlRenderer* renderer, - const FlutterBackingStore* backing_store) { - g_autoptr(GError) error = nullptr; - gboolean result = fl_renderer_make_current(renderer, &error); - if (!result) { - g_warning( - "Failed to make renderer current when collecting backing store: %s", - error->message); - return FALSE; - } - - // OpenGL context is required when destroying #FlBackingStoreProvider. - g_object_unref(backing_store->open_gl.framebuffer.user_data); - return TRUE; -} - -// Implements FlRenderer::present_layers. -static gboolean fl_renderer_gl_present_layers(FlRenderer* renderer, - const FlutterLayer** layers, - size_t layers_count) { - FlView* view = fl_renderer_get_view(renderer); - GdkGLContext* context = fl_renderer_get_context(renderer); - if (!view || !context) { - return FALSE; - } - - g_autoptr(GPtrArray) textures = g_ptr_array_new(); - for (size_t i = 0; i < layers_count; ++i) { - const FlutterLayer* layer = layers[i]; - switch (layer->type) { - case kFlutterLayerContentTypeBackingStore: { - const FlutterBackingStore* backing_store = layer->backing_store; - auto framebuffer = &backing_store->open_gl.framebuffer; - g_ptr_array_add(textures, reinterpret_cast( - framebuffer->user_data)); - } break; - case kFlutterLayerContentTypePlatformView: { - // Currently unsupported. - } break; - } - } - - fl_view_set_textures(view, context, textures); - - return TRUE; -} - -static void fl_renderer_gl_class_init(FlRendererGLClass* klass) { - FL_RENDERER_CLASS(klass)->create_contexts = fl_renderer_gl_create_contexts; - FL_RENDERER_CLASS(klass)->create_backing_store = - fl_renderer_gl_create_backing_store; - FL_RENDERER_CLASS(klass)->collect_backing_store = - fl_renderer_gl_collect_backing_store; - FL_RENDERER_CLASS(klass)->present_layers = fl_renderer_gl_present_layers; -} - -static void fl_renderer_gl_init(FlRendererGL* self) {} - -FlRendererGL* fl_renderer_gl_new() { - return FL_RENDERER_GL(g_object_new(fl_renderer_gl_get_type(), nullptr)); -} diff --git a/shell/platform/linux/fl_renderer_gl.h b/shell/platform/linux/fl_renderer_gl.h deleted file mode 100644 index 25216b49cab10..0000000000000 --- a/shell/platform/linux/fl_renderer_gl.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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_RENDERER_GL_H_ -#define FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_GL_H_ - -#include "flutter/shell/platform/linux/fl_renderer.h" - -G_BEGIN_DECLS - -G_DECLARE_FINAL_TYPE(FlRendererGL, fl_renderer_gl, FL, RENDERER_GL, FlRenderer) - -/** - * FlRendererGL: - * - * #FlRendererGL is an implementation of #FlRenderer that renders by OpenGL ES. - */ - -/** - * fl_renderer_gl_new: - * - * Creates an object that allows Flutter to render by OpenGL ES. - * - * Returns: a new #FlRendererGL. - */ -FlRendererGL* fl_renderer_gl_new(); - -G_END_DECLS - -#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_GL_H_ diff --git a/shell/platform/linux/fl_renderer_headless.cc b/shell/platform/linux/fl_renderer_headless.cc index 19d2da931e8e2..1736e1437396f 100644 --- a/shell/platform/linux/fl_renderer_headless.cc +++ b/shell/platform/linux/fl_renderer_headless.cc @@ -10,46 +10,20 @@ struct _FlRendererHeadless { G_DEFINE_TYPE(FlRendererHeadless, fl_renderer_headless, fl_renderer_get_type()) -// Implements FlRenderer::create_contexts. -static gboolean fl_renderer_headless_create_contexts(FlRenderer* renderer, - GtkWidget* widget, - GdkGLContext** visible, - GdkGLContext** resource, - GError** error) { - return FALSE; -} +// Implements FlRenderer::make_current. +static void fl_renderer_headless_make_current(FlRenderer* renderer) {} -// Implements FlRenderer::create_backing_store. -static gboolean fl_renderer_headless_create_backing_store( - FlRenderer* renderer, - const FlutterBackingStoreConfig* config, - FlutterBackingStore* backing_store_out) { - return FALSE; -} +// Implements FlRenderer::make_resource_current. +static void fl_renderer_headless_make_resource_current(FlRenderer* renderer) {} -// Implements FlRenderer::collect_backing_store. -static gboolean fl_renderer_headless_collect_backing_store( - FlRenderer* self, - const FlutterBackingStore* backing_store) { - return FALSE; -} - -// Implements FlRenderer::present_layers. -static gboolean fl_renderer_headless_present_layers(FlRenderer* self, - const FlutterLayer** layers, - size_t layers_count) { - return FALSE; -} +// Implements FlRenderer::clear_current. +static void fl_renderer_headless_clear_current(FlRenderer* renderer) {} static void fl_renderer_headless_class_init(FlRendererHeadlessClass* klass) { - FL_RENDERER_CLASS(klass)->create_contexts = - fl_renderer_headless_create_contexts; - FL_RENDERER_CLASS(klass)->create_backing_store = - fl_renderer_headless_create_backing_store; - FL_RENDERER_CLASS(klass)->collect_backing_store = - fl_renderer_headless_collect_backing_store; - FL_RENDERER_CLASS(klass)->present_layers = - fl_renderer_headless_present_layers; + FL_RENDERER_CLASS(klass)->make_current = fl_renderer_headless_make_current; + FL_RENDERER_CLASS(klass)->make_resource_current = + fl_renderer_headless_make_resource_current; + FL_RENDERER_CLASS(klass)->clear_current = fl_renderer_headless_clear_current; } static void fl_renderer_headless_init(FlRendererHeadless* self) {} diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index b80bbb55365b5..0d1fb4709f703 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -8,6 +8,7 @@ #include +#include "flutter/shell/platform/linux/fl_backing_store_provider.h" #include "flutter/shell/platform/linux/fl_engine_private.h" #include "flutter/shell/platform/linux/fl_key_event.h" #include "flutter/shell/platform/linux/fl_keyboard_manager.h" @@ -15,7 +16,7 @@ #include "flutter/shell/platform/linux/fl_mouse_cursor_plugin.h" #include "flutter/shell/platform/linux/fl_platform_plugin.h" #include "flutter/shell/platform/linux/fl_plugin_registrar_private.h" -#include "flutter/shell/platform/linux/fl_renderer_gl.h" +#include "flutter/shell/platform/linux/fl_renderer_gdk.h" #include "flutter/shell/platform/linux/fl_scrolling_manager.h" #include "flutter/shell/platform/linux/fl_scrolling_view_delegate.h" #include "flutter/shell/platform/linux/fl_text_input_plugin.h" @@ -33,7 +34,7 @@ struct _FlView { FlDartProject* project; // Rendering output. - FlRenderer* renderer; + FlRendererGdk* renderer; // Engine running @project. FlEngine* engine; @@ -52,7 +53,7 @@ struct _FlView { FlPlatformPlugin* platform_plugin; GtkWidget* event_box; - FlGLArea* gl_area; + GtkGLArea* gl_area; // Tracks whether mouse pointer is inside the view. gboolean pointer_inside; @@ -218,7 +219,8 @@ static void handle_geometry_changed(FlView* self) { // Note: `gtk_widget_init()` initializes the size allocation to 1x1. if (allocation.width > 1 && allocation.height > 1 && gtk_widget_get_realized(GTK_WIDGET(self))) { - fl_renderer_wait_for_frame(self->renderer, allocation.width * scale_factor, + fl_renderer_wait_for_frame(FL_RENDERER(self->renderer), + allocation.width * scale_factor, allocation.height * scale_factor); } } @@ -518,9 +520,49 @@ static gboolean window_state_event_cb(FlView* self, GdkEvent* event) { return FALSE; } +static GdkGLContext* create_context_cb(FlView* self) { + self->renderer = + fl_renderer_gdk_new(gtk_widget_get_parent_window(GTK_WIDGET(self))); + self->engine = fl_engine_new(self->project, FL_RENDERER(self->renderer)); + fl_engine_set_update_semantics_handler(self->engine, update_semantics_cb, + self, nullptr); + fl_engine_set_on_pre_engine_restart_handler( + self->engine, on_pre_engine_restart_cb, self, nullptr); + + // Must initialize the keymap before the keyboard. + self->keymap = gdk_keymap_get_for_display(gdk_display_get_default()); + self->keymap_keys_changed_cb_id = g_signal_connect_swapped( + self->keymap, "keys-changed", G_CALLBACK(keymap_keys_changed_cb), self); + + // Create system channel handlers. + FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine); + init_scrolling(self); + self->mouse_cursor_plugin = fl_mouse_cursor_plugin_new(messenger, self); + self->platform_plugin = fl_platform_plugin_new(messenger); + + g_autoptr(GError) error = nullptr; + if (!fl_renderer_gdk_create_contexts(self->renderer, &error)) { + gtk_gl_area_set_error(self->gl_area, error); + return nullptr; + } + + return GDK_GL_CONTEXT( + g_object_ref(fl_renderer_gdk_get_context(self->renderer))); +} + static void realize_cb(FlView* self) { g_autoptr(GError) error = nullptr; + fl_renderer_make_current(FL_RENDERER(self->renderer)); + + GError* gl_error = gtk_gl_area_get_error(self->gl_area); + if (gl_error != NULL) { + g_warning("Failed to initialize GLArea: %s", gl_error->message); + return; + } + + fl_renderer_setup(FL_RENDERER(self->renderer)); + // Handle requests by the user to close the application. GtkWidget* toplevel_window = gtk_widget_get_toplevel(GTK_WIDGET(self)); @@ -536,10 +578,7 @@ static void realize_cb(FlView* self) { init_keyboard(self); - if (!fl_renderer_start(self->renderer, self, &error)) { - g_warning("Failed to start Flutter renderer: %s", error->message); - return; - } + fl_renderer_start(FL_RENDERER(FL_RENDERER(self->renderer)), self); if (!fl_engine_start(self->engine, &error)) { g_warning("Failed to start Flutter engine: %s", error->message); @@ -549,70 +588,36 @@ static void realize_cb(FlView* self) { handle_geometry_changed(self); } -static void size_allocate_cb(FlView* self) { - handle_geometry_changed(self); -} +static gboolean render_cb(FlView* self, GdkGLContext* context) { + if (gtk_gl_area_get_error(self->gl_area) != NULL) { + return FALSE; + } -static void fl_view_constructed(GObject* object) { - FlView* self = FL_VIEW(object); + int width = gtk_widget_get_allocated_width(GTK_WIDGET(self->gl_area)); + int height = gtk_widget_get_allocated_height(GTK_WIDGET(self->gl_area)); + gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self->gl_area)); + fl_renderer_render(FL_RENDERER(self->renderer), width * scale_factor, + height * scale_factor); - self->renderer = FL_RENDERER(fl_renderer_gl_new()); - self->engine = fl_engine_new(self->project, self->renderer); - fl_engine_set_update_semantics_handler(self->engine, update_semantics_cb, - self, nullptr); - fl_engine_set_on_pre_engine_restart_handler( - self->engine, on_pre_engine_restart_cb, self, nullptr); + return TRUE; +} - // Must initialize the keymap before the keyboard. - self->keymap = gdk_keymap_get_for_display(gdk_display_get_default()); +static void unrealize_cb(FlView* self) { + g_autoptr(GError) error = nullptr; - // Create system channel handlers. - FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine); - init_scrolling(self); - self->mouse_cursor_plugin = fl_mouse_cursor_plugin_new(messenger, self); - self->platform_plugin = fl_platform_plugin_new(messenger); + fl_renderer_make_current(FL_RENDERER(self->renderer)); - self->event_box = gtk_event_box_new(); - gtk_widget_set_hexpand(self->event_box, TRUE); - gtk_widget_set_vexpand(self->event_box, TRUE); - gtk_container_add(GTK_CONTAINER(self), self->event_box); - gtk_widget_show(self->event_box); - gtk_widget_add_events(self->event_box, - GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | - GDK_SMOOTH_SCROLL_MASK); + GError* gl_error = gtk_gl_area_get_error(self->gl_area); + if (gl_error != NULL) { + g_warning("Failed to uninitialize GLArea: %s", gl_error->message); + return; + } - g_signal_connect_swapped(self->event_box, "button-press-event", - G_CALLBACK(button_press_event_cb), self); - g_signal_connect_swapped(self->event_box, "button-release-event", - G_CALLBACK(button_release_event_cb), self); - g_signal_connect_swapped(self->event_box, "scroll-event", - G_CALLBACK(scroll_event_cb), self); - g_signal_connect_swapped(self->event_box, "motion-notify-event", - G_CALLBACK(motion_notify_event_cb), self); - g_signal_connect_swapped(self->event_box, "enter-notify-event", - G_CALLBACK(enter_notify_event_cb), self); - g_signal_connect_swapped(self->event_box, "leave-notify-event", - G_CALLBACK(leave_notify_event_cb), self); - self->keymap_keys_changed_cb_id = g_signal_connect_swapped( - self->keymap, "keys-changed", G_CALLBACK(keymap_keys_changed_cb), self); - GtkGesture* zoom = gtk_gesture_zoom_new(self->event_box); - g_signal_connect_swapped(zoom, "begin", G_CALLBACK(gesture_zoom_begin_cb), - self); - g_signal_connect_swapped(zoom, "scale-changed", - G_CALLBACK(gesture_zoom_update_cb), self); - g_signal_connect_swapped(zoom, "end", G_CALLBACK(gesture_zoom_end_cb), self); - GtkGesture* rotate = gtk_gesture_rotate_new(self->event_box); - g_signal_connect_swapped(rotate, "begin", - G_CALLBACK(gesture_rotation_begin_cb), self); - g_signal_connect_swapped(rotate, "angle-changed", - G_CALLBACK(gesture_rotation_update_cb), self); - g_signal_connect_swapped(rotate, "end", G_CALLBACK(gesture_rotation_end_cb), - self); + fl_renderer_cleanup(FL_RENDERER(self->renderer)); +} - g_signal_connect_swapped(self, "realize", G_CALLBACK(realize_cb), self); - g_signal_connect_swapped(self, "size-allocate", G_CALLBACK(size_allocate_cb), - self); +static void size_allocate_cb(FlView* self) { + handle_geometry_changed(self); } static void fl_view_set_property(GObject* object, @@ -710,7 +715,6 @@ static gboolean fl_view_key_release_event(GtkWidget* widget, static void fl_view_class_init(FlViewClass* klass) { GObjectClass* object_class = G_OBJECT_CLASS(klass); - object_class->constructed = fl_view_constructed; object_class->set_property = fl_view_set_property; object_class->get_property = fl_view_get_property; object_class->notify = fl_view_notify; @@ -734,6 +738,58 @@ static void fl_view_class_init(FlViewClass* klass) { static void fl_view_init(FlView* self) { gtk_widget_set_can_focus(GTK_WIDGET(self), TRUE); + + self->event_box = gtk_event_box_new(); + gtk_widget_set_hexpand(self->event_box, TRUE); + gtk_widget_set_vexpand(self->event_box, TRUE); + gtk_container_add(GTK_CONTAINER(self), self->event_box); + gtk_widget_show(self->event_box); + gtk_widget_add_events(self->event_box, + GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | + GDK_SMOOTH_SCROLL_MASK); + + g_signal_connect_swapped(self->event_box, "button-press-event", + G_CALLBACK(button_press_event_cb), self); + g_signal_connect_swapped(self->event_box, "button-release-event", + G_CALLBACK(button_release_event_cb), self); + g_signal_connect_swapped(self->event_box, "scroll-event", + G_CALLBACK(scroll_event_cb), self); + g_signal_connect_swapped(self->event_box, "motion-notify-event", + G_CALLBACK(motion_notify_event_cb), self); + g_signal_connect_swapped(self->event_box, "enter-notify-event", + G_CALLBACK(enter_notify_event_cb), self); + g_signal_connect_swapped(self->event_box, "leave-notify-event", + G_CALLBACK(leave_notify_event_cb), self); + GtkGesture* zoom = gtk_gesture_zoom_new(self->event_box); + g_signal_connect_swapped(zoom, "begin", G_CALLBACK(gesture_zoom_begin_cb), + self); + g_signal_connect_swapped(zoom, "scale-changed", + G_CALLBACK(gesture_zoom_update_cb), self); + g_signal_connect_swapped(zoom, "end", G_CALLBACK(gesture_zoom_end_cb), self); + GtkGesture* rotate = gtk_gesture_rotate_new(self->event_box); + g_signal_connect_swapped(rotate, "begin", + G_CALLBACK(gesture_rotation_begin_cb), self); + g_signal_connect_swapped(rotate, "angle-changed", + G_CALLBACK(gesture_rotation_update_cb), self); + g_signal_connect_swapped(rotate, "end", G_CALLBACK(gesture_rotation_end_cb), + self); + + self->gl_area = GTK_GL_AREA(gtk_gl_area_new()); + gtk_widget_show(GTK_WIDGET(self->gl_area)); + gtk_container_add(GTK_CONTAINER(self->event_box), GTK_WIDGET(self->gl_area)); + + g_signal_connect_swapped(self->gl_area, "create-context", + G_CALLBACK(create_context_cb), self); + g_signal_connect_swapped(self->gl_area, "realize", G_CALLBACK(realize_cb), + self); + g_signal_connect_swapped(self->gl_area, "render", G_CALLBACK(render_cb), + self); + g_signal_connect_swapped(self->gl_area, "unrealize", G_CALLBACK(unrealize_cb), + self); + + g_signal_connect_swapped(self, "size-allocate", G_CALLBACK(size_allocate_cb), + self); } G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) { @@ -746,23 +802,12 @@ G_MODULE_EXPORT FlEngine* fl_view_get_engine(FlView* self) { return self->engine; } -void fl_view_set_textures(FlView* self, - GdkGLContext* context, - GPtrArray* textures) { +void fl_view_redraw(FlView* self) { g_return_if_fail(FL_IS_VIEW(self)); - - if (self->gl_area == nullptr) { - self->gl_area = FL_GL_AREA(fl_gl_area_new(context)); - gtk_widget_show(GTK_WIDGET(self->gl_area)); - gtk_container_add(GTK_CONTAINER(self->event_box), - GTK_WIDGET(self->gl_area)); - } - - fl_gl_area_queue_render(self->gl_area, textures); + gtk_widget_queue_draw(GTK_WIDGET(self->gl_area)); } GHashTable* fl_view_get_keyboard_state(FlView* self) { g_return_val_if_fail(FL_IS_VIEW(self), nullptr); - return fl_keyboard_manager_get_pressed_state(self->keyboard_manager); } diff --git a/shell/platform/linux/fl_view_private.h b/shell/platform/linux/fl_view_private.h index dffc5114e5387..de3fc9c911876 100644 --- a/shell/platform/linux/fl_view_private.h +++ b/shell/platform/linux/fl_view_private.h @@ -7,20 +7,13 @@ #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" -#include "flutter/shell/platform/linux/fl_gl_area.h" - /** - * fl_view_set_textures: + * fl_view_redraw: * @view: an #FlView. - * @context: a #GdkGLContext, for #FlGLArea to render. - * @textures: (transfer none) (element-type FlBackingStoreProvider): a list of - * #FlBackingStoreProvider. * - * Set the textures for this view to render. + * Indicate the view needs to redraw. */ -void fl_view_set_textures(FlView* view, - GdkGLContext* context, - GPtrArray* textures); +void fl_view_redraw(FlView* view); /** * fl_view_get_keyboard_state: diff --git a/shell/platform/linux/testing/mock_renderer.cc b/shell/platform/linux/testing/mock_renderer.cc index ce801724ec3c8..91d87b460cf3d 100644 --- a/shell/platform/linux/testing/mock_renderer.cc +++ b/shell/platform/linux/testing/mock_renderer.cc @@ -10,44 +10,20 @@ struct _FlMockRenderer { G_DEFINE_TYPE(FlMockRenderer, fl_mock_renderer, fl_renderer_get_type()) -// Implements FlRenderer::create_contexts. -static gboolean fl_mock_renderer_create_contexts(FlRenderer* renderer, - GtkWidget* widget, - GdkGLContext** visible, - GdkGLContext** resource, - GError** error) { - return TRUE; -} +// Implements FlRenderer::make_current. +static void fl_mock_renderer_make_current(FlRenderer* renderer) {} -// Implements FlRenderer::create_backing_store. -static gboolean fl_mock_renderer_create_backing_store( - FlRenderer* renderer, - const FlutterBackingStoreConfig* config, - FlutterBackingStore* backing_store_out) { - return TRUE; -} +// Implements FlRenderer::make_resource_current. +static void fl_mock_renderer_make_resource_current(FlRenderer* renderer) {} -// Implements FlRenderer::collect_backing_store. -static gboolean fl_mock_renderer_collect_backing_store( - FlRenderer* self, - const FlutterBackingStore* backing_store) { - return TRUE; -} - -// Implements FlRenderer::present_layers. -static gboolean fl_mock_renderer_present_layers(FlRenderer* self, - const FlutterLayer** layers, - size_t layers_count) { - return TRUE; -} +// Implements FlRenderer::clear_current. +static void fl_mock_renderer_clear_current(FlRenderer* renderer) {} static void fl_mock_renderer_class_init(FlMockRendererClass* klass) { - FL_RENDERER_CLASS(klass)->create_contexts = fl_mock_renderer_create_contexts; - FL_RENDERER_CLASS(klass)->create_backing_store = - fl_mock_renderer_create_backing_store; - FL_RENDERER_CLASS(klass)->collect_backing_store = - fl_mock_renderer_collect_backing_store; - FL_RENDERER_CLASS(klass)->present_layers = fl_mock_renderer_present_layers; + FL_RENDERER_CLASS(klass)->make_current = fl_mock_renderer_make_current; + FL_RENDERER_CLASS(klass)->make_resource_current = + fl_mock_renderer_make_resource_current; + FL_RENDERER_CLASS(klass)->clear_current = fl_mock_renderer_clear_current; } static void fl_mock_renderer_init(FlMockRenderer* self) {}