Flutter Engine
android_context_gl.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/shell/platform/android/android_context_gl.h"
6 
7 #include <EGL/eglext.h>
8 
9 #include <utility>
10 
11 #include "flutter/fml/trace_event.h"
12 
13 namespace flutter {
14 
15 template <class T>
16 using EGLResult = std::pair<bool, T>;
17 
18 static void LogLastEGLError() {
19  struct EGLNameErrorPair {
20  const char* name;
21  EGLint code;
22  };
23 
24 #define _EGL_ERROR_DESC(a) \
25  { #a, a }
26 
27  const EGLNameErrorPair pairs[] = {
28  _EGL_ERROR_DESC(EGL_SUCCESS),
29  _EGL_ERROR_DESC(EGL_NOT_INITIALIZED),
30  _EGL_ERROR_DESC(EGL_BAD_ACCESS),
31  _EGL_ERROR_DESC(EGL_BAD_ALLOC),
32  _EGL_ERROR_DESC(EGL_BAD_ATTRIBUTE),
33  _EGL_ERROR_DESC(EGL_BAD_CONTEXT),
34  _EGL_ERROR_DESC(EGL_BAD_CONFIG),
35  _EGL_ERROR_DESC(EGL_BAD_CURRENT_SURFACE),
36  _EGL_ERROR_DESC(EGL_BAD_DISPLAY),
37  _EGL_ERROR_DESC(EGL_BAD_SURFACE),
38  _EGL_ERROR_DESC(EGL_BAD_MATCH),
39  _EGL_ERROR_DESC(EGL_BAD_PARAMETER),
40  _EGL_ERROR_DESC(EGL_BAD_NATIVE_PIXMAP),
41  _EGL_ERROR_DESC(EGL_BAD_NATIVE_WINDOW),
42  _EGL_ERROR_DESC(EGL_CONTEXT_LOST),
43  };
44 
45 #undef _EGL_ERROR_DESC
46 
47  const auto count = sizeof(pairs) / sizeof(EGLNameErrorPair);
48 
49  EGLint last_error = eglGetError();
50 
51  for (size_t i = 0; i < count; i++) {
52  if (last_error == pairs[i].code) {
53  FML_LOG(ERROR) << "EGL Error: " << pairs[i].name << " (" << pairs[i].code
54  << ")";
55  return;
56  }
57  }
58 
59  FML_LOG(ERROR) << "Unknown EGL Error";
60 }
61 
62 static EGLResult<EGLContext> CreateContext(EGLDisplay display,
63  EGLConfig config,
64  EGLContext share = EGL_NO_CONTEXT) {
65  EGLint attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
66 
67  EGLContext context = eglCreateContext(display, config, share, attributes);
68 
69  return {context != EGL_NO_CONTEXT, context};
70 }
71 
72 static EGLResult<EGLConfig> ChooseEGLConfiguration(EGLDisplay display) {
73  EGLint attributes[] = {
74  // clang-format off
75  EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
76  EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
77  EGL_RED_SIZE, 8,
78  EGL_GREEN_SIZE, 8,
79  EGL_BLUE_SIZE, 8,
80  EGL_ALPHA_SIZE, 8,
81  EGL_DEPTH_SIZE, 0,
82  EGL_STENCIL_SIZE, 0,
83  EGL_NONE, // termination sentinel
84  // clang-format on
85  };
86 
87  EGLint config_count = 0;
88  EGLConfig egl_config = nullptr;
89 
90  if (eglChooseConfig(display, attributes, &egl_config, 1, &config_count) !=
91  EGL_TRUE) {
92  return {false, nullptr};
93  }
94 
95  bool success = config_count > 0 && egl_config != nullptr;
96 
97  return {success, success ? egl_config : nullptr};
98 }
99 
100 static bool TeardownContext(EGLDisplay display, EGLContext context) {
101  if (context != EGL_NO_CONTEXT) {
102  return eglDestroyContext(display, context) == EGL_TRUE;
103  }
104 
105  return true;
106 }
107 
109  EGLDisplay display,
110  EGLContext context)
111  : surface_(surface), display_(display), context_(context) {}
112 
114  auto result = eglDestroySurface(display_, surface_);
115  FML_DCHECK(result == EGL_TRUE);
116 }
117 
119  return surface_ != EGL_NO_SURFACE;
120 }
121 
123  if (eglMakeCurrent(display_, surface_, surface_, context_) != EGL_TRUE) {
124  FML_LOG(ERROR) << "Could not make the context current";
125  LogLastEGLError();
126  return false;
127  }
128  return true;
129 }
130 
132  TRACE_EVENT0("flutter", "AndroidContextGL::SwapBuffers");
133  return eglSwapBuffers(display_, surface_);
134 }
135 
136 SkISize AndroidEGLSurface::GetSize() const {
137  EGLint width = 0;
138  EGLint height = 0;
139 
140  if (!eglQuerySurface(display_, surface_, EGL_WIDTH, &width) ||
141  !eglQuerySurface(display_, surface_, EGL_HEIGHT, &height)) {
142  FML_LOG(ERROR) << "Unable to query EGL surface size";
143  LogLastEGLError();
144  return SkISize::Make(0, 0);
145  }
146  return SkISize::Make(width, height);
147 }
148 
150  AndroidRenderingAPI rendering_api,
153  environment_(environment),
154  config_(nullptr) {
155  if (!environment_->IsValid()) {
156  FML_LOG(ERROR) << "Could not create an Android GL environment.";
157  return;
158  }
159 
160  bool success = false;
161 
162  // Choose a valid configuration.
163  std::tie(success, config_) = ChooseEGLConfiguration(environment_->Display());
164  if (!success) {
165  FML_LOG(ERROR) << "Could not choose an EGL configuration.";
166  LogLastEGLError();
167  return;
168  }
169 
170  // Create a context for the configuration.
171  std::tie(success, context_) =
172  CreateContext(environment_->Display(), config_, EGL_NO_CONTEXT);
173  if (!success) {
174  FML_LOG(ERROR) << "Could not create an EGL context";
175  LogLastEGLError();
176  return;
177  }
178 
179  std::tie(success, resource_context_) =
180  CreateContext(environment_->Display(), config_, context_);
181  if (!success) {
182  FML_LOG(ERROR) << "Could not create an EGL resource context";
183  LogLastEGLError();
184  return;
185  }
186 
187  // All done!
188  valid_ = true;
189 }
190 
192  if (!TeardownContext(environment_->Display(), context_)) {
193  FML_LOG(ERROR)
194  << "Could not tear down the EGL context. Possible resource leak.";
195  LogLastEGLError();
196  }
197 
198  if (!TeardownContext(environment_->Display(), resource_context_)) {
199  FML_LOG(ERROR) << "Could not tear down the EGL resource context. Possible "
200  "resource leak.";
201  LogLastEGLError();
202  }
203 }
204 
205 std::unique_ptr<AndroidEGLSurface> AndroidContextGL::CreateOnscreenSurface(
206  fml::RefPtr<AndroidNativeWindow> window) const {
207  EGLDisplay display = environment_->Display();
208 
209  const EGLint attribs[] = {EGL_NONE};
210 
211  EGLSurface surface = eglCreateWindowSurface(
212  display, config_, reinterpret_cast<EGLNativeWindowType>(window->handle()),
213  attribs);
214  return std::make_unique<AndroidEGLSurface>(surface, display, context_);
215 }
216 
217 std::unique_ptr<AndroidEGLSurface> AndroidContextGL::CreateOffscreenSurface()
218  const {
219  // We only ever create pbuffer surfaces for background resource loading
220  // contexts. We never bind the pbuffer to anything.
221  EGLDisplay display = environment_->Display();
222 
223  const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
224 
225  EGLSurface surface = eglCreatePbufferSurface(display, config_, attribs);
226  return std::make_unique<AndroidEGLSurface>(surface, display,
227  resource_context_);
228 }
229 
231  return environment_;
232 }
233 
235  return valid_;
236 }
237 
239  if (eglGetCurrentContext() != context_) {
240  return true;
241  }
242  if (eglMakeCurrent(environment_->Display(), EGL_NO_SURFACE, EGL_NO_SURFACE,
243  EGL_NO_CONTEXT) != EGL_TRUE) {
244  FML_LOG(ERROR) << "Could not clear the current context";
245  LogLastEGLError();
246  return false;
247  }
248  return true;
249 }
250 
252  bool success;
253  EGLContext context;
254  std::tie(success, context) =
255  CreateContext(environment_->Display(), config_, EGL_NO_CONTEXT);
256  return success ? context : EGL_NO_CONTEXT;
257 }
258 
259 } // namespace flutter
AndroidEGLSurface(EGLSurface surface, EGLDisplay display, EGLContext context)
static void LogLastEGLError()
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:75
Holds state that is shared across Android surfaces.
#define FML_DCHECK(condition)
Definition: logging.h:86
bool MakeCurrent()
Binds the EGLContext context to the current rendering thread and to the draw and read surface...
fml::RefPtr< AndroidEnvironmentGL > Environment() const
#define FML_LOG(severity)
Definition: logging.h:65
EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list)
Definition: mock_egl.cc:121
AndroidContextGL(AndroidRenderingAPI rendering_api, fml::RefPtr< AndroidEnvironmentGL > environment)
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list)
Definition: mock_egl.cc:142
EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx)
Definition: mock_egl.cc:306
EGLint eglGetError()
Definition: mock_egl.cc:252
std::pair< bool, T > EGLResult
EGLContext CreateNewContext() const
Create a new EGLContext using the same EGLConfig.
static EGLResult< EGLContext > CreateContext(EGLDisplay display, EGLConfig config, EGLContext share=EGL_NO_CONTEXT)
int32_t height
int32_t width
#define _EGL_ERROR_DESC(a)
static bool TeardownContext(EGLDisplay display, EGLContext context)
const char * name
Definition: fuchsia.cc:50
bool SwapBuffers()
This only applies to on-screen surfaces such as those created by AndroidContextGL::CreateOnscreenSurf...
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
Definition: mock_egl.cc:316
std::unique_ptr< AndroidEGLSurface > CreateOffscreenSurface() const
Allocates an 1x1 pbuffer surface that is used for making the offscreen current for texture uploads...
static EGLResult< EGLConfig > ChooseEGLConfiguration(EGLDisplay display)
std::unique_ptr< AndroidEGLSurface > CreateOnscreenSurface(fml::RefPtr< AndroidNativeWindow > window) const
Allocates an new EGL window surface that is used for on-screen pixels.
EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list)
Definition: mock_egl.cc:132
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config)
Definition: mock_egl.cc:95