[filesystem][SpecialProtocol] Removed assert from GetPath
[xbmc.git] / xbmc / windowing / X11 / GLContextEGL.cpp
blob7999e76b982cb94c378cb4eaa00be34c307a04b3
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 "settings/AdvancedSettings.h"
18 #include "settings/SettingsComponent.h"
19 #include "utils/log.h"
21 #include <clocale>
22 #include <mutex>
24 #include <EGL/eglext.h>
25 #include <unistd.h>
27 #include "PlatformDefs.h"
28 #include "system_gl.h"
30 #define EGL_NO_CONFIG (EGLConfig)0
32 CGLContextEGL::CGLContextEGL(Display* dpy, EGLint renderingApi)
33 : CGLContext(dpy),
34 m_renderingApi(renderingApi),
35 m_eglConfig(EGL_NO_CONFIG),
36 m_eglGetPlatformDisplayEXT(
37 (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"))
39 m_extPrefix = "EGL_";
41 const auto settings = CServiceBroker::GetSettingsComponent();
42 if (settings)
44 m_omlSync = settings->GetAdvancedSettings()->m_omlSync;
48 CGLContextEGL::~CGLContextEGL()
50 Destroy();
53 bool CGLContextEGL::Refresh(bool force, int screen, Window glWindow, bool &newContext)
55 m_sync.cont = 0;
57 // refresh context
58 if (m_eglContext && !force)
60 if (m_eglSurface == EGL_NO_SURFACE)
62 m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, glWindow, NULL);
63 if (m_eglSurface == EGL_NO_SURFACE)
65 CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
66 return false;
70 CLog::Log(LOGDEBUG, "CWinSystemX11::RefreshEGLContext: refreshing context");
71 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
72 eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
73 return true;
76 Destroy();
77 newContext = true;
79 if (m_eglGetPlatformDisplayEXT)
81 EGLint attribs[] =
83 EGL_PLATFORM_X11_SCREEN_EXT, screen,
84 EGL_NONE
86 m_eglDisplay = m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,(EGLNativeDisplayType)m_dpy,
87 attribs);
89 else
90 m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_dpy);
92 if (m_eglDisplay == EGL_NO_DISPLAY)
94 CLog::Log(LOGERROR, "failed to get egl display");
95 return false;
97 if (!eglInitialize(m_eglDisplay, NULL, NULL))
99 CLog::Log(LOGERROR, "failed to initialize egl");
100 Destroy();
101 return false;
103 if (!eglBindAPI(m_renderingApi))
105 CLog::Log(LOGERROR, "failed to bind rendering API");
106 Destroy();
107 return false;
110 // create context
112 XVisualInfo vMask;
113 XVisualInfo *vInfo = nullptr;
114 int availableVisuals = 0;
115 vMask.screen = screen;
116 XWindowAttributes winAttr;
118 if (!XGetWindowAttributes(m_dpy, glWindow, &winAttr))
120 CLog::Log(LOGWARNING, "Failed to get window attributes");
121 Destroy();
122 return false;
125 vMask.visualid = XVisualIDFromVisual(winAttr.visual);
126 vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals);
127 if (!vInfo)
129 CLog::Log(LOGERROR, "Failed to get VisualInfo of visual 0x{:x}", (unsigned)vMask.visualid);
130 Destroy();
131 return false;
134 unsigned int visualid = static_cast<unsigned int>(vInfo->visualid);
135 m_eglConfig = GetEGLConfig(m_eglDisplay, vInfo);
136 XFree(vInfo);
138 if (m_eglConfig == EGL_NO_CONFIG)
140 CLog::Log(LOGERROR, "failed to get suitable eglconfig for visual 0x{:x}", visualid);
141 Destroy();
142 return false;
145 CLog::Log(LOGINFO, "Using visual 0x{:x}", visualid);
147 m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, glWindow, NULL);
148 if (m_eglSurface == EGL_NO_SURFACE)
150 CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
151 Destroy();
152 return false;
155 EGLint contextAttributes[] =
157 EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
158 EGL_CONTEXT_MINOR_VERSION_KHR, 2,
159 EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
160 EGL_NONE
162 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
163 if (m_eglContext == EGL_NO_CONTEXT)
165 EGLint contextAttributes[] =
167 EGL_CONTEXT_MAJOR_VERSION_KHR, 2,
168 EGL_NONE
170 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
172 if (m_eglContext == EGL_NO_CONTEXT)
174 CLog::Log(LOGERROR, "failed to create EGL context");
175 Destroy();
176 return false;
179 CLog::Log(LOGWARNING, "Failed to get an OpenGL context supporting core profile 3.2, "
180 "using legacy mode with reduced feature set");
183 if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
185 CLog::Log(LOGERROR, "Failed to make context current {} {} {}", fmt::ptr(m_eglDisplay),
186 fmt::ptr(m_eglSurface), fmt::ptr(m_eglContext));
187 Destroy();
188 return false;
191 m_eglGetSyncValuesCHROMIUM = (PFNEGLGETSYNCVALUESCHROMIUMPROC)eglGetProcAddress("eglGetSyncValuesCHROMIUM");
193 m_usePB = false;
194 return true;
197 bool CGLContextEGL::CreatePB()
199 const EGLint configAttribs[] =
201 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
202 EGL_BLUE_SIZE, 8,
203 EGL_GREEN_SIZE, 8,
204 EGL_RED_SIZE, 8,
205 EGL_DEPTH_SIZE, 8,
206 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
207 EGL_NONE
210 const EGLint pbufferAttribs[] =
212 EGL_WIDTH, 9,
213 EGL_HEIGHT, 9,
214 EGL_NONE,
217 Destroy();
219 if (m_eglGetPlatformDisplayEXT)
221 m_eglDisplay = m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,(EGLNativeDisplayType)m_dpy,
222 NULL);
224 else
225 m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_dpy);
227 if (m_eglDisplay == EGL_NO_DISPLAY)
229 CLog::Log(LOGERROR, "failed to get egl display");
230 return false;
232 if (!eglInitialize(m_eglDisplay, NULL, NULL))
234 CLog::Log(LOGERROR, "failed to initialize egl");
235 Destroy();
236 return false;
238 if (!eglBindAPI(m_renderingApi))
240 CLog::Log(LOGERROR, "failed to bind rendering API");
241 Destroy();
242 return false;
245 EGLint numConfigs;
247 eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig, 1, &numConfigs);
248 m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufferAttribs);
249 if (m_eglSurface == EGL_NO_SURFACE)
251 CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError());
252 Destroy();
253 return false;
256 EGLint contextAttributes[] =
258 EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
259 EGL_CONTEXT_MINOR_VERSION_KHR, 2,
260 EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR,
261 EGL_NONE
263 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
264 if (m_eglContext == EGL_NO_CONTEXT)
266 EGLint contextAttributes[] =
268 EGL_CONTEXT_MAJOR_VERSION_KHR, 2,
269 EGL_NONE
271 m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes);
273 if (m_eglContext == EGL_NO_CONTEXT)
275 CLog::Log(LOGERROR, "failed to create EGL context");
276 Destroy();
277 return false;
281 if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
283 CLog::Log(LOGERROR, "Failed to make context current {} {} {}", fmt::ptr(m_eglDisplay),
284 fmt::ptr(m_eglSurface), fmt::ptr(m_eglContext));
285 Destroy();
286 return false;
289 m_usePB = true;
290 return true;
293 void CGLContextEGL::Destroy()
295 if (m_eglContext)
297 glFinish();
298 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
299 eglDestroyContext(m_eglDisplay, m_eglContext);
300 m_eglContext = EGL_NO_CONTEXT;
303 if (m_eglSurface)
305 eglDestroySurface(m_eglDisplay, m_eglSurface);
306 m_eglSurface = EGL_NO_SURFACE;
309 if (m_eglDisplay)
311 eglTerminate(m_eglDisplay);
312 m_eglDisplay = EGL_NO_DISPLAY;
315 m_eglConfig = EGL_NO_CONFIG;
318 void CGLContextEGL::Detach()
320 if (m_eglContext)
322 glFinish();
323 eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
325 if (m_eglSurface)
327 eglDestroySurface(m_eglDisplay, m_eglSurface);
328 m_eglSurface = EGL_NO_SURFACE;
332 bool CGLContextEGL::SuitableCheck(EGLDisplay eglDisplay, EGLConfig config)
334 if (config == EGL_NO_CONFIG)
335 return false;
337 EGLint value;
338 if (!eglGetConfigAttrib(eglDisplay, config, EGL_RED_SIZE, &value) || value < 8)
339 return false;
340 if (!eglGetConfigAttrib(eglDisplay, config, EGL_GREEN_SIZE, &value) || value < 8)
341 return false;
342 if (!eglGetConfigAttrib(eglDisplay, config, EGL_BLUE_SIZE, &value) || value < 8)
343 return false;
344 if (!eglGetConfigAttrib(eglDisplay, config, EGL_DEPTH_SIZE, &value) || value < 24)
345 return false;
347 return true;
350 EGLConfig CGLContextEGL::GetEGLConfig(EGLDisplay eglDisplay, XVisualInfo *vInfo)
352 EGLint numConfigs;
354 if (!eglGetConfigs(eglDisplay, nullptr, 0, &numConfigs))
356 CLog::Log(LOGERROR, "Failed to query number of egl configs");
357 return EGL_NO_CONFIG;
359 if (numConfigs == 0)
361 CLog::Log(LOGERROR, "No suitable egl configs found");
362 return EGL_NO_CONFIG;
365 EGLConfig *eglConfigs;
366 eglConfigs = (EGLConfig*)malloc(numConfigs * sizeof(EGLConfig));
367 if (!eglConfigs)
369 CLog::Log(LOGERROR, "eglConfigs malloc failed");
370 return EGL_NO_CONFIG;
372 EGLConfig eglConfig = EGL_NO_CONFIG;
373 if (!eglGetConfigs(eglDisplay, eglConfigs, numConfigs, &numConfigs))
375 CLog::Log(LOGERROR, "Failed to query egl configs");
376 goto Exit;
378 for (EGLint i = 0; i < numConfigs; ++i)
380 if (!SuitableCheck(eglDisplay, eglConfigs[i]))
381 continue;
383 EGLint value;
384 if (!eglGetConfigAttrib(eglDisplay, eglConfigs[i], EGL_NATIVE_VISUAL_ID, &value))
386 CLog::Log(LOGERROR, "Failed to query EGL_NATIVE_VISUAL_ID for egl config.");
387 break;
389 if (value == (EGLint)vInfo->visualid)
391 eglConfig = eglConfigs[i];
392 break;
396 Exit:
397 free(eglConfigs);
398 return eglConfig;
401 void CGLContextEGL::SetVSync(bool enable)
403 eglSwapInterval(m_eglDisplay, enable ? 1 : 0);
406 void CGLContextEGL::SwapBuffers()
408 if ((m_eglDisplay == EGL_NO_DISPLAY) || (m_eglSurface == EGL_NO_SURFACE))
409 return;
411 if (m_usePB)
413 eglSwapBuffers(m_eglDisplay, m_eglSurface);
414 usleep(20 * 1000);
415 return;
418 uint64_t ust1, ust2;
419 uint64_t msc1, msc2;
420 uint64_t sbc1, sbc2;
421 struct timespec nowTs;
422 uint64_t now;
423 uint64_t cont = m_sync.cont;
424 uint64_t interval = m_sync.interval;
426 if (m_eglGetSyncValuesCHROMIUM)
428 m_eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust1, &msc1, &sbc1);
431 eglSwapBuffers(m_eglDisplay, m_eglSurface);
433 if (!m_eglGetSyncValuesCHROMIUM)
434 return;
436 clock_gettime(CLOCK_MONOTONIC, &nowTs);
437 now = static_cast<uint64_t>(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec;
439 m_eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust2, &msc2, &sbc2);
441 if ((msc1 - m_sync.msc1) > 2)
443 cont = 0;
446 // we want to block in SwapBuffers
447 // if a vertical retrace occurs 5 times in a row outside
448 // of this function, we take action
449 if (m_sync.cont < 5)
451 if ((msc1 - m_sync.msc1) == 2)
453 cont = 0;
455 else if ((msc1 - m_sync.msc1) == 1)
457 interval = (ust1 - m_sync.ust1) / (msc1 - m_sync.msc1);
458 cont++;
461 else if (m_sync.cont == 5 && m_omlSync)
463 CLog::Log(LOGDEBUG, "CGLContextEGL::SwapBuffers: sync check blocking");
465 if (msc2 == msc1)
467 // if no vertical retrace has occurred in eglSwapBuffers,
468 // sleep until next vertical retrace
469 uint64_t lastIncrement = (now / 1000 - ust2);
470 if (lastIncrement > m_sync.interval)
472 lastIncrement = m_sync.interval;
473 CLog::Log(LOGWARNING, "CGLContextEGL::SwapBuffers: last msc time greater than interval");
475 uint64_t sleeptime = m_sync.interval - lastIncrement;
476 usleep(sleeptime);
477 cont++;
478 msc2++;
479 CLog::Log(LOGDEBUG, "CGLContextEGL::SwapBuffers: sync sleep: {}", sleeptime);
482 else if ((m_sync.cont > 5) && (msc2 == m_sync.msc2))
484 // sleep until next vertical retrace
485 // this avoids blocking outside of this function
486 uint64_t lastIncrement = (now / 1000 - ust2);
487 if (lastIncrement > m_sync.interval)
489 lastIncrement = m_sync.interval;
490 CLog::Log(LOGWARNING, "CGLContextEGL::SwapBuffers: last msc time greater than interval (1)");
492 uint64_t sleeptime = m_sync.interval - lastIncrement;
493 usleep(sleeptime);
494 msc2++;
497 std::unique_lock<CCriticalSection> lock(m_syncLock);
498 m_sync.ust1 = ust1;
499 m_sync.ust2 = ust2;
500 m_sync.msc1 = msc1;
501 m_sync.msc2 = msc2;
502 m_sync.interval = interval;
503 m_sync.cont = cont;
507 uint64_t CGLContextEGL::GetVblankTiming(uint64_t &msc, uint64_t &interval)
509 struct timespec nowTs;
510 uint64_t now;
511 clock_gettime(CLOCK_MONOTONIC, &nowTs);
512 now = static_cast<uint64_t>(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec;
513 now /= 1000;
515 std::unique_lock<CCriticalSection> lock(m_syncLock);
516 msc = m_sync.msc2;
518 interval = (m_sync.cont >= 5) ? m_sync.interval : m_sync.ust2 - m_sync.ust1;
519 if (interval == 0)
520 return 0;
522 if (now < m_sync.ust2)
524 return 0;
527 uint64_t ret = now - m_sync.ust2;
528 while (ret > interval)
530 ret -= interval;
531 msc++;
534 return ret;
537 void CGLContextEGL::QueryExtensions()
539 std::string extensions = eglQueryString(m_eglDisplay, EGL_EXTENSIONS);
540 m_extensions = std::string(" ") + extensions + " ";
542 CLog::Log(LOGDEBUG, "EGL_EXTENSIONS:{}", m_extensions);