mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
294 lines
10 KiB
C++
294 lines
10 KiB
C++
// 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.
|
|
|
|
#include "fl_renderer.h"
|
|
|
|
#include "flutter/shell/platform/embedder/embedder.h"
|
|
#include "flutter/shell/platform/linux/egl_utils.h"
|
|
|
|
G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error)
|
|
|
|
typedef struct {
|
|
EGLDisplay egl_display;
|
|
EGLConfig egl_config;
|
|
EGLSurface egl_surface;
|
|
EGLContext egl_context;
|
|
|
|
EGLSurface resource_surface;
|
|
EGLContext resource_context;
|
|
} FlRendererPrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT)
|
|
|
|
static void fl_renderer_class_init(FlRendererClass* klass) {}
|
|
|
|
static void fl_renderer_init(FlRenderer* self) {}
|
|
|
|
// Initializes EGL and gets a valid EGL OpenGL ES configuration.
|
|
static gboolean setup_egl_display(FlRenderer* self, GError** error) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
if (priv->egl_display != EGL_NO_DISPLAY) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"EGL display already set up");
|
|
return FALSE;
|
|
}
|
|
|
|
priv->egl_display = FL_RENDERER_GET_CLASS(self)->create_display(self);
|
|
|
|
if (priv->egl_display == EGL_NO_DISPLAY) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to create EGL display");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!eglInitialize(priv->egl_display, nullptr, nullptr)) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to initialze EGL");
|
|
return FALSE;
|
|
}
|
|
|
|
EGLint attributes[] = {EGL_RENDERABLE_TYPE,
|
|
EGL_OPENGL_ES2_BIT,
|
|
EGL_RED_SIZE,
|
|
8,
|
|
EGL_GREEN_SIZE,
|
|
8,
|
|
EGL_BLUE_SIZE,
|
|
8,
|
|
EGL_ALPHA_SIZE,
|
|
8,
|
|
EGL_NONE};
|
|
EGLint n_config;
|
|
if (!eglChooseConfig(priv->egl_display, attributes, &priv->egl_config, 1,
|
|
&n_config)) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to choose EGL config: %s",
|
|
egl_error_to_string(eglGetError()));
|
|
return FALSE;
|
|
}
|
|
|
|
if (n_config == 0) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to find appropriate EGL config: %s",
|
|
egl_error_to_string(eglGetError()));
|
|
return FALSE;
|
|
}
|
|
|
|
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
|
|
EGLint egl_error = eglGetError(); // Must be before egl_config_to_string().
|
|
g_autofree gchar* config_string =
|
|
egl_config_to_string(priv->egl_display, priv->egl_config);
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to bind EGL OpenGL ES API using configuration (%s): %s",
|
|
config_string, egl_error_to_string(egl_error));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Creates a GDK window that can be rendered to using EGL.
|
|
static gboolean setup_gdk_window(FlRenderer* self,
|
|
GtkWidget* widget,
|
|
GError** error) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
if (priv->egl_display == EGL_NO_DISPLAY) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Can not set up GDK window: EGL display not created");
|
|
return FALSE;
|
|
}
|
|
|
|
GtkAllocation allocation;
|
|
gtk_widget_get_allocation(widget, &allocation);
|
|
|
|
GdkWindowAttr window_attributes;
|
|
window_attributes.window_type = GDK_WINDOW_CHILD;
|
|
window_attributes.x = allocation.x;
|
|
window_attributes.y = allocation.y;
|
|
window_attributes.width = allocation.width;
|
|
window_attributes.height = allocation.height;
|
|
window_attributes.wclass = GDK_INPUT_OUTPUT;
|
|
window_attributes.event_mask =
|
|
gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK |
|
|
GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
|
|
GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK |
|
|
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK;
|
|
|
|
gint window_attributes_mask = GDK_WA_X | GDK_WA_Y;
|
|
|
|
if (FL_RENDERER_GET_CLASS(self)->setup_window_attr) {
|
|
if (!FL_RENDERER_GET_CLASS(self)->setup_window_attr(
|
|
self, widget, priv->egl_display, priv->egl_config,
|
|
&window_attributes, &window_attributes_mask, error)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
GdkWindow* window =
|
|
gdk_window_new(gtk_widget_get_parent_window(widget), &window_attributes,
|
|
window_attributes_mask);
|
|
gtk_widget_register_window(widget, window);
|
|
gtk_widget_set_window(widget, window);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Creates the EGL surfaces that Flutter will render to.
|
|
static gboolean setup_egl_surfaces(FlRenderer* self,
|
|
GtkWidget* widget,
|
|
GError** error) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
if (priv->egl_surface != EGL_NO_SURFACE ||
|
|
priv->resource_surface != EGL_NO_SURFACE) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"setup_egl_surfaces() called after surfaces already created");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!FL_RENDERER_GET_CLASS(self)->create_surfaces(
|
|
self, widget, priv->egl_display, priv->egl_config, &priv->egl_surface,
|
|
&priv->resource_surface, error)) {
|
|
return FALSE;
|
|
}
|
|
|
|
EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
|
|
|
priv->egl_context = eglCreateContext(priv->egl_display, priv->egl_config,
|
|
EGL_NO_CONTEXT, context_attributes);
|
|
priv->resource_context =
|
|
eglCreateContext(priv->egl_display, priv->egl_config, priv->egl_context,
|
|
context_attributes);
|
|
if (priv->egl_context == EGL_NO_CONTEXT ||
|
|
priv->resource_context == EGL_NO_CONTEXT) {
|
|
EGLint egl_error = eglGetError(); // Must be before egl_config_to_string().
|
|
g_autofree gchar* config_string =
|
|
egl_config_to_string(priv->egl_display, priv->egl_config);
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to create EGL contexts using configuration (%s): %s",
|
|
config_string, egl_error_to_string(egl_error));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean fl_renderer_start(FlRenderer* self,
|
|
GtkWidget* widget,
|
|
GError** error) {
|
|
g_return_val_if_fail(FL_IS_RENDERER(self), FALSE);
|
|
|
|
if (!setup_egl_display(self, error)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!setup_gdk_window(self, widget, error)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!setup_egl_surfaces(self, widget, error)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void fl_renderer_set_geometry(FlRenderer* self,
|
|
GdkRectangle* geometry,
|
|
gint scale) {
|
|
g_return_if_fail(FL_IS_RENDERER(self));
|
|
|
|
if (FL_RENDERER_GET_CLASS(self)->set_geometry) {
|
|
FL_RENDERER_GET_CLASS(self)->set_geometry(self, geometry, scale);
|
|
}
|
|
}
|
|
|
|
void* fl_renderer_get_proc_address(FlRenderer* self, const char* name) {
|
|
return reinterpret_cast<void*>(eglGetProcAddress(name));
|
|
}
|
|
|
|
gboolean fl_renderer_make_current(FlRenderer* self, GError** error) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
if (priv->egl_surface == EGL_NO_SURFACE ||
|
|
priv->egl_context == EGL_NO_CONTEXT) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to make EGL context current: No surface created");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!eglMakeCurrent(priv->egl_display, priv->egl_surface, priv->egl_surface,
|
|
priv->egl_context)) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to make EGL context current: %s",
|
|
egl_error_to_string(eglGetError()));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean fl_renderer_make_resource_current(FlRenderer* self, GError** error) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
if (priv->resource_surface == EGL_NO_SURFACE ||
|
|
priv->resource_context == EGL_NO_CONTEXT) {
|
|
g_set_error(
|
|
error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to make EGL resource context current: No surface created");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!eglMakeCurrent(priv->egl_display, priv->resource_surface,
|
|
priv->resource_surface, priv->resource_context)) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to make EGL resource context current: %s",
|
|
egl_error_to_string(eglGetError()));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean fl_renderer_clear_current(FlRenderer* self, GError** error) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
if (!eglMakeCurrent(priv->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
EGL_NO_CONTEXT)) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to clear EGL context: %s",
|
|
egl_error_to_string(eglGetError()));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
guint32 fl_renderer_get_fbo(FlRenderer* self) {
|
|
// There is only one frame buffer object - always return that.
|
|
return 0;
|
|
}
|
|
|
|
gboolean fl_renderer_present(FlRenderer* self, GError** error) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
if (!eglSwapBuffers(priv->egl_display, priv->egl_surface)) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to swap EGL buffers: %s",
|
|
egl_error_to_string(eglGetError()));
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|