• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LogLastEGLError()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 
CreateContext(EGLDisplay display,EGLConfig config,EGLContext share=EGL_NO_CONTEXT)62 static EGLResult<EGLSurface> 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 
ChooseEGLConfiguration(EGLDisplay display)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 
TeardownContext(EGLDisplay display,EGLContext context)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 
TeardownSurface(EGLDisplay display,EGLSurface surface)108 static bool TeardownSurface(EGLDisplay display, EGLSurface surface) {
109   if (surface != EGL_NO_SURFACE) {
110     return eglDestroySurface(display, surface) == EGL_TRUE;
111   }
112 
113   return true;
114 }
115 
116 // For onscreen rendering.
CreateWindowSurface(fml::RefPtr<AndroidNativeWindow> window)117 bool AndroidContextGL::CreateWindowSurface(
118     fml::RefPtr<AndroidNativeWindow> window) {
119   // The configurations are only required when dealing with extensions or VG.
120   // We do neither.
121 
122   window_ = std::move(window);
123   EGLDisplay display = environment_->Display();
124 
125   const EGLint attribs[] = {EGL_NONE};
126 
127   // If surface not empty, create a new one will lead mem-leak in mali driver.
128   // So should teardown the old surface before create a new one.
129   TeardownSurface(display, surface_);
130   surface_ = eglCreateWindowSurface(
131       display, config_,
132       reinterpret_cast<EGLNativeWindowType>(window_->handle()), attribs);
133   return surface_ != EGL_NO_SURFACE;
134 }
135 
136 // For offscreen rendering.
CreatePBufferSurface()137 bool AndroidContextGL::CreatePBufferSurface() {
138   // We only ever create pbuffer surfaces for background resource loading
139   // contexts. We never bind the pbuffer to anything.
140 
141   EGLDisplay display = environment_->Display();
142 
143   const EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
144 
145   surface_ = eglCreatePbufferSurface(display, config_, attribs);
146   return surface_ != EGL_NO_SURFACE;
147 }
148 
AndroidContextGL(fml::RefPtr<AndroidEnvironmentGL> env,const AndroidContextGL * share_context)149 AndroidContextGL::AndroidContextGL(fml::RefPtr<AndroidEnvironmentGL> env,
150                                    const AndroidContextGL* share_context)
151     : environment_(env),
152       window_(nullptr),
153       config_(nullptr),
154       surface_(EGL_NO_SURFACE),
155       context_(EGL_NO_CONTEXT),
156       valid_(false) {
157   if (!environment_->IsValid()) {
158     return;
159   }
160 
161   bool success = false;
162 
163   // Choose a valid configuration.
164 
165   std::tie(success, config_) = ChooseEGLConfiguration(environment_->Display());
166 
167   if (!success) {
168     FML_LOG(ERROR) << "Could not choose an EGL configuration.";
169     LogLastEGLError();
170     return;
171   }
172 
173   // Create a context for the configuration.
174 
175   std::tie(success, context_) = CreateContext(
176       environment_->Display(), config_,
177       share_context != nullptr ? share_context->context_ : EGL_NO_CONTEXT);
178 
179   if (!success) {
180     FML_LOG(ERROR) << "Could not create an EGL context";
181     LogLastEGLError();
182     return;
183   }
184 
185   if (!this->CreatePBufferSurface()) {
186     FML_LOG(ERROR) << "Could not create the EGL surface.";
187     LogLastEGLError();
188     return;
189   }
190 
191   // All done!
192   valid_ = true;
193 }
194 
~AndroidContextGL()195 AndroidContextGL::~AndroidContextGL() {
196   if (!TeardownContext(environment_->Display(), context_)) {
197     FML_LOG(ERROR)
198         << "Could not tear down the EGL context. Possible resource leak.";
199     LogLastEGLError();
200   }
201 
202   if (!TeardownSurface(environment_->Display(), surface_)) {
203     FML_LOG(ERROR)
204         << "Could not tear down the EGL surface. Possible resource leak.";
205     LogLastEGLError();
206   }
207 }
208 
Environment() const209 fml::RefPtr<AndroidEnvironmentGL> AndroidContextGL::Environment() const {
210   return environment_;
211 }
212 
IsValid() const213 bool AndroidContextGL::IsValid() const {
214   return valid_;
215 }
216 
MakeCurrent()217 bool AndroidContextGL::MakeCurrent() {
218   // If same context just return, avoid unused egl call.
219   if (eglGetCurrentContext() == context_) {
220     return true;
221   }
222   if (eglMakeCurrent(environment_->Display(), surface_, surface_, context_) !=
223       EGL_TRUE) {
224     FML_LOG(ERROR) << "Could not make the context current";
225     LogLastEGLError();
226     return false;
227   }
228   return true;
229 }
230 
ClearCurrent()231 bool AndroidContextGL::ClearCurrent() {
232   if (eglGetCurrentContext() != context_) {
233     return true;
234   }
235   if (eglMakeCurrent(environment_->Display(), EGL_NO_SURFACE, EGL_NO_SURFACE,
236                      EGL_NO_CONTEXT) != EGL_TRUE) {
237     FML_LOG(ERROR) << "Could not clear the current context";
238     LogLastEGLError();
239     return false;
240   }
241   return true;
242 }
243 
SwapBuffers()244 bool AndroidContextGL::SwapBuffers() {
245   TRACE_EVENT0("flutter", "AndroidContextGL::SwapBuffers");
246   return eglSwapBuffers(environment_->Display(), surface_);
247 }
248 
GetSize()249 SkISize AndroidContextGL::GetSize() {
250   EGLint width = 0;
251   EGLint height = 0;
252 
253   if (!eglQuerySurface(environment_->Display(), surface_, EGL_WIDTH, &width) ||
254       !eglQuerySurface(environment_->Display(), surface_, EGL_HEIGHT,
255                        &height)) {
256     FML_LOG(ERROR) << "Unable to query EGL surface size";
257     LogLastEGLError();
258     return SkISize::Make(0, 0);
259   }
260   return SkISize::Make(width, height);
261 }
262 
Resize(const SkISize & size)263 bool AndroidContextGL::Resize(const SkISize& size) {
264   if (size == GetSize()) {
265     return true;
266   }
267 
268   ClearCurrent();
269 
270   TeardownSurface(environment_->Display(), surface_);
271 
272   if (!this->CreateWindowSurface(window_)) {
273     FML_LOG(ERROR) << "Unable to create EGL window surface on resize.";
274     return false;
275   }
276 
277   MakeCurrent();
278 
279   return true;
280 }
281 
282 }  // namespace flutter
283