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