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