mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
232 lines
7.9 KiB
C++
232 lines
7.9 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"
|
|
|
|
G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error)
|
|
|
|
typedef struct {
|
|
EGLDisplay egl_display;
|
|
EGLSurface egl_surface;
|
|
EGLContext egl_context;
|
|
|
|
EGLSurface resource_surface;
|
|
EGLContext resource_context;
|
|
} FlRendererPrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT)
|
|
|
|
// Gets a string representation of the last EGL error.
|
|
static const gchar* get_egl_error() {
|
|
EGLint error = eglGetError();
|
|
switch (error) {
|
|
case EGL_SUCCESS:
|
|
return "Success";
|
|
case EGL_NOT_INITIALIZED:
|
|
return "Not Initialized";
|
|
case EGL_BAD_ACCESS:
|
|
return "Bad Access";
|
|
case EGL_BAD_ALLOC:
|
|
return "Bad Allocation";
|
|
case EGL_BAD_ATTRIBUTE:
|
|
return "Bad Attribute";
|
|
case EGL_BAD_CONTEXT:
|
|
return "Bad Context";
|
|
case EGL_BAD_CONFIG:
|
|
return "Bad Configuration";
|
|
case EGL_BAD_CURRENT_SURFACE:
|
|
return "Bad Current Surface";
|
|
case EGL_BAD_DISPLAY:
|
|
return "Bad Display";
|
|
case EGL_BAD_SURFACE:
|
|
return "Bad Surface";
|
|
case EGL_BAD_MATCH:
|
|
return "Bad Match";
|
|
case EGL_BAD_PARAMETER:
|
|
return "Bad Parameter";
|
|
case EGL_BAD_NATIVE_PIXMAP:
|
|
return "Bad Native Pixmap";
|
|
case EGL_BAD_NATIVE_WINDOW:
|
|
return "Bad Native Window";
|
|
case EGL_CONTEXT_LOST:
|
|
return "Context Lost";
|
|
default:
|
|
return "Unknown Error";
|
|
}
|
|
}
|
|
|
|
// Creates a resource surface.
|
|
static void create_resource_surface(FlRenderer* self, EGLConfig config) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
|
const EGLint resource_context_attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1,
|
|
EGL_NONE};
|
|
priv->resource_surface = eglCreatePbufferSurface(priv->egl_display, config,
|
|
resource_context_attribs);
|
|
if (priv->resource_surface == nullptr) {
|
|
g_warning("Failed to create EGL resource surface: %s", get_egl_error());
|
|
return;
|
|
}
|
|
|
|
priv->resource_context = eglCreateContext(
|
|
priv->egl_display, config, priv->egl_context, context_attributes);
|
|
if (priv->resource_context == nullptr)
|
|
g_warning("Failed to create EGL resource context: %s", get_egl_error());
|
|
}
|
|
|
|
// Default implementation for the start virtual method.
|
|
// Provided so subclasses can chain up to here.
|
|
static gboolean fl_renderer_real_start(FlRenderer* self, GError** error) {
|
|
FlRendererPrivate* priv =
|
|
static_cast<FlRendererPrivate*>(fl_renderer_get_instance_private(self));
|
|
|
|
// Note the use of EGL_DEFAULT_DISPLAY rather than sharing an existing
|
|
// display connection (e.g. an X11 connection from GTK). This is because
|
|
// this EGL display is going to be accessed by a thread from Flutter. In the
|
|
// case of GTK/X11 the display connection is not thread safe and this would
|
|
// cause a crash.
|
|
priv->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
|
|
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};
|
|
EGLConfig egl_config;
|
|
EGLint n_config;
|
|
if (!eglChooseConfig(priv->egl_display, attributes, &egl_config, 1,
|
|
&n_config)) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to choose EGL config: %s", get_egl_error());
|
|
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", get_egl_error());
|
|
return FALSE;
|
|
}
|
|
if (!eglBindAPI(EGL_OPENGL_ES_API)) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to bind EGL OpenGL ES API: %s", get_egl_error());
|
|
return FALSE;
|
|
}
|
|
|
|
priv->egl_surface = FL_RENDERER_GET_CLASS(self)->create_surface(
|
|
self, priv->egl_display, egl_config);
|
|
if (priv->egl_surface == nullptr) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to create EGL surface: %s", get_egl_error());
|
|
return FALSE;
|
|
}
|
|
EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
|
priv->egl_context = eglCreateContext(priv->egl_display, egl_config,
|
|
EGL_NO_CONTEXT, context_attributes);
|
|
if (priv->egl_context == nullptr) {
|
|
g_set_error(error, fl_renderer_error_quark(), FL_RENDERER_ERROR_FAILED,
|
|
"Failed to create EGL context: %s", get_egl_error());
|
|
return FALSE;
|
|
}
|
|
|
|
create_resource_surface(self, egl_config);
|
|
|
|
EGLint value;
|
|
eglQueryContext(priv->egl_display, priv->egl_context,
|
|
EGL_CONTEXT_CLIENT_VERSION, &value);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void fl_renderer_class_init(FlRendererClass* klass) {
|
|
klass->start = fl_renderer_real_start;
|
|
}
|
|
|
|
static void fl_renderer_init(FlRenderer* self) {}
|
|
|
|
gboolean fl_renderer_start(FlRenderer* self, GError** error) {
|
|
return FL_RENDERER_GET_CLASS(self)->start(self, error);
|
|
}
|
|
|
|
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 (!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", get_egl_error());
|
|
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 == nullptr || priv->resource_context == nullptr)
|
|
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 context current: %s", get_egl_error());
|
|
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", get_egl_error());
|
|
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", get_egl_error());
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|