Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Use glBlitFramebuffer when rendering #53080

Merged
merged 11 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 150 additions & 98 deletions shell/platform/linux/fl_renderer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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*>(
Expand Down Expand Up @@ -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];
Expand All @@ -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;
}
Expand All @@ -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");

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);
if (!priv->has_gl_framebuffer_blit) {
setup_shader(self);
}

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) {
Expand All @@ -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) {
Expand All @@ -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);
}
}
Loading