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:
parent
095683b513
commit
dde01765c9
@ -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<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 @@ 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,
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
if (priv->program != 0) {
|
||||
glDeleteProgram(priv->program);
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ TEST(FlRendererTest, RefreshRate) {
|
||||
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);
|
||||
}
|
||||
|
@ -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 {
|
||||
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;
|
||||
|
40
engine/src/flutter/shell/platform/linux/testing/mock_epoxy.h
Normal file
40
engine/src/flutter/shell/platform/linux/testing/mock_epoxy.h
Normal 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_
|
Loading…
x
Reference in New Issue
Block a user