Merge pull request #26312 from garbear/update-controllers
[xbmc.git] / xbmc / windowing / X11 / GLContextEGL.cpp
blob4031270db8ec5bd6893818117142aed4a10c63a5
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 // always define GL_GLEXT_PROTOTYPES before include gl headers
10 #if !defined(GL_GLEXT_PROTOTYPES)
11 #define GL_GLEXT_PROTOTYPES
12 #endif
14 #include "GLContextEGL.h"
16 #include "ServiceBroker.h"
17 #include "commons/ilog.h"
18 #include "settings/AdvancedSettings.h"
19 #include "settings/SettingsComponent.h"
20 #include "utils/log.h"
22 #include <clocale>
23 #include <mutex>
25 #include <unistd.h>
27 #include "PlatformDefs.h"
28 #include "system_gl.h"
30 #include <EGL/eglext.h>
32 #define EGL_NO_CONFIG (EGLConfig)0
34 CGLContextEGL::CGLContextEGL(Display* dpy, EGLint renderingApi)
35 : CGLContext(dpy),
36 m_renderingApi(renderingApi),
37 m_eglConfig(EGL_NO_CONFIG),
38 m_eglGetPlatformDisplayEXT(
39 (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"))
41 m_extPrefix = "EGL_";
43 const auto settings = CServiceBroker::GetSettingsComponent();
44 if (settings)
46 m_omlSync = settings->GetAdvancedSettings()->m_omlSync;
50 CGLContextEGL::~CGLContextEGL()
52 Destroy();
55 bool CGLContextEGL::Refresh(bool force, int screen, Window glWindow, bool &newContext)
57 m_sync.cont = 0;
59 // refresh context
60 if (m_eglContext && !force)
62 if (m_eglSurface == EGL_NO_SURFACE)
64 m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, glWindow, NULL);
65 if (m_eglSurface == EGL_NO_SURFACE)
67 CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
68 return false;
72 CLog::Log(LOGDEBUG, "CWinSystemX11::RefreshEGLContext: refreshing context");
73 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
74 eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
75 return true;
78 Destroy();
79 newContext = true;
81 if (m_eglGetPlatformDisplayEXT)
83 EGLint attribs[] =
85 EGL_PLATFORM_X11_SCREEN_EXT, screen,
86 EGL_NONE
88 m_eglDisplay = m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,(EGLNativeDisplayType)m_dpy,
89 attribs);
91 else
92 m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_dpy);
94 if (m_eglDisplay == EGL_NO_DISPLAY)
96 CLog::Log(LOGERROR, "failed to get egl display");
97 return false;
99 if (!eglInitialize(m_eglDisplay, NULL, NULL))
101 CLog::Log(LOGERROR, "failed to initialize egl");
102 Destroy();
103 return false;
105 if (!eglBindAPI(m_renderingApi))
107 CLog::Log(LOGERROR, "failed to bind rendering API");
108 Destroy();
109 return false;
112 // create context
114 XVisualInfo vMask;
115 XVisualInfo *vInfo = nullptr;
116 int availableVisuals = 0;
117 vMask.screen = screen;
118 XWindowAttributes winAttr;
120 if (!XGetWindowAttributes(m_dpy, glWindow, &winAttr))
122 CLog::Log(LOGWARNING, "Failed to get window attributes");
123 Destroy();
124 return false;
127 vMask.visualid = XVisualIDFromVisual(winAttr.visual);
128 vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals);
129 if (!vInfo)
131 CLog::Log(LOGERROR, "Failed to get VisualInfo of visual 0x{:x}", (unsigned)vMask.visualid);
132 Destroy();
133 return false;
136 unsigned int visualid = static_cast<unsigned int>(vInfo->visualid);
137 m_eglConfig = GetEGLConfig(m_eglDisplay, vInfo);
138 XFree(vInfo);
140 if (m_eglConfig == EGL_NO_CONFIG)
142 CLog::Log(LOGERROR, "failed to get suitable eglconfig for visual 0x{:x}", visualid);
143 Destroy();
144 return false;
147 CLog::Log(LOGINFO, "Using visual 0x{:x}", visualid);
149 m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, glWindow, NULL);
150 if (m_eglSurface == EGL_NO_SURFACE)
152 CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
153 Destroy();
154 return false;
157 EGLint contextAttributes[] =
159 EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
160 EGL_CONTEXT_MINOR_VERSION_KHR, 2,
161 EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
162 EGL_NONE
164 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
165 m_eglUploadContext = eglCreateContext(m_eglDisplay, m_eglConfig, m_eglContext, contextAttributes);
167 if (m_eglContext == EGL_NO_CONTEXT)
169 EGLint contextAttributes[] =
171 EGL_CONTEXT_MAJOR_VERSION_KHR, 2,
172 EGL_NONE
174 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
175 m_eglUploadContext =
176 eglCreateContext(m_eglDisplay, m_eglConfig, m_eglContext, contextAttributes);
178 if (m_eglContext == EGL_NO_CONTEXT)
180 CLog::Log(LOGERROR, "failed to create EGL context");
181 Destroy();
182 return false;
185 CLog::Log(LOGWARNING, "Failed to get an OpenGL context supporting core profile 3.2, "
186 "using legacy mode with reduced feature set");
189 if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
191 CLog::Log(LOGERROR, "Failed to make context current {} {} {}", fmt::ptr(m_eglDisplay),
192 fmt::ptr(m_eglSurface), fmt::ptr(m_eglContext));
193 Destroy();
194 return false;
197 m_eglGetSyncValuesCHROMIUM = (PFNEGLGETSYNCVALUESCHROMIUMPROC)eglGetProcAddress("eglGetSyncValuesCHROMIUM");
199 if (m_eglUploadContext == EGL_NO_CONTEXT)
200 CLog::Log(LOGWARNING, "failed to create EGL upload context");
202 m_usePB = false;
203 return true;
206 bool CGLContextEGL::CreatePB()
208 const EGLint configAttribs[] =
210 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
211 EGL_BLUE_SIZE, 8,
212 EGL_GREEN_SIZE, 8,
213 EGL_RED_SIZE, 8,
214 EGL_DEPTH_SIZE, 8,
215 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
216 EGL_NONE
219 const EGLint pbufferAttribs[] =
221 EGL_WIDTH, 9,
222 EGL_HEIGHT, 9,
223 EGL_NONE,
226 Destroy();
228 if (m_eglGetPlatformDisplayEXT)
230 m_eglDisplay = m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,(EGLNativeDisplayType)m_dpy,
231 NULL);
233 else
234 m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_dpy);
236 if (m_eglDisplay == EGL_NO_DISPLAY)
238 CLog::Log(LOGERROR, "failed to get egl display");
239 return false;
241 if (!eglInitialize(m_eglDisplay, NULL, NULL))
243 CLog::Log(LOGERROR, "failed to initialize egl");
244 Destroy();
245 return false;
247 if (!eglBindAPI(m_renderingApi))
249 CLog::Log(LOGERROR, "failed to bind rendering API");
250 Destroy();
251 return false;
254 EGLint numConfigs;
256 eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig, 1, &numConfigs);
257 m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufferAttribs);
258 if (m_eglSurface == EGL_NO_SURFACE)
260 CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
261 Destroy();
262 return false;
265 EGLint contextAttributes[] =
267 EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
268 EGL_CONTEXT_MINOR_VERSION_KHR, 2,
269 EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
270 EGL_NONE
272 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
273 if (m_eglContext == EGL_NO_CONTEXT)
275 EGLint contextAttributes[] =
277 EGL_CONTEXT_MAJOR_VERSION_KHR, 2,
278 EGL_NONE
280 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
282 if (m_eglContext == EGL_NO_CONTEXT)
284 CLog::Log(LOGERROR, "failed to create EGL context");
285 Destroy();
286 return false;
290 if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
292 CLog::Log(LOGERROR, "Failed to make context current {} {} {}", fmt::ptr(m_eglDisplay),
293 fmt::ptr(m_eglSurface), fmt::ptr(m_eglContext));
294 Destroy();
295 return false;
298 m_eglUploadContext = eglCreateContext(m_eglDisplay, m_eglConfig, m_eglContext, contextAttributes);
300 if (m_eglUploadContext == EGL_NO_CONTEXT)
301 CLog::Log(LOGWARNING, "Failed to create EGL upload context");
303 m_usePB = true;
304 return true;
307 void CGLContextEGL::Destroy()
309 if (m_eglContext)
311 glFinish();
312 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
313 eglDestroyContext(m_eglDisplay, m_eglContext);
314 m_eglContext = EGL_NO_CONTEXT;
317 if (m_eglUploadContext)
319 eglDestroyContext(m_eglDisplay, m_eglUploadContext);
320 m_eglUploadContext = EGL_NO_CONTEXT;
323 if (m_eglSurface)
325 eglDestroySurface(m_eglDisplay, m_eglSurface);
326 m_eglSurface = EGL_NO_SURFACE;
329 if (m_eglDisplay)
331 eglTerminate(m_eglDisplay);
332 m_eglDisplay = EGL_NO_DISPLAY;
335 m_eglConfig = EGL_NO_CONFIG;
338 void CGLContextEGL::Detach()
340 if (m_eglContext)
342 glFinish();
343 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
345 if (m_eglSurface)
347 eglDestroySurface(m_eglDisplay, m_eglSurface);
348 m_eglSurface = EGL_NO_SURFACE;
352 bool CGLContextEGL::SuitableCheck(EGLDisplay eglDisplay, EGLConfig config)
354 if (config == EGL_NO_CONFIG)
355 return false;
357 EGLint value;
358 if (!eglGetConfigAttrib(eglDisplay, config, EGL_RED_SIZE, &value) || value < 8)
359 return false;
360 if (!eglGetConfigAttrib(eglDisplay, config, EGL_GREEN_SIZE, &value) || value < 8)
361 return false;
362 if (!eglGetConfigAttrib(eglDisplay, config, EGL_BLUE_SIZE, &value) || value < 8)
363 return false;
364 if (!eglGetConfigAttrib(eglDisplay, config, EGL_DEPTH_SIZE, &value) || value < 16)
365 return false;
367 return true;
370 EGLConfig CGLContextEGL::GetEGLConfig(EGLDisplay eglDisplay, XVisualInfo *vInfo)
372 EGLint numConfigs;
374 if (!eglGetConfigs(eglDisplay, nullptr, 0, &numConfigs))
376 CLog::Log(LOGERROR, "Failed to query number of egl configs");
377 return EGL_NO_CONFIG;
379 if (numConfigs == 0)
381 CLog::Log(LOGERROR, "No suitable egl configs found");
382 return EGL_NO_CONFIG;
385 EGLConfig *eglConfigs;
386 eglConfigs = (EGLConfig*)malloc(numConfigs * sizeof(EGLConfig));
387 if (!eglConfigs)
389 CLog::Log(LOGERROR, "eglConfigs malloc failed");
390 return EGL_NO_CONFIG;
392 EGLConfig eglConfig = EGL_NO_CONFIG;
393 if (!eglGetConfigs(eglDisplay, eglConfigs, numConfigs, &numConfigs))
395 CLog::Log(LOGERROR, "Failed to query egl configs");
396 goto Exit;
398 for (EGLint i = 0; i < numConfigs; ++i)
400 if (!SuitableCheck(eglDisplay, eglConfigs[i]))
401 continue;
403 EGLint value;
404 if (!eglGetConfigAttrib(eglDisplay, eglConfigs[i], EGL_NATIVE_VISUAL_ID, &value))
406 CLog::Log(LOGERROR, "Failed to query EGL_NATIVE_VISUAL_ID for egl config.");
407 break;
409 if (value == (EGLint)vInfo->visualid)
411 eglConfig = eglConfigs[i];
412 break;
416 Exit:
417 free(eglConfigs);
418 return eglConfig;
421 void CGLContextEGL::SetVSync(bool enable)
423 eglSwapInterval(m_eglDisplay, enable ? 1 : 0);
426 void CGLContextEGL::SwapBuffers()
428 if ((m_eglDisplay == EGL_NO_DISPLAY) || (m_eglSurface == EGL_NO_SURFACE))
429 return;
431 if (m_usePB)
433 eglSwapBuffers(m_eglDisplay, m_eglSurface);
434 usleep(20 * 1000);
435 return;
438 uint64_t ust1, ust2;
439 uint64_t msc1, msc2;
440 uint64_t sbc1, sbc2;
441 struct timespec nowTs;
442 uint64_t now;
443 uint64_t cont = m_sync.cont;
444 uint64_t interval = m_sync.interval;
446 if (m_eglGetSyncValuesCHROMIUM)
448 m_eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust1, &msc1, &sbc1);
451 eglSwapBuffers(m_eglDisplay, m_eglSurface);
453 if (!m_eglGetSyncValuesCHROMIUM)
454 return;
456 clock_gettime(CLOCK_MONOTONIC, &nowTs);
457 now = static_cast<uint64_t>(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec;
459 m_eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust2, &msc2, &sbc2);
461 if ((msc1 - m_sync.msc1) > 2)
463 cont = 0;
466 // we want to block in SwapBuffers
467 // if a vertical retrace occurs 5 times in a row outside
468 // of this function, we take action
469 if (m_sync.cont < 5)
471 if ((msc1 - m_sync.msc1) == 2)
473 cont = 0;
475 else if ((msc1 - m_sync.msc1) == 1)
477 interval = (ust1 - m_sync.ust1) / (msc1 - m_sync.msc1);
478 cont++;
481 else if (m_sync.cont == 5 && m_omlSync)
483 CLog::Log(LOGDEBUG, "CGLContextEGL::SwapBuffers: sync check blocking");
485 if (msc2 == msc1)
487 // if no vertical retrace has occurred in eglSwapBuffers,
488 // sleep until next vertical retrace
489 uint64_t lastIncrement = (now / 1000 - ust2);
490 if (lastIncrement > m_sync.interval)
492 lastIncrement = m_sync.interval;
493 CLog::Log(LOGWARNING, "CGLContextEGL::SwapBuffers: last msc time greater than interval");
495 uint64_t sleeptime = m_sync.interval - lastIncrement;
496 usleep(sleeptime);
497 cont++;
498 msc2++;
499 CLog::Log(LOGDEBUG, "CGLContextEGL::SwapBuffers: sync sleep: {}", sleeptime);
502 else if ((m_sync.cont > 5) && (msc2 == m_sync.msc2))
504 // sleep until next vertical retrace
505 // this avoids blocking outside of this function
506 uint64_t lastIncrement = (now / 1000 - ust2);
507 if (lastIncrement > m_sync.interval)
509 lastIncrement = m_sync.interval;
510 CLog::Log(LOGWARNING, "CGLContextEGL::SwapBuffers: last msc time greater than interval (1)");
512 uint64_t sleeptime = m_sync.interval - lastIncrement;
513 usleep(sleeptime);
514 msc2++;
517 std::unique_lock<CCriticalSection> lock(m_syncLock);
518 m_sync.ust1 = ust1;
519 m_sync.ust2 = ust2;
520 m_sync.msc1 = msc1;
521 m_sync.msc2 = msc2;
522 m_sync.interval = interval;
523 m_sync.cont = cont;
527 uint64_t CGLContextEGL::GetVblankTiming(uint64_t &msc, uint64_t &interval)
529 struct timespec nowTs;
530 uint64_t now;
531 clock_gettime(CLOCK_MONOTONIC, &nowTs);
532 now = static_cast<uint64_t>(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec;
533 now /= 1000;
535 std::unique_lock<CCriticalSection> lock(m_syncLock);
536 msc = m_sync.msc2;
538 interval = (m_sync.cont >= 5) ? m_sync.interval : m_sync.ust2 - m_sync.ust1;
539 if (interval == 0)
540 return 0;
542 if (now < m_sync.ust2)
544 return 0;
547 uint64_t ret = now - m_sync.ust2;
548 while (ret > interval)
550 ret -= interval;
551 msc++;
554 return ret;
557 bool CGLContextEGL::BindTextureUploadContext()
559 if (m_eglDisplay == EGL_NO_DISPLAY || m_eglUploadContext == EGL_NO_CONTEXT)
561 CLog::LogF(LOGERROR, "No texture upload context found.");
562 return false;
565 m_textureUploadLock.lock();
567 if (!eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, m_eglUploadContext))
569 m_textureUploadLock.unlock();
570 CLog::LogF(LOGERROR, "Couldn't bind texture upload context.");
571 return false;
574 return true;
577 bool CGLContextEGL::UnbindTextureUploadContext()
579 if (m_eglDisplay == EGL_NO_DISPLAY || m_eglUploadContext == EGL_NO_CONTEXT)
581 CLog::LogF(LOGERROR, "No texture upload context found.");
582 m_textureUploadLock.unlock();
583 return false;
586 if (!eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
588 CLog::LogF(LOGERROR, "Couldn't release texture upload context");
589 m_textureUploadLock.unlock();
590 return false;
593 m_textureUploadLock.unlock();
595 return true;
598 bool CGLContextEGL::HasContext()
600 return eglGetCurrentContext() != EGL_NO_CONTEXT;
603 void CGLContextEGL::QueryExtensions()
605 std::string extensions = eglQueryString(m_eglDisplay, EGL_EXTENSIONS);
606 m_extensions = std::string(" ") + extensions + " ";
608 CLog::Log(LOGDEBUG, "EGL_EXTENSIONS:{}", m_extensions);
611 int CGLContextEGL::GetBufferAge()
613 #ifdef EGL_BUFFER_AGE_EXT
614 EGLint age;
615 eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_EXT, &age);
616 return static_cast<int>(age);
617 #else
618 return CGLContext::GetBufferAge();
619 #endif