1 /*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/gl/GrGLDefines.h"
9 #include "src/gpu/gl/GrGLUtil.h"
10 #include "tools/gpu/gl/GLTestContext.h"
11
12 #define GL_GLEXT_PROTOTYPES
13 #include <EGL/egl.h>
14 #include <EGL/eglext.h>
15
16 namespace {
17
context_restorer()18 std::function<void()> context_restorer() {
19 auto display = eglGetCurrentDisplay();
20 auto dsurface = eglGetCurrentSurface(EGL_DRAW);
21 auto rsurface = eglGetCurrentSurface(EGL_READ);
22 auto context = eglGetCurrentContext();
23 return [display, dsurface, rsurface, context] {
24 eglMakeCurrent(display, dsurface, rsurface, context);
25 };
26 }
27
28 class EGLGLTestContext : public sk_gpu_test::GLTestContext {
29 public:
30 EGLGLTestContext(GrGLStandard forcedGpuAPI, EGLGLTestContext* shareContext);
31 ~EGLGLTestContext() override;
32
33 GrEGLImage texture2DToEGLImage(GrGLuint texID) const override;
34 void destroyEGLImage(GrEGLImage) const override;
35 GrGLuint eglImageToExternalTexture(GrEGLImage) const override;
36 std::unique_ptr<sk_gpu_test::GLTestContext> makeNew() const override;
37
38 private:
39 void destroyGLContext();
40
41 void onPlatformMakeNotCurrent() const override;
42 void onPlatformMakeCurrent() const override;
43 std::function<void()> onPlatformGetAutoContextRestore() const override;
44 GrGLFuncPtr onPlatformGetProcAddress(const char*) const override;
45
46 void setupFenceSync(sk_sp<const GrGLInterface>);
47
48 PFNEGLCREATEIMAGEKHRPROC fEglCreateImageProc = nullptr;
49 PFNEGLDESTROYIMAGEKHRPROC fEglDestroyImageProc = nullptr;
50
51 EGLContext fContext;
52 EGLDisplay fDisplay;
53 EGLSurface fSurface;
54 };
55
create_gles_egl_context(EGLDisplay display,EGLConfig surfaceConfig,EGLContext eglShareContext,EGLint eglContextClientVersion)56 static EGLContext create_gles_egl_context(EGLDisplay display,
57 EGLConfig surfaceConfig,
58 EGLContext eglShareContext,
59 EGLint eglContextClientVersion) {
60 const EGLint contextAttribsForOpenGLES[] = {
61 EGL_CONTEXT_CLIENT_VERSION,
62 eglContextClientVersion,
63 EGL_NONE
64 };
65 return eglCreateContext(display, surfaceConfig, eglShareContext, contextAttribsForOpenGLES);
66 }
create_gl_egl_context(EGLDisplay display,EGLConfig surfaceConfig,EGLContext eglShareContext)67 static EGLContext create_gl_egl_context(EGLDisplay display,
68 EGLConfig surfaceConfig,
69 EGLContext eglShareContext) {
70 const EGLint contextAttribsForOpenGL[] = {
71 EGL_NONE
72 };
73 return eglCreateContext(display, surfaceConfig, eglShareContext, contextAttribsForOpenGL);
74 }
75
EGLGLTestContext(GrGLStandard forcedGpuAPI,EGLGLTestContext * shareContext)76 EGLGLTestContext::EGLGLTestContext(GrGLStandard forcedGpuAPI, EGLGLTestContext* shareContext)
77 : fContext(EGL_NO_CONTEXT)
78 , fDisplay(EGL_NO_DISPLAY)
79 , fSurface(EGL_NO_SURFACE) {
80
81 EGLContext eglShareContext = shareContext ? shareContext->fContext : nullptr;
82
83 static const GrGLStandard kStandards[] = {
84 kGL_GrGLStandard,
85 kGLES_GrGLStandard,
86 };
87
88 size_t apiLimit = SK_ARRAY_COUNT(kStandards);
89 size_t api = 0;
90 if (forcedGpuAPI == kGL_GrGLStandard) {
91 apiLimit = 1;
92 } else if (forcedGpuAPI == kGLES_GrGLStandard) {
93 api = 1;
94 }
95 SkASSERT(forcedGpuAPI == kNone_GrGLStandard || kStandards[api] == forcedGpuAPI);
96
97 sk_sp<const GrGLInterface> gl;
98
99 for (; nullptr == gl.get() && api < apiLimit; ++api) {
100 fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
101
102 EGLint majorVersion;
103 EGLint minorVersion;
104 eglInitialize(fDisplay, &majorVersion, &minorVersion);
105
106 #if 0
107 SkDebugf("VENDOR: %s\n", eglQueryString(fDisplay, EGL_VENDOR));
108 SkDebugf("APIS: %s\n", eglQueryString(fDisplay, EGL_CLIENT_APIS));
109 SkDebugf("VERSION: %s\n", eglQueryString(fDisplay, EGL_VERSION));
110 SkDebugf("EXTENSIONS %s\n", eglQueryString(fDisplay, EGL_EXTENSIONS));
111 #endif
112 bool gles = kGLES_GrGLStandard == kStandards[api];
113
114 if (!eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) {
115 continue;
116 }
117
118 EGLint numConfigs = 0;
119 const EGLint configAttribs[] = {
120 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
121 EGL_RENDERABLE_TYPE, gles ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT,
122 EGL_RED_SIZE, 8,
123 EGL_GREEN_SIZE, 8,
124 EGL_BLUE_SIZE, 8,
125 EGL_ALPHA_SIZE, 8,
126 EGL_NONE
127 };
128
129 EGLConfig surfaceConfig;
130 if (!eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs)) {
131 SkDebugf("eglChooseConfig failed. EGL Error: 0x%08x\n", eglGetError());
132 continue;
133 }
134
135 if (0 == numConfigs) {
136 SkDebugf("No suitable EGL config found.\n");
137 continue;
138 }
139
140 if (gles) {
141 #ifdef GR_EGL_TRY_GLES3_THEN_GLES2
142 // Some older devices (Nexus7/Tegra3) crash when you try this. So it is (for now)
143 // hidden behind this flag.
144 fContext = create_gles_egl_context(fDisplay, surfaceConfig, eglShareContext, 3);
145 if (EGL_NO_CONTEXT == fContext) {
146 fContext = create_gles_egl_context(fDisplay, surfaceConfig, eglShareContext, 2);
147 }
148 #else
149 fContext = create_gles_egl_context(fDisplay, surfaceConfig, eglShareContext, 2);
150 #endif
151 } else {
152 fContext = create_gl_egl_context(fDisplay, surfaceConfig, eglShareContext);
153 }
154 if (EGL_NO_CONTEXT == fContext) {
155 SkDebugf("eglCreateContext failed. EGL Error: 0x%08x\n", eglGetError());
156 continue;
157 }
158
159 static const EGLint kSurfaceAttribs[] = {
160 EGL_WIDTH, 1,
161 EGL_HEIGHT, 1,
162 EGL_NONE
163 };
164
165 fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, kSurfaceAttribs);
166 if (EGL_NO_SURFACE == fSurface) {
167 SkDebugf("eglCreatePbufferSurface failed. EGL Error: 0x%08x\n", eglGetError());
168 this->destroyGLContext();
169 continue;
170 }
171
172 SkScopeExit restorer(context_restorer());
173 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
174 SkDebugf("eglMakeCurrent failed. EGL Error: 0x%08x\n", eglGetError());
175 this->destroyGLContext();
176 continue;
177 }
178
179 #ifdef SK_GL
180 gl = GrGLMakeNativeInterface();
181 if (!gl) {
182 SkDebugf("Failed to create gl interface.\n");
183 this->destroyGLContext();
184 continue;
185 }
186
187 this->setupFenceSync(gl);
188
189 if (!gl->validate()) {
190 SkDebugf("Failed to validate gl interface.\n");
191 this->destroyGLContext();
192 continue;
193 }
194 const char* extensions = eglQueryString(fDisplay, EGL_EXTENSIONS);
195 if (strstr(extensions, "EGL_KHR_image")) {
196 fEglCreateImageProc = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR");
197 fEglDestroyImageProc =
198 (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR");
199 }
200
201 this->init(std::move(gl));
202 #else
203 // Allow the GLTestContext creation to succeed without a GrGLInterface to support
204 // GrContextFactory's persistent GL context workaround for Vulkan. We won't need the
205 // GrGLInterface since we're not running the GL backend.
206 this->init(nullptr);
207 #endif
208 break;
209 }
210 }
211
supports_egl_extension(EGLDisplay display,const char * extension)212 static bool supports_egl_extension(EGLDisplay display, const char* extension) {
213 size_t extensionLength = strlen(extension);
214 const char* extensionsStr = eglQueryString(display, EGL_EXTENSIONS);
215 while (const char* match = strstr(extensionsStr, extension)) {
216 // Ensure the string we found is its own extension, not a substring of a larger extension
217 // (e.g. GL_ARB_occlusion_query / GL_ARB_occlusion_query2).
218 if ((match == extensionsStr || match[-1] == ' ') &&
219 (match[extensionLength] == ' ' || match[extensionLength] == '\0')) {
220 return true;
221 }
222 extensionsStr = match + extensionLength;
223 }
224 return false;
225 }
226
setupFenceSync(sk_sp<const GrGLInterface> interface)227 void EGLGLTestContext::setupFenceSync(sk_sp<const GrGLInterface> interface) {
228 GrGLInterface* glInt = const_cast<GrGLInterface*>(interface.get());
229
230
231 if (kGL_GrGLStandard == glInt->fStandard) {
232 if (GrGLGetVersion(glInt) >= GR_GL_VER(3,2) || glInt->hasExtension("GL_ARB_sync")) {
233 return;
234 }
235 } else {
236 if (glInt->hasExtension("GL_APPLE_sync") || glInt->hasExtension("GL_NV_fence") ||
237 GrGLGetVersion(glInt) >= GR_GL_VER(3, 0)) {
238 return;
239 }
240 }
241
242 if (!supports_egl_extension(fDisplay, "EGL_KHR_fence_sync")) {
243 return;
244 }
245
246 auto grEGLCreateSyncKHR = (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR");
247 auto grEGLClientWaitSyncKHR =
248 (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress("eglClientWaitSyncKHR");
249 auto grEGLDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR");
250 auto grEGLGetSyncAttribKHR =
251 (PFNEGLGETSYNCATTRIBKHRPROC) eglGetProcAddress("eglGetSyncAttribKHR");
252 SkASSERT(grEGLCreateSyncKHR && grEGLClientWaitSyncKHR && grEGLDestroySyncKHR &&
253 grEGLGetSyncAttribKHR);
254
255 PFNEGLWAITSYNCKHRPROC grEGLWaitSyncKHR = nullptr;
256 if (supports_egl_extension(fDisplay, "EGL_KHR_wait_sync")) {
257 grEGLWaitSyncKHR = (PFNEGLWAITSYNCKHRPROC)eglGetProcAddress("eglWaitSyncKHR");
258 SkASSERT(grEGLWaitSyncKHR);
259 }
260
261 // Fake out glSync using eglSync
262 glInt->fExtensions.add("GL_APPLE_sync");
263
264 glInt->fFunctions.fFenceSync =
265 [grEGLCreateSyncKHR, display = fDisplay](GrGLenum condition, GrGLbitfield flags) {
266 SkASSERT(condition == GR_GL_SYNC_GPU_COMMANDS_COMPLETE);
267 SkASSERT(flags == 0);
268
269 EGLSyncKHR sync = grEGLCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
270
271 return reinterpret_cast<GrGLsync>(sync);
272 };
273
274 glInt->fFunctions.fDeleteSync = [grEGLDestroySyncKHR, display = fDisplay](GrGLsync sync) {
275 EGLSyncKHR eglSync = reinterpret_cast<EGLSyncKHR>(sync);
276 grEGLDestroySyncKHR(display, eglSync);
277 };
278
279 glInt->fFunctions.fClientWaitSync =
280 [grEGLClientWaitSyncKHR, display = fDisplay] (GrGLsync sync, GrGLbitfield flags,
281 GrGLuint64 timeout) -> GrGLenum {
282 EGLSyncKHR eglSync = reinterpret_cast<EGLSyncKHR>(sync);
283
284 EGLint egl_flags = 0;
285
286 if (flags & GR_GL_SYNC_FLUSH_COMMANDS_BIT) {
287 egl_flags |= EGL_SYNC_FLUSH_COMMANDS_BIT_KHR;
288 }
289
290 EGLint result = grEGLClientWaitSyncKHR(display, eglSync, egl_flags, timeout);
291
292 switch (result) {
293 case EGL_CONDITION_SATISFIED_KHR:
294 return GR_GL_CONDITION_SATISFIED;
295 case EGL_TIMEOUT_EXPIRED_KHR:
296 return GR_GL_TIMEOUT_EXPIRED;
297 case EGL_FALSE:
298 return GR_GL_WAIT_FAILED;
299 }
300 SkUNREACHABLE;
301 };
302
303 glInt->fFunctions.fWaitSync =
304 [grEGLClientWaitSyncKHR, grEGLWaitSyncKHR, display = fDisplay](GrGLsync sync,
305 GrGLbitfield flags,
306 GrGLuint64 timeout) {
307 EGLSyncKHR eglSync = reinterpret_cast<EGLSyncKHR>(sync);
308
309 SkASSERT(timeout == GR_GL_TIMEOUT_IGNORED);
310 SkASSERT(flags == 0);
311
312 if (!grEGLWaitSyncKHR) {
313 grEGLClientWaitSyncKHR(display, eglSync, 0, EGL_FOREVER_KHR);
314 return;
315 }
316
317 SkDEBUGCODE(EGLint result =) grEGLWaitSyncKHR(display, eglSync, 0);
318 SkASSERT(result);
319 };
320
321 glInt->fFunctions.fIsSync =
322 [grEGLGetSyncAttribKHR, display = fDisplay](GrGLsync sync) -> GrGLboolean {
323 EGLSyncKHR eglSync = reinterpret_cast<EGLSyncKHR>(sync);
324 EGLint value;
325 if (grEGLGetSyncAttribKHR(display, eglSync, EGL_SYNC_TYPE_KHR, &value)) {
326 return true;
327 }
328 return false;
329 };
330 }
331
~EGLGLTestContext()332 EGLGLTestContext::~EGLGLTestContext() {
333 this->teardown();
334 this->destroyGLContext();
335 }
336
destroyGLContext()337 void EGLGLTestContext::destroyGLContext() {
338 if (fDisplay) {
339 if (fContext) {
340 if (eglGetCurrentContext() == fContext) {
341 // This will ensure that the context is immediately deleted.
342 eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
343 }
344 eglDestroyContext(fDisplay, fContext);
345 fContext = EGL_NO_CONTEXT;
346 }
347
348 if (fSurface) {
349 eglDestroySurface(fDisplay, fSurface);
350 fSurface = EGL_NO_SURFACE;
351 }
352
353 //TODO should we close the display?
354 fDisplay = EGL_NO_DISPLAY;
355 }
356 }
357
texture2DToEGLImage(GrGLuint texID) const358 GrEGLImage EGLGLTestContext::texture2DToEGLImage(GrGLuint texID) const {
359 #ifdef SK_GL
360 if (!this->gl()->hasExtension("EGL_KHR_gl_texture_2D_image") || !fEglCreateImageProc) {
361 return GR_EGL_NO_IMAGE;
362 }
363 EGLint attribs[] = { GR_EGL_GL_TEXTURE_LEVEL, 0, GR_EGL_NONE };
364 GrEGLClientBuffer clientBuffer = reinterpret_cast<GrEGLClientBuffer>(texID);
365 return fEglCreateImageProc(fDisplay, fContext, GR_EGL_GL_TEXTURE_2D, clientBuffer, attribs);
366 #else
367 (void)fEglCreateImageProc;
368 return nullptr;
369 #endif
370 }
371
destroyEGLImage(GrEGLImage image) const372 void EGLGLTestContext::destroyEGLImage(GrEGLImage image) const {
373 fEglDestroyImageProc(fDisplay, image);
374 }
375
eglImageToExternalTexture(GrEGLImage image) const376 GrGLuint EGLGLTestContext::eglImageToExternalTexture(GrEGLImage image) const {
377 #ifdef SK_GL
378 while (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {}
379 if (!this->gl()->hasExtension("GL_OES_EGL_image_external")) {
380 return 0;
381 }
382 typedef GrGLvoid (*EGLImageTargetTexture2DProc)(GrGLenum, GrGLeglImage);
383
384 EGLImageTargetTexture2DProc glEGLImageTargetTexture2D =
385 (EGLImageTargetTexture2DProc) eglGetProcAddress("glEGLImageTargetTexture2DOES");
386 if (!glEGLImageTargetTexture2D) {
387 return 0;
388 }
389 GrGLuint texID;
390 GR_GL_CALL(this->gl(), GenTextures(1, &texID));
391 if (!texID) {
392 return 0;
393 }
394 GR_GL_CALL_NOERRCHECK(this->gl(), BindTexture(GR_GL_TEXTURE_EXTERNAL, texID));
395 if (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
396 GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
397 return 0;
398 }
399 glEGLImageTargetTexture2D(GR_GL_TEXTURE_EXTERNAL, image);
400 if (this->gl()->fFunctions.fGetError() != GR_GL_NO_ERROR) {
401 GR_GL_CALL(this->gl(), DeleteTextures(1, &texID));
402 return 0;
403 }
404 return texID;
405 #else
406 return 0;
407 #endif
408 }
409
makeNew() const410 std::unique_ptr<sk_gpu_test::GLTestContext> EGLGLTestContext::makeNew() const {
411 std::unique_ptr<sk_gpu_test::GLTestContext> ctx(new EGLGLTestContext(this->gl()->fStandard,
412 nullptr));
413 if (ctx) {
414 ctx->makeCurrent();
415 }
416 return ctx;
417 }
418
onPlatformMakeNotCurrent() const419 void EGLGLTestContext::onPlatformMakeNotCurrent() const {
420 if (!eglMakeCurrent(fDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )) {
421 SkDebugf("Could not reset the context.\n");
422 }
423 }
424
onPlatformMakeCurrent() const425 void EGLGLTestContext::onPlatformMakeCurrent() const {
426 if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
427 SkDebugf("Could not set the context.\n");
428 }
429 }
430
onPlatformGetAutoContextRestore() const431 std::function<void()> EGLGLTestContext::onPlatformGetAutoContextRestore() const {
432 if (eglGetCurrentContext() == fContext) {
433 return nullptr;
434 }
435 return context_restorer();
436 }
437
onPlatformGetProcAddress(const char * procName) const438 GrGLFuncPtr EGLGLTestContext::onPlatformGetProcAddress(const char* procName) const {
439 return eglGetProcAddress(procName);
440 }
441
442 } // anonymous namespace
443
444 namespace sk_gpu_test {
CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,GLTestContext * shareContext)445 GLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
446 GLTestContext *shareContext) {
447 EGLGLTestContext* eglShareContext = reinterpret_cast<EGLGLTestContext*>(shareContext);
448 EGLGLTestContext *ctx = new EGLGLTestContext(forcedGpuAPI, eglShareContext);
449 if (!ctx->isValid()) {
450 delete ctx;
451 return nullptr;
452 }
453 return ctx;
454 }
455 } // namespace sk_gpu_test
456