From 8c973846f4217bc619f272eb9c29b31142c2d374 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 29 May 2024 13:56:58 +1200 Subject: [PATCH 1/9] Use glBlitFramebuffer when rendering This is much faster than using a shader which is not required currently (we are not doing any transformations). --- shell/platform/linux/fl_renderer.cc | 168 ++-------------------------- shell/platform/linux/fl_renderer.h | 17 --- shell/platform/linux/fl_view.cc | 18 --- 3 files changed, 9 insertions(+), 194 deletions(-) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index a2163aae6ffb4..9f756e316d481 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -12,26 +12,6 @@ #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) typedef struct { @@ -48,46 +28,12 @@ typedef struct { // was rendered bool had_first_frame; - // 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)); @@ -271,125 +217,29 @@ gboolean fl_renderer_present_layers(FlRenderer* self, 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)); - // Save bindings that are set by this function. All bindings must be restored - // to their original values because Skia expects that its bindings have not - // been altered. - GLint saved_texture_binding; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding); - GLint saved_vao_binding; - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao_binding); - GLint saved_array_buffer_binding; - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &saved_array_buffer_binding); - 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); + uint32_t framebuffer_id = + fl_backing_store_provider_get_gl_framebuffer_id(texture); + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer_id); + GdkRectangle geometry = fl_backing_store_provider_get_geometry(texture); + glBlitFramebuffer(0, 0, geometry.width, geometry.height, geometry.x, + geometry.y, geometry.x + geometry.width, + geometry.x + geometry.height, GL_COLOR_BUFFER_BIT, + GL_NEAREST); } + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glFlush(); - - glBindTexture(GL_TEXTURE_2D, saved_texture_binding); - glBindVertexArray(saved_vao_binding); - glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding); -} - -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 39222ef1f2f96..836fbbbaf6705 100644 --- a/shell/platform/linux/fl_renderer.h +++ b/shell/platform/linux/fl_renderer.h @@ -206,15 +206,6 @@ 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. @@ -225,14 +216,6 @@ void fl_renderer_setup(FlRenderer* renderer); */ 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); - /** * fl_renderer_get_refresh_rate: * @renderer: an #FlRenderer. diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 8dc057e6fa027..61448bfc12b7f 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -567,8 +567,6 @@ static void realize_cb(FlView* self) { 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)); @@ -613,20 +611,6 @@ static gboolean render_cb(FlView* self, GdkGLContext* context) { return TRUE; } -static void unrealize_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 uninitialize GLArea: %s", gl_error->message); - return; - } - - fl_renderer_cleanup(FL_RENDERER(self->renderer)); -} - static void size_allocate_cb(FlView* self) { handle_geometry_changed(self); } @@ -797,8 +781,6 @@ static void fl_view_init(FlView* self) { 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); From ac3e6dae1f64051f7f146b83506d40ca6229ddb7 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 4 Jul 2024 15:23:25 +1200 Subject: [PATCH 2/9] Fallback to texture rendering if don't have glBlitFramebuffer --- shell/platform/linux/fl_renderer.cc | 226 ++++++++++++++++++++++++++-- shell/platform/linux/fl_renderer.h | 17 +++ shell/platform/linux/fl_view.cc | 18 +++ 3 files changed, 248 insertions(+), 13 deletions(-) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 9f756e316d481..79af26532a0ea 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -12,6 +12,26 @@ #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) typedef struct { @@ -28,12 +48,49 @@ typedef struct { // was rendered bool had_first_frame; + // True if we can use glBlitFramebuffer. + bool has_gl_framebuffer_blit; + + // 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)); @@ -46,6 +103,131 @@ static void fl_renderer_unblock_main_thread(FlRenderer* self) { } } +static void setup_shader(FlRenderer* self) { + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(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); +} + +static void render_with_blit(FlRenderer* self) { + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + + for (guint i = 0; i < priv->textures->len; i++) { + FlBackingStoreProvider* texture = + FL_BACKING_STORE_PROVIDER(g_ptr_array_index(priv->textures, i)); + + uint32_t framebuffer_id = + fl_backing_store_provider_get_gl_framebuffer_id(texture); + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer_id); + GdkRectangle geometry = fl_backing_store_provider_get_geometry(texture); + glBlitFramebuffer(0, 0, geometry.width, geometry.height, geometry.x, + geometry.y, geometry.x + geometry.width, + geometry.x + geometry.height, GL_COLOR_BUFFER_BIT, + GL_NEAREST); + } + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +} + +static void render_with_textures(FlRenderer* self, int width, int height) { + FlRendererPrivate* priv = reinterpret_cast( + fl_renderer_get_instance_private(self)); + + // Save bindings that are set by this function. All bindings must be restored + // to their original values because Skia expects that its bindings have not + // been altered. + GLint saved_texture_binding; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &saved_texture_binding); + GLint saved_vao_binding; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao_binding); + GLint saved_array_buffer_binding; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &saved_array_buffer_binding); + + 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); + } + + glBindTexture(GL_TEXTURE_2D, saved_texture_binding); + glBindVertexArray(saved_vao_binding); + glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding); +} + static void fl_renderer_dispose(GObject* object) { FlRenderer* self = FL_RENDERER(object); FlRendererPrivate* priv = reinterpret_cast( @@ -217,6 +399,22 @@ gboolean fl_renderer_present_layers(FlRenderer* self, 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)); + + // FIXME + priv->has_gl_framebuffer_blit = + epoxy_gl_version() >= 30 || + epoxy_has_gl_extension("GL_EXT_framebuffer_blit"); + + if (!priv->has_gl_framebuffer_blit) { + setup_shader(self); + } +} + void fl_renderer_render(FlRenderer* self, int width, int height) { FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); @@ -226,20 +424,22 @@ void fl_renderer_render(FlRenderer* self, int width, int height) { glClearColor(0.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); - for (guint i = 0; i < priv->textures->len; i++) { - FlBackingStoreProvider* texture = - FL_BACKING_STORE_PROVIDER(g_ptr_array_index(priv->textures, i)); - - uint32_t framebuffer_id = - fl_backing_store_provider_get_gl_framebuffer_id(texture); - glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer_id); - GdkRectangle geometry = fl_backing_store_provider_get_geometry(texture); - glBlitFramebuffer(0, 0, geometry.width, geometry.height, geometry.x, - geometry.y, geometry.x + geometry.width, - geometry.x + geometry.height, GL_COLOR_BUFFER_BIT, - GL_NEAREST); + if (priv->has_gl_framebuffer_blit) { + render_with_blit(self); + } else { + render_with_textures(self, width, height); } - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); 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)); + + if (priv->program != 0) { + glDeleteProgram(priv->program); + } +} diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h index 836fbbbaf6705..39222ef1f2f96 100644 --- a/shell/platform/linux/fl_renderer.h +++ b/shell/platform/linux/fl_renderer.h @@ -206,6 +206,15 @@ 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. @@ -216,6 +225,14 @@ void fl_renderer_wait_for_frame(FlRenderer* renderer, */ 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); + /** * fl_renderer_get_refresh_rate: * @renderer: an #FlRenderer. diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 61448bfc12b7f..8dc057e6fa027 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -567,6 +567,8 @@ static void realize_cb(FlView* self) { 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)); @@ -611,6 +613,20 @@ static gboolean render_cb(FlView* self, GdkGLContext* context) { return TRUE; } +static void unrealize_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 uninitialize GLArea: %s", gl_error->message); + return; + } + + fl_renderer_cleanup(FL_RENDERER(self->renderer)); +} + static void size_allocate_cb(FlView* self) { handle_geometry_changed(self); } @@ -781,6 +797,8 @@ static void fl_view_init(FlView* self) { 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); From 3e3db78628a526965233476032c015b7a4e55440 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 4 Jul 2024 16:36:03 +1200 Subject: [PATCH 3/9] Remove FIXME comment --- shell/platform/linux/fl_renderer.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 79af26532a0ea..ae8e4a979adad 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -405,7 +405,6 @@ void fl_renderer_setup(FlRenderer* self) { g_return_if_fail(FL_IS_RENDERER(self)); - // FIXME priv->has_gl_framebuffer_blit = epoxy_gl_version() >= 30 || epoxy_has_gl_extension("GL_EXT_framebuffer_blit"); From a0b2b7f1dc0268c06bcb4cadac972bcfb487273b Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 4 Jul 2024 16:50:05 +1200 Subject: [PATCH 4/9] Disable scissor test when using blit --- shell/platform/linux/fl_renderer.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index ae8e4a979adad..a78bebf333b33 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -147,6 +147,11 @@ static void render_with_blit(FlRenderer* self) { FlRendererPrivate* priv = reinterpret_cast( fl_renderer_get_instance_private(self)); + // Disable the scissor test as it can affect blit operations. + // Prevents regressions like: https://github.com/flutter/flutter/issues/140828 + // See OpenGL specification version 4.6, section 18.3.1. + glDisable(GL_SCISSOR_TEST); + for (guint i = 0; i < priv->textures->len; i++) { FlBackingStoreProvider* texture = FL_BACKING_STORE_PROVIDER(g_ptr_array_index(priv->textures, i)); From 2ae678c732a0624a3001044607479394d373611f Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 11 Jul 2024 16:29:58 +1200 Subject: [PATCH 5/9] Update shell/platform/linux/fl_renderer.cc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com> --- shell/platform/linux/fl_renderer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index a78bebf333b33..cde7e00b9e5f6 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -162,7 +162,7 @@ static void render_with_blit(FlRenderer* self) { GdkRectangle geometry = fl_backing_store_provider_get_geometry(texture); glBlitFramebuffer(0, 0, geometry.width, geometry.height, geometry.x, geometry.y, geometry.x + geometry.width, - geometry.x + geometry.height, GL_COLOR_BUFFER_BIT, + geometry.y + geometry.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); } glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); From 89dd5c7a09185f7f9ad3db19edfa8b9950966084 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Fri, 12 Jul 2024 18:57:36 +1200 Subject: [PATCH 6/9] Use GL variable types when returning values from GL functions --- shell/platform/linux/fl_renderer.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index cde7e00b9e5f6..6f0b91d3410b4 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -62,7 +62,7 @@ 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; + GLint log_length; gchar* log; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); @@ -75,7 +75,7 @@ static gchar* get_shader_log(GLuint shader) { // Returns the log for the given OpenGL program. Must be freed by the caller. static gchar* get_program_log(GLuint program) { - int log_length; + GLint log_length; gchar* log; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); @@ -110,7 +110,7 @@ static void setup_shader(FlRenderer* self) { GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex_shader, 1, &vertex_shader_src, nullptr); glCompileShader(vertex_shader); - int vertex_compile_status; + GLint 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); @@ -120,7 +120,7 @@ static void setup_shader(FlRenderer* self) { GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &fragment_shader_src, nullptr); glCompileShader(fragment_shader); - int fragment_compile_status; + GLint 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); @@ -132,7 +132,7 @@ static void setup_shader(FlRenderer* self) { glAttachShader(priv->program, fragment_shader); glLinkProgram(priv->program); - int link_status; + GLint 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); From 3f7d67a7b26ec6424cc9e89f87219ee206b76052 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 16 Jul 2024 19:56:36 +1200 Subject: [PATCH 7/9] Mock OpenGL calls --- shell/platform/linux/fl_renderer.cc | 8 +- shell/platform/linux/fl_renderer_test.cc | 96 ++++++++++++++++++- shell/platform/linux/testing/mock_epoxy.cc | 105 ++++++++++++++++++++- shell/platform/linux/testing/mock_epoxy.h | 40 ++++++++ 4 files changed, 237 insertions(+), 12 deletions(-) create mode 100644 shell/platform/linux/testing/mock_epoxy.h diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc index 6f0b91d3410b4..9db2bee5bdf7e 100644 --- a/shell/platform/linux/fl_renderer.cc +++ b/shell/platform/linux/fl_renderer.cc @@ -377,10 +377,6 @@ gboolean fl_renderer_present_layers(FlRenderer* self, fl_renderer_unblock_main_thread(self); - 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]; @@ -399,7 +395,9 @@ gboolean fl_renderer_present_layers(FlRenderer* self, } } - fl_view_redraw(priv->view); + if (priv->view != nullptr) { + fl_view_redraw(priv->view); + } return TRUE; } diff --git a/shell/platform/linux/fl_renderer_test.cc b/shell/platform/linux/fl_renderer_test.cc index a44e546560a39..ea1342e177d52 100644 --- a/shell/platform/linux/fl_renderer_test.cc +++ b/shell/platform/linux/fl_renderer_test.cc @@ -4,14 +4,17 @@ #include "gtest/gtest.h" -#include - #include "flutter/fml/logging.h" #include "flutter/shell/platform/linux/fl_backing_store_provider.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" +#include + TEST(FlRendererTest, RestoresGLState) { + ::testing::NiceMock epoxy; + constexpr int kWidth = 100; constexpr int kHeight = 100; @@ -67,3 +70,92 @@ TEST(FlRendererTest, RefreshRate) { fl_renderer_get_refresh_rate(FL_RENDERER(renderer)); EXPECT_DOUBLE_EQ(result_refresh_rate, kExpectedRefreshRate); } + +TEST(FlRendererTest, BlitFramebuffer) { + ::testing::NiceMock epoxy; + + g_autoptr(FlMockRenderer) renderer = + fl_mock_renderer_new(&renderer_get_refresh_rate); + + // OpenGL 3.0 + ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); + ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(30)); + + EXPECT_CALL(epoxy, glBlitFramebuffer); + + fl_renderer_setup(FL_RENDERER(renderer)); + fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); + FlutterBackingStoreConfig config = { + .struct_size = sizeof(FlutterBackingStoreConfig), + .size = {.width = 1024, .height = 1024}}; + FlutterBackingStore backing_store; + fl_renderer_create_backing_store(FL_RENDERER(renderer), &config, + &backing_store); + const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer), + .type = kFlutterLayerContentTypeBackingStore, + .backing_store = &backing_store, + .size = {.width = 1024, .height = 1024}}; + const FlutterLayer* layers[] = {&layer0}; + fl_renderer_present_layers(FL_RENDERER(renderer), layers, 1); + fl_renderer_render(FL_RENDERER(renderer), 1024, 1024); +} + +TEST(FlRendererTest, BlitFramebufferExtension) { + ::testing::NiceMock epoxy; + + g_autoptr(FlMockRenderer) renderer = + fl_mock_renderer_new(&renderer_get_refresh_rate); + + // OpenGL 2.0 with GL_EXT_framebuffer_blit extension + ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); + ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(20)); + ON_CALL(epoxy, epoxy_has_gl_extension("GL_EXT_framebuffer_blit")) + .WillByDefault(::testing::Return(true)); + + EXPECT_CALL(epoxy, glBlitFramebuffer); + + fl_renderer_setup(FL_RENDERER(renderer)); + fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); + FlutterBackingStoreConfig config = { + .struct_size = sizeof(FlutterBackingStoreConfig), + .size = {.width = 1024, .height = 1024}}; + FlutterBackingStore backing_store; + fl_renderer_create_backing_store(FL_RENDERER(renderer), &config, + &backing_store); + const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer), + .type = kFlutterLayerContentTypeBackingStore, + .backing_store = &backing_store, + .size = {.width = 1024, .height = 1024}}; + const FlutterLayer* layers[] = {&layer0}; + fl_renderer_present_layers(FL_RENDERER(renderer), layers, 1); + fl_renderer_render(FL_RENDERER(renderer), 1024, 1024); +} + +TEST(FlRendererTest, NoBlitFramebuffer) { + ::testing::NiceMock epoxy; + + g_autoptr(FlMockRenderer) renderer = + fl_mock_renderer_new(&renderer_get_refresh_rate); + + // OpenGL 2.0 + ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); + ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(20)); + + // EXPECT_CALL(epoxy, glBlitFramebuffer); + + fl_renderer_setup(FL_RENDERER(renderer)); + fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); + FlutterBackingStoreConfig config = { + .struct_size = sizeof(FlutterBackingStoreConfig), + .size = {.width = 1024, .height = 1024}}; + FlutterBackingStore backing_store; + fl_renderer_create_backing_store(FL_RENDERER(renderer), &config, + &backing_store); + const FlutterLayer layer0 = {.struct_size = sizeof(FlutterLayer), + .type = kFlutterLayerContentTypeBackingStore, + .backing_store = &backing_store, + .size = {.width = 1024, .height = 1024}}; + const FlutterLayer* layers[] = {&layer0}; + fl_renderer_present_layers(FL_RENDERER(renderer), layers, 1); + fl_renderer_render(FL_RENDERER(renderer), 1024, 1024); +} diff --git a/shell/platform/linux/testing/mock_epoxy.cc b/shell/platform/linux/testing/mock_epoxy.cc index e82e626caae57..0b60e9d9193c1 100644 --- a/shell/platform/linux/testing/mock_epoxy.cc +++ b/shell/platform/linux/testing/mock_epoxy.cc @@ -3,8 +3,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include +#include "flutter/shell/platform/linux/testing/mock_epoxy.h" + +using namespace flutter::testing; typedef struct { EGLint config_id; @@ -45,6 +46,7 @@ typedef struct { typedef struct { } MockSurface; +static MockEpoxy* mock = nullptr; static bool display_initialized = false; static MockDisplay mock_display; static MockConfig mock_config; @@ -53,6 +55,10 @@ static MockSurface mock_surface; static EGLint mock_error = EGL_SUCCESS; +MockEpoxy::MockEpoxy() { + mock = this; +} + static bool check_display(EGLDisplay dpy) { if (dpy == nullptr) { mock_error = EGL_BAD_DISPLAY; @@ -346,6 +352,8 @@ EGLBoolean _eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { static GLuint bound_texture_2d; +void _glAttachShader(GLuint program, GLuint shader) {} + static void _glBindFramebuffer(GLenum target, GLuint framebuffer) {} static void _glBindTexture(GLenum target, GLuint texture) { @@ -354,8 +362,34 @@ static void _glBindTexture(GLenum target, GLuint texture) { } } +static void _glBlitFramebuffer(GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter) { + mock->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, + dstY1, mask, filter); +} + +GLuint _glCreateProgram() { + return 0; +} + +void _glCompileShader(GLuint shader) {} + +GLuint _glCreateShader(GLenum shaderType) { + return 0; +} + void _glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) {} +void _glDeleteShader(GLuint shader) {} + void _glDeleteTextures(GLsizei n, const GLuint* textures) {} static void _glFramebufferTexture2D(GLenum target, @@ -382,6 +416,28 @@ static void _glGetIntegerv(GLenum pname, GLint* data) { } } +static void _glGetProgramiv(GLuint program, GLenum pname, GLint* params) { + if (pname == GL_LINK_STATUS) { + *params = GL_TRUE; + } +} + +static void _glGetProgramInfoLog(GLuint program, + GLsizei maxLength, + GLsizei* length, + GLchar* infoLog) {} + +static void _glGetShaderiv(GLuint shader, GLenum pname, GLint* params) { + if (pname == GL_COMPILE_STATUS) { + *params = GL_TRUE; + } +} + +static void _glGetShaderInfoLog(GLuint shader, + GLsizei maxLength, + GLsizei* length, + GLchar* infoLog) {} + static void _glTexParameterf(GLenum target, GLenum pname, GLfloat param) {} static void _glTexParameteri(GLenum target, GLenum pname, GLint param) {} @@ -400,16 +456,23 @@ static GLenum _glGetError() { return GL_NO_ERROR; } +void _glLinkProgram(GLuint program) {} + +void _glShaderSource(GLuint shader, + GLsizei count, + const GLchar* const* string, + const GLint* length) {} + bool epoxy_has_gl_extension(const char* extension) { - return false; + return mock->epoxy_has_gl_extension(extension); } bool epoxy_is_desktop_gl(void) { - return false; + return mock->epoxy_is_desktop_gl(); } int epoxy_gl_version(void) { - return 0; + return mock->epoxy_gl_version(); } #ifdef __GNUC__ @@ -463,9 +526,24 @@ EGLBoolean (*epoxy_eglMakeCurrent)(EGLDisplay dpy, EGLContext ctx); EGLBoolean (*epoxy_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface); +void (*epoxy_glAttachShader)(GLuint program, GLuint shader); void (*epoxy_glBindFramebuffer)(GLenum target, GLuint framebuffer); void (*epoxy_glBindTexture)(GLenum target, GLuint texture); +void (*epoxy_glBlitFramebuffer)(GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter); +void (*epoxy_glCompileShader)(GLuint shader); +GLuint (*epoxy_glCreateProgram)(); +GLuint (*epoxy_glCreateShader)(GLenum shaderType); void (*epoxy_glDeleteFramebuffers)(GLsizei n, const GLuint* framebuffers); +void (*expoxy_glDeleteShader)(GLuint shader); void (*epoxy_glDeleteTextures)(GLsizei n, const GLuint* textures); void (*epoxy_glFramebufferTexture2D)(GLenum target, GLenum attachment, @@ -474,6 +552,11 @@ void (*epoxy_glFramebufferTexture2D)(GLenum target, GLint level); void (*epoxy_glGenFramebuffers)(GLsizei n, GLuint* framebuffers); void (*epoxy_glGenTextures)(GLsizei n, GLuint* textures); +void (*epoxy_glLinkProgram)(GLuint program); +void (*epoxy_glShaderSource)(GLuint shader, + GLsizei count, + const GLchar* const* string, + const GLint* length); void (*epoxy_glTexParameterf)(GLenum target, GLenum pname, GLfloat param); void (*epoxy_glTexParameteri)(GLenum target, GLenum pname, GLint param); void (*epoxy_glTexImage2D)(GLenum target, @@ -502,14 +585,26 @@ static void library_init() { epoxy_eglQueryContext = _eglQueryContext; epoxy_eglSwapBuffers = _eglSwapBuffers; + epoxy_glAttachShader = _glAttachShader; epoxy_glBindFramebuffer = _glBindFramebuffer; epoxy_glBindTexture = _glBindTexture; + epoxy_glBlitFramebuffer = _glBlitFramebuffer; + epoxy_glCompileShader = _glCompileShader; + epoxy_glCreateProgram = _glCreateProgram; + epoxy_glCreateShader = _glCreateShader; epoxy_glDeleteFramebuffers = _glDeleteFramebuffers; + epoxy_glDeleteShader = _glDeleteShader; epoxy_glDeleteTextures = _glDeleteTextures; epoxy_glFramebufferTexture2D = _glFramebufferTexture2D; epoxy_glGenFramebuffers = _glGenFramebuffers; epoxy_glGenTextures = _glGenTextures; epoxy_glGetIntegerv = _glGetIntegerv; + epoxy_glGetProgramiv = _glGetProgramiv; + epoxy_glGetProgramInfoLog = _glGetProgramInfoLog; + epoxy_glGetShaderiv = _glGetShaderiv; + epoxy_glGetShaderInfoLog = _glGetShaderInfoLog; + epoxy_glLinkProgram = _glLinkProgram; + epoxy_glShaderSource = _glShaderSource; epoxy_glTexParameterf = _glTexParameterf; epoxy_glTexParameteri = _glTexParameteri; epoxy_glTexImage2D = _glTexImage2D; diff --git a/shell/platform/linux/testing/mock_epoxy.h b/shell/platform/linux/testing/mock_epoxy.h new file mode 100644 index 0000000000000..c21f9ee00bbaf --- /dev/null +++ b/shell/platform/linux/testing/mock_epoxy.h @@ -0,0 +1,40 @@ +// 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_TESTING_MOCK_EPOXY_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_EPOXY_H_ + +#include "gmock/gmock.h" + +#include +#include + +namespace flutter { +namespace testing { + +class MockEpoxy { + public: + MockEpoxy(); + + MOCK_METHOD(bool, epoxy_has_gl_extension, (const char* extension)); + MOCK_METHOD(bool, epoxy_is_desktop_gl, ()); + MOCK_METHOD(int, epoxy_gl_version, ()); + MOCK_METHOD(void, + glBlitFramebuffer, + (GLint srcX0, + GLint srcY0, + GLint srcX1, + GLint srcY1, + GLint dstX0, + GLint dstY0, + GLint dstX1, + GLint dstY1, + GLbitfield mask, + GLenum filter)); +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_EPOXY_H_ From 73979b5fa0a36aa6b08e4ac1cb582e914f17404d Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 18 Jul 2024 14:36:02 +1200 Subject: [PATCH 8/9] Adjust tests that are failing on the CI --- shell/platform/linux/fl_renderer_test.cc | 27 ++++++++++-------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/shell/platform/linux/fl_renderer_test.cc b/shell/platform/linux/fl_renderer_test.cc index ea1342e177d52..894b84b188d7f 100644 --- a/shell/platform/linux/fl_renderer_test.cc +++ b/shell/platform/linux/fl_renderer_test.cc @@ -74,15 +74,14 @@ TEST(FlRendererTest, RefreshRate) { TEST(FlRendererTest, BlitFramebuffer) { ::testing::NiceMock epoxy; - g_autoptr(FlMockRenderer) renderer = - fl_mock_renderer_new(&renderer_get_refresh_rate); - // OpenGL 3.0 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); - ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(30)); + EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(30)); EXPECT_CALL(epoxy, glBlitFramebuffer); + g_autoptr(FlMockRenderer) renderer = + fl_mock_renderer_new(&renderer_get_refresh_rate); fl_renderer_setup(FL_RENDERER(renderer)); fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); FlutterBackingStoreConfig config = { @@ -103,17 +102,16 @@ TEST(FlRendererTest, BlitFramebuffer) { TEST(FlRendererTest, BlitFramebufferExtension) { ::testing::NiceMock epoxy; - g_autoptr(FlMockRenderer) renderer = - fl_mock_renderer_new(&renderer_get_refresh_rate); - // OpenGL 2.0 with GL_EXT_framebuffer_blit extension ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); - ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(20)); - ON_CALL(epoxy, epoxy_has_gl_extension("GL_EXT_framebuffer_blit")) - .WillByDefault(::testing::Return(true)); + EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20)); + EXPECT_CALL(epoxy, epoxy_has_gl_extension("GL_EXT_framebuffer_blit")) + .WillRepeatedly(::testing::Return(true)); EXPECT_CALL(epoxy, glBlitFramebuffer); + g_autoptr(FlMockRenderer) renderer = + fl_mock_renderer_new(&renderer_get_refresh_rate); fl_renderer_setup(FL_RENDERER(renderer)); fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); FlutterBackingStoreConfig config = { @@ -134,15 +132,12 @@ TEST(FlRendererTest, BlitFramebufferExtension) { TEST(FlRendererTest, NoBlitFramebuffer) { ::testing::NiceMock epoxy; - g_autoptr(FlMockRenderer) renderer = - fl_mock_renderer_new(&renderer_get_refresh_rate); - // OpenGL 2.0 ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); - ON_CALL(epoxy, epoxy_gl_version).WillByDefault(::testing::Return(20)); - - // EXPECT_CALL(epoxy, glBlitFramebuffer); + EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20)); + g_autoptr(FlMockRenderer) renderer = + fl_mock_renderer_new(&renderer_get_refresh_rate); fl_renderer_setup(FL_RENDERER(renderer)); fl_renderer_wait_for_frame(FL_RENDERER(renderer), 1024, 1024); FlutterBackingStoreConfig config = { From 415ecccfa3f6f71b2e81b02c74a9dbb17ec3fa87 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 18 Jul 2024 15:21:25 +1200 Subject: [PATCH 9/9] Fix matching gmock string arg --- shell/platform/linux/fl_renderer_test.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/linux/fl_renderer_test.cc b/shell/platform/linux/fl_renderer_test.cc index 894b84b188d7f..7c5144df9f14f 100644 --- a/shell/platform/linux/fl_renderer_test.cc +++ b/shell/platform/linux/fl_renderer_test.cc @@ -105,7 +105,8 @@ TEST(FlRendererTest, BlitFramebufferExtension) { // OpenGL 2.0 with GL_EXT_framebuffer_blit extension ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); EXPECT_CALL(epoxy, epoxy_gl_version).WillRepeatedly(::testing::Return(20)); - EXPECT_CALL(epoxy, epoxy_has_gl_extension("GL_EXT_framebuffer_blit")) + EXPECT_CALL(epoxy, epoxy_has_gl_extension( + ::testing::StrEq("GL_EXT_framebuffer_blit"))) .WillRepeatedly(::testing::Return(true)); EXPECT_CALL(epoxy, glBlitFramebuffer);