android_context_gl.cc 7.1 KB
Newer Older
1 2 3 4 5
// Copyright 2016 The Chromium 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"
6

7
#include <EGL/eglext.h>
8

9
#include <utility>
10

11 12
#include "flutter/fml/trace_event.h"

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
namespace shell {

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) {
53
      FXL_LOG(ERROR) << "EGL Error: " << pairs[i].name << " (" << pairs[i].code
54
                     << ")";
55 56 57 58
      return;
    }
  }

59
  FXL_LOG(ERROR) << "Unknown EGL Error";
60 61 62 63 64 65 66 67 68 69 70 71
}

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};
}

72
static EGLResult<EGLConfig> ChooseEGLConfiguration(EGLDisplay display) {
73 74 75 76
  EGLint attributes[] = {
      // clang-format off
      EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
      EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,
77 78 79 80 81 82
      EGL_RED_SIZE,        8,
      EGL_GREEN_SIZE,      8,
      EGL_BLUE_SIZE,       8,
      EGL_ALPHA_SIZE,      8,
      EGL_DEPTH_SIZE,      0,
      EGL_STENCIL_SIZE,    0,
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
      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.
117
bool AndroidContextGL::CreateWindowSurface(
118
    fxl::RefPtr<AndroidNativeWindow> window) {
119 120
  // The configurations are only required when dealing with extensions or VG.
  // We do neither.
121 122 123 124

  window_ = std::move(window);
  EGLDisplay display = environment_->Display();

125
  const EGLint attribs[] = {EGL_NONE};
126 127 128 129 130

  surface_ = eglCreateWindowSurface(
      display, config_,
      reinterpret_cast<EGLNativeWindowType>(window_->handle()), attribs);
  return surface_ != EGL_NO_SURFACE;
131 132 133
}

// For offscreen rendering.
134
bool AndroidContextGL::CreatePBufferSurface() {
135 136 137
  // We only ever create pbuffer surfaces for background resource loading
  // contexts. We never bind the pbuffer to anything.

138 139
  EGLDisplay display = environment_->Display();

140
  const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
141 142 143

  surface_ = eglCreatePbufferSurface(display, config_, attribs);
  return surface_ != EGL_NO_SURFACE;
144 145
}

146
AndroidContextGL::AndroidContextGL(fxl::RefPtr<AndroidEnvironmentGL> env,
147 148
                                   const AndroidContextGL* share_context)
    : environment_(env),
149
      window_(nullptr),
150 151 152 153 154 155 156 157 158 159 160 161
      config_(nullptr),
      surface_(EGL_NO_SURFACE),
      context_(EGL_NO_CONTEXT),
      valid_(false) {
  if (!environment_->IsValid()) {
    return;
  }

  bool success = false;

  // Choose a valid configuration.

162
  std::tie(success, config_) = ChooseEGLConfiguration(environment_->Display());
163 164

  if (!success) {
165
    FXL_LOG(ERROR) << "Could not choose an EGL configuration.";
166 167 168 169
    LogLastEGLError();
    return;
  }

170
  // Create a context for the configuration.
171

172 173 174
  std::tie(success, context_) = CreateContext(
      environment_->Display(), config_,
      share_context != nullptr ? share_context->context_ : EGL_NO_CONTEXT);
175 176

  if (!success) {
177
    FXL_LOG(ERROR) << "Could not create an EGL context";
178 179 180 181
    LogLastEGLError();
    return;
  }

182
  if (!this->CreatePBufferSurface()) {
183
    FXL_LOG(ERROR) << "Could not create the EGL surface.";
184 185 186 187 188 189 190 191 192 193
    LogLastEGLError();
    return;
  }

  // All done!
  valid_ = true;
}

AndroidContextGL::~AndroidContextGL() {
  if (!TeardownContext(environment_->Display(), context_)) {
194 195
    FXL_LOG(ERROR)
        << "Could not tear down the EGL context. Possible resource leak.";
196 197 198 199
    LogLastEGLError();
  }

  if (!TeardownSurface(environment_->Display(), surface_)) {
200 201
    FXL_LOG(ERROR)
        << "Could not tear down the EGL surface. Possible resource leak.";
202 203 204 205
    LogLastEGLError();
  }
}

206
fxl::RefPtr<AndroidEnvironmentGL> AndroidContextGL::Environment() const {
207 208 209 210 211 212 213 214 215 216
  return environment_;
}

bool AndroidContextGL::IsValid() const {
  return valid_;
}

bool AndroidContextGL::MakeCurrent() {
  if (eglMakeCurrent(environment_->Display(), surface_, surface_, context_) !=
      EGL_TRUE) {
217
    FXL_LOG(ERROR) << "Could not make the context current";
218 219 220 221 222 223 224 225 226
    LogLastEGLError();
    return false;
  }
  return true;
}

bool AndroidContextGL::ClearCurrent() {
  if (eglMakeCurrent(environment_->Display(), EGL_NO_SURFACE, EGL_NO_SURFACE,
                     EGL_NO_CONTEXT) != EGL_TRUE) {
227
    FXL_LOG(ERROR) << "Could not clear the current context";
228 229 230 231 232 233 234
    LogLastEGLError();
    return false;
  }
  return true;
}

bool AndroidContextGL::SwapBuffers() {
235
  TRACE_EVENT0("flutter", "AndroidContextGL::SwapBuffers");
236 237 238 239 240 241 242 243 244 245
  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)) {
246
    FXL_LOG(ERROR) << "Unable to query EGL surface size";
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    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_);

262
  if (!this->CreateWindowSurface(window_)) {
263
    FXL_LOG(ERROR) << "Unable to create EGL window surface on resize.";
264 265 266
    return false;
  }

267 268
  MakeCurrent();

269 270 271 272
  return true;
}

}  // namespace shell