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.
9 // always define GL_GLEXT_PROTOTYPES before include gl headers
10 #if !defined(GL_GLEXT_PROTOTYPES)
11 #define GL_GLEXT_PROTOTYPES
14 #include "GLContextEGL.h"
16 #include "ServiceBroker.h"
17 #include "settings/AdvancedSettings.h"
18 #include "settings/SettingsComponent.h"
19 #include "utils/log.h"
24 #include <EGL/eglext.h>
27 #include "PlatformDefs.h"
28 #include "system_gl.h"
30 #define EGL_NO_CONFIG (EGLConfig)0
32 CGLContextEGL::CGLContextEGL(Display
* dpy
, EGLint renderingApi
)
34 m_renderingApi(renderingApi
),
35 m_eglConfig(EGL_NO_CONFIG
),
36 m_eglGetPlatformDisplayEXT(
37 (PFNEGLGETPLATFORMDISPLAYEXTPROC
)eglGetProcAddress("eglGetPlatformDisplayEXT"))
41 const auto settings
= CServiceBroker::GetSettingsComponent();
44 m_omlSync
= settings
->GetAdvancedSettings()->m_omlSync
;
48 CGLContextEGL::~CGLContextEGL()
53 bool CGLContextEGL::Refresh(bool force
, int screen
, Window glWindow
, bool &newContext
)
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());
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
);
79 if (m_eglGetPlatformDisplayEXT
)
83 EGL_PLATFORM_X11_SCREEN_EXT
, screen
,
86 m_eglDisplay
= m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT
,(EGLNativeDisplayType
)m_dpy
,
90 m_eglDisplay
= eglGetDisplay((EGLNativeDisplayType
)m_dpy
);
92 if (m_eglDisplay
== EGL_NO_DISPLAY
)
94 CLog::Log(LOGERROR
, "failed to get egl display");
97 if (!eglInitialize(m_eglDisplay
, NULL
, NULL
))
99 CLog::Log(LOGERROR
, "failed to initialize egl");
103 if (!eglBindAPI(m_renderingApi
))
105 CLog::Log(LOGERROR
, "failed to bind rendering API");
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");
125 vMask
.visualid
= XVisualIDFromVisual(winAttr
.visual
);
126 vInfo
= XGetVisualInfo(m_dpy
, VisualScreenMask
| VisualIDMask
, &vMask
, &availableVisuals
);
129 CLog::Log(LOGERROR
, "Failed to get VisualInfo of visual 0x{:x}", (unsigned)vMask
.visualid
);
134 unsigned int visualid
= static_cast<unsigned int>(vInfo
->visualid
);
135 m_eglConfig
= GetEGLConfig(m_eglDisplay
, vInfo
);
138 if (m_eglConfig
== EGL_NO_CONFIG
)
140 CLog::Log(LOGERROR
, "failed to get suitable eglconfig for visual 0x{:x}", visualid
);
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());
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
,
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,
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");
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
));
191 m_eglGetSyncValuesCHROMIUM
= (PFNEGLGETSYNCVALUESCHROMIUMPROC
)eglGetProcAddress("eglGetSyncValuesCHROMIUM");
197 bool CGLContextEGL::CreatePB()
199 const EGLint configAttribs
[] =
201 EGL_SURFACE_TYPE
, EGL_PBUFFER_BIT
,
206 EGL_RENDERABLE_TYPE
, EGL_OPENGL_BIT
,
210 const EGLint pbufferAttribs
[] =
219 if (m_eglGetPlatformDisplayEXT
)
221 m_eglDisplay
= m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT
,(EGLNativeDisplayType
)m_dpy
,
225 m_eglDisplay
= eglGetDisplay((EGLNativeDisplayType
)m_dpy
);
227 if (m_eglDisplay
== EGL_NO_DISPLAY
)
229 CLog::Log(LOGERROR
, "failed to get egl display");
232 if (!eglInitialize(m_eglDisplay
, NULL
, NULL
))
234 CLog::Log(LOGERROR
, "failed to initialize egl");
238 if (!eglBindAPI(m_renderingApi
))
240 CLog::Log(LOGERROR
, "failed to bind rendering API");
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());
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
,
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,
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");
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
));
293 void CGLContextEGL::Destroy()
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
;
305 eglDestroySurface(m_eglDisplay
, m_eglSurface
);
306 m_eglSurface
= EGL_NO_SURFACE
;
311 eglTerminate(m_eglDisplay
);
312 m_eglDisplay
= EGL_NO_DISPLAY
;
315 m_eglConfig
= EGL_NO_CONFIG
;
318 void CGLContextEGL::Detach()
323 eglMakeCurrent(m_eglDisplay
, EGL_NO_SURFACE
, EGL_NO_SURFACE
, EGL_NO_CONTEXT
);
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
)
338 if (!eglGetConfigAttrib(eglDisplay
, config
, EGL_RED_SIZE
, &value
) || value
< 8)
340 if (!eglGetConfigAttrib(eglDisplay
, config
, EGL_GREEN_SIZE
, &value
) || value
< 8)
342 if (!eglGetConfigAttrib(eglDisplay
, config
, EGL_BLUE_SIZE
, &value
) || value
< 8)
344 if (!eglGetConfigAttrib(eglDisplay
, config
, EGL_DEPTH_SIZE
, &value
) || value
< 24)
350 EGLConfig
CGLContextEGL::GetEGLConfig(EGLDisplay eglDisplay
, XVisualInfo
*vInfo
)
354 if (!eglGetConfigs(eglDisplay
, nullptr, 0, &numConfigs
))
356 CLog::Log(LOGERROR
, "Failed to query number of egl configs");
357 return EGL_NO_CONFIG
;
361 CLog::Log(LOGERROR
, "No suitable egl configs found");
362 return EGL_NO_CONFIG
;
365 EGLConfig
*eglConfigs
;
366 eglConfigs
= (EGLConfig
*)malloc(numConfigs
* sizeof(EGLConfig
));
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");
378 for (EGLint i
= 0; i
< numConfigs
; ++i
)
380 if (!SuitableCheck(eglDisplay
, eglConfigs
[i
]))
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.");
389 if (value
== (EGLint
)vInfo
->visualid
)
391 eglConfig
= eglConfigs
[i
];
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
))
413 eglSwapBuffers(m_eglDisplay
, m_eglSurface
);
421 struct timespec nowTs
;
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
)
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)
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
451 if ((msc1
- m_sync
.msc1
) == 2)
455 else if ((msc1
- m_sync
.msc1
) == 1)
457 interval
= (ust1
- m_sync
.ust1
) / (msc1
- m_sync
.msc1
);
461 else if (m_sync
.cont
== 5 && m_omlSync
)
463 CLog::Log(LOGDEBUG
, "CGLContextEGL::SwapBuffers: sync check blocking");
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
;
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
;
497 std::unique_lock
<CCriticalSection
> lock(m_syncLock
);
502 m_sync
.interval
= interval
;
507 uint64_t CGLContextEGL::GetVblankTiming(uint64_t &msc
, uint64_t &interval
)
509 struct timespec nowTs
;
511 clock_gettime(CLOCK_MONOTONIC
, &nowTs
);
512 now
= static_cast<uint64_t>(nowTs
.tv_sec
) * 1000000000ULL + nowTs
.tv_nsec
;
515 std::unique_lock
<CCriticalSection
> lock(m_syncLock
);
518 interval
= (m_sync
.cont
>= 5) ? m_sync
.interval
: m_sync
.ust2
- m_sync
.ust1
;
522 if (now
< m_sync
.ust2
)
527 uint64_t ret
= now
- m_sync
.ust2
;
528 while (ret
> interval
)
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
);