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 #include "VideoSyncGLX.h"
11 #include "cores/VideoPlayer/VideoReferenceClock.h"
12 #include "utils/TimeUtils.h"
13 #include "utils/XTimeUtils.h"
14 #include "utils/log.h"
15 #include "windowing/GraphicContext.h"
16 #include "windowing/X11/WinSystemX11GLContext.h"
21 #include <X11/extensions/Xrandr.h>
23 using namespace KODI::WINDOWING::X11
;
25 using namespace std::chrono_literals
;
27 Display
* CVideoSyncGLX::m_Dpy
= NULL
;
29 void CVideoSyncGLX::OnLostDisplay()
38 void CVideoSyncGLX::OnResetDisplay()
40 m_displayReset
= true;
43 bool CVideoSyncGLX::Setup()
45 std::unique_lock
<CCriticalSection
> lock(m_winSystem
.GetGfxContext());
47 m_glXWaitVideoSyncSGI
= NULL
;
48 m_glXGetVideoSyncSGI
= NULL
;
53 int singleBufferAttributes
[] = {
63 XSetWindowAttributes Swa
;
69 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Setting up GLX");
71 static_cast<CWinSystemX11
*>(&m_winSystem
)->Register(this);
73 m_displayLost
= false;
74 m_displayReset
= false;
79 m_Dpy
= XOpenDisplay(NULL
);
82 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Unable to open display");
87 if (!glXQueryExtension(m_Dpy
, NULL
, NULL
))
89 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: X server does not support GLX");
93 bool ExtensionFound
= false;
94 std::istringstream
Extensions(glXQueryExtensionsString(m_Dpy
, m_winSystem
.GetScreen()));
95 std::string ExtensionStr
;
97 while (!ExtensionFound
)
99 Extensions
>> ExtensionStr
;
100 if (Extensions
.fail())
103 if (ExtensionStr
== "GLX_SGI_video_sync")
104 ExtensionFound
= true;
109 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: X server does not support GLX_SGI_video_sync");
113 m_vInfo
= glXChooseVisual(m_Dpy
, m_winSystem
.GetScreen(), singleBufferAttributes
);
116 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXChooseVisual returned NULL");
120 Swa
.border_pixel
= 0;
121 Swa
.event_mask
= StructureNotifyMask
;
122 Swa
.colormap
= XCreateColormap(m_Dpy
, m_winSystem
.GetWindow(), m_vInfo
->visual
, AllocNone
);
123 SwaMask
= CWBorderPixel
| CWColormap
| CWEventMask
;
125 m_Window
= XCreateWindow(m_Dpy
, m_winSystem
.GetWindow(), 0, 0, 256, 256, 0,
126 m_vInfo
->depth
, InputOutput
, m_vInfo
->visual
, SwaMask
, &Swa
);
128 m_Context
= glXCreateContext(m_Dpy
, m_vInfo
, NULL
, True
);
131 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXCreateContext returned NULL");
135 ReturnV
= glXMakeCurrent(m_Dpy
, m_Window
, m_Context
);
138 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXMakeCurrent returned {}", ReturnV
);
142 m_glXWaitVideoSyncSGI
= (int (*)(int, int, unsigned int*))glXGetProcAddress((const GLubyte
*)"glXWaitVideoSyncSGI");
143 if (!m_glXWaitVideoSyncSGI
)
145 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXWaitVideoSyncSGI not found");
149 ReturnV
= m_glXWaitVideoSyncSGI(2, 0, &GlxTest
);
152 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXWaitVideoSyncSGI returned {}", ReturnV
);
156 m_glXGetVideoSyncSGI
= (int (*)(unsigned int*))glXGetProcAddress((const GLubyte
*)"glXGetVideoSyncSGI");
157 if (!m_glXGetVideoSyncSGI
)
159 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXGetVideoSyncSGI not found");
163 ReturnV
= m_glXGetVideoSyncSGI(&GlxTest
);
166 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXGetVideoSyncSGI returned {}", ReturnV
);
173 void CVideoSyncGLX::Run(CEvent
& stopEvent
)
175 unsigned int PrevVblankCount
;
176 unsigned int VblankCount
;
178 bool IsReset
= false;
181 //get the current vblank counter
182 m_glXGetVideoSyncSGI(&VblankCount
);
183 PrevVblankCount
= VblankCount
;
185 while(!stopEvent
.Signaled() && !m_displayLost
&& !m_displayReset
)
187 //wait for the next vblank
188 ReturnV
= m_glXWaitVideoSyncSGI(2, (VblankCount
+ 1) % 2, &VblankCount
);
189 m_glXGetVideoSyncSGI(&VblankCount
); //the vblank count returned by glXWaitVideoSyncSGI is not always correct
190 Now
= CurrentHostCounter(); //get the timestamp of this vblank
194 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXWaitVideoSyncSGI returned {}", ReturnV
);
198 if (VblankCount
> PrevVblankCount
)
200 m_refClock
->UpdateClock((int)(VblankCount
- PrevVblankCount
), Now
);
205 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Vblank counter has reset");
207 //only try reattaching once
211 //because of a bug in the nvidia driver, glXWaitVideoSyncSGI breaks when the vblank counter resets
212 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Detaching glX context");
213 ReturnV
= glXMakeCurrent(m_Dpy
, None
, NULL
);
216 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXMakeCurrent returned {}", ReturnV
);
220 //sleep here so we don't busy spin when this constantly happens, for example when the display went to sleep
221 KODI::TIME::Sleep(1s
);
223 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Attaching glX context");
224 ReturnV
= glXMakeCurrent(m_Dpy
, m_Window
, m_Context
);
227 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: glXMakeCurrent returned {}", ReturnV
);
231 m_glXGetVideoSyncSGI(&VblankCount
);
235 PrevVblankCount
= VblankCount
;
238 while(!stopEvent
.Signaled() && m_displayLost
&& !m_displayReset
)
240 KODI::TIME::Sleep(10ms
);
244 void CVideoSyncGLX::Cleanup()
246 CLog::Log(LOGDEBUG
, "CVideoReferenceClock: Cleaning up GLX");
249 std::unique_lock
<CCriticalSection
> lock(m_winSystem
.GetGfxContext());
258 glXMakeCurrent(m_Dpy
, None
, NULL
);
259 glXDestroyContext(m_Dpy
, m_Context
);
264 XDestroyWindow(m_Dpy
, m_Window
);
270 m_winSystem
.Unregister(this);
273 float CVideoSyncGLX::GetFps()
275 m_fps
= m_winSystem
.GetGfxContext().GetFPS();