blob: d3fd715a38abc5cda22a68eeac643a199e7eb04a [file] [log] [blame]
// 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 "flutter/shell/platform/android/android_context_gl.h"
#include <EGL/eglext.h>
#include <utility>
#include "flutter/fml/trace_event.h"
namespace flutter {
template <class T>
using EGLResult = std::pair<bool, T>;
static void LogLastEGLError() {
struct EGLNameErrorPair {
const char* name;
EGLint code;
};
#define _EGL_ERROR_DESC(a) \
{ #a, a }
const EGLNameErrorPair pairs[] = {
_EGL_ERROR_DESC(EGL_SUCCESS),
_EGL_ERROR_DESC(EGL_NOT_INITIALIZED),
_EGL_ERROR_DESC(EGL_BAD_ACCESS),
_EGL_ERROR_DESC(EGL_BAD_ALLOC),
_EGL_ERROR_DESC(EGL_BAD_ATTRIBUTE),
_EGL_ERROR_DESC(EGL_BAD_CONTEXT),
_EGL_ERROR_DESC(EGL_BAD_CONFIG),
_EGL_ERROR_DESC(EGL_BAD_CURRENT_SURFACE),
_EGL_ERROR_DESC(EGL_BAD_DISPLAY),
_EGL_ERROR_DESC(EGL_BAD_SURFACE),
_EGL_ERROR_DESC(EGL_BAD_MATCH),
_EGL_ERROR_DESC(EGL_BAD_PARAMETER),
_EGL_ERROR_DESC(EGL_BAD_NATIVE_PIXMAP),
_EGL_ERROR_DESC(EGL_BAD_NATIVE_WINDOW),
_EGL_ERROR_DESC(EGL_CONTEXT_LOST),
};
#undef _EGL_ERROR_DESC
const auto count = sizeof(pairs) / sizeof(EGLNameErrorPair);
EGLint last_error = eglGetError();
for (size_t i = 0; i < count; i++) {
if (last_error == pairs[i].code) {
FML_LOG(ERROR) << "EGL Error: " << pairs[i].name << " (" << pairs[i].code
<< ")";
return;
}
}
FML_LOG(ERROR) << "Unknown EGL Error";
}
static EGLResult<EGLSurface> CreateContext(EGLDisplay display,
EGLConfig config,
EGLContext share = EGL_NO_CONTEXT) {
EGLint attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
EGLContext context = eglCreateContext(display, config, share, attributes);
return {context != EGL_NO_CONTEXT, context};
}
static EGLResult<EGLConfig> ChooseEGLConfiguration(EGLDisplay display) {
EGLint attributes[] = {
// clang-format off
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_STENCIL_SIZE, 0,
EGL_NONE, // termination sentinel
// clang-format on
};
EGLint config_count = 0;
EGLConfig egl_config = nullptr;
if (eglChooseConfig(display, attributes, &egl_config, 1, &config_count) !=
EGL_TRUE) {
return {false, nullptr};
}
bool success = config_count > 0 && egl_config != nullptr;
return {success, success ? egl_config : nullptr};
}
static bool TeardownContext(EGLDisplay display, EGLContext context) {
if (context != EGL_NO_CONTEXT) {
return eglDestroyContext(display, context) == EGL_TRUE;
}
return true;
}
static bool TeardownSurface(EGLDisplay display, EGLSurface surface) {
if (surface != EGL_NO_SURFACE) {
return eglDestroySurface(display, surface) == EGL_TRUE;
}
return true;
}
// For onscreen rendering.
bool AndroidContextGL::CreateWindowSurface(
fml::RefPtr<AndroidNativeWindow> window) {
// The configurations are only required when dealing with extensions or VG.
// We do neither.
window_ = std::move(window);
EGLDisplay display = environment_->Display();
const EGLint attribs[] = {EGL_NONE};
surface_ = eglCreateWindowSurface(
display, config_,
reinterpret_cast<EGLNativeWindowType>(window_->handle()), attribs);
return surface_ != EGL_NO_SURFACE;
}
// For offscreen rendering.
bool AndroidContextGL::CreatePBufferSurface() {
// We only ever create pbuffer surfaces for background resource loading
// contexts. We never bind the pbuffer to anything.
EGLDisplay display = environment_->Display();
const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
surface_ = eglCreatePbufferSurface(display, config_, attribs);
return surface_ != EGL_NO_SURFACE;
}
AndroidContextGL::AndroidContextGL(fml::RefPtr<AndroidEnvironmentGL> env,
const AndroidContextGL* share_context)
: environment_(env),
window_(nullptr),
config_(nullptr),
surface_(EGL_NO_SURFACE),
context_(EGL_NO_CONTEXT),
valid_(false) {
if (!environment_->IsValid()) {
return;
}
bool success = false;
// Choose a valid configuration.
std::tie(success, config_) = ChooseEGLConfiguration(environment_->Display());
if (!success) {
FML_LOG(ERROR) << "Could not choose an EGL configuration.";
LogLastEGLError();
return;
}
// Create a context for the configuration.
std::tie(success, context_) = CreateContext(
environment_->Display(), config_,
share_context != nullptr ? share_context->context_ : EGL_NO_CONTEXT);
if (!success) {
FML_LOG(ERROR) << "Could not create an EGL context";
LogLastEGLError();
return;
}
if (!this->CreatePBufferSurface()) {
FML_LOG(ERROR) << "Could not create the EGL surface.";
LogLastEGLError();
return;
}
// All done!
valid_ = true;
}
AndroidContextGL::~AndroidContextGL() {
if (!TeardownContext(environment_->Display(), context_)) {
FML_LOG(ERROR)
<< "Could not tear down the EGL context. Possible resource leak.";
LogLastEGLError();
}
if (!TeardownSurface(environment_->Display(), surface_)) {
FML_LOG(ERROR)
<< "Could not tear down the EGL surface. Possible resource leak.";
LogLastEGLError();
}
}
fml::RefPtr<AndroidEnvironmentGL> AndroidContextGL::Environment() const {
return environment_;
}
bool AndroidContextGL::IsValid() const {
return valid_;
}
bool AndroidContextGL::MakeCurrent() {
if (eglMakeCurrent(environment_->Display(), surface_, surface_, context_) !=
EGL_TRUE) {
FML_LOG(ERROR) << "Could not make the context current";
LogLastEGLError();
return false;
}
return true;
}
bool AndroidContextGL::ClearCurrent() {
if (eglGetCurrentContext() != context_) {
return true;
}
if (eglMakeCurrent(environment_->Display(), EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT) != EGL_TRUE) {
FML_LOG(ERROR) << "Could not clear the current context";
LogLastEGLError();
return false;
}
return true;
}
bool AndroidContextGL::SwapBuffers() {
TRACE_EVENT0("flutter", "AndroidContextGL::SwapBuffers");
return eglSwapBuffers(environment_->Display(), surface_);
}
SkISize AndroidContextGL::GetSize() {
EGLint width = 0;
EGLint height = 0;
if (!eglQuerySurface(environment_->Display(), surface_, EGL_WIDTH, &width) ||
!eglQuerySurface(environment_->Display(), surface_, EGL_HEIGHT,
&height)) {
FML_LOG(ERROR) << "Unable to query EGL surface size";
LogLastEGLError();
return SkISize::Make(0, 0);
}
return SkISize::Make(width, height);
}
bool AndroidContextGL::Resize(const SkISize& size) {
if (size == GetSize()) {
return true;
}
ClearCurrent();
TeardownSurface(environment_->Display(), surface_);
if (!this->CreateWindowSurface(window_)) {
FML_LOG(ERROR) << "Unable to create EGL window surface on resize.";
return false;
}
MakeCurrent();
return true;
}
} // namespace flutter