Use glBlitFramebuffer when rendering (#53080)
This is much faster than using a shader which is not required currently
(we are not doing any transformations).
diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc
index a2163aa..9db2bee 100644
--- a/shell/platform/linux/fl_renderer.cc
+++ b/shell/platform/linux/fl_renderer.cc
@@ -48,6 +48,9 @@
// 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 @@
// 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 @@
// 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 setup_shader(FlRenderer* self) {
+ FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
+ 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<FlRendererPrivate*>(
+ 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<FlRendererPrivate*>(
+ 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<FlRendererPrivate*>(
@@ -244,10 +377,6 @@
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 @@
}
}
- fl_view_redraw(priv->view);
+ if (priv->view != nullptr) {
+ fl_view_redraw(priv->view);
+ }
return TRUE;
}
@@ -277,40 +408,13 @@
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 @@
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 @@
g_return_if_fail(FL_IS_RENDERER(self));
- glDeleteProgram(priv->program);
+ if (priv->program != 0) {
+ glDeleteProgram(priv->program);
+ }
}
diff --git a/shell/platform/linux/fl_renderer_test.cc b/shell/platform/linux/fl_renderer_test.cc
index a44e546..7c5144d 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 <epoxy/egl.h>
-
#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 <epoxy/egl.h>
+
TEST(FlRendererTest, RestoresGLState) {
+ ::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
+
constexpr int kWidth = 100;
constexpr int kHeight = 100;
@@ -67,3 +70,88 @@
fl_renderer_get_refresh_rate(FL_RENDERER(renderer));
EXPECT_DOUBLE_EQ(result_refresh_rate, kExpectedRefreshRate);
}
+
+TEST(FlRendererTest, BlitFramebuffer) {
+ ::testing::NiceMock<flutter::testing::MockEpoxy> 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<flutter::testing::MockEpoxy> 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<flutter::testing::MockEpoxy> 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/shell/platform/linux/testing/mock_epoxy.cc b/shell/platform/linux/testing/mock_epoxy.cc
index e82e626..0b60e9d 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 <epoxy/egl.h>
-#include <epoxy/gl.h>
+#include "flutter/shell/platform/linux/testing/mock_epoxy.h"
+
+using namespace flutter::testing;
typedef struct {
EGLint config_id;
@@ -45,6 +46,7 @@
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 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 @@
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 _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 _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 @@
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 @@
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 @@
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 @@
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 0000000..c21f9ee
--- /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 <epoxy/egl.h>
+#include <epoxy/gl.h>
+
+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_