Use glBlitFramebuffer when rendering (flutter/engine#53080)

This is much faster than using a shader which is not required currently
(we are not doing any transformations).
This commit is contained in:
Robert Ancell 2024-07-18 16:02:39 +12:00 committed by GitHub
parent 095683b513
commit dde01765c9
4 changed files with 381 additions and 106 deletions

View File

@ -48,6 +48,9 @@ typedef struct {
// was rendered // was rendered
bool had_first_frame; bool had_first_frame;
// True if we can use glBlitFramebuffer.
bool has_gl_framebuffer_blit;
// Shader program. // Shader program.
GLuint 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. // Returns the log for the given OpenGL shader. Must be freed by the caller.
static gchar* get_shader_log(GLuint shader) { static gchar* get_shader_log(GLuint shader) {
int log_length; GLint log_length;
gchar* log; gchar* log;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); 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. // Returns the log for the given OpenGL program. Must be freed by the caller.
static gchar* get_program_log(GLuint program) { static gchar* get_program_log(GLuint program) {
int log_length; GLint log_length;
gchar* log; gchar* log;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); 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<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) { static void fl_renderer_dispose(GObject* object) {
FlRenderer* self = FL_RENDERER(object); FlRenderer* self = FL_RENDERER(object);
FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>( FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
@ -244,10 +377,6 @@ gboolean fl_renderer_present_layers(FlRenderer* self,
fl_renderer_unblock_main_thread(self); fl_renderer_unblock_main_thread(self);
if (!priv->view) {
return FALSE;
}
g_ptr_array_set_size(priv->textures, 0); g_ptr_array_set_size(priv->textures, 0);
for (size_t i = 0; i < layers_count; ++i) { for (size_t i = 0; i < layers_count; ++i) {
const FlutterLayer* layer = layers[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; return TRUE;
} }
@ -277,40 +408,13 @@ void fl_renderer_setup(FlRenderer* self) {
g_return_if_fail(FL_IS_RENDERER(self)); g_return_if_fail(FL_IS_RENDERER(self));
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); priv->has_gl_framebuffer_blit =
glShaderSource(vertex_shader, 1, &vertex_shader_src, nullptr); epoxy_gl_version() >= 30 ||
glCompileShader(vertex_shader); epoxy_has_gl_extension("GL_EXT_framebuffer_blit");
int vertex_compile_status;
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &vertex_compile_status); if (!priv->has_gl_framebuffer_blit) {
if (vertex_compile_status == GL_FALSE) { setup_shader(self);
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) { 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)); 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); glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(priv->program); if (priv->has_gl_framebuffer_blit) {
render_with_blit(self);
for (guint i = 0; i < priv->textures->len; i++) { } else {
FlBackingStoreProvider* texture = render_with_textures(self, width, height);
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(); 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) { void fl_renderer_cleanup(FlRenderer* self) {
@ -391,5 +441,7 @@ void fl_renderer_cleanup(FlRenderer* self) {
g_return_if_fail(FL_IS_RENDERER(self)); g_return_if_fail(FL_IS_RENDERER(self));
glDeleteProgram(priv->program); if (priv->program != 0) {
glDeleteProgram(priv->program);
}
} }

View File

@ -4,14 +4,17 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include <epoxy/egl.h>
#include "flutter/fml/logging.h" #include "flutter/fml/logging.h"
#include "flutter/shell/platform/linux/fl_backing_store_provider.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/fl_test_gtk_logs.h"
#include "flutter/shell/platform/linux/testing/mock_epoxy.h"
#include "flutter/shell/platform/linux/testing/mock_renderer.h" #include "flutter/shell/platform/linux/testing/mock_renderer.h"
#include <epoxy/egl.h>
TEST(FlRendererTest, RestoresGLState) { TEST(FlRendererTest, RestoresGLState) {
::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
constexpr int kWidth = 100; constexpr int kWidth = 100;
constexpr int kHeight = 100; constexpr int kHeight = 100;
@ -67,3 +70,88 @@ TEST(FlRendererTest, RefreshRate) {
fl_renderer_get_refresh_rate(FL_RENDERER(renderer)); fl_renderer_get_refresh_rate(FL_RENDERER(renderer));
EXPECT_DOUBLE_EQ(result_refresh_rate, kExpectedRefreshRate); 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);
}

View File

@ -3,8 +3,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <epoxy/egl.h> #include "flutter/shell/platform/linux/testing/mock_epoxy.h"
#include <epoxy/gl.h>
using namespace flutter::testing;
typedef struct { typedef struct {
EGLint config_id; EGLint config_id;
@ -45,6 +46,7 @@ typedef struct {
typedef struct { typedef struct {
} MockSurface; } MockSurface;
static MockEpoxy* mock = nullptr;
static bool display_initialized = false; static bool display_initialized = false;
static MockDisplay mock_display; static MockDisplay mock_display;
static MockConfig mock_config; static MockConfig mock_config;
@ -53,6 +55,10 @@ static MockSurface mock_surface;
static EGLint mock_error = EGL_SUCCESS; static EGLint mock_error = EGL_SUCCESS;
MockEpoxy::MockEpoxy() {
mock = this;
}
static bool check_display(EGLDisplay dpy) { static bool check_display(EGLDisplay dpy) {
if (dpy == nullptr) { if (dpy == nullptr) {
mock_error = EGL_BAD_DISPLAY; mock_error = EGL_BAD_DISPLAY;
@ -346,6 +352,8 @@ EGLBoolean _eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
static GLuint bound_texture_2d; static GLuint bound_texture_2d;
void _glAttachShader(GLuint program, GLuint shader) {}
static void _glBindFramebuffer(GLenum target, GLuint framebuffer) {} static void _glBindFramebuffer(GLenum target, GLuint framebuffer) {}
static void _glBindTexture(GLenum target, GLuint texture) { 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 _glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) {}
void _glDeleteShader(GLuint shader) {}
void _glDeleteTextures(GLsizei n, const GLuint* textures) {} void _glDeleteTextures(GLsizei n, const GLuint* textures) {}
static void _glFramebufferTexture2D(GLenum target, 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 _glTexParameterf(GLenum target, GLenum pname, GLfloat param) {}
static void _glTexParameteri(GLenum target, GLenum pname, GLint param) {} static void _glTexParameteri(GLenum target, GLenum pname, GLint param) {}
@ -400,16 +456,23 @@ static GLenum _glGetError() {
return GL_NO_ERROR; 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) { bool epoxy_has_gl_extension(const char* extension) {
return false; return mock->epoxy_has_gl_extension(extension);
} }
bool epoxy_is_desktop_gl(void) { bool epoxy_is_desktop_gl(void) {
return false; return mock->epoxy_is_desktop_gl();
} }
int epoxy_gl_version(void) { int epoxy_gl_version(void) {
return 0; return mock->epoxy_gl_version();
} }
#ifdef __GNUC__ #ifdef __GNUC__
@ -463,9 +526,24 @@ EGLBoolean (*epoxy_eglMakeCurrent)(EGLDisplay dpy,
EGLContext ctx); EGLContext ctx);
EGLBoolean (*epoxy_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface); EGLBoolean (*epoxy_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
void (*epoxy_glAttachShader)(GLuint program, GLuint shader);
void (*epoxy_glBindFramebuffer)(GLenum target, GLuint framebuffer); void (*epoxy_glBindFramebuffer)(GLenum target, GLuint framebuffer);
void (*epoxy_glBindTexture)(GLenum target, GLuint texture); 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 (*epoxy_glDeleteFramebuffers)(GLsizei n, const GLuint* framebuffers);
void (*expoxy_glDeleteShader)(GLuint shader);
void (*epoxy_glDeleteTextures)(GLsizei n, const GLuint* textures); void (*epoxy_glDeleteTextures)(GLsizei n, const GLuint* textures);
void (*epoxy_glFramebufferTexture2D)(GLenum target, void (*epoxy_glFramebufferTexture2D)(GLenum target,
GLenum attachment, GLenum attachment,
@ -474,6 +552,11 @@ void (*epoxy_glFramebufferTexture2D)(GLenum target,
GLint level); GLint level);
void (*epoxy_glGenFramebuffers)(GLsizei n, GLuint* framebuffers); void (*epoxy_glGenFramebuffers)(GLsizei n, GLuint* framebuffers);
void (*epoxy_glGenTextures)(GLsizei n, GLuint* textures); 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_glTexParameterf)(GLenum target, GLenum pname, GLfloat param);
void (*epoxy_glTexParameteri)(GLenum target, GLenum pname, GLint param); void (*epoxy_glTexParameteri)(GLenum target, GLenum pname, GLint param);
void (*epoxy_glTexImage2D)(GLenum target, void (*epoxy_glTexImage2D)(GLenum target,
@ -502,14 +585,26 @@ static void library_init() {
epoxy_eglQueryContext = _eglQueryContext; epoxy_eglQueryContext = _eglQueryContext;
epoxy_eglSwapBuffers = _eglSwapBuffers; epoxy_eglSwapBuffers = _eglSwapBuffers;
epoxy_glAttachShader = _glAttachShader;
epoxy_glBindFramebuffer = _glBindFramebuffer; epoxy_glBindFramebuffer = _glBindFramebuffer;
epoxy_glBindTexture = _glBindTexture; epoxy_glBindTexture = _glBindTexture;
epoxy_glBlitFramebuffer = _glBlitFramebuffer;
epoxy_glCompileShader = _glCompileShader;
epoxy_glCreateProgram = _glCreateProgram;
epoxy_glCreateShader = _glCreateShader;
epoxy_glDeleteFramebuffers = _glDeleteFramebuffers; epoxy_glDeleteFramebuffers = _glDeleteFramebuffers;
epoxy_glDeleteShader = _glDeleteShader;
epoxy_glDeleteTextures = _glDeleteTextures; epoxy_glDeleteTextures = _glDeleteTextures;
epoxy_glFramebufferTexture2D = _glFramebufferTexture2D; epoxy_glFramebufferTexture2D = _glFramebufferTexture2D;
epoxy_glGenFramebuffers = _glGenFramebuffers; epoxy_glGenFramebuffers = _glGenFramebuffers;
epoxy_glGenTextures = _glGenTextures; epoxy_glGenTextures = _glGenTextures;
epoxy_glGetIntegerv = _glGetIntegerv; 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_glTexParameterf = _glTexParameterf;
epoxy_glTexParameteri = _glTexParameteri; epoxy_glTexParameteri = _glTexParameteri;
epoxy_glTexImage2D = _glTexImage2D; epoxy_glTexImage2D = _glTexImage2D;

View File

@ -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_