1 | /* |
2 | * Copyright (C) 2012 Igalia, S.L. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
17 | */ |
18 | |
19 | #include "config.h" |
20 | #include "GLContextEGL.h" |
21 | |
22 | #if USE(EGL) |
23 | |
24 | #include "GraphicsContext3D.h" |
25 | #include "PlatformDisplay.h" |
26 | |
27 | #if USE(LIBEPOXY) |
28 | #include "EpoxyEGL.h" |
29 | #else |
30 | #include <EGL/egl.h> |
31 | #include <EGL/eglext.h> |
32 | #endif |
33 | |
34 | #if USE(CAIRO) |
35 | #include <cairo.h> |
36 | #endif |
37 | |
38 | #if USE(LIBEPOXY) |
39 | #include <epoxy/gl.h> |
40 | #elif USE(OPENGL_ES) |
41 | #include <GLES2/gl2.h> |
42 | #include <GLES2/gl2ext.h> |
43 | #else |
44 | #include "OpenGLShims.h" |
45 | #endif |
46 | |
47 | #if ENABLE(ACCELERATED_2D_CANVAS) |
48 | // cairo-gl.h includes some definitions from GLX that conflict with |
49 | // the ones provided by us. Since GLContextEGL doesn't use any GLX |
50 | // functions we can safely disable them. |
51 | #undef CAIRO_HAS_GLX_FUNCTIONS |
52 | #include <cairo-gl.h> |
53 | #endif |
54 | |
55 | #include <wtf/Vector.h> |
56 | |
57 | namespace WebCore { |
58 | |
59 | #if USE(OPENGL_ES) |
60 | static const EGLenum gEGLAPIVersion = EGL_OPENGL_ES_API; |
61 | #else |
62 | static const EGLenum gEGLAPIVersion = EGL_OPENGL_API; |
63 | #endif |
64 | |
65 | const char* GLContextEGL::errorString(int statusCode) |
66 | { |
67 | static_assert(sizeof(int) >= sizeof(EGLint), "EGLint must not be wider than int" ); |
68 | switch (statusCode) { |
69 | #define CASE_RETURN_STRING(name) case name: return #name |
70 | // https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglGetError.xhtml |
71 | CASE_RETURN_STRING(EGL_SUCCESS); |
72 | CASE_RETURN_STRING(EGL_NOT_INITIALIZED); |
73 | CASE_RETURN_STRING(EGL_BAD_ACCESS); |
74 | CASE_RETURN_STRING(EGL_BAD_ALLOC); |
75 | CASE_RETURN_STRING(EGL_BAD_ATTRIBUTE); |
76 | CASE_RETURN_STRING(EGL_BAD_CONTEXT); |
77 | CASE_RETURN_STRING(EGL_BAD_CONFIG); |
78 | CASE_RETURN_STRING(EGL_BAD_CURRENT_SURFACE); |
79 | CASE_RETURN_STRING(EGL_BAD_DISPLAY); |
80 | CASE_RETURN_STRING(EGL_BAD_SURFACE); |
81 | CASE_RETURN_STRING(EGL_BAD_MATCH); |
82 | CASE_RETURN_STRING(EGL_BAD_PARAMETER); |
83 | CASE_RETURN_STRING(EGL_BAD_NATIVE_PIXMAP); |
84 | CASE_RETURN_STRING(EGL_BAD_NATIVE_WINDOW); |
85 | CASE_RETURN_STRING(EGL_CONTEXT_LOST); |
86 | #undef CASE_RETURN_STRING |
87 | default: return "Unknown EGL error" ; |
88 | } |
89 | } |
90 | |
91 | const char* GLContextEGL::lastErrorString() |
92 | { |
93 | return errorString(eglGetError()); |
94 | } |
95 | |
96 | bool GLContextEGL::getEGLConfig(EGLDisplay display, EGLConfig* config, EGLSurfaceType surfaceType) |
97 | { |
98 | std::array<EGLint, 4> rgbaSize = { 8, 8, 8, 8 }; |
99 | if (const char* environmentVariable = getenv("WEBKIT_EGL_PIXEL_LAYOUT" )) { |
100 | if (!strcmp(environmentVariable, "RGB565" )) |
101 | rgbaSize = { 5, 6, 5, 0 }; |
102 | else |
103 | WTFLogAlways("Unknown pixel layout %s, falling back to RGBA8888" , environmentVariable); |
104 | } |
105 | |
106 | EGLint attributeList[] = { |
107 | #if USE(OPENGL_ES) |
108 | EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
109 | #else |
110 | EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, |
111 | #endif |
112 | EGL_RED_SIZE, rgbaSize[0], |
113 | EGL_GREEN_SIZE, rgbaSize[1], |
114 | EGL_BLUE_SIZE, rgbaSize[2], |
115 | EGL_ALPHA_SIZE, rgbaSize[3], |
116 | EGL_STENCIL_SIZE, 8, |
117 | EGL_SURFACE_TYPE, EGL_NONE, |
118 | EGL_NONE |
119 | }; |
120 | |
121 | switch (surfaceType) { |
122 | case GLContextEGL::PbufferSurface: |
123 | attributeList[13] = EGL_PBUFFER_BIT; |
124 | break; |
125 | case GLContextEGL::PixmapSurface: |
126 | attributeList[13] = EGL_PIXMAP_BIT; |
127 | break; |
128 | case GLContextEGL::WindowSurface: |
129 | case GLContextEGL::Surfaceless: |
130 | attributeList[13] = EGL_WINDOW_BIT; |
131 | break; |
132 | } |
133 | |
134 | EGLint count; |
135 | if (!eglChooseConfig(display, attributeList, nullptr, 0, &count)) |
136 | return false; |
137 | |
138 | EGLint numberConfigsReturned; |
139 | Vector<EGLConfig> configs(count); |
140 | if (!eglChooseConfig(display, attributeList, reinterpret_cast<EGLConfig*>(configs.data()), count, &numberConfigsReturned) || !numberConfigsReturned) |
141 | return false; |
142 | |
143 | auto index = configs.findMatching([&](EGLConfig value) { |
144 | EGLint redSize, greenSize, blueSize, alphaSize; |
145 | eglGetConfigAttrib(display, value, EGL_RED_SIZE, &redSize); |
146 | eglGetConfigAttrib(display, value, EGL_GREEN_SIZE, &greenSize); |
147 | eglGetConfigAttrib(display, value, EGL_BLUE_SIZE, &blueSize); |
148 | eglGetConfigAttrib(display, value, EGL_ALPHA_SIZE, &alphaSize); |
149 | return redSize == rgbaSize[0] && greenSize == rgbaSize[1] |
150 | && blueSize == rgbaSize[2] && alphaSize == rgbaSize[3]; |
151 | }); |
152 | |
153 | if (index != notFound) { |
154 | *config = configs[index]; |
155 | return true; |
156 | } |
157 | return false; |
158 | } |
159 | |
160 | std::unique_ptr<GLContextEGL> GLContextEGL::createWindowContext(GLNativeWindowType window, PlatformDisplay& platformDisplay, EGLContext sharingContext) |
161 | { |
162 | EGLDisplay display = platformDisplay.eglDisplay(); |
163 | EGLConfig config; |
164 | if (!getEGLConfig(display, &config, WindowSurface)) { |
165 | WTFLogAlways("Cannot obtain EGL window context configuration: %s\n" , lastErrorString()); |
166 | return nullptr; |
167 | } |
168 | |
169 | EGLContext context = createContextForEGLVersion(platformDisplay, config, sharingContext); |
170 | if (context == EGL_NO_CONTEXT) { |
171 | WTFLogAlways("Cannot create EGL window context: %s\n" , lastErrorString()); |
172 | return nullptr; |
173 | } |
174 | |
175 | EGLSurface surface = EGL_NO_SURFACE; |
176 | #if PLATFORM(GTK) |
177 | #if PLATFORM(X11) |
178 | if (platformDisplay.type() == PlatformDisplay::Type::X11) |
179 | surface = createWindowSurfaceX11(display, config, window); |
180 | #endif |
181 | #if PLATFORM(WAYLAND) |
182 | if (platformDisplay.type() == PlatformDisplay::Type::Wayland) |
183 | surface = createWindowSurfaceWayland(display, config, window); |
184 | #endif |
185 | #elif PLATFORM(WPE) |
186 | if (platformDisplay.type() == PlatformDisplay::Type::WPE) |
187 | surface = createWindowSurfaceWPE(display, config, window); |
188 | #else |
189 | surface = eglCreateWindowSurface(display, config, static_cast<EGLNativeWindowType>(window), nullptr); |
190 | #endif |
191 | if (surface == EGL_NO_SURFACE) { |
192 | WTFLogAlways("Cannot create EGL window surface: %s\n" , lastErrorString()); |
193 | eglDestroyContext(display, context); |
194 | return nullptr; |
195 | } |
196 | |
197 | return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, WindowSurface)); |
198 | } |
199 | |
200 | std::unique_ptr<GLContextEGL> GLContextEGL::createPbufferContext(PlatformDisplay& platformDisplay, EGLContext sharingContext) |
201 | { |
202 | EGLDisplay display = platformDisplay.eglDisplay(); |
203 | EGLConfig config; |
204 | if (!getEGLConfig(display, &config, PbufferSurface)) { |
205 | WTFLogAlways("Cannot obtain EGL Pbuffer configuration: %s\n" , lastErrorString()); |
206 | return nullptr; |
207 | } |
208 | |
209 | EGLContext context = createContextForEGLVersion(platformDisplay, config, sharingContext); |
210 | if (context == EGL_NO_CONTEXT) { |
211 | WTFLogAlways("Cannot create EGL Pbuffer context: %s\n" , lastErrorString()); |
212 | return nullptr; |
213 | } |
214 | |
215 | static const int pbufferAttributes[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; |
216 | EGLSurface surface = eglCreatePbufferSurface(display, config, pbufferAttributes); |
217 | if (surface == EGL_NO_SURFACE) { |
218 | WTFLogAlways("Cannot create EGL Pbuffer surface: %s\n" , lastErrorString()); |
219 | eglDestroyContext(display, context); |
220 | return nullptr; |
221 | } |
222 | |
223 | return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, surface, PbufferSurface)); |
224 | } |
225 | |
226 | std::unique_ptr<GLContextEGL> GLContextEGL::createSurfacelessContext(PlatformDisplay& platformDisplay, EGLContext sharingContext) |
227 | { |
228 | EGLDisplay display = platformDisplay.eglDisplay(); |
229 | if (display == EGL_NO_DISPLAY) { |
230 | WTFLogAlways("Cannot create surfaceless EGL context: invalid display (last error: %s)\n" , lastErrorString()); |
231 | return nullptr; |
232 | } |
233 | |
234 | const char* extensions = eglQueryString(display, EGL_EXTENSIONS); |
235 | if (!GLContext::isExtensionSupported(extensions, "EGL_KHR_surfaceless_context" ) && !GLContext::isExtensionSupported(extensions, "EGL_KHR_surfaceless_opengl" )) |
236 | return nullptr; |
237 | |
238 | EGLConfig config; |
239 | if (!getEGLConfig(display, &config, Surfaceless)) { |
240 | WTFLogAlways("Cannot obtain EGL surfaceless configuration: %s\n" , lastErrorString()); |
241 | return nullptr; |
242 | } |
243 | |
244 | EGLContext context = createContextForEGLVersion(platformDisplay, config, sharingContext); |
245 | if (context == EGL_NO_CONTEXT) { |
246 | WTFLogAlways("Cannot create EGL surfaceless context: %s\n" , lastErrorString()); |
247 | return nullptr; |
248 | } |
249 | |
250 | return std::unique_ptr<GLContextEGL>(new GLContextEGL(platformDisplay, context, EGL_NO_SURFACE, Surfaceless)); |
251 | } |
252 | |
253 | std::unique_ptr<GLContextEGL> GLContextEGL::createContext(GLNativeWindowType window, PlatformDisplay& platformDisplay) |
254 | { |
255 | if (platformDisplay.eglDisplay() == EGL_NO_DISPLAY) { |
256 | WTFLogAlways("Cannot create EGL context: invalid display (last error: %s)\n" , lastErrorString()); |
257 | return nullptr; |
258 | } |
259 | |
260 | if (eglBindAPI(gEGLAPIVersion) == EGL_FALSE) { |
261 | #if USE(OPENGL_ES) |
262 | WTFLogAlways("Cannot create EGL context: error binding OpenGL ES API (%s)\n" , lastErrorString()); |
263 | #else |
264 | WTFLogAlways("Cannot create EGL context: error binding OpenGL API (%s)\n" , lastErrorString()); |
265 | #endif |
266 | return nullptr; |
267 | } |
268 | |
269 | EGLContext eglSharingContext = platformDisplay.sharingGLContext() ? static_cast<GLContextEGL*>(platformDisplay.sharingGLContext())->m_context : EGL_NO_CONTEXT; |
270 | auto context = window ? createWindowContext(window, platformDisplay, eglSharingContext) : nullptr; |
271 | if (!context) |
272 | context = createSurfacelessContext(platformDisplay, eglSharingContext); |
273 | if (!context) { |
274 | #if PLATFORM(X11) |
275 | if (platformDisplay.type() == PlatformDisplay::Type::X11) |
276 | context = createPixmapContext(platformDisplay, eglSharingContext); |
277 | #endif |
278 | #if PLATFORM(WAYLAND) |
279 | if (platformDisplay.type() == PlatformDisplay::Type::Wayland) |
280 | context = createWaylandContext(platformDisplay, eglSharingContext); |
281 | #endif |
282 | #if PLATFORM(WPE) |
283 | if (platformDisplay.type() == PlatformDisplay::Type::WPE) |
284 | context = createWPEContext(platformDisplay, eglSharingContext); |
285 | #endif |
286 | } |
287 | if (!context) |
288 | context = createPbufferContext(platformDisplay, eglSharingContext); |
289 | |
290 | return context; |
291 | } |
292 | |
293 | std::unique_ptr<GLContextEGL> GLContextEGL::createSharingContext(PlatformDisplay& platformDisplay) |
294 | { |
295 | if (platformDisplay.eglDisplay() == EGL_NO_DISPLAY) { |
296 | WTFLogAlways("Cannot create EGL sharing context: invalid display (last error: %s)" , lastErrorString()); |
297 | return nullptr; |
298 | } |
299 | |
300 | if (eglBindAPI(gEGLAPIVersion) == EGL_FALSE) { |
301 | #if USE(OPENGL_ES) |
302 | WTFLogAlways("Cannot create EGL sharing context: error binding OpenGL ES API (%s)\n" , lastErrorString()); |
303 | #else |
304 | WTFLogAlways("Cannot create EGL sharing context: error binding OpenGL API (%s)\n" , lastErrorString()); |
305 | #endif |
306 | return nullptr; |
307 | } |
308 | |
309 | auto context = createSurfacelessContext(platformDisplay); |
310 | if (!context) { |
311 | #if PLATFORM(X11) |
312 | if (platformDisplay.type() == PlatformDisplay::Type::X11) |
313 | context = createPixmapContext(platformDisplay); |
314 | #endif |
315 | #if PLATFORM(WAYLAND) |
316 | if (platformDisplay.type() == PlatformDisplay::Type::Wayland) |
317 | context = createWaylandContext(platformDisplay); |
318 | #endif |
319 | #if PLATFORM(WPE) |
320 | if (platformDisplay.type() == PlatformDisplay::Type::WPE) |
321 | context = createWPEContext(platformDisplay); |
322 | #endif |
323 | } |
324 | if (!context) |
325 | context = createPbufferContext(platformDisplay); |
326 | |
327 | return context; |
328 | } |
329 | |
330 | GLContextEGL::GLContextEGL(PlatformDisplay& display, EGLContext context, EGLSurface surface, EGLSurfaceType type) |
331 | : GLContext(display) |
332 | , m_context(context) |
333 | , m_surface(surface) |
334 | , m_type(type) |
335 | { |
336 | ASSERT(type != PixmapSurface); |
337 | ASSERT(type == Surfaceless || surface != EGL_NO_SURFACE); |
338 | RELEASE_ASSERT(m_display.eglDisplay() != EGL_NO_DISPLAY); |
339 | RELEASE_ASSERT(context != EGL_NO_CONTEXT); |
340 | } |
341 | |
342 | GLContextEGL::~GLContextEGL() |
343 | { |
344 | #if USE(CAIRO) |
345 | if (m_cairoDevice) |
346 | cairo_device_destroy(m_cairoDevice); |
347 | #endif |
348 | |
349 | EGLDisplay display = m_display.eglDisplay(); |
350 | if (m_context) { |
351 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
352 | eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
353 | eglDestroyContext(display, m_context); |
354 | } |
355 | |
356 | if (m_surface) |
357 | eglDestroySurface(display, m_surface); |
358 | |
359 | #if PLATFORM(WAYLAND) |
360 | destroyWaylandWindow(); |
361 | #endif |
362 | #if PLATFORM(WPE) |
363 | destroyWPETarget(); |
364 | #endif |
365 | } |
366 | |
367 | bool GLContextEGL::canRenderToDefaultFramebuffer() |
368 | { |
369 | return m_type == WindowSurface; |
370 | } |
371 | |
372 | IntSize GLContextEGL::defaultFrameBufferSize() |
373 | { |
374 | if (!canRenderToDefaultFramebuffer()) |
375 | return IntSize(); |
376 | |
377 | EGLDisplay display = m_display.eglDisplay(); |
378 | EGLint width, height; |
379 | if (!eglQuerySurface(display, m_surface, EGL_WIDTH, &width) |
380 | || !eglQuerySurface(display, m_surface, EGL_HEIGHT, &height)) |
381 | return IntSize(); |
382 | |
383 | return IntSize(width, height); |
384 | } |
385 | |
386 | EGLContext GLContextEGL::createContextForEGLVersion(PlatformDisplay& platformDisplay, EGLConfig config, EGLContext sharingContext) |
387 | { |
388 | static EGLint contextAttributes[7]; |
389 | static bool contextAttributesInitialized = false; |
390 | |
391 | if (!contextAttributesInitialized) { |
392 | contextAttributesInitialized = true; |
393 | |
394 | #if USE(OPENGL_ES) |
395 | // GLES case. Not much to do here besides requesting a GLES2 version. |
396 | contextAttributes[0] = EGL_CONTEXT_CLIENT_VERSION; |
397 | contextAttributes[1] = 2; |
398 | contextAttributes[2] = EGL_NONE; |
399 | #else |
400 | // OpenGL case. We want to request an OpenGL version >= 3.2 with a core profile. If that's not possible, |
401 | // we'll use whatever is available. In order to request a concrete version of OpenGL we need EGL version |
402 | // 1.5 or EGL version 1.4 with the extension EGL_KHR_create_context. |
403 | EGLContext context = EGL_NO_CONTEXT; |
404 | |
405 | if (platformDisplay.eglCheckVersion(1, 5)) { |
406 | contextAttributes[0] = EGL_CONTEXT_MAJOR_VERSION; |
407 | contextAttributes[1] = 3; |
408 | contextAttributes[2] = EGL_CONTEXT_MINOR_VERSION; |
409 | contextAttributes[3] = 2; |
410 | contextAttributes[4] = EGL_CONTEXT_OPENGL_PROFILE_MASK; |
411 | contextAttributes[5] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT; |
412 | contextAttributes[6] = EGL_NONE; |
413 | |
414 | // Try to create a context with this configuration. |
415 | context = eglCreateContext(platformDisplay.eglDisplay(), config, sharingContext, contextAttributes); |
416 | } else if (platformDisplay.eglCheckVersion(1, 4)) { |
417 | const char* extensions = eglQueryString(platformDisplay.eglDisplay(), EGL_EXTENSIONS); |
418 | if (GLContext::isExtensionSupported(extensions, "EGL_KHR_create_context" )) { |
419 | contextAttributes[0] = EGL_CONTEXT_MAJOR_VERSION_KHR; |
420 | contextAttributes[1] = 3; |
421 | contextAttributes[2] = EGL_CONTEXT_MINOR_VERSION_KHR; |
422 | contextAttributes[3] = 2; |
423 | contextAttributes[4] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; |
424 | contextAttributes[5] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; |
425 | contextAttributes[6] = EGL_NONE; |
426 | |
427 | // Try to create a context with this configuration. |
428 | context = eglCreateContext(platformDisplay.eglDisplay(), config, sharingContext, contextAttributes); |
429 | } |
430 | } |
431 | |
432 | // If the context creation worked, just return it. |
433 | if (context != EGL_NO_CONTEXT) |
434 | return context; |
435 | |
436 | // Legacy case: the required EGL version is not present, or we haven't been able to create a >= 3.2 OpenGL |
437 | // context, so just request whatever is available. |
438 | contextAttributes[0] = EGL_NONE; |
439 | #endif |
440 | } |
441 | |
442 | return eglCreateContext(platformDisplay.eglDisplay(), config, sharingContext, contextAttributes); |
443 | } |
444 | |
445 | bool GLContextEGL::makeContextCurrent() |
446 | { |
447 | ASSERT(m_context); |
448 | |
449 | GLContext::makeContextCurrent(); |
450 | if (eglGetCurrentContext() == m_context) |
451 | return true; |
452 | |
453 | return eglMakeCurrent(m_display.eglDisplay(), m_surface, m_surface, m_context); |
454 | } |
455 | |
456 | void GLContextEGL::swapBuffers() |
457 | { |
458 | ASSERT(m_surface); |
459 | eglSwapBuffers(m_display.eglDisplay(), m_surface); |
460 | } |
461 | |
462 | void GLContextEGL::waitNative() |
463 | { |
464 | eglWaitNative(EGL_CORE_NATIVE_ENGINE); |
465 | } |
466 | |
467 | void GLContextEGL::swapInterval(int interval) |
468 | { |
469 | ASSERT(m_surface); |
470 | eglSwapInterval(m_display.eglDisplay(), interval); |
471 | } |
472 | |
473 | #if USE(CAIRO) |
474 | cairo_device_t* GLContextEGL::cairoDevice() |
475 | { |
476 | if (m_cairoDevice) |
477 | return m_cairoDevice; |
478 | |
479 | #if ENABLE(ACCELERATED_2D_CANVAS) |
480 | m_cairoDevice = cairo_egl_device_create(m_display.eglDisplay(), m_context); |
481 | #endif |
482 | |
483 | return m_cairoDevice; |
484 | } |
485 | #endif |
486 | |
487 | #if ENABLE(GRAPHICS_CONTEXT_3D) |
488 | PlatformGraphicsContext3D GLContextEGL::platformContext() |
489 | { |
490 | return m_context; |
491 | } |
492 | #endif |
493 | |
494 | } // namespace WebCore |
495 | |
496 | #endif // USE(EGL) |
497 | |