mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Perform OpenGL compositing in the Flutter thread and write to a framebuffer. (#172090)
This framebuffer then gets copied into GTK during the draw calls. This disconnects the Flutter rendering from the GTK rendering which will allow us to render Flutter using EGL directly and support rendering without any views present (required for multi-window). Framebuffers are shared between the Flutter and GTK OpenGL contexts using EGLImage. When running under X11 GTK uses GLX and this is not possible - instead the buffers are copied via the CPU in this case.
This commit is contained in:
parent
c0f1279e77
commit
e983e59620
@ -12,12 +12,6 @@ static void fl_compositor_class_init(FlCompositorClass* klass) {}
|
||||
|
||||
static void fl_compositor_init(FlCompositor* self) {}
|
||||
|
||||
FlutterRendererType fl_compositor_get_renderer_type(FlCompositor* self) {
|
||||
g_return_val_if_fail(FL_IS_COMPOSITOR(self),
|
||||
static_cast<FlutterRendererType>(0));
|
||||
return FL_COMPOSITOR_GET_CLASS(self)->get_renderer_type(self);
|
||||
}
|
||||
|
||||
void fl_compositor_wait_for_frame(FlCompositor* self,
|
||||
int target_width,
|
||||
int target_height) {
|
||||
|
||||
@ -16,8 +16,6 @@ G_DECLARE_DERIVABLE_TYPE(FlCompositor, fl_compositor, FL, COMPOSITOR, GObject)
|
||||
struct _FlCompositorClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
FlutterRendererType (*get_renderer_type)(FlCompositor* compositor);
|
||||
|
||||
gboolean (*present_layers)(FlCompositor* compositor,
|
||||
const FlutterLayer** layers,
|
||||
size_t layers_count);
|
||||
@ -33,16 +31,6 @@ struct _FlCompositorClass {
|
||||
* #FlCompositor is an abstract class that implements Flutter compositing.
|
||||
*/
|
||||
|
||||
/**
|
||||
* fl_compositor_get_renderer_type:
|
||||
* @compositor: an #FlCompositor.
|
||||
*
|
||||
* Gets the rendering method this compositor uses.
|
||||
*
|
||||
* Returns: a FlutterRendererType.
|
||||
*/
|
||||
FlutterRendererType fl_compositor_get_renderer_type(FlCompositor* compositor);
|
||||
|
||||
/**
|
||||
* fl_compositor_present_layers:
|
||||
* @compositor: an #FlCompositor.
|
||||
|
||||
@ -44,12 +44,18 @@ struct _FlCompositorOpenGL {
|
||||
// Engine we are rendering.
|
||||
GWeakRef engine;
|
||||
|
||||
// Target OpenGL context;
|
||||
GdkGLContext* context;
|
||||
// TRUE if can share framebuffers between contexts.
|
||||
gboolean shareable;
|
||||
|
||||
// Flutter OpenGL contexts.
|
||||
FlOpenGLManager* opengl_manager;
|
||||
|
||||
// Last rendered frame.
|
||||
FlFramebuffer* framebuffer;
|
||||
|
||||
// Last rendered frame pixels (only set if shareable is TRUE).
|
||||
uint8_t* pixels;
|
||||
|
||||
// target dimension for resizing
|
||||
int target_width;
|
||||
int target_height;
|
||||
@ -61,9 +67,6 @@ struct _FlCompositorOpenGL {
|
||||
// was rendered
|
||||
bool had_first_frame;
|
||||
|
||||
// True if we can use glBlitFramebuffer.
|
||||
bool has_gl_framebuffer_blit;
|
||||
|
||||
// Shader program.
|
||||
GLuint program;
|
||||
|
||||
@ -76,9 +79,6 @@ struct _FlCompositorOpenGL {
|
||||
// Verticies for the uniform square.
|
||||
GLuint vertex_buffer;
|
||||
|
||||
// Framebuffers to render.
|
||||
GPtrArray* framebuffers;
|
||||
|
||||
// Mutex used when blocking the raster thread until a task is completed on
|
||||
// platform thread.
|
||||
GMutex present_mutex;
|
||||
@ -86,34 +86,15 @@ struct _FlCompositorOpenGL {
|
||||
// Condition to unblock the raster thread after task is completed on platform
|
||||
// thread.
|
||||
GCond present_condition;
|
||||
|
||||
// Control thread access to the frame.
|
||||
GMutex frame_mutex;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FlCompositorOpenGL,
|
||||
fl_compositor_opengl,
|
||||
fl_compositor_get_type())
|
||||
|
||||
// Check if running on driver supporting blit.
|
||||
static gboolean driver_supports_blit() {
|
||||
const gchar* vendor = reinterpret_cast<const gchar*>(glGetString(GL_VENDOR));
|
||||
|
||||
// Note: List of unsupported vendors due to issue
|
||||
// https://github.com/flutter/flutter/issues/152099
|
||||
const char* unsupported_vendors_exact[] = {"Vivante Corporation", "ARM"};
|
||||
const char* unsupported_vendors_fuzzy[] = {"NVIDIA"};
|
||||
|
||||
for (const char* unsupported : unsupported_vendors_fuzzy) {
|
||||
if (strstr(vendor, unsupported) != nullptr) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
for (const char* unsupported : unsupported_vendors_exact) {
|
||||
if (strcmp(vendor, unsupported) == 0) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Returns the log for the given OpenGL shader. Must be freed by the caller.
|
||||
static gchar* get_shader_log(GLuint shader) {
|
||||
GLint log_length;
|
||||
@ -203,31 +184,50 @@ static void setup_shader(FlCompositorOpenGL* self) {
|
||||
GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
static void render_with_blit(FlCompositorOpenGL* self,
|
||||
GPtrArray* framebuffers) {
|
||||
// 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);
|
||||
static void composite_layer(FlCompositorOpenGL* self,
|
||||
FlFramebuffer* framebuffer,
|
||||
double x,
|
||||
double y,
|
||||
int width,
|
||||
int height) {
|
||||
size_t texture_width = fl_framebuffer_get_width(framebuffer);
|
||||
size_t texture_height = fl_framebuffer_get_height(framebuffer);
|
||||
glUniform2f(self->offset_location, (2 * x / width) - 1.0,
|
||||
(2 * y / width) - 1.0);
|
||||
glUniform2f(self->scale_location, texture_width / width,
|
||||
texture_height / height);
|
||||
|
||||
for (guint i = 0; i < framebuffers->len; i++) {
|
||||
FlFramebuffer* framebuffer =
|
||||
FL_FRAMEBUFFER(g_ptr_array_index(framebuffers, i));
|
||||
GLuint texture_id = fl_framebuffer_get_texture_id(framebuffer);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
|
||||
GLuint framebuffer_id = fl_framebuffer_get_id(framebuffer);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer_id);
|
||||
size_t width = fl_framebuffer_get_width(framebuffer);
|
||||
size_t height = fl_framebuffer_get_height(framebuffer);
|
||||
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
}
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
static void render_with_textures(FlCompositorOpenGL* self,
|
||||
GPtrArray* framebuffers,
|
||||
int width,
|
||||
int height) {
|
||||
static gboolean present_layers(FlCompositorOpenGL* self,
|
||||
const FlutterLayer** layers,
|
||||
size_t layers_count) {
|
||||
g_return_val_if_fail(FL_IS_COMPOSITOR_OPENGL(self), FALSE);
|
||||
|
||||
g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
|
||||
|
||||
// ignore incoming frame with wrong dimensions in trivial case with just one
|
||||
// layer
|
||||
if (self->blocking_main_thread && layers_count == 1 &&
|
||||
layers[0]->offset.x == 0 && layers[0]->offset.y == 0 &&
|
||||
(layers[0]->size.width != self->target_width ||
|
||||
layers[0]->size.height != self->target_height)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (layers_count == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GLint general_format = GL_RGBA;
|
||||
if (epoxy_has_gl_extension("GL_EXT_texture_format_BGRA8888")) {
|
||||
general_format = GL_BGRA_EXT;
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -237,6 +237,29 @@ static void render_with_textures(FlCompositorOpenGL* self,
|
||||
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &saved_vao_binding);
|
||||
GLint saved_array_buffer_binding;
|
||||
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &saved_array_buffer_binding);
|
||||
GLint saved_framebuffer_binding;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &saved_framebuffer_binding);
|
||||
|
||||
// Update framebuffer to write into.
|
||||
size_t width = layers[0]->size.width;
|
||||
size_t height = layers[0]->size.height;
|
||||
if (self->framebuffer == nullptr ||
|
||||
fl_framebuffer_get_width(self->framebuffer) != width ||
|
||||
fl_framebuffer_get_height(self->framebuffer) != height) {
|
||||
g_clear_object(&self->framebuffer);
|
||||
self->framebuffer =
|
||||
fl_framebuffer_new(general_format, width, height, self->shareable);
|
||||
|
||||
// If not shareable make buffer to copy frame pixels into.
|
||||
if (!self->shareable) {
|
||||
size_t data_length = width * height * 4;
|
||||
self->pixels = static_cast<uint8_t*>(realloc(self->pixels, data_length));
|
||||
}
|
||||
}
|
||||
|
||||
self->had_first_frame = true;
|
||||
|
||||
fl_compositor_opengl_unblock_main_thread(self);
|
||||
|
||||
// FIXME(robert-ancell): The vertex array is the same for all views, but
|
||||
// cannot be shared in OpenGL. Find a way to not generate this every time.
|
||||
@ -259,23 +282,45 @@ static void render_with_textures(FlCompositorOpenGL* self,
|
||||
|
||||
glUseProgram(self->program);
|
||||
|
||||
for (guint i = 0; i < framebuffers->len; i++) {
|
||||
FlFramebuffer* framebuffer =
|
||||
FL_FRAMEBUFFER(g_ptr_array_index(framebuffers, i));
|
||||
// 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);
|
||||
|
||||
// FIXME(robert-ancell): The offset from present_layers() is not here, needs
|
||||
// to be updated.
|
||||
size_t texture_width = fl_framebuffer_get_width(framebuffer);
|
||||
size_t texture_height = fl_framebuffer_get_height(framebuffer);
|
||||
glUniform2f(self->offset_location, 0, 0);
|
||||
glUniform2f(self->scale_location, texture_width / width,
|
||||
texture_height / height);
|
||||
|
||||
GLuint texture_id = fl_framebuffer_get_texture_id(framebuffer);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
|
||||
fl_framebuffer_get_id(self->framebuffer));
|
||||
gboolean first_layer = TRUE;
|
||||
for (size_t i = 0; i < layers_count; ++i) {
|
||||
const FlutterLayer* layer = layers[i];
|
||||
switch (layer->type) {
|
||||
case kFlutterLayerContentTypeBackingStore: {
|
||||
const FlutterBackingStore* backing_store = layer->backing_store;
|
||||
FlFramebuffer* framebuffer =
|
||||
FL_FRAMEBUFFER(backing_store->open_gl.framebuffer.user_data);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER,
|
||||
fl_framebuffer_get_id(framebuffer));
|
||||
// The first layer can be blitted, and following layers composited with
|
||||
// this.
|
||||
if (first_layer) {
|
||||
glBlitFramebuffer(layer->offset.x, layer->offset.y, layer->size.width,
|
||||
layer->size.height, layer->offset.x,
|
||||
layer->offset.y, layer->size.width,
|
||||
layer->size.height, GL_COLOR_BUFFER_BIT,
|
||||
GL_NEAREST);
|
||||
first_layer = FALSE;
|
||||
} else {
|
||||
composite_layer(self, framebuffer, layer->offset.x, layer->offset.y,
|
||||
width, height);
|
||||
}
|
||||
} break;
|
||||
case kFlutterLayerContentTypePlatformView: {
|
||||
// TODO(robert-ancell) Not implemented -
|
||||
// https://github.com/flutter/flutter/issues/41724
|
||||
} break;
|
||||
}
|
||||
}
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glFlush();
|
||||
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
|
||||
@ -284,118 +329,13 @@ static void render_with_textures(FlCompositorOpenGL* self,
|
||||
glBindTexture(GL_TEXTURE_2D, saved_texture_binding);
|
||||
glBindVertexArray(saved_vao_binding);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, saved_array_buffer_binding);
|
||||
}
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, saved_framebuffer_binding);
|
||||
|
||||
static void render(FlCompositorOpenGL* self,
|
||||
GPtrArray* framebuffers,
|
||||
int width,
|
||||
int height) {
|
||||
if (self->has_gl_framebuffer_blit) {
|
||||
render_with_blit(self, framebuffers);
|
||||
} else {
|
||||
render_with_textures(self, framebuffers, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean present_layers(FlCompositorOpenGL* self,
|
||||
const FlutterLayer** layers,
|
||||
size_t layers_count) {
|
||||
g_return_val_if_fail(FL_IS_COMPOSITOR_OPENGL(self), FALSE);
|
||||
|
||||
// ignore incoming frame with wrong dimensions in trivial case with just one
|
||||
// layer
|
||||
if (self->blocking_main_thread && layers_count == 1 &&
|
||||
layers[0]->offset.x == 0 && layers[0]->offset.y == 0 &&
|
||||
(layers[0]->size.width != self->target_width ||
|
||||
layers[0]->size.height != self->target_height)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
self->had_first_frame = true;
|
||||
|
||||
fl_compositor_opengl_unblock_main_thread(self);
|
||||
|
||||
GLint general_format = GL_RGBA;
|
||||
if (epoxy_has_gl_extension("GL_EXT_texture_format_BGRA8888")) {
|
||||
general_format = GL_BGRA_EXT;
|
||||
}
|
||||
|
||||
g_autoptr(GPtrArray) framebuffers =
|
||||
g_ptr_array_new_with_free_func(g_object_unref);
|
||||
for (size_t i = 0; i < layers_count; ++i) {
|
||||
const FlutterLayer* layer = layers[i];
|
||||
switch (layer->type) {
|
||||
case kFlutterLayerContentTypeBackingStore: {
|
||||
const FlutterBackingStore* backing_store = layer->backing_store;
|
||||
FlFramebuffer* framebuffer =
|
||||
FL_FRAMEBUFFER(backing_store->open_gl.framebuffer.user_data);
|
||||
g_ptr_array_add(framebuffers, g_object_ref(framebuffer));
|
||||
} break;
|
||||
case kFlutterLayerContentTypePlatformView: {
|
||||
// TODO(robert-ancell) Not implemented -
|
||||
// https://github.com/flutter/flutter/issues/41724
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->context == nullptr) {
|
||||
// Store for rendering later
|
||||
g_ptr_array_unref(self->framebuffers);
|
||||
self->framebuffers = g_ptr_array_ref(framebuffers);
|
||||
} else {
|
||||
// Composite into a single framebuffer.
|
||||
if (framebuffers->len > 1) {
|
||||
size_t width = 0, height = 0;
|
||||
|
||||
for (guint i = 0; i < framebuffers->len; i++) {
|
||||
FlFramebuffer* framebuffer =
|
||||
FL_FRAMEBUFFER(g_ptr_array_index(framebuffers, i));
|
||||
|
||||
size_t w = fl_framebuffer_get_width(framebuffer);
|
||||
size_t h = fl_framebuffer_get_height(framebuffer);
|
||||
if (w > width) {
|
||||
width = w;
|
||||
}
|
||||
if (h > height) {
|
||||
height = h;
|
||||
}
|
||||
}
|
||||
|
||||
FlFramebuffer* view_framebuffer =
|
||||
fl_framebuffer_new(general_format, width, height);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
|
||||
fl_framebuffer_get_id(view_framebuffer));
|
||||
render(self, framebuffers, width, height);
|
||||
g_ptr_array_set_size(framebuffers, 0);
|
||||
g_ptr_array_add(framebuffers, view_framebuffer);
|
||||
}
|
||||
|
||||
// Read back pixel values.
|
||||
FlFramebuffer* framebuffer =
|
||||
FL_FRAMEBUFFER(g_ptr_array_index(framebuffers, 0));
|
||||
size_t width = fl_framebuffer_get_width(framebuffer);
|
||||
size_t height = fl_framebuffer_get_height(framebuffer);
|
||||
size_t data_length = width * height * 4;
|
||||
g_autofree uint8_t* data = static_cast<uint8_t*>(malloc(data_length));
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fl_framebuffer_get_id(framebuffer));
|
||||
glReadPixels(0, 0, width, height, general_format, GL_UNSIGNED_BYTE, data);
|
||||
|
||||
// Write into a texture in the views context.
|
||||
gdk_gl_context_make_current(self->context);
|
||||
g_autoptr(FlFramebuffer) view_framebuffer =
|
||||
fl_framebuffer_new(general_format, width, height);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,
|
||||
fl_framebuffer_get_id(view_framebuffer));
|
||||
glBindTexture(GL_TEXTURE_2D,
|
||||
fl_framebuffer_get_texture_id(view_framebuffer));
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
|
||||
g_autoptr(GPtrArray) secondary_framebuffers =
|
||||
g_ptr_array_new_with_free_func(g_object_unref);
|
||||
g_ptr_array_add(secondary_framebuffers, g_object_ref(view_framebuffer));
|
||||
g_ptr_array_unref(self->framebuffers);
|
||||
self->framebuffers = g_ptr_array_ref(secondary_framebuffers);
|
||||
if (!self->shareable) {
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER,
|
||||
fl_framebuffer_get_id(self->framebuffer));
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, self->pixels);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@ -428,11 +368,6 @@ static void present_layers_task_cb(gpointer user_data) {
|
||||
g_cond_signal(&self->present_condition);
|
||||
}
|
||||
|
||||
static FlutterRendererType fl_compositor_opengl_get_renderer_type(
|
||||
FlCompositor* compositor) {
|
||||
return kOpenGL;
|
||||
}
|
||||
|
||||
static void fl_compositor_opengl_wait_for_frame(FlCompositor* compositor,
|
||||
int target_width,
|
||||
int target_height) {
|
||||
@ -491,9 +426,9 @@ static void fl_compositor_opengl_dispose(GObject* object) {
|
||||
fl_compositor_opengl_unblock_main_thread(self);
|
||||
|
||||
g_weak_ref_clear(&self->engine);
|
||||
g_clear_object(&self->context);
|
||||
g_clear_object(&self->opengl_manager);
|
||||
g_clear_pointer(&self->framebuffers, g_ptr_array_unref);
|
||||
g_clear_object(&self->framebuffer);
|
||||
g_clear_pointer(&self->pixels, g_free);
|
||||
g_mutex_clear(&self->present_mutex);
|
||||
g_cond_clear(&self->present_condition);
|
||||
|
||||
@ -501,8 +436,6 @@ static void fl_compositor_opengl_dispose(GObject* object) {
|
||||
}
|
||||
|
||||
static void fl_compositor_opengl_class_init(FlCompositorOpenGLClass* klass) {
|
||||
FL_COMPOSITOR_CLASS(klass)->get_renderer_type =
|
||||
fl_compositor_opengl_get_renderer_type;
|
||||
FL_COMPOSITOR_CLASS(klass)->wait_for_frame =
|
||||
fl_compositor_opengl_wait_for_frame;
|
||||
FL_COMPOSITOR_CLASS(klass)->present_layers =
|
||||
@ -512,45 +445,64 @@ static void fl_compositor_opengl_class_init(FlCompositorOpenGLClass* klass) {
|
||||
}
|
||||
|
||||
static void fl_compositor_opengl_init(FlCompositorOpenGL* self) {
|
||||
self->framebuffers = g_ptr_array_new();
|
||||
g_mutex_init(&self->present_mutex);
|
||||
g_cond_init(&self->present_condition);
|
||||
}
|
||||
|
||||
FlCompositorOpenGL* fl_compositor_opengl_new(FlEngine* engine,
|
||||
GdkGLContext* context) {
|
||||
gboolean shareable) {
|
||||
FlCompositorOpenGL* self = FL_COMPOSITOR_OPENGL(
|
||||
g_object_new(fl_compositor_opengl_get_type(), nullptr));
|
||||
|
||||
g_weak_ref_init(&self->engine, engine);
|
||||
self->context =
|
||||
context != nullptr ? GDK_GL_CONTEXT(g_object_ref(context)) : nullptr;
|
||||
self->shareable = shareable;
|
||||
self->opengl_manager =
|
||||
FL_OPENGL_MANAGER(g_object_ref(fl_engine_get_opengl_manager(engine)));
|
||||
|
||||
fl_opengl_manager_make_current(self->opengl_manager);
|
||||
|
||||
self->has_gl_framebuffer_blit =
|
||||
driver_supports_blit() &&
|
||||
(epoxy_gl_version() >= 30 ||
|
||||
epoxy_has_gl_extension("GL_EXT_framebuffer_blit"));
|
||||
|
||||
if (!self->has_gl_framebuffer_blit) {
|
||||
setup_shader(self);
|
||||
}
|
||||
setup_shader(self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void fl_compositor_opengl_render(FlCompositorOpenGL* self,
|
||||
int width,
|
||||
int height) {
|
||||
void fl_compositor_opengl_render(FlCompositorOpenGL* self) {
|
||||
g_return_if_fail(FL_IS_COMPOSITOR_OPENGL(self));
|
||||
|
||||
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
|
||||
|
||||
render(self, self->framebuffers, width, height);
|
||||
if (self->framebuffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t width = fl_framebuffer_get_width(self->framebuffer);
|
||||
size_t height = fl_framebuffer_get_height(self->framebuffer);
|
||||
if (fl_framebuffer_get_shareable(self->framebuffer)) {
|
||||
g_autoptr(FlFramebuffer) sibling =
|
||||
fl_framebuffer_create_sibling(self->framebuffer);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, fl_framebuffer_get_id(sibling));
|
||||
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
} else {
|
||||
GLuint texture_id;
|
||||
glGenTextures(1, &texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, self->pixels);
|
||||
|
||||
GLuint framebuffer_id;
|
||||
glGenFramebuffers(1, &framebuffer_id);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer_id);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, texture_id, 0);
|
||||
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
glDeleteFramebuffers(1, &framebuffer_id);
|
||||
glDeleteTextures(1, &texture_id);
|
||||
}
|
||||
|
||||
glFlush();
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
#include "flutter/shell/platform/embedder/embedder.h"
|
||||
#include "flutter/shell/platform/linux/fl_compositor.h"
|
||||
#include "flutter/shell/platform/linux/fl_framebuffer.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@ -28,26 +29,23 @@ G_DECLARE_FINAL_TYPE(FlCompositorOpenGL,
|
||||
/**
|
||||
* fl_compositor_opengl_new:
|
||||
* @engine: an #FlEngine.
|
||||
* @context: the OpenGL context that is being rendered into.
|
||||
* @shareable: %TRUE if the can use a framebuffer that is shared between
|
||||
* contexts.
|
||||
*
|
||||
* Creates a new OpenGL compositor.
|
||||
*
|
||||
* Returns: a new #FlCompositorOpenGL.
|
||||
*/
|
||||
FlCompositorOpenGL* fl_compositor_opengl_new(FlEngine* engine,
|
||||
GdkGLContext* context);
|
||||
gboolean shareable);
|
||||
|
||||
/**
|
||||
* fl_compositor_opengl_render:
|
||||
* @compositor: an #FlCompositorOpenGL.
|
||||
* @width: width of the window in pixels.
|
||||
* @height: height of the window in pixels.
|
||||
*
|
||||
* Performs OpenGL commands to render current Flutter view.
|
||||
* Renders the current frame.
|
||||
*/
|
||||
void fl_compositor_opengl_render(FlCompositorOpenGL* compositor,
|
||||
int width,
|
||||
int height);
|
||||
void fl_compositor_opengl_render(FlCompositorOpenGL* compositor);
|
||||
|
||||
/**
|
||||
* fl_compositor_opengl_cleanup:
|
||||
|
||||
@ -34,14 +34,14 @@ TEST(FlCompositorOpenGLTest, RestoresGLState) {
|
||||
|
||||
g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
|
||||
g_autoptr(FlCompositorOpenGL) compositor =
|
||||
fl_compositor_opengl_new(engine, nullptr);
|
||||
fl_compositor_opengl_new(engine, FALSE);
|
||||
fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
|
||||
fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), kWidth, kHeight);
|
||||
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
|
||||
g_autoptr(FlFramebuffer) framebuffer =
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight);
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight, FALSE);
|
||||
FlutterBackingStore backing_store = {
|
||||
.type = kFlutterBackingStoreTypeOpenGL,
|
||||
.open_gl = {.framebuffer = {.user_data = framebuffer}}};
|
||||
@ -64,8 +64,6 @@ TEST(FlCompositorOpenGLTest, RestoresGLState) {
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
fl_compositor_opengl_render(compositor, kWidth, kHeight);
|
||||
|
||||
GLuint texture_2d_binding;
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D,
|
||||
reinterpret_cast<GLint*>(&texture_2d_binding));
|
||||
@ -95,14 +93,14 @@ TEST(FlCompositorOpenGLTest, BlitFramebuffer) {
|
||||
|
||||
g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
|
||||
g_autoptr(FlCompositorOpenGL) compositor =
|
||||
fl_compositor_opengl_new(engine, nullptr);
|
||||
fl_compositor_opengl_new(engine, FALSE);
|
||||
fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
|
||||
fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), kWidth, kHeight);
|
||||
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
|
||||
g_autoptr(FlFramebuffer) framebuffer =
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight);
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight, FALSE);
|
||||
FlutterBackingStore backing_store = {
|
||||
.type = kFlutterBackingStoreTypeOpenGL,
|
||||
.open_gl = {.framebuffer = {.user_data = framebuffer}}};
|
||||
@ -122,8 +120,6 @@ TEST(FlCompositorOpenGLTest, BlitFramebuffer) {
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
fl_compositor_opengl_render(compositor, kWidth, kHeight);
|
||||
|
||||
latch.Wait();
|
||||
}
|
||||
|
||||
@ -151,14 +147,14 @@ TEST(FlCompositorOpenGLTest, BlitFramebufferExtension) {
|
||||
|
||||
g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
|
||||
g_autoptr(FlCompositorOpenGL) compositor =
|
||||
fl_compositor_opengl_new(engine, nullptr);
|
||||
fl_compositor_opengl_new(engine, FALSE);
|
||||
fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
|
||||
fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), kWidth, kHeight);
|
||||
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
|
||||
g_autoptr(FlFramebuffer) framebuffer =
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight);
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight, FALSE);
|
||||
FlutterBackingStore backing_store = {
|
||||
.type = kFlutterBackingStoreTypeOpenGL,
|
||||
.open_gl = {.framebuffer = {.user_data = framebuffer}}};
|
||||
@ -178,7 +174,6 @@ TEST(FlCompositorOpenGLTest, BlitFramebufferExtension) {
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
fl_compositor_opengl_render(compositor, kWidth, kHeight);
|
||||
// Wait until the raster thread has finished before letting
|
||||
// the engine go out of scope.
|
||||
latch.Wait();
|
||||
@ -201,14 +196,14 @@ TEST(FlCompositorOpenGLTest, NoBlitFramebuffer) {
|
||||
|
||||
g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
|
||||
g_autoptr(FlCompositorOpenGL) compositor =
|
||||
fl_compositor_opengl_new(engine, nullptr);
|
||||
fl_compositor_opengl_new(engine, FALSE);
|
||||
fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
|
||||
fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), kWidth, kHeight);
|
||||
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
|
||||
g_autoptr(FlFramebuffer) framebuffer =
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight);
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight, FALSE);
|
||||
FlutterBackingStore backing_store = {
|
||||
.type = kFlutterBackingStoreTypeOpenGL,
|
||||
.open_gl = {.framebuffer = {.user_data = framebuffer}}};
|
||||
@ -228,8 +223,6 @@ TEST(FlCompositorOpenGLTest, NoBlitFramebuffer) {
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
fl_compositor_opengl_render(compositor, kWidth, kHeight);
|
||||
|
||||
// Wait until the raster thread has finished before letting
|
||||
// the engine go out of scope.
|
||||
latch.Wait();
|
||||
@ -253,14 +246,14 @@ TEST(FlCompositorOpenGLTest, BlitFramebufferNvidia) {
|
||||
|
||||
g_autoptr(FlMockRenderable) renderable = fl_mock_renderable_new();
|
||||
g_autoptr(FlCompositorOpenGL) compositor =
|
||||
fl_compositor_opengl_new(engine, nullptr);
|
||||
fl_compositor_opengl_new(engine, FALSE);
|
||||
fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
|
||||
fl_compositor_wait_for_frame(FL_COMPOSITOR(compositor), kWidth, kHeight);
|
||||
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
|
||||
g_autoptr(FlFramebuffer) framebuffer =
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight);
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight, FALSE);
|
||||
FlutterBackingStore backing_store = {
|
||||
.type = kFlutterBackingStoreTypeOpenGL,
|
||||
.open_gl = {.framebuffer = {.user_data = framebuffer}}};
|
||||
@ -280,8 +273,6 @@ TEST(FlCompositorOpenGLTest, BlitFramebufferNvidia) {
|
||||
|
||||
g_main_loop_run(loop);
|
||||
|
||||
fl_compositor_opengl_render(compositor, kWidth, kHeight);
|
||||
|
||||
// Wait until the raster thread has finished before letting
|
||||
// the engine go out of scope.
|
||||
latch.Wait();
|
||||
@ -306,7 +297,7 @@ TEST(FlCompositorOpenGLTest, MultiView) {
|
||||
g_autoptr(FlMockRenderable) secondary_renderable = fl_mock_renderable_new();
|
||||
|
||||
g_autoptr(FlCompositorOpenGL) compositor =
|
||||
fl_compositor_opengl_new(engine, nullptr);
|
||||
fl_compositor_opengl_new(engine, FALSE);
|
||||
fl_engine_set_implicit_view(engine, FL_RENDERABLE(renderable));
|
||||
fl_engine_add_view(engine, FL_RENDERABLE(secondary_renderable), 1024, 768,
|
||||
1.0, nullptr, nullptr, nullptr);
|
||||
@ -315,7 +306,7 @@ TEST(FlCompositorOpenGLTest, MultiView) {
|
||||
fml::AutoResetWaitableEvent latch;
|
||||
|
||||
g_autoptr(FlFramebuffer) framebuffer =
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight);
|
||||
fl_framebuffer_new(GL_RGB, kWidth, kHeight, FALSE);
|
||||
FlutterBackingStore backing_store = {
|
||||
.type = kFlutterBackingStoreTypeOpenGL,
|
||||
.open_gl = {.framebuffer = {.user_data = framebuffer}}};
|
||||
|
||||
@ -18,11 +18,6 @@ G_DEFINE_TYPE(FlCompositorSoftware,
|
||||
fl_compositor_software,
|
||||
fl_compositor_get_type())
|
||||
|
||||
static FlutterRendererType fl_compositor_software_get_renderer_type(
|
||||
FlCompositor* compositor) {
|
||||
return kSoftware;
|
||||
}
|
||||
|
||||
static void fl_compositor_software_wait_for_frame(FlCompositor* compositor,
|
||||
int target_width,
|
||||
int target_height) {}
|
||||
@ -72,8 +67,6 @@ static void fl_compositor_software_dispose(GObject* object) {
|
||||
|
||||
static void fl_compositor_software_class_init(
|
||||
FlCompositorSoftwareClass* klass) {
|
||||
FL_COMPOSITOR_CLASS(klass)->get_renderer_type =
|
||||
fl_compositor_software_get_renderer_type;
|
||||
FL_COMPOSITOR_CLASS(klass)->wait_for_frame =
|
||||
fl_compositor_software_wait_for_frame;
|
||||
FL_COMPOSITOR_CLASS(klass)->present_layers =
|
||||
@ -93,6 +86,8 @@ FlCompositorSoftware* fl_compositor_software_new() {
|
||||
gboolean fl_compositor_software_render(FlCompositorSoftware* self,
|
||||
cairo_t* cr,
|
||||
gint scale_factor) {
|
||||
g_return_val_if_fail(FL_IS_COMPOSITOR_SOFTWARE(self), FALSE);
|
||||
|
||||
g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->frame_mutex);
|
||||
|
||||
if (self->surface == nullptr) {
|
||||
|
||||
@ -261,7 +261,7 @@ static bool create_opengl_backing_store(
|
||||
}
|
||||
|
||||
FlFramebuffer* framebuffer = fl_framebuffer_new(
|
||||
general_format, config->size.width, config->size.height);
|
||||
general_format, config->size.width, config->size.height, FALSE);
|
||||
if (!framebuffer) {
|
||||
g_warning("Failed to create backing store");
|
||||
return false;
|
||||
@ -668,6 +668,7 @@ static FlEngine* fl_engine_new_full(FlDartProject* project,
|
||||
}
|
||||
self->renderer_type = kOpenGL;
|
||||
}
|
||||
|
||||
if (binary_messenger != nullptr) {
|
||||
self->binary_messenger =
|
||||
FL_BINARY_MESSENGER(g_object_ref(binary_messenger));
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "fl_framebuffer.h"
|
||||
|
||||
#include <epoxy/egl.h>
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
struct _FlFramebuffer {
|
||||
@ -23,10 +24,32 @@ struct _FlFramebuffer {
|
||||
|
||||
// Stencil buffer associated with this framebuffer.
|
||||
GLuint depth_stencil;
|
||||
|
||||
// EGL image for this texture.
|
||||
EGLImage image;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(FlFramebuffer, fl_framebuffer, G_TYPE_OBJECT)
|
||||
|
||||
static EGLImage create_egl_image(GLuint texture_id) {
|
||||
EGLDisplay egl_display = eglGetCurrentDisplay();
|
||||
if (egl_display == EGL_NO_DISPLAY) {
|
||||
g_warning("Failed to create EGL image: Failed to get current EGL display");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EGLContext egl_context = eglGetCurrentContext();
|
||||
if (egl_context == EGL_NO_CONTEXT) {
|
||||
g_warning("Failed to create EGL image: Failed to get current EGL context");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return eglCreateImage(
|
||||
egl_display, egl_context, EGL_GL_TEXTURE_2D,
|
||||
reinterpret_cast<EGLClientBuffer>(static_cast<intptr_t>(texture_id)),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
static void fl_framebuffer_dispose(GObject* object) {
|
||||
FlFramebuffer* self = FL_FRAMEBUFFER(object);
|
||||
|
||||
@ -43,19 +66,22 @@ static void fl_framebuffer_class_init(FlFramebufferClass* klass) {
|
||||
|
||||
static void fl_framebuffer_init(FlFramebuffer* self) {}
|
||||
|
||||
FlFramebuffer* fl_framebuffer_new(GLint format, size_t width, size_t height) {
|
||||
FlFramebuffer* provider =
|
||||
FlFramebuffer* fl_framebuffer_new(GLint format,
|
||||
size_t width,
|
||||
size_t height,
|
||||
gboolean shareable) {
|
||||
FlFramebuffer* self =
|
||||
FL_FRAMEBUFFER(g_object_new(fl_framebuffer_get_type(), nullptr));
|
||||
|
||||
provider->width = width;
|
||||
provider->height = height;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
|
||||
glGenTextures(1, &provider->texture_id);
|
||||
glGenFramebuffers(1, &provider->framebuffer_id);
|
||||
glGenTextures(1, &self->texture_id);
|
||||
glGenFramebuffers(1, &self->framebuffer_id);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, provider->framebuffer_id);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self->framebuffer_id);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, provider->texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, self->texture_id);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
@ -64,22 +90,59 @@ FlFramebuffer* fl_framebuffer_new(GLint format, size_t width, size_t height) {
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
provider->texture_id, 0);
|
||||
if (shareable) {
|
||||
self->image = create_egl_image(self->texture_id);
|
||||
}
|
||||
|
||||
glGenRenderbuffers(1, &provider->depth_stencil);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, provider->depth_stencil);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
self->texture_id, 0);
|
||||
|
||||
glGenRenderbuffers(1, &self->depth_stencil);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self->depth_stencil);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, // target
|
||||
GL_DEPTH24_STENCIL8, // internal format
|
||||
width, // width
|
||||
height // height
|
||||
);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, provider->depth_stencil);
|
||||
GL_RENDERBUFFER, self->depth_stencil);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, provider->depth_stencil);
|
||||
GL_RENDERBUFFER, self->depth_stencil);
|
||||
|
||||
return provider;
|
||||
return self;
|
||||
}
|
||||
|
||||
gboolean fl_framebuffer_get_shareable(FlFramebuffer* self) {
|
||||
g_return_val_if_fail(FL_IS_FRAMEBUFFER(self), FALSE);
|
||||
return self->image != nullptr;
|
||||
}
|
||||
|
||||
FlFramebuffer* fl_framebuffer_create_sibling(FlFramebuffer* self) {
|
||||
g_return_val_if_fail(FL_IS_FRAMEBUFFER(self), nullptr);
|
||||
g_return_val_if_fail(self->image != nullptr, nullptr);
|
||||
|
||||
FlFramebuffer* sibling =
|
||||
FL_FRAMEBUFFER(g_object_new(fl_framebuffer_get_type(), nullptr));
|
||||
|
||||
sibling->width = self->width;
|
||||
sibling->height = self->height;
|
||||
sibling->image = self->image;
|
||||
|
||||
// Make texture from existing image.
|
||||
glGenTextures(1, &sibling->texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, sibling->texture_id);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, self->image);
|
||||
|
||||
// Make framebuffer that uses this texture.
|
||||
glGenFramebuffers(1, &sibling->framebuffer_id);
|
||||
GLint saved_framebuffer_binding;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &saved_framebuffer_binding);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, sibling->framebuffer_id);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
sibling->texture_id, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, saved_framebuffer_binding);
|
||||
|
||||
return sibling;
|
||||
}
|
||||
|
||||
GLuint fl_framebuffer_get_id(FlFramebuffer* self) {
|
||||
@ -90,10 +153,6 @@ GLuint fl_framebuffer_get_texture_id(FlFramebuffer* self) {
|
||||
return self->texture_id;
|
||||
}
|
||||
|
||||
GLenum fl_framebuffer_get_target(FlFramebuffer* self) {
|
||||
return GL_TEXTURE_2D;
|
||||
}
|
||||
|
||||
size_t fl_framebuffer_get_width(FlFramebuffer* self) {
|
||||
return self->width;
|
||||
}
|
||||
|
||||
@ -24,12 +24,40 @@ G_DECLARE_FINAL_TYPE(FlFramebuffer, fl_framebuffer, FL, FRAMEBUFFER, GObject)
|
||||
* @format: format, e.g. GL_RGB, GL_BGR
|
||||
* @width: width of texture.
|
||||
* @height: height of texture.
|
||||
* @shareable: %TRUE if this framebuffer can be shared between contexts
|
||||
* (requires EGL).
|
||||
*
|
||||
* Creates a new frame buffer. Requires a valid OpenGL context to create.
|
||||
*
|
||||
* Returns: a new #FlFramebuffer.
|
||||
*/
|
||||
FlFramebuffer* fl_framebuffer_new(GLint format, size_t width, size_t height);
|
||||
FlFramebuffer* fl_framebuffer_new(GLint format,
|
||||
size_t width,
|
||||
size_t height,
|
||||
gboolean shareable);
|
||||
|
||||
/**
|
||||
* fl_framebuffer_get_shareable:
|
||||
* @framebuffer: an #FlFramebuffer.
|
||||
*
|
||||
* Checks if this framebuffer can be shared between contexts (using
|
||||
* fl_framebuffer_create_sibling).
|
||||
*
|
||||
* Returns: %TRUE if this framebuffer can be shared.
|
||||
*/
|
||||
gboolean fl_framebuffer_get_shareable(FlFramebuffer* framebuffer);
|
||||
|
||||
/**
|
||||
* fl_framebuffer_create_sibling:
|
||||
* @framebuffer: an #FlFramebuffer.
|
||||
*
|
||||
* Creates a new framebuffer with the same backing texture as the original. This
|
||||
* uses EGLImage to share the texture and allows a framebuffer created in one
|
||||
* OpenGL context to be used in another.
|
||||
*
|
||||
* Returns: a new #FlFramebuffer.
|
||||
*/
|
||||
FlFramebuffer* fl_framebuffer_create_sibling(FlFramebuffer* framebuffer);
|
||||
|
||||
/**
|
||||
* fl_framebuffer_get_id:
|
||||
@ -51,16 +79,6 @@ GLuint fl_framebuffer_get_id(FlFramebuffer* framebuffer);
|
||||
*/
|
||||
GLuint fl_framebuffer_get_texture_id(FlFramebuffer* framebuffer);
|
||||
|
||||
/**
|
||||
* fl_framebuffer_get_target:
|
||||
* @framebuffer: an #FlFramebuffer.
|
||||
*
|
||||
* Gets target texture (example GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE).
|
||||
*
|
||||
* Returns: target texture.
|
||||
*/
|
||||
GLenum fl_framebuffer_get_target(FlFramebuffer* framebuffer);
|
||||
|
||||
/**
|
||||
* fl_framebuffer_get_width:
|
||||
* @framebuffer: an #FlFramebuffer.
|
||||
|
||||
@ -10,7 +10,8 @@
|
||||
TEST(FlFramebufferTest, HasDepthStencil) {
|
||||
::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
|
||||
|
||||
g_autoptr(FlFramebuffer) framebuffer = fl_framebuffer_new(GL_RGB, 100, 100);
|
||||
g_autoptr(FlFramebuffer) framebuffer =
|
||||
fl_framebuffer_new(GL_RGB, 100, 100, FALSE);
|
||||
|
||||
GLint depth_type = GL_NONE;
|
||||
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
@ -31,10 +32,19 @@ TEST(FlFramebufferTest, ResourcesRemoved) {
|
||||
EXPECT_CALL(epoxy, glGenFramebuffers);
|
||||
EXPECT_CALL(epoxy, glGenTextures);
|
||||
EXPECT_CALL(epoxy, glGenRenderbuffers);
|
||||
FlFramebuffer* framebuffer = fl_framebuffer_new(GL_RGB, 100, 100);
|
||||
FlFramebuffer* framebuffer = fl_framebuffer_new(GL_RGB, 100, 100, FALSE);
|
||||
|
||||
EXPECT_CALL(epoxy, glDeleteFramebuffers);
|
||||
EXPECT_CALL(epoxy, glDeleteTextures);
|
||||
EXPECT_CALL(epoxy, glDeleteRenderbuffers);
|
||||
g_object_unref(framebuffer);
|
||||
}
|
||||
|
||||
TEST(FlFramebufferTest, Sibling) {
|
||||
::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;
|
||||
|
||||
EXPECT_CALL(epoxy, eglCreateImage);
|
||||
g_autoptr(FlFramebuffer) framebuffer =
|
||||
fl_framebuffer_new(GL_RGB, 100, 100, TRUE);
|
||||
g_autoptr(FlFramebuffer) sibling = fl_framebuffer_create_sibling(framebuffer);
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
|
||||
|
||||
#include <atk/atk.h>
|
||||
#include <gdk/gdkwayland.h>
|
||||
#include <gtk/gtk-a11y.h>
|
||||
|
||||
#include <cstring>
|
||||
@ -461,13 +462,16 @@ static GdkGLContext* create_context_cb(FlView* self) {
|
||||
|
||||
static void realize_cb(FlView* self) {
|
||||
FlutterRendererType renderer_type = fl_engine_get_renderer_type(self->engine);
|
||||
gboolean shareable;
|
||||
switch (renderer_type) {
|
||||
case kOpenGL:
|
||||
self->compositor = FL_COMPOSITOR(fl_compositor_opengl_new(
|
||||
self->engine,
|
||||
self->view_id == flutter::kFlutterImplicitViewId
|
||||
? nullptr
|
||||
: gtk_gl_area_get_context(GTK_GL_AREA(self->render_area))));
|
||||
// If using Wayland, then EGL is in use and we can access the frame
|
||||
// from the Flutter context using EGLImage. If not (i.e. X11 using GLX)
|
||||
// then we have to copy the texture via the CPU.
|
||||
shareable =
|
||||
GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(GTK_WIDGET(self)));
|
||||
self->compositor =
|
||||
FL_COMPOSITOR(fl_compositor_opengl_new(self->engine, shareable));
|
||||
break;
|
||||
case kSoftware:
|
||||
self->compositor = FL_COMPOSITOR(fl_compositor_software_new());
|
||||
@ -529,11 +533,7 @@ static gboolean render_cb(FlView* self, GdkGLContext* context) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int width = gtk_widget_get_allocated_width(self->render_area);
|
||||
int height = gtk_widget_get_allocated_height(self->render_area);
|
||||
gint scale_factor = gtk_widget_get_scale_factor(self->render_area);
|
||||
fl_compositor_opengl_render(FL_COMPOSITOR_OPENGL(self->compositor),
|
||||
width * scale_factor, height * scale_factor);
|
||||
fl_compositor_opengl_render(FL_COMPOSITOR_OPENGL(self->compositor));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -551,14 +551,13 @@ static void unrealize_cb(FlView* self) {
|
||||
return;
|
||||
}
|
||||
|
||||
fl_opengl_manager_make_current(fl_engine_get_opengl_manager(self->engine));
|
||||
|
||||
GError* gl_error = gtk_gl_area_get_error(GTK_GL_AREA(self->render_area));
|
||||
if (gl_error != NULL) {
|
||||
g_warning("Failed to uninitialize GLArea: %s", gl_error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
fl_opengl_manager_make_current(fl_engine_get_opengl_manager(self->engine));
|
||||
fl_compositor_opengl_cleanup(FL_COMPOSITOR_OPENGL(self->compositor));
|
||||
}
|
||||
|
||||
|
||||
@ -47,12 +47,16 @@ typedef struct {
|
||||
typedef struct {
|
||||
} MockSurface;
|
||||
|
||||
typedef struct {
|
||||
} MockImage;
|
||||
|
||||
static MockEpoxy* mock = nullptr;
|
||||
static bool display_initialized = false;
|
||||
static MockDisplay mock_display;
|
||||
static MockConfig mock_config;
|
||||
static MockContext mock_context;
|
||||
static MockSurface mock_surface;
|
||||
static MockImage mock_image;
|
||||
|
||||
static EGLint mock_error = EGL_SUCCESS;
|
||||
|
||||
@ -148,6 +152,10 @@ EGLContext _eglCreateContext(EGLDisplay dpy,
|
||||
return &mock_context;
|
||||
}
|
||||
|
||||
EGLContext _eglGetCurrentContext() {
|
||||
return &mock_context;
|
||||
}
|
||||
|
||||
EGLSurface _eglCreatePbufferSurface(EGLDisplay dpy,
|
||||
EGLConfig config,
|
||||
const EGLint* attrib_list) {
|
||||
@ -271,6 +279,10 @@ EGLDisplay _eglGetDisplay(EGLNativeDisplayType display_id) {
|
||||
return &mock_display;
|
||||
}
|
||||
|
||||
EGLDisplay _eglGetCurrentDisplay() {
|
||||
return &mock_display;
|
||||
}
|
||||
|
||||
EGLint _eglGetError() {
|
||||
EGLint error = mock_error;
|
||||
mock_error = EGL_SUCCESS;
|
||||
@ -357,6 +369,15 @@ EGLBoolean _eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) {
|
||||
return bool_success();
|
||||
}
|
||||
|
||||
EGLImage _eglCreateImage(EGLDisplay dpy,
|
||||
EGLContext ctx,
|
||||
EGLenum target,
|
||||
EGLClientBuffer buffer,
|
||||
const EGLAttrib* attrib_list) {
|
||||
mock->eglCreateImage(dpy, ctx, target, buffer, attrib_list);
|
||||
return &mock_image;
|
||||
}
|
||||
|
||||
static GLuint bound_texture_2d;
|
||||
|
||||
static std::map<GLenum, GLuint> framebuffer_renderbuffers;
|
||||
@ -599,6 +620,11 @@ EGLBoolean (*epoxy_eglMakeCurrent)(EGLDisplay dpy,
|
||||
EGLSurface read,
|
||||
EGLContext ctx);
|
||||
EGLBoolean (*epoxy_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
|
||||
EGLImage (*epoxy_eglCreateImage)(EGLDisplay dpy,
|
||||
EGLContext ctx,
|
||||
EGLenum target,
|
||||
EGLClientBuffer buffer,
|
||||
const EGLAttrib* attrib_list);
|
||||
|
||||
void (*epoxy_glAttachShader)(GLuint program, GLuint shader);
|
||||
void (*epoxy_glBindFramebuffer)(GLenum target, GLuint framebuffer);
|
||||
@ -661,16 +687,19 @@ static void library_init() {
|
||||
epoxy_eglBindAPI = _eglBindAPI;
|
||||
epoxy_eglChooseConfig = _eglChooseConfig;
|
||||
epoxy_eglCreateContext = _eglCreateContext;
|
||||
epoxy_eglGetCurrentContext = _eglGetCurrentContext;
|
||||
epoxy_eglCreatePbufferSurface = _eglCreatePbufferSurface;
|
||||
epoxy_eglCreateWindowSurface = _eglCreateWindowSurface;
|
||||
epoxy_eglGetConfigAttrib = _eglGetConfigAttrib;
|
||||
epoxy_eglGetDisplay = _eglGetDisplay;
|
||||
epoxy_eglGetCurrentDisplay = _eglGetCurrentDisplay;
|
||||
epoxy_eglGetError = _eglGetError;
|
||||
epoxy_eglGetProcAddress = _eglGetProcAddress;
|
||||
epoxy_eglInitialize = _eglInitialize;
|
||||
epoxy_eglMakeCurrent = _eglMakeCurrent;
|
||||
epoxy_eglQueryContext = _eglQueryContext;
|
||||
epoxy_eglSwapBuffers = _eglSwapBuffers;
|
||||
epoxy_eglCreateImage = _eglCreateImage;
|
||||
|
||||
epoxy_glAttachShader = _glAttachShader;
|
||||
epoxy_glBindFramebuffer = _glBindFramebuffer;
|
||||
|
||||
@ -21,6 +21,13 @@ class 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,
|
||||
eglCreateImage,
|
||||
(EGLDisplay dpy,
|
||||
EGLContext ctx,
|
||||
EGLenum target,
|
||||
EGLClientBuffer buffer,
|
||||
const EGLAttrib* attrib_list));
|
||||
MOCK_METHOD(void, glClearColor, (GLfloat r, GLfloat g, GLfloat b, GLfloat a));
|
||||
MOCK_METHOD(void,
|
||||
glBlitFramebuffer,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user