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 "commons/ilog.h"
18 #include "settings/AdvancedSettings.h"
19 #include "settings/SettingsComponent.h"
20 #include "utils/log.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
)
36 m_renderingApi(renderingApi
),
37 m_eglConfig(EGL_NO_CONFIG
),
38 m_eglGetPlatformDisplayEXT(
39 (PFNEGLGETPLATFORMDISPLAYEXTPROC
)eglGetProcAddress("eglGetPlatformDisplayEXT"))
43 const auto settings
= CServiceBroker::GetSettingsComponent();
46 m_omlSync
= settings
->GetAdvancedSettings()->m_omlSync
;
50 CGLContextEGL::~CGLContextEGL()
55 bool CGLContextEGL::Refresh(bool force
, int screen
, Window glWindow
, bool &newContext
)
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());
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
);
81 if (m_eglGetPlatformDisplayEXT
)
85 EGL_PLATFORM_X11_SCREEN_EXT
, screen
,
88 m_eglDisplay
= m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT
,(EGLNativeDisplayType
)m_dpy
,
92 m_eglDisplay
= eglGetDisplay((EGLNativeDisplayType
)m_dpy
);
94 if (m_eglDisplay
== EGL_NO_DISPLAY
)
96 CLog::Log(LOGERROR
, "failed to get egl display");
99 if (!eglInitialize(m_eglDisplay
, NULL
, NULL
))
101 CLog::Log(LOGERROR
, "failed to initialize egl");
105 if (!eglBindAPI(m_renderingApi
))
107 CLog::Log(LOGERROR
, "failed to bind rendering API");
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");
127 vMask
.visualid
= XVisualIDFromVisual(winAttr
.visual
);
128 vInfo
= XGetVisualInfo(m_dpy
, VisualScreenMask
| VisualIDMask
, &vMask
, &availableVisuals
);
131 CLog::Log(LOGERROR
, "Failed to get VisualInfo of visual 0x{:x}", (unsigned)vMask
.visualid
);
136 unsigned int visualid
= static_cast<unsigned int>(vInfo
->visualid
);
137 m_eglConfig
= GetEGLConfig(m_eglDisplay
, vInfo
);
140 if (m_eglConfig
== EGL_NO_CONFIG
)
142 CLog::Log(LOGERROR
, "failed to get suitable eglconfig for visual 0x{:x}", visualid
);
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());
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
,
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,
174 m_eglContext
= eglCreateContext(m_eglDisplay
, m_eglConfig
, EGL_NO_CONTEXT
, contextAttributes
);
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");
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
));
197 m_eglGetSyncValuesCHROMIUM
= (PFNEGLGETSYNCVALUESCHROMIUMPROC
)eglGetProcAddress("eglGetSyncValuesCHROMIUM");
199 if (m_eglUploadContext
== EGL_NO_CONTEXT
)
200 CLog::Log(LOGWARNING
, "failed to create EGL upload context");
206 bool CGLContextEGL::CreatePB()
208 const EGLint configAttribs
[] =
210 EGL_SURFACE_TYPE
, EGL_PBUFFER_BIT
,
215 EGL_RENDERABLE_TYPE
, EGL_OPENGL_BIT
,
219 const EGLint pbufferAttribs
[] =
228 if (m_eglGetPlatformDisplayEXT
)
230 m_eglDisplay
= m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT
,(EGLNativeDisplayType
)m_dpy
,
234 m_eglDisplay
= eglGetDisplay((EGLNativeDisplayType
)m_dpy
);
236 if (m_eglDisplay
== EGL_NO_DISPLAY
)
238 CLog::Log(LOGERROR
, "failed to get egl display");
241 if (!eglInitialize(m_eglDisplay
, NULL
, NULL
))
243 CLog::Log(LOGERROR
, "failed to initialize egl");
247 if (!eglBindAPI(m_renderingApi
))
249 CLog::Log(LOGERROR
, "failed to bind rendering API");
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());
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
,
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,
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");
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
));
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");
307 void CGLContextEGL::Destroy()
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
;
325 eglDestroySurface(m_eglDisplay
, m_eglSurface
);
326 m_eglSurface
= EGL_NO_SURFACE
;
331 eglTerminate(m_eglDisplay
);
332 m_eglDisplay
= EGL_NO_DISPLAY
;
335 m_eglConfig
= EGL_NO_CONFIG
;
338 void CGLContextEGL::Detach()
343 eglMakeCurrent(m_eglDisplay
, EGL_NO_SURFACE
, EGL_NO_SURFACE
, EGL_NO_CONTEXT
);
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
)
358 if (!eglGetConfigAttrib(eglDisplay
, config
, EGL_RED_SIZE
, &value
) || value
< 8)
360 if (!eglGetConfigAttrib(eglDisplay
, config
, EGL_GREEN_SIZE
, &value
) || value
< 8)
362 if (!eglGetConfigAttrib(eglDisplay
, config
, EGL_BLUE_SIZE
, &value
) || value
< 8)
364 if (!eglGetConfigAttrib(eglDisplay
, config
, EGL_DEPTH_SIZE
, &value
) || value
< 16)
370 EGLConfig
CGLContextEGL::GetEGLConfig(EGLDisplay eglDisplay
, XVisualInfo
*vInfo
)
374 if (!eglGetConfigs(eglDisplay
, nullptr, 0, &numConfigs
))
376 CLog::Log(LOGERROR
, "Failed to query number of egl configs");
377 return EGL_NO_CONFIG
;
381 CLog::Log(LOGERROR
, "No suitable egl configs found");
382 return EGL_NO_CONFIG
;
385 EGLConfig
*eglConfigs
;
386 eglConfigs
= (EGLConfig
*)malloc(numConfigs
* sizeof(EGLConfig
));
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");
398 for (EGLint i
= 0; i
< numConfigs
; ++i
)
400 if (!SuitableCheck(eglDisplay
, eglConfigs
[i
]))
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.");
409 if (value
== (EGLint
)vInfo
->visualid
)
411 eglConfig
= eglConfigs
[i
];
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
))
433 eglSwapBuffers(m_eglDisplay
, m_eglSurface
);
441 struct timespec nowTs
;
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
)
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)
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
471 if ((msc1
- m_sync
.msc1
) == 2)
475 else if ((msc1
- m_sync
.msc1
) == 1)
477 interval
= (ust1
- m_sync
.ust1
) / (msc1
- m_sync
.msc1
);
481 else if (m_sync
.cont
== 5 && m_omlSync
)
483 CLog::Log(LOGDEBUG
, "CGLContextEGL::SwapBuffers: sync check blocking");
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
;
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
;
517 std::unique_lock
<CCriticalSection
> lock(m_syncLock
);
522 m_sync
.interval
= interval
;
527 uint64_t CGLContextEGL::GetVblankTiming(uint64_t &msc
, uint64_t &interval
)
529 struct timespec nowTs
;
531 clock_gettime(CLOCK_MONOTONIC
, &nowTs
);
532 now
= static_cast<uint64_t>(nowTs
.tv_sec
) * 1000000000ULL + nowTs
.tv_nsec
;
535 std::unique_lock
<CCriticalSection
> lock(m_syncLock
);
538 interval
= (m_sync
.cont
>= 5) ? m_sync
.interval
: m_sync
.ust2
- m_sync
.ust1
;
542 if (now
< m_sync
.ust2
)
547 uint64_t ret
= now
- m_sync
.ust2
;
548 while (ret
> interval
)
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.");
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.");
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();
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();
593 m_textureUploadLock
.unlock();
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
615 eglQuerySurface(m_eglDisplay
, m_eglSurface
, EGL_BUFFER_AGE_EXT
, &age
);
616 return static_cast<int>(age
);
618 return CGLContext::GetBufferAge();