diff --git a/engine/src/flutter/shell/platform/linux/fl_renderer.cc b/engine/src/flutter/shell/platform/linux/fl_renderer.cc index a2163aae6f..9db2bee5bd 100644 --- a/engine/src/flutter/shell/platform/linux/fl_renderer.cc +++ b/engine/src/flutter/shell/platform/linux/fl_renderer.cc @@ -48,6 +48,9 @@ typedef struct { // was rendered bool had_first_frame; + // True if we can use glBlitFramebuffer. + bool has_gl_framebuffer_blit; + // Shader program. GLuint program; @@ -59,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); @@ -72,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); @@ -100,6 +103,136 @@ 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); + 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); + 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); + 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); + 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); + + 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); + 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)); + + // 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)); + + 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.y + 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( @@ -244,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]; @@ -266,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; } @@ -277,40 +408,13 @@ void fl_renderer_setup(FlRenderer* 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); + 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); } - - 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) { @@ -319,70 +423,16 @@ void fl_renderer_render(FlRenderer* self, int width, int height) { 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); + if (priv->has_gl_framebuffer_blit) { + render_with_blit(self); + } else { + render_with_textures(self, width, height); } 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) { @@ -391,5 +441,7 @@ void fl_renderer_cleanup(FlRenderer* self) { g_return_if_fail(FL_IS_RENDERER(self)); - glDeleteProgram(priv->program); + if (priv->program != 0) { + glDeleteProgram(priv->program); + } } diff --git a/engine/src/flutter/shell/platform/linux/fl_renderer_test.cc b/engine/src/flutter/shell/platform/linux/fl_renderer_test.cc index a44e546560..7c5144df9f 100644 --- a/engine/src/flutter/shell/platform/linux/fl_renderer_test.cc +++ b/engine/src/flutter/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,88 @@ TEST(FlRendererTest, RefreshRate) { fl_renderer_get_refresh_rate(FL_RENDERER(renderer)); EXPECT_DOUBLE_EQ(result_refresh_rate, kExpectedRefreshRate); } + +TEST(FlRendererTest, BlitFramebuffer) { + ::testing::NiceMock epoxy; + + // OpenGL 3.0 + ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); + 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 = { + .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; + + // 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( + ::testing::StrEq("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 = { + .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; + + // OpenGL 2.0 + ON_CALL(epoxy, epoxy_is_desktop_gl).WillByDefault(::testing::Return(true)); + 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 = { + .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/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.cc b/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.cc index e82e626caa..0b60e9d919 100644 --- a/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.cc +++ b/engine/src/flutter/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/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.h b/engine/src/flutter/shell/platform/linux/testing/mock_epoxy.h new file mode 100644 index 0000000000..c21f9ee00b --- /dev/null +++ b/engine/src/flutter/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_