• 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/testing/test_gl_surface.h"
6 
7 #include <EGL/egl.h>
8 #include <GLES2/gl2.h>
9 
10 #include <sstream>
11 #include <string>
12 
13 #include "flutter/fml/logging.h"
14 #include "third_party/skia/include/core/SkSurface.h"
15 #include "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h"
16 #include "third_party/skia/src/gpu/gl/GrGLDefines.h"
17 
18 namespace flutter {
19 namespace testing {
20 
GetEGLError()21 static std::string GetEGLError() {
22   std::stringstream stream;
23 
24   auto error = ::eglGetError();
25 
26   stream << "EGL Result: '";
27 
28   switch (error) {
29     case EGL_SUCCESS:
30       stream << "EGL_SUCCESS";
31       break;
32     case EGL_NOT_INITIALIZED:
33       stream << "EGL_NOT_INITIALIZED";
34       break;
35     case EGL_BAD_ACCESS:
36       stream << "EGL_BAD_ACCESS";
37       break;
38     case EGL_BAD_ALLOC:
39       stream << "EGL_BAD_ALLOC";
40       break;
41     case EGL_BAD_ATTRIBUTE:
42       stream << "EGL_BAD_ATTRIBUTE";
43       break;
44     case EGL_BAD_CONTEXT:
45       stream << "EGL_BAD_CONTEXT";
46       break;
47     case EGL_BAD_CONFIG:
48       stream << "EGL_BAD_CONFIG";
49       break;
50     case EGL_BAD_CURRENT_SURFACE:
51       stream << "EGL_BAD_CURRENT_SURFACE";
52       break;
53     case EGL_BAD_DISPLAY:
54       stream << "EGL_BAD_DISPLAY";
55       break;
56     case EGL_BAD_SURFACE:
57       stream << "EGL_BAD_SURFACE";
58       break;
59     case EGL_BAD_MATCH:
60       stream << "EGL_BAD_MATCH";
61       break;
62     case EGL_BAD_PARAMETER:
63       stream << "EGL_BAD_PARAMETER";
64       break;
65     case EGL_BAD_NATIVE_PIXMAP:
66       stream << "EGL_BAD_NATIVE_PIXMAP";
67       break;
68     case EGL_BAD_NATIVE_WINDOW:
69       stream << "EGL_BAD_NATIVE_WINDOW";
70       break;
71     case EGL_CONTEXT_LOST:
72       stream << "EGL_CONTEXT_LOST";
73       break;
74     default:
75       stream << "Unknown";
76   }
77 
78   stream << "' (0x" << std::hex << error << std::dec << ").";
79   return stream.str();
80 }
81 
82 constexpr size_t kTestGLSurfaceWidth = 800;
83 constexpr size_t kTestGLSurfaceHeight = 600;
84 
TestGLSurface()85 TestGLSurface::TestGLSurface() {
86   display_ = ::eglGetDisplay(EGL_DEFAULT_DISPLAY);
87   FML_CHECK(display_ != EGL_NO_DISPLAY);
88 
89   auto result = ::eglInitialize(display_, NULL, NULL);
90   FML_CHECK(result == EGL_TRUE) << GetEGLError();
91 
92   EGLConfig config = {0};
93 
94   EGLint num_config = 0;
95   const EGLint attribute_list[] = {EGL_RED_SIZE,
96                                    8,
97                                    EGL_GREEN_SIZE,
98                                    8,
99                                    EGL_BLUE_SIZE,
100                                    8,
101                                    EGL_ALPHA_SIZE,
102                                    8,
103                                    EGL_SURFACE_TYPE,
104                                    EGL_PBUFFER_BIT,
105                                    EGL_CONFORMANT,
106                                    EGL_OPENGL_ES2_BIT,
107                                    EGL_RENDERABLE_TYPE,
108                                    EGL_OPENGL_ES2_BIT,
109                                    EGL_NONE};
110 
111   result = ::eglChooseConfig(display_, attribute_list, &config, 1, &num_config);
112   FML_CHECK(result == EGL_TRUE) << GetEGLError();
113   FML_CHECK(num_config == 1) << GetEGLError();
114 
115   {
116     const EGLint surface_attributes[] = {
117         EGL_WIDTH,  kTestGLSurfaceWidth,   //
118         EGL_HEIGHT, kTestGLSurfaceHeight,  //
119         EGL_NONE,
120     };
121 
122     onscreen_surface_ =
123         ::eglCreatePbufferSurface(display_,           // display connection
124                                   config,             // config
125                                   surface_attributes  // surface attributes
126         );
127     FML_CHECK(onscreen_surface_ != EGL_NO_SURFACE) << GetEGLError();
128 
129     offscreen_surface_ =
130         ::eglCreatePbufferSurface(display_,           // display connection
131                                   config,             // config
132                                   surface_attributes  // surface attributes
133         );
134     FML_CHECK(offscreen_surface_ != EGL_NO_SURFACE) << GetEGLError();
135   }
136 
137   {
138     const EGLint context_attributes[] = {
139         EGL_CONTEXT_CLIENT_VERSION,  //
140         2,                           //
141         EGL_NONE                     //
142     };
143 
144     onscreen_context_ =
145         ::eglCreateContext(display_,           // display connection
146                            config,             // config
147                            EGL_NO_CONTEXT,     // sharegroup
148                            context_attributes  // context attributes
149         );
150     FML_CHECK(onscreen_context_ != EGL_NO_CONTEXT) << GetEGLError();
151 
152     offscreen_context_ =
153         ::eglCreateContext(display_,           // display connection
154                            config,             // config
155                            onscreen_context_,  // sharegroup
156                            context_attributes  // context attributes
157         );
158     FML_CHECK(offscreen_context_ != EGL_NO_CONTEXT) << GetEGLError();
159   }
160 }
161 
~TestGLSurface()162 TestGLSurface::~TestGLSurface() {
163   context_ = nullptr;
164 
165   auto result = ::eglDestroyContext(display_, onscreen_context_);
166   FML_CHECK(result == EGL_TRUE) << GetEGLError();
167 
168   result = ::eglDestroyContext(display_, offscreen_context_);
169   FML_CHECK(result == EGL_TRUE) << GetEGLError();
170 
171   result = ::eglDestroySurface(display_, onscreen_surface_);
172   FML_CHECK(result == EGL_TRUE) << GetEGLError();
173 
174   result = ::eglDestroySurface(display_, offscreen_surface_);
175   FML_CHECK(result == EGL_TRUE) << GetEGLError();
176 
177   result = ::eglTerminate(display_);
178   FML_CHECK(result == EGL_TRUE);
179 }
180 
GetSize() const181 SkISize TestGLSurface::GetSize() const {
182   return SkISize::Make(kTestGLSurfaceWidth, kTestGLSurfaceHeight);
183 }
184 
MakeCurrent()185 bool TestGLSurface::MakeCurrent() {
186   auto result = ::eglMakeCurrent(display_, onscreen_surface_, onscreen_surface_,
187                                  onscreen_context_);
188 
189   if (result == EGL_FALSE) {
190     FML_LOG(ERROR) << "Could not make the context current. " << GetEGLError();
191   }
192 
193   return result == EGL_TRUE;
194 }
195 
ClearCurrent()196 bool TestGLSurface::ClearCurrent() {
197   auto result = ::eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE,
198                                  EGL_NO_CONTEXT);
199 
200   if (result == EGL_FALSE) {
201     FML_LOG(ERROR) << "Could not clear the current context. " << GetEGLError();
202   }
203 
204   return result == EGL_TRUE;
205 }
206 
Present()207 bool TestGLSurface::Present() {
208   auto result = ::eglSwapBuffers(display_, onscreen_surface_);
209 
210   if (result == EGL_FALSE) {
211     FML_LOG(ERROR) << "Could not swap buffers. " << GetEGLError();
212   }
213 
214   return result == EGL_TRUE;
215 }
216 
GetFramebuffer() const217 uint32_t TestGLSurface::GetFramebuffer() const {
218   // Return FBO0
219   return 0;
220 }
221 
MakeResourceCurrent()222 bool TestGLSurface::MakeResourceCurrent() {
223   auto result = ::eglMakeCurrent(display_, offscreen_surface_,
224                                  offscreen_surface_, offscreen_context_);
225 
226   if (result == EGL_FALSE) {
227     FML_LOG(ERROR) << "Could not make the resource context current. "
228                    << GetEGLError();
229   }
230 
231   return result == EGL_TRUE;
232 }
233 
GetProcAddress(const char * name) const234 void* TestGLSurface::GetProcAddress(const char* name) const {
235   if (name == nullptr) {
236     return nullptr;
237   }
238   auto symbol = ::eglGetProcAddress(name);
239   if (symbol == NULL) {
240     FML_LOG(ERROR) << "Could not fetch symbol for name: " << name;
241   }
242   return reinterpret_cast<void*>(symbol);
243 }
244 
GetGrContext()245 sk_sp<GrContext> TestGLSurface::GetGrContext() {
246   if (context_) {
247     return context_;
248   }
249 
250   context_ = CreateGrContext();
251 
252   return context_;
253 }
254 
CreateGrContext()255 sk_sp<GrContext> TestGLSurface::CreateGrContext() {
256   if (!MakeCurrent()) {
257     return nullptr;
258   }
259 
260   auto get_string =
261       reinterpret_cast<PFNGLGETSTRINGPROC>(GetProcAddress("glGetString"));
262 
263   if (!get_string) {
264     return nullptr;
265   }
266 
267   auto c_version = reinterpret_cast<const char*>(get_string(GL_VERSION));
268 
269   if (c_version == NULL) {
270     return nullptr;
271   }
272 
273   GrGLGetProc get_proc = [](void* context, const char name[]) -> GrGLFuncPtr {
274     return reinterpret_cast<GrGLFuncPtr>(
275         reinterpret_cast<TestGLSurface*>(context)->GetProcAddress(name));
276   };
277 
278   std::string version(c_version);
279   auto interface = version.find("OpenGL ES") == std::string::npos
280                        ? GrGLMakeAssembledGLInterface(this, get_proc)
281                        : GrGLMakeAssembledGLESInterface(this, get_proc);
282 
283   if (!interface) {
284     return nullptr;
285   }
286 
287   context_ = GrContext::MakeGL(interface);
288   return context_;
289 }
290 
GetOnscreenSurface()291 sk_sp<SkSurface> TestGLSurface::GetOnscreenSurface() {
292   GrGLFramebufferInfo framebuffer_info = {};
293   framebuffer_info.fFBOID = GetFramebuffer();
294   framebuffer_info.fFormat = GR_GL_RGBA8;
295 
296   const auto size = GetSize();
297 
298   GrBackendRenderTarget backend_render_target(
299       size.width(),     // width
300       size.height(),    // height
301       1,                // sample count
302       8,                // stencil bits
303       framebuffer_info  // framebuffer info
304   );
305 
306   SkSurfaceProps surface_properties(
307       SkSurfaceProps::InitType::kLegacyFontHost_InitType);
308 
309   auto surface = SkSurface::MakeFromBackendRenderTarget(
310       GetGrContext().get(),         //  context
311       backend_render_target,        // backend render target
312       kBottomLeft_GrSurfaceOrigin,  // surface origin
313       kN32_SkColorType,             // color type
314       SkColorSpace::MakeSRGB(),     // color space
315       &surface_properties,          // surface properties
316       nullptr,                      // release proc
317       nullptr                       // release context
318   );
319 
320   if (!surface) {
321     FML_LOG(ERROR) << "Could not wrap the surface while attempting to "
322                       "snapshot the GL surface.";
323     return nullptr;
324   }
325 
326   return surface;
327 }
328 
GetRasterSurfaceSnapshot()329 sk_sp<SkImage> TestGLSurface::GetRasterSurfaceSnapshot() {
330   auto surface = GetOnscreenSurface();
331 
332   if (!surface) {
333     FML_LOG(ERROR) << "Aborting snapshot because of on-screen surface "
334                       "acquisition failure.";
335     return nullptr;
336   }
337 
338   auto device_snapshot = surface->makeImageSnapshot();
339 
340   if (!device_snapshot) {
341     FML_LOG(ERROR) << "Could not create the device snapshot while attempting "
342                       "to snapshot the GL surface.";
343     return nullptr;
344   }
345 
346   auto host_snapshot = device_snapshot->makeRasterImage();
347 
348   if (!host_snapshot) {
349     FML_LOG(ERROR) << "Could not create the host snapshot while attempting to "
350                       "snapshot the GL surface.";
351     return nullptr;
352   }
353 
354   return host_snapshot;
355 }
356 
357 }  // namespace testing
358 }  // namespace flutter
359