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