From c04dcc2e7d834218ef2d4194331e383402495ae1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 20:07:22 +0200 Subject: Adding upstream version 2:20.4+dfsg. Signed-off-by: Daniel Baumann --- xbmc/windowing/X11/GLContextEGL.cpp | 542 ++++++++++++++++++++++++++++++++++++ 1 file changed, 542 insertions(+) create mode 100644 xbmc/windowing/X11/GLContextEGL.cpp (limited to 'xbmc/windowing/X11/GLContextEGL.cpp') diff --git a/xbmc/windowing/X11/GLContextEGL.cpp b/xbmc/windowing/X11/GLContextEGL.cpp new file mode 100644 index 0000000..66de374 --- /dev/null +++ b/xbmc/windowing/X11/GLContextEGL.cpp @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +// always define GL_GLEXT_PROTOTYPES before include gl headers +#if !defined(GL_GLEXT_PROTOTYPES) + #define GL_GLEXT_PROTOTYPES +#endif + +#include "GLContextEGL.h" + +#include "ServiceBroker.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" +#include "utils/log.h" + +#include +#include + +#include +#include + +#include "PlatformDefs.h" +#include "system_gl.h" + +#define EGL_NO_CONFIG (EGLConfig)0 + +CGLContextEGL::CGLContextEGL(Display* dpy, EGLint renderingApi) + : CGLContext(dpy), m_renderingApi(renderingApi) +{ + m_extPrefix = "EGL_"; + m_eglConfig = EGL_NO_CONFIG; + + m_eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + + const auto settings = CServiceBroker::GetSettingsComponent(); + if (settings) + { + m_omlSync = settings->GetAdvancedSettings()->m_omlSync; + } +} + +CGLContextEGL::~CGLContextEGL() +{ + Destroy(); +} + +bool CGLContextEGL::Refresh(bool force, int screen, Window glWindow, bool &newContext) +{ + m_sync.cont = 0; + + // refresh context + if (m_eglContext && !force) + { + if (m_eglSurface == EGL_NO_SURFACE) + { + m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, glWindow, NULL); + if (m_eglSurface == EGL_NO_SURFACE) + { + CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError()); + return false; + } + } + + CLog::Log(LOGDEBUG, "CWinSystemX11::RefreshEGLContext: refreshing context"); + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext); + return true; + } + + Destroy(); + newContext = true; + + if (m_eglGetPlatformDisplayEXT) + { + EGLint attribs[] = + { + EGL_PLATFORM_X11_SCREEN_EXT, screen, + EGL_NONE + }; + m_eglDisplay = m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,(EGLNativeDisplayType)m_dpy, + attribs); + } + else + m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_dpy); + + if (m_eglDisplay == EGL_NO_DISPLAY) + { + CLog::Log(LOGERROR, "failed to get egl display"); + return false; + } + if (!eglInitialize(m_eglDisplay, NULL, NULL)) + { + CLog::Log(LOGERROR, "failed to initialize egl"); + Destroy(); + return false; + } + if (!eglBindAPI(m_renderingApi)) + { + CLog::Log(LOGERROR, "failed to bind rendering API"); + Destroy(); + return false; + } + + // create context + + XVisualInfo vMask; + XVisualInfo *vInfo = nullptr; + int availableVisuals = 0; + vMask.screen = screen; + XWindowAttributes winAttr; + + if (!XGetWindowAttributes(m_dpy, glWindow, &winAttr)) + { + CLog::Log(LOGWARNING, "Failed to get window attributes"); + Destroy(); + return false; + } + + vMask.visualid = XVisualIDFromVisual(winAttr.visual); + vInfo = XGetVisualInfo(m_dpy, VisualScreenMask | VisualIDMask, &vMask, &availableVisuals); + if (!vInfo) + { + CLog::Log(LOGERROR, "Failed to get VisualInfo of visual 0x{:x}", (unsigned)vMask.visualid); + Destroy(); + return false; + } + + unsigned int visualid = static_cast(vInfo->visualid); + m_eglConfig = GetEGLConfig(m_eglDisplay, vInfo); + XFree(vInfo); + + if (m_eglConfig == EGL_NO_CONFIG) + { + CLog::Log(LOGERROR, "failed to get suitable eglconfig for visual 0x{:x}", visualid); + Destroy(); + return false; + } + + CLog::Log(LOGINFO, "Using visual 0x{:x}", visualid); + + m_eglSurface = eglCreateWindowSurface(m_eglDisplay, m_eglConfig, glWindow, NULL); + if (m_eglSurface == EGL_NO_SURFACE) + { + CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError()); + Destroy(); + return false; + } + + EGLint contextAttributes[] = + { + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 2, + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, + EGL_NONE + }; + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes); + if (m_eglContext == EGL_NO_CONTEXT) + { + EGLint contextAttributes[] = + { + EGL_CONTEXT_MAJOR_VERSION_KHR, 2, + EGL_NONE + }; + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes); + + if (m_eglContext == EGL_NO_CONTEXT) + { + CLog::Log(LOGERROR, "failed to create EGL context"); + Destroy(); + return false; + } + + CLog::Log(LOGWARNING, "Failed to get an OpenGL context supporting core profile 3.2, " + "using legacy mode with reduced feature set"); + } + + if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) + { + CLog::Log(LOGERROR, "Failed to make context current {} {} {}", fmt::ptr(m_eglDisplay), + fmt::ptr(m_eglSurface), fmt::ptr(m_eglContext)); + Destroy(); + return false; + } + + m_eglGetSyncValuesCHROMIUM = (PFNEGLGETSYNCVALUESCHROMIUMPROC)eglGetProcAddress("eglGetSyncValuesCHROMIUM"); + + m_usePB = false; + return true; +} + +bool CGLContextEGL::CreatePB() +{ + const EGLint configAttribs[] = + { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_DEPTH_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE + }; + + const EGLint pbufferAttribs[] = + { + EGL_WIDTH, 9, + EGL_HEIGHT, 9, + EGL_NONE, + }; + + Destroy(); + + if (m_eglGetPlatformDisplayEXT) + { + m_eglDisplay = m_eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT,(EGLNativeDisplayType)m_dpy, + NULL); + } + else + m_eglDisplay = eglGetDisplay((EGLNativeDisplayType)m_dpy); + + if (m_eglDisplay == EGL_NO_DISPLAY) + { + CLog::Log(LOGERROR, "failed to get egl display"); + return false; + } + if (!eglInitialize(m_eglDisplay, NULL, NULL)) + { + CLog::Log(LOGERROR, "failed to initialize egl"); + Destroy(); + return false; + } + if (!eglBindAPI(m_renderingApi)) + { + CLog::Log(LOGERROR, "failed to bind rendering API"); + Destroy(); + return false; + } + + EGLint numConfigs; + + eglChooseConfig(m_eglDisplay, configAttribs, &m_eglConfig, 1, &numConfigs); + m_eglSurface = eglCreatePbufferSurface(m_eglDisplay, m_eglConfig, pbufferAttribs); + if (m_eglSurface == EGL_NO_SURFACE) + { + CLog::Log(LOGERROR, "failed to create EGL window surface {}", eglGetError()); + Destroy(); + return false; + } + + EGLint contextAttributes[] = + { + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 2, + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, + EGL_NONE + }; + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes); + if (m_eglContext == EGL_NO_CONTEXT) + { + EGLint contextAttributes[] = + { + EGL_CONTEXT_MAJOR_VERSION_KHR, 2, + EGL_NONE + }; + m_eglContext = eglCreateContext(m_eglDisplay, m_eglConfig, EGL_NO_CONTEXT, contextAttributes); + + if (m_eglContext == EGL_NO_CONTEXT) + { + CLog::Log(LOGERROR, "failed to create EGL context"); + Destroy(); + return false; + } + } + + if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) + { + CLog::Log(LOGERROR, "Failed to make context current {} {} {}", fmt::ptr(m_eglDisplay), + fmt::ptr(m_eglSurface), fmt::ptr(m_eglContext)); + Destroy(); + return false; + } + + m_usePB = true; + return true; +} + +void CGLContextEGL::Destroy() +{ + if (m_eglContext) + { + glFinish(); + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(m_eglDisplay, m_eglContext); + m_eglContext = EGL_NO_CONTEXT; + } + + if (m_eglSurface) + { + eglDestroySurface(m_eglDisplay, m_eglSurface); + m_eglSurface = EGL_NO_SURFACE; + } + + if (m_eglDisplay) + { + eglTerminate(m_eglDisplay); + m_eglDisplay = EGL_NO_DISPLAY; + } + + m_eglConfig = EGL_NO_CONFIG; +} + +void CGLContextEGL::Detach() +{ + if (m_eglContext) + { + glFinish(); + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } + if (m_eglSurface) + { + eglDestroySurface(m_eglDisplay, m_eglSurface); + m_eglSurface = EGL_NO_SURFACE; + } +} + +bool CGLContextEGL::SuitableCheck(EGLDisplay eglDisplay, EGLConfig config) +{ + if (config == EGL_NO_CONFIG) + return false; + + EGLint value; + if (!eglGetConfigAttrib(eglDisplay, config, EGL_RED_SIZE, &value) || value < 8) + return false; + if (!eglGetConfigAttrib(eglDisplay, config, EGL_GREEN_SIZE, &value) || value < 8) + return false; + if (!eglGetConfigAttrib(eglDisplay, config, EGL_BLUE_SIZE, &value) || value < 8) + return false; + if (!eglGetConfigAttrib(eglDisplay, config, EGL_DEPTH_SIZE, &value) || value < 24) + return false; + + return true; +} + +EGLConfig CGLContextEGL::GetEGLConfig(EGLDisplay eglDisplay, XVisualInfo *vInfo) +{ + EGLint numConfigs; + + if (!eglGetConfigs(eglDisplay, nullptr, 0, &numConfigs)) + { + CLog::Log(LOGERROR, "Failed to query number of egl configs"); + return EGL_NO_CONFIG; + } + if (numConfigs == 0) + { + CLog::Log(LOGERROR, "No suitable egl configs found"); + return EGL_NO_CONFIG; + } + + EGLConfig *eglConfigs; + eglConfigs = (EGLConfig*)malloc(numConfigs * sizeof(EGLConfig)); + if (!eglConfigs) + { + CLog::Log(LOGERROR, "eglConfigs malloc failed"); + return EGL_NO_CONFIG; + } + EGLConfig eglConfig = EGL_NO_CONFIG; + if (!eglGetConfigs(eglDisplay, eglConfigs, numConfigs, &numConfigs)) + { + CLog::Log(LOGERROR, "Failed to query egl configs"); + goto Exit; + } + for (EGLint i = 0; i < numConfigs; ++i) + { + if (!SuitableCheck(eglDisplay, eglConfigs[i])) + continue; + + EGLint value; + if (!eglGetConfigAttrib(eglDisplay, eglConfigs[i], EGL_NATIVE_VISUAL_ID, &value)) + { + CLog::Log(LOGERROR, "Failed to query EGL_NATIVE_VISUAL_ID for egl config."); + break; + } + if (value == (EGLint)vInfo->visualid) + { + eglConfig = eglConfigs[i]; + break; + } + } + +Exit: + free(eglConfigs); + return eglConfig; +} + +void CGLContextEGL::SetVSync(bool enable) +{ + eglSwapInterval(m_eglDisplay, enable ? 1 : 0); +} + +void CGLContextEGL::SwapBuffers() +{ + if ((m_eglDisplay == EGL_NO_DISPLAY) || (m_eglSurface == EGL_NO_SURFACE)) + return; + + if (m_usePB) + { + eglSwapBuffers(m_eglDisplay, m_eglSurface); + usleep(20 * 1000); + return; + } + + uint64_t ust1, ust2; + uint64_t msc1, msc2; + uint64_t sbc1, sbc2; + struct timespec nowTs; + uint64_t now; + uint64_t cont = m_sync.cont; + uint64_t interval = m_sync.interval; + + if (m_eglGetSyncValuesCHROMIUM) + { + m_eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust1, &msc1, &sbc1); + } + + eglSwapBuffers(m_eglDisplay, m_eglSurface); + + if (!m_eglGetSyncValuesCHROMIUM) + return; + + clock_gettime(CLOCK_MONOTONIC, &nowTs); + now = static_cast(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec; + + m_eglGetSyncValuesCHROMIUM(m_eglDisplay, m_eglSurface, &ust2, &msc2, &sbc2); + + if ((msc1 - m_sync.msc1) > 2) + { + cont = 0; + } + + // we want to block in SwapBuffers + // if a vertical retrace occurs 5 times in a row outside + // of this function, we take action + if (m_sync.cont < 5) + { + if ((msc1 - m_sync.msc1) == 2) + { + cont = 0; + } + else if ((msc1 - m_sync.msc1) == 1) + { + interval = (ust1 - m_sync.ust1) / (msc1 - m_sync.msc1); + cont++; + } + } + else if (m_sync.cont == 5 && m_omlSync) + { + CLog::Log(LOGDEBUG, "CGLContextEGL::SwapBuffers: sync check blocking"); + + if (msc2 == msc1) + { + // if no vertical retrace has occurred in eglSwapBuffers, + // sleep until next vertical retrace + uint64_t lastIncrement = (now / 1000 - ust2); + if (lastIncrement > m_sync.interval) + { + lastIncrement = m_sync.interval; + CLog::Log(LOGWARNING, "CGLContextEGL::SwapBuffers: last msc time greater than interval"); + } + uint64_t sleeptime = m_sync.interval - lastIncrement; + usleep(sleeptime); + cont++; + msc2++; + CLog::Log(LOGDEBUG, "CGLContextEGL::SwapBuffers: sync sleep: {}", sleeptime); + } + } + else if ((m_sync.cont > 5) && (msc2 == m_sync.msc2)) + { + // sleep until next vertical retrace + // this avoids blocking outside of this function + uint64_t lastIncrement = (now / 1000 - ust2); + if (lastIncrement > m_sync.interval) + { + lastIncrement = m_sync.interval; + CLog::Log(LOGWARNING, "CGLContextEGL::SwapBuffers: last msc time greater than interval (1)"); + } + uint64_t sleeptime = m_sync.interval - lastIncrement; + usleep(sleeptime); + msc2++; + } + { + std::unique_lock lock(m_syncLock); + m_sync.ust1 = ust1; + m_sync.ust2 = ust2; + m_sync.msc1 = msc1; + m_sync.msc2 = msc2; + m_sync.interval = interval; + m_sync.cont = cont; + } +} + +uint64_t CGLContextEGL::GetVblankTiming(uint64_t &msc, uint64_t &interval) +{ + struct timespec nowTs; + uint64_t now; + clock_gettime(CLOCK_MONOTONIC, &nowTs); + now = static_cast(nowTs.tv_sec) * 1000000000ULL + nowTs.tv_nsec; + now /= 1000; + + std::unique_lock lock(m_syncLock); + msc = m_sync.msc2; + + interval = (m_sync.cont >= 5) ? m_sync.interval : m_sync.ust2 - m_sync.ust1; + if (interval == 0) + return 0; + + if (now < m_sync.ust2) + { + return 0; + } + + uint64_t ret = now - m_sync.ust2; + while (ret > interval) + { + ret -= interval; + msc++; + } + + return ret; +} + +void CGLContextEGL::QueryExtensions() +{ + std::string extensions = eglQueryString(m_eglDisplay, EGL_EXTENSIONS); + m_extensions = std::string(" ") + extensions + " "; + + CLog::Log(LOGDEBUG, "EGL_EXTENSIONS:{}", m_extensions); +} -- cgit v1.2.3