Merge pull request #4797 from Karlson2k/Gotham-fix_tag_read
[vuplus_xbmc] / xbmc / video / VideoReferenceClock.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 #include "system.h"
21 #include <list>
22 #include "utils/StdString.h"
23 #include "VideoReferenceClock.h"
24 #include "utils/MathUtils.h"
25 #include "utils/log.h"
26 #include "utils/TimeUtils.h"
27 #include "utils/StringUtils.h"
28 #include "threads/SingleLock.h"
29
30 #if defined(HAS_GLX) && defined(HAS_XRANDR)
31   #include <sstream>
32   #include <X11/extensions/Xrandr.h>
33   #include "windowing/WindowingFactory.h"
34   #define NVSETTINGSCMD "nvidia-settings -nt -q RefreshRate3"
35 #elif defined(TARGET_DARWIN_OSX)
36   #include <QuartzCore/CVDisplayLink.h>
37   #include "osx/CocoaInterface.h"
38 #elif defined(TARGET_DARWIN_IOS)
39   #include "windowing/WindowingFactory.h"
40 #elif defined(TARGET_WINDOWS) && defined(HAS_DX)
41   #pragma comment (lib,"d3d9.lib")
42   #if (D3DX_SDK_VERSION >= 42) //aug 2009 sdk and up there is no dxerr9 anymore
43     #include <Dxerr.h>
44     #pragma comment (lib,"DxErr.lib")
45   #else
46     #include <dxerr9.h>
47     #define DXGetErrorString(hr)      DXGetErrorString9(hr)
48     #define DXGetErrorDescription(hr) DXGetErrorDescription9(hr)
49     #pragma comment (lib,"Dxerr9.lib")
50   #endif
51   #include "windowing/WindowingFactory.h"
52   #include "settings/AdvancedSettings.h"
53 #endif
54
55 using namespace std;
56
57 #if defined(TARGET_WINDOWS) && defined(HAS_DX)
58
59   void CD3DCallback::Reset()
60   {
61     m_devicevalid = true;
62     m_deviceused = false;
63   }
64
65   void CD3DCallback::OnDestroyDevice()
66   {
67     CSingleLock lock(m_critsection);
68     m_devicevalid = false;
69     while (m_deviceused)
70     {
71       lock.Leave();
72       m_releaseevent.Wait();
73       lock.Enter();
74     }
75   }
76
77   void CD3DCallback::OnCreateDevice()
78   {
79     CSingleLock lock(m_critsection);
80     m_devicevalid = true;
81     m_createevent.Set();
82   }
83
84   void CD3DCallback::Aquire()
85   {
86     CSingleLock lock(m_critsection);
87     while(!m_devicevalid)
88     {
89       lock.Leave();
90       m_createevent.Wait();
91       lock.Enter();
92     }
93     m_deviceused = true;
94   }
95
96   void CD3DCallback::Release()
97   {
98     CSingleLock lock(m_critsection);
99     m_deviceused = false;
100     m_releaseevent.Set();
101   }
102
103   bool CD3DCallback::IsValid()
104   {
105     return m_devicevalid;
106   }
107
108 #endif
109
110 CVideoReferenceClock::CVideoReferenceClock() : CThread("VideoReferenceClock")
111 {
112   m_SystemFrequency = CurrentHostFrequency();
113   m_ClockSpeed = 1.0;
114   m_ClockOffset = 0;
115   m_TotalMissedVblanks = 0;
116   m_UseVblank = false;
117   m_Started.Reset();
118
119   m_CurrTime = 0;
120   m_LastIntTime = 0;
121   m_CurrTimeFract = 0.0;
122   m_LastRefreshTime = 0;
123   m_fineadjust = 0.0;
124   m_RefreshRate = 0;
125   m_PrevRefreshRate = 0;
126   m_MissedVblanks = 0;
127   m_RefreshChanged = 0;
128   m_VblankTime = 0;
129
130 #if defined(HAS_GLX) && defined(HAS_XRANDR)
131   m_glXWaitVideoSyncSGI = NULL;
132   m_glXGetVideoSyncSGI = NULL;
133   m_Dpy = NULL;
134   m_vInfo = NULL;
135   m_Window = 0;
136   m_Context = NULL;
137   m_pixmap = None;
138   m_glPixmap = None;
139   m_RREventBase = 0;
140   m_UseNvSettings = true;
141   m_bIsATI = false;
142 #endif
143 }
144
145 void CVideoReferenceClock::Process()
146 {
147   bool SetupSuccess = false;
148   int64_t Now;
149
150 #if defined(TARGET_WINDOWS) && defined(HAS_DX)
151   //register callback
152   m_D3dCallback.Reset();
153   g_Windowing.Register(&m_D3dCallback);
154 #endif
155
156   while(!m_bStop)
157   {
158     //set up the vblank clock
159 #if defined(HAS_GLX) && defined(HAS_XRANDR)
160     SetupSuccess = SetupGLX();
161 #elif defined(TARGET_WINDOWS) && defined(HAS_DX)
162     SetupSuccess = SetupD3D();
163 #elif defined(TARGET_DARWIN)
164     SetupSuccess = SetupCocoa();
165 #elif defined(HAS_GLX)
166     CLog::Log(LOGDEBUG, "CVideoReferenceClock: compiled without RandR support");
167 #elif defined(TARGET_WINDOWS)
168     CLog::Log(LOGDEBUG, "CVideoReferenceClock: only available on directx build");
169 #else
170     CLog::Log(LOGDEBUG, "CVideoReferenceClock: no implementation available");
171 #endif
172
173     CSingleLock SingleLock(m_CritSection);
174     Now = CurrentHostCounter();
175     m_CurrTime = Now + m_ClockOffset; //add the clock offset from the previous time we stopped
176     m_LastIntTime = m_CurrTime;
177     m_CurrTimeFract = 0.0;
178     m_ClockSpeed = 1.0;
179     m_TotalMissedVblanks = 0;
180     m_fineadjust = 1.0;
181     m_RefreshChanged = 0;
182     m_Started.Set();
183
184     if (SetupSuccess)
185     {
186       m_UseVblank = true;          //tell other threads we're using vblank as clock
187       m_VblankTime = Now;          //initialize the timestamp of the last vblank
188       SingleLock.Leave();
189
190       //run the clock
191 #if defined(HAS_GLX) && defined(HAS_XRANDR)
192       RunGLX();
193 #elif defined(TARGET_WINDOWS) && defined(HAS_DX)
194       RunD3D();
195 #elif defined(TARGET_DARWIN)
196       RunCocoa();
197 #endif
198
199     }
200     else
201     {
202       SingleLock.Leave();
203       CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setup failed, falling back to CurrentHostCounter()");
204     }
205
206     SingleLock.Enter();
207     m_UseVblank = false;                       //we're back to using the systemclock
208     Now = CurrentHostCounter();                //set the clockoffset between the vblank clock and systemclock
209     m_ClockOffset = m_CurrTime - Now;
210     SingleLock.Leave();
211
212     //clean up the vblank clock
213 #if defined(HAS_GLX) && defined(HAS_XRANDR)
214     CleanupGLX();
215 #elif defined(TARGET_WINDOWS) && defined(HAS_DX)
216     CleanupD3D();
217 #elif defined(TARGET_DARWIN)
218     CleanupCocoa();
219 #endif
220     if (!SetupSuccess) break;
221   }
222
223 #if defined(TARGET_WINDOWS) && defined(HAS_DX)
224   g_Windowing.Unregister(&m_D3dCallback);
225 #endif
226 }
227
228 bool CVideoReferenceClock::WaitStarted(int MSecs)
229 {
230   //not waiting here can cause issues with alsa
231   return m_Started.WaitMSec(MSecs);
232 }
233
234 #if defined(HAS_GLX) && defined(HAS_XRANDR)
235 bool CVideoReferenceClock::SetupGLX()
236 {
237   int singleBufferAttributes[] = {
238     GLX_RGBA,
239     GLX_RED_SIZE,      0,
240     GLX_GREEN_SIZE,    0,
241     GLX_BLUE_SIZE,     0,
242     None
243   };
244
245   int ReturnV, SwaMask;
246   unsigned int GlxTest;
247   XSetWindowAttributes Swa;
248
249   m_vInfo = NULL;
250   m_Context = NULL;
251   m_Window = 0;
252   m_pixmap = None;
253   m_glPixmap = None;
254
255   CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setting up GLX");
256
257   if (!m_Dpy)
258   {
259     m_Dpy = XOpenDisplay(NULL);
260     if (!m_Dpy)
261     {
262       CLog::Log(LOGDEBUG, "CVideoReferenceClock: Unable to open display");
263       return false;
264     }
265   }
266
267   if (!glXQueryExtension(m_Dpy, NULL, NULL))
268   {
269     CLog::Log(LOGDEBUG, "CVideoReferenceClock: X server does not support GLX");
270     return false;
271   }
272
273   bool          ExtensionFound = false;
274   istringstream Extensions(glXQueryExtensionsString(m_Dpy, DefaultScreen(m_Dpy)));
275   string        ExtensionStr;
276
277   while (!ExtensionFound)
278   {
279     Extensions >> ExtensionStr;
280     if (Extensions.fail())
281       break;
282
283     if (ExtensionStr == "GLX_SGI_video_sync")
284       ExtensionFound = true;
285   }
286
287   if (!ExtensionFound)
288   {
289     CLog::Log(LOGDEBUG, "CVideoReferenceClock: X server does not support GLX_SGI_video_sync");
290     return false;
291   }
292
293   CStdString Vendor = g_Windowing.GetRenderVendor();
294   if (StringUtils::StartsWithNoCase(Vendor, "ati"))
295   {
296     CLog::Log(LOGDEBUG, "CVideoReferenceClock: GL_VENDOR: %s, using ati workaround", Vendor.c_str());
297     m_bIsATI = true;
298   }
299
300   m_vInfo = glXChooseVisual(m_Dpy, DefaultScreen(m_Dpy), singleBufferAttributes);
301   if (!m_vInfo)
302   {
303     CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXChooseVisual returned NULL");
304     return false;
305   }
306
307   if (!m_bIsATI)
308   {
309     Swa.border_pixel = 0;
310     Swa.event_mask = StructureNotifyMask;
311     Swa.colormap = XCreateColormap(m_Dpy, RootWindow(m_Dpy, m_vInfo->screen), m_vInfo->visual, AllocNone );
312     SwaMask = CWBorderPixel | CWColormap | CWEventMask;
313
314     m_Window = XCreateWindow(m_Dpy, RootWindow(m_Dpy, m_vInfo->screen), 0, 0, 256, 256, 0,
315                            m_vInfo->depth, InputOutput, m_vInfo->visual, SwaMask, &Swa);
316   }
317   else
318   {
319     m_pixmap = XCreatePixmap(m_Dpy, DefaultRootWindow(m_Dpy), 256, 256, m_vInfo->depth);
320     if (!m_pixmap)
321     {
322       CLog::Log(LOGDEBUG, "CVideoReferenceClock: unable to create pixmap");
323       return false;
324     }
325     m_glPixmap = glXCreateGLXPixmap(m_Dpy, m_vInfo, m_pixmap);
326   }
327
328   m_Context = glXCreateContext(m_Dpy, m_vInfo, NULL, True);
329   if (!m_Context)
330   {
331     CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXCreateContext returned NULL");
332     return false;
333   }
334
335   if (!m_bIsATI)
336     ReturnV = glXMakeCurrent(m_Dpy, m_Window, m_Context);
337   else
338     ReturnV = glXMakeCurrent(m_Dpy, m_glPixmap, m_Context);
339
340   if (ReturnV != True)
341   {
342     CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV);
343     return false;
344   }
345
346   if (!m_bIsATI)
347   {
348     m_glXWaitVideoSyncSGI = (int (*)(int, int, unsigned int*))glXGetProcAddress((const GLubyte*)"glXWaitVideoSyncSGI");
349     if (!m_glXWaitVideoSyncSGI)
350     {
351       CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI not found");
352       return false;
353     }
354
355     ReturnV = m_glXWaitVideoSyncSGI(2, 0, &GlxTest);
356     if (ReturnV)
357     {
358       CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI returned %i", ReturnV);
359       return false;
360     }
361   }
362
363   m_glXGetVideoSyncSGI = (int (*)(unsigned int*))glXGetProcAddress((const GLubyte*)"glXGetVideoSyncSGI");
364   if (!m_glXGetVideoSyncSGI)
365   {
366     CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXGetVideoSyncSGI not found");
367     return false;
368   }
369
370   ReturnV = m_glXGetVideoSyncSGI(&GlxTest);
371   if (ReturnV)
372   {
373     CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXGetVideoSyncSGI returned %i", ReturnV);
374     return false;
375   }
376
377   XRRSizes(m_Dpy, m_vInfo->screen, &ReturnV);
378   if (ReturnV == 0)
379   {
380     CLog::Log(LOGDEBUG, "CVideoReferenceClock: RandR not supported");
381     return false;
382   }
383
384   //set up receiving of RandR events, we'll get one when the refreshrate changes
385   XRRQueryExtension(m_Dpy, &m_RREventBase, &ReturnV);
386   XRRSelectInput(m_Dpy, RootWindow(m_Dpy, m_vInfo->screen), RRScreenChangeNotifyMask);
387
388   UpdateRefreshrate(true); //forced refreshrate update
389   m_MissedVblanks = 0;
390
391   return true;
392 }
393
394 bool CVideoReferenceClock::ParseNvSettings(int& RefreshRate)
395 {
396   double fRefreshRate;
397   char   Buff[255];
398   int    buffpos;
399   int    ReturnV;
400   struct lconv *Locale = localeconv();
401   FILE*  NvSettings;
402   int    fd;
403   int64_t now;
404
405   const char* VendorPtr = (const char*)glGetString(GL_VENDOR);
406   if (!VendorPtr)
407   {
408     CLog::Log(LOGDEBUG, "CVideoReferenceClock: glGetString(GL_VENDOR) returned NULL, not using nvidia-settings");
409     return false;
410   }
411
412   CStdString Vendor = VendorPtr;
413   StringUtils::ToLower(Vendor);
414   if (Vendor.find("nvidia") == std::string::npos)
415   {
416     CLog::Log(LOGDEBUG, "CVideoReferenceClock: GL_VENDOR:%s, not using nvidia-settings", Vendor.c_str());
417     return false;
418   }
419
420   NvSettings = popen(NVSETTINGSCMD, "r");
421   if (!NvSettings)
422   {
423     CLog::Log(LOGDEBUG, "CVideoReferenceClock: %s: %s", NVSETTINGSCMD, strerror(errno));
424     return false;
425   }
426
427   fd = fileno(NvSettings);
428   if (fd == -1)
429   {
430     CLog::Log(LOGDEBUG, "CVideoReferenceClock: unable to get nvidia-settings file descriptor: %s", strerror(errno));
431     pclose(NvSettings);
432     return false;
433   }
434
435   now = CurrentHostCounter();
436   buffpos = 0;
437   while (CurrentHostCounter() - now < CurrentHostFrequency() * 5)
438   {
439     fd_set set;
440     FD_ZERO(&set);
441     FD_SET(fd, &set);
442     struct timeval timeout = {1, 0};
443     ReturnV = select(fd + 1, &set, NULL, NULL, &timeout);
444     if (ReturnV == -1)
445     {
446       CLog::Log(LOGDEBUG, "CVideoReferenceClock: select failed on %s: %s", NVSETTINGSCMD, strerror(errno));
447       pclose(NvSettings);
448       return false;
449     }
450     else if (FD_ISSET(fd, &set))
451     {
452       ReturnV = read(fd, Buff + buffpos, (int)sizeof(Buff) - buffpos);
453       if (ReturnV == -1)
454       {
455         CLog::Log(LOGDEBUG, "CVideoReferenceClock: read failed on %s: %s", NVSETTINGSCMD, strerror(errno));
456         pclose(NvSettings);
457         return false;
458       }
459       else if (ReturnV > 0)
460       {
461         buffpos += ReturnV;
462         if (buffpos >= (int)sizeof(Buff) - 1)
463           break;
464       }
465       else
466       {
467         break;
468       }
469     }
470   }
471
472   if (buffpos <= 0)
473   {
474     CLog::Log(LOGDEBUG, "CVideoReferenceClock: %s produced no output", NVSETTINGSCMD);
475     //calling pclose() here might hang
476     //what should be done instead is fork, call nvidia-settings
477     //then kill the process if it hangs
478     return false;
479   }
480   else if (buffpos > (int)sizeof(Buff) - 1)
481   {
482     buffpos = sizeof(Buff) - 1;
483     pclose(NvSettings);
484   }
485   Buff[buffpos] = 0;
486
487   CLog::Log(LOGDEBUG, "CVideoReferenceClock: output of %s: %s", NVSETTINGSCMD, Buff);
488
489   if (!strchr(Buff, '\n'))
490   {
491     CLog::Log(LOGDEBUG, "CVideoReferenceClock: %s incomplete output (no newline)", NVSETTINGSCMD);
492     return false;
493   }
494
495   for (int i = 0; i < buffpos; i++)
496   {
497       //workaround for locale mismatch
498     if (Buff[i] == '.' || Buff[i] == ',')
499       Buff[i] = *Locale->decimal_point;
500   }
501
502   ReturnV = sscanf(Buff, "%lf", &fRefreshRate);
503   if (ReturnV != 1 || fRefreshRate <= 0.0)
504   {
505     CLog::Log(LOGDEBUG, "CVideoReferenceClock: can't make sense of that");
506     return false;
507   }
508
509   RefreshRate = MathUtils::round_int(fRefreshRate);
510   CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate by nvidia-settings: %f hertz, rounding to %i hertz",
511             fRefreshRate, RefreshRate);
512
513   return true;
514 }
515
516 int CVideoReferenceClock::GetRandRRate()
517 {
518   int RefreshRate;
519   XRRScreenConfiguration *CurrInfo;
520
521   CurrInfo = XRRGetScreenInfo(m_Dpy, RootWindow(m_Dpy, m_vInfo->screen));
522   RefreshRate = XRRConfigCurrentRate(CurrInfo);
523   XRRFreeScreenConfigInfo(CurrInfo);
524
525   return RefreshRate;
526 }
527
528 void CVideoReferenceClock::CleanupGLX()
529 {
530   CLog::Log(LOGDEBUG, "CVideoReferenceClock: Cleaning up GLX");
531
532   if (m_vInfo)
533   {
534     XFree(m_vInfo);
535     m_vInfo = NULL;
536   }
537   if (m_Context)
538   {
539     glXMakeCurrent(m_Dpy, None, NULL);
540     glXDestroyContext(m_Dpy, m_Context);
541     m_Context = NULL;
542   }
543   if (m_Window)
544   {
545     XDestroyWindow(m_Dpy, m_Window);
546     m_Window = 0;
547   }
548   if (m_glPixmap)
549   {
550     glXDestroyPixmap(m_Dpy, m_glPixmap);
551     m_glPixmap = None;
552   }
553   if (m_pixmap)
554   {
555     XFreePixmap(m_Dpy, m_pixmap);
556     m_pixmap = None;
557   }
558
559   //ati saves the Display* in their libGL, if we close it here, we crash
560   if (m_Dpy && !m_bIsATI)
561   {
562     XCloseDisplay(m_Dpy);
563     m_Dpy = NULL;
564   }
565 }
566
567 void CVideoReferenceClock::RunGLX()
568 {
569   unsigned int  PrevVblankCount;
570   unsigned int  VblankCount;
571   int           ReturnV;
572   bool          IsReset = false;
573   int64_t       Now;
574
575   CSingleLock SingleLock(m_CritSection);
576   SingleLock.Leave();
577
578   //get the current vblank counter
579   m_glXGetVideoSyncSGI(&VblankCount);
580   PrevVblankCount = VblankCount;
581
582   uint64_t lastVblankTime = CurrentHostCounter();
583   int sleepTime, correction;
584   int integral = 0;
585
586   while(!m_bStop)
587   {
588     //wait for the next vblank
589     if (!m_bIsATI)
590     {
591       ReturnV = m_glXWaitVideoSyncSGI(2, (VblankCount + 1) % 2, &VblankCount);
592       m_glXGetVideoSyncSGI(&VblankCount); //the vblank count returned by glXWaitVideoSyncSGI is not always correct
593     }
594     else
595     {
596       // calculate sleep time in micro secs
597       // we start with 50% of interval
598       sleepTime = 500000LL / m_RefreshRate;
599
600       // correct sleepTime by time used for processing since last vblank
601       correction = (CurrentHostCounter() - lastVblankTime) * 1000000LL / m_SystemFrequency;
602       sleepTime -= correction;
603
604       // correct sleep time by integral term
605       // consider 10 cycles as desired
606       sleepTime += integral;
607
608       // clamp sleepTime to a min value of 30% of interval
609       // integral is already clamped to a max value
610       sleepTime = std::max(int(300000LL/m_RefreshRate), sleepTime);
611
612       unsigned int iterations = 0;
613       while (VblankCount == PrevVblankCount && !m_bStop)
614       {
615         usleep(sleepTime);
616         m_glXGetVideoSyncSGI(&VblankCount);
617         sleepTime = sleepTime > 200 ? sleepTime/2 : 100;
618         iterations++;
619       }
620       if (iterations > 10)
621         integral += 100;
622       else if (iterations < 10)
623         integral -= 100;
624
625       // clamp integral to an absolute value of 20% of interval
626       if (integral > 200000LL/m_RefreshRate)
627         integral = 200000LL/m_RefreshRate;
628       else if (integral < -200000LL/m_RefreshRate)
629         integral = -200000LL/m_RefreshRate;
630
631       lastVblankTime = CurrentHostCounter();
632       ReturnV = 0;
633     }
634
635     Now = CurrentHostCounter();         //get the timestamp of this vblank
636
637     if(ReturnV)
638     {
639       CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI returned %i", ReturnV);
640       return;
641     }
642
643     if (VblankCount > PrevVblankCount)
644     {
645       //update the vblank timestamp, update the clock and send a signal that we got a vblank
646       SingleLock.Enter();
647       m_VblankTime = Now;
648       UpdateClock((int)(VblankCount - PrevVblankCount), true);
649       SingleLock.Leave();
650       SendVblankSignal();
651       UpdateRefreshrate();
652       IsReset = false;
653     }
654     else if (!m_bStop)
655     {
656       CLog::Log(LOGDEBUG, "CVideoReferenceClock: Vblank counter has reset");
657
658       integral = 0;
659
660       //only try reattaching once
661       if (IsReset)
662         return;
663
664       //because of a bug in the nvidia driver, glXWaitVideoSyncSGI breaks when the vblank counter resets
665       CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detaching glX context");
666       ReturnV = glXMakeCurrent(m_Dpy, None, NULL);
667       if (ReturnV != True)
668       {
669         CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV);
670         return;
671       }
672
673       //sleep here so we don't busy spin when this constantly happens, for example when the display went to sleep
674       Sleep(1000);
675
676       CLog::Log(LOGDEBUG, "CVideoReferenceClock: Attaching glX context");
677       if (!m_bIsATI)
678         ReturnV = glXMakeCurrent(m_Dpy, m_Window, m_Context);
679       else
680         ReturnV = glXMakeCurrent(m_Dpy, m_glPixmap, m_Context);
681
682       if (ReturnV != True)
683       {
684         CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV);
685         return;
686       }
687
688       m_glXGetVideoSyncSGI(&VblankCount);
689
690       IsReset = true;
691     }
692     PrevVblankCount = VblankCount;
693   }
694 }
695
696 #elif defined(TARGET_WINDOWS) && defined(HAS_DX)
697
698 void CVideoReferenceClock::RunD3D()
699 {
700   D3DRASTER_STATUS RasterStatus;
701   int64_t       Now;
702   int64_t       LastVBlankTime;
703   unsigned int  LastLine;
704   int           NrVBlanks;
705   double        VBlankTime;
706   int           ReturnV;
707
708   CSingleLock SingleLock(m_CritSection);
709   SingleLock.Leave();
710
711   //get the scanline we're currently at
712   m_D3dDev->GetRasterStatus(0, &RasterStatus);
713   if (RasterStatus.InVBlank) LastLine = 0;
714   else LastLine = RasterStatus.ScanLine;
715
716   //init the vblanktime
717   Now = CurrentHostCounter();
718   LastVBlankTime = Now;
719
720   while(!m_bStop && m_D3dCallback.IsValid())
721   {
722     //get the scanline we're currently at
723     ReturnV = m_D3dDev->GetRasterStatus(0, &RasterStatus);
724     if (ReturnV != D3D_OK)
725     {
726       CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetRasterStatus returned returned %s: %s",
727                 DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV));
728       return;
729     }
730
731     //if InVBlank is set, or the current scanline is lower than the previous scanline, a vblank happened
732     if ((RasterStatus.InVBlank && LastLine > 0) || (RasterStatus.ScanLine < LastLine))
733     {
734       //calculate how many vblanks happened
735       Now = CurrentHostCounter();
736       VBlankTime = (double)(Now - LastVBlankTime) / (double)m_SystemFrequency;
737       NrVBlanks = MathUtils::round_int(VBlankTime * (double)m_RefreshRate);
738
739       //update the vblank timestamp, update the clock and send a signal that we got a vblank
740       SingleLock.Enter();
741       m_VblankTime = Now;
742       UpdateClock(NrVBlanks, true);
743       SingleLock.Leave();
744       SendVblankSignal();
745
746       if (UpdateRefreshrate())
747       {
748         //we have to measure the refreshrate again
749         CLog::Log(LOGDEBUG, "CVideoReferenceClock: Displaymode changed");
750         return;
751       }
752
753       //save the timestamp of this vblank so we can calculate how many vblanks happened next time
754       LastVBlankTime = Now;
755
756       //because we had a vblank, sleep until half the refreshrate period
757       Now = CurrentHostCounter();
758       int SleepTime = (int)((LastVBlankTime + (m_SystemFrequency / m_RefreshRate / 2) - Now) * 1000 / m_SystemFrequency);
759       if (SleepTime > 100) SleepTime = 100; //failsafe
760       if (SleepTime > 0) ::Sleep(SleepTime);
761     }
762     else
763     {
764       ::Sleep(1);
765     }
766
767     if (RasterStatus.InVBlank) LastLine = 0;
768     else LastLine = RasterStatus.ScanLine;
769   }
770 }
771
772 //how many times we measure the refreshrate
773 #define NRMEASURES 6
774 //how long to measure in milliseconds
775 #define MEASURETIME 250
776
777 bool CVideoReferenceClock::SetupD3D()
778 {
779   int ReturnV;
780
781   CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setting up Direct3d");
782
783   m_D3dCallback.Aquire();
784
785   //get d3d device
786   m_D3dDev = g_Windowing.Get3DDevice();
787
788   //we need a high priority thread to get accurate timing
789   if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
790     CLog::Log(LOGDEBUG, "CVideoReferenceClock: SetThreadPriority failed");
791
792   D3DCAPS9 DevCaps;
793   ReturnV = m_D3dDev->GetDeviceCaps(&DevCaps);
794   if (ReturnV != D3D_OK)
795   {
796     CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetDeviceCaps returned %s: %s",
797                          DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV));
798     return false;
799   }
800
801   if ((DevCaps.Caps & D3DCAPS_READ_SCANLINE) != D3DCAPS_READ_SCANLINE)
802   {
803     CLog::Log(LOGDEBUG, "CVideoReferenceClock: Hardware does not support GetRasterStatus");
804     return false;
805   }
806
807   D3DRASTER_STATUS RasterStatus;
808   ReturnV = m_D3dDev->GetRasterStatus(0, &RasterStatus);
809   if (ReturnV != D3D_OK)
810   {
811     CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetRasterStatus returned returned %s: %s",
812               DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV));
813     return false;
814   }
815
816   D3DDISPLAYMODE DisplayMode;
817   ReturnV = m_D3dDev->GetDisplayMode(0, &DisplayMode);
818   if (ReturnV != D3D_OK)
819   {
820     CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetDisplayMode returned returned %s: %s",
821               DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV));
822     return false;
823   }
824
825   //forced update of windows refreshrate
826   UpdateRefreshrate(true);
827
828   if (g_advancedSettings.m_measureRefreshrate)
829   {
830     //measure the refreshrate a couple times
831     list<double> Measures;
832     for (int i = 0; i < NRMEASURES; i++)
833       Measures.push_back(MeasureRefreshrate(MEASURETIME));
834
835     //build up a string of measured rates
836     CStdString StrRates;
837     for (list<double>::iterator it = Measures.begin(); it != Measures.end(); it++)
838       StrRates += StringUtils::Format("%.2f ", *it);
839
840     //get the top half of the measured rates
841     Measures.sort();
842     double RefreshRate = 0.0;
843     int    NrMeasurements = 0;
844     while (NrMeasurements < NRMEASURES / 2 && !Measures.empty())
845     {
846       if (Measures.back() > 0.0)
847       {
848         RefreshRate += Measures.back();
849         NrMeasurements++;
850       }
851       Measures.pop_back();
852     }
853
854     if (NrMeasurements < NRMEASURES / 2)
855     {
856       CLog::Log(LOGDEBUG, "CVideoReferenceClock: refreshrate measurements: %s, unable to get a good measurement",
857         StrRates.c_str(), m_RefreshRate);
858       return false;
859     }
860
861     RefreshRate /= NrMeasurements;
862     m_RefreshRate = MathUtils::round_int(RefreshRate);
863
864     CLog::Log(LOGDEBUG, "CVideoReferenceClock: refreshrate measurements: %s, assuming %i hertz", StrRates.c_str(), m_RefreshRate);
865   }
866   else
867   {
868     m_RefreshRate = m_PrevRefreshRate;
869     if (m_RefreshRate == 23 || m_RefreshRate == 29 || m_RefreshRate == 59)
870       m_RefreshRate++;
871
872     if (m_Interlaced)
873     {
874       m_RefreshRate *= 2;
875       CLog::Log(LOGDEBUG, "CVideoReferenceClock: display is interlaced");
876     }
877
878     CLog::Log(LOGDEBUG, "CVideoReferenceClock: detected refreshrate: %i hertz, assuming %i hertz", m_PrevRefreshRate, (int)m_RefreshRate);
879   }
880
881   m_MissedVblanks = 0;
882
883   return true;
884 }
885
886 double CVideoReferenceClock::MeasureRefreshrate(int MSecs)
887 {
888   D3DRASTER_STATUS RasterStatus;
889   int64_t          Now;
890   int64_t          Target;
891   int64_t          Prev;
892   int64_t          AvgInterval;
893   int64_t          MeasureCount;
894   unsigned int     LastLine;
895   int              ReturnV;
896
897   Now = CurrentHostCounter();
898   Target = Now + (m_SystemFrequency * MSecs / 1000);
899   Prev = -1;
900   AvgInterval = 0;
901   MeasureCount = 0;
902
903   //start measuring vblanks
904   LastLine = 0;
905   while(Now <= Target)
906   {
907     ReturnV = m_D3dDev->GetRasterStatus(0, &RasterStatus);
908     Now = CurrentHostCounter();
909     if (ReturnV != D3D_OK)
910     {
911       CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetRasterStatus returned returned %s: %s",
912                 DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV));
913       return -1.0;
914     }
915
916     if ((RasterStatus.InVBlank && LastLine != 0) || (!RasterStatus.InVBlank && RasterStatus.ScanLine < LastLine))
917     { //we got a vblank
918       if (Prev != -1) //need two for a measurement
919       {
920         AvgInterval += Now - Prev; //save how long this vblank lasted
921         MeasureCount++;
922       }
923       Prev = Now; //save this time for the next measurement
924     }
925
926     //save the current scanline
927     if (RasterStatus.InVBlank)
928       LastLine = 0;
929     else
930       LastLine = RasterStatus.ScanLine;
931
932     ::Sleep(1);
933   }
934
935   if (MeasureCount < 1)
936   {
937     CLog::Log(LOGDEBUG, "CVideoReferenceClock: Didn't measure any vblanks");
938     return -1.0;
939   }
940
941   double fRefreshRate = 1.0 / ((double)AvgInterval / (double)MeasureCount / (double)m_SystemFrequency);
942
943   return fRefreshRate;
944 }
945
946 void CVideoReferenceClock::CleanupD3D()
947 {
948   CLog::Log(LOGDEBUG, "CVideoReferenceClock: Cleaning up Direct3d");
949   m_D3dCallback.Release();
950 }
951
952 #elif defined(TARGET_DARWIN)
953 #if defined(TARGET_DARWIN_OSX)
954 // Called by the Core Video Display Link whenever it's appropriate to render a frame.
955 static CVReturn DisplayLinkCallBack(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
956 {
957   double fps = 60.0;
958
959   if (inNow->videoRefreshPeriod > 0)
960     fps = (double)inOutputTime->videoTimeScale / (double)inOutputTime->videoRefreshPeriod;
961
962   // Create an autorelease pool (necessary to call into non-Obj-C code from Obj-C code)
963   void* pool = Cocoa_Create_AutoReleasePool();
964
965   CVideoReferenceClock *VideoReferenceClock = reinterpret_cast<CVideoReferenceClock*>(displayLinkContext);
966   VideoReferenceClock->VblankHandler(inNow->hostTime, fps);
967
968   // Destroy the autorelease pool
969   Cocoa_Destroy_AutoReleasePool(pool);
970
971   return kCVReturnSuccess;
972 }
973 #endif
974 bool CVideoReferenceClock::SetupCocoa()
975 {
976   CLog::Log(LOGDEBUG, "CVideoReferenceClock: setting up Cocoa");
977
978   //init the vblank timestamp
979   m_LastVBlankTime = CurrentHostCounter();
980   m_MissedVblanks = 0;
981   m_RefreshRate = 60;              //init the refreshrate so we don't get any division by 0 errors
982
983   #if defined(TARGET_DARWIN_IOS)
984   {
985     g_Windowing.InitDisplayLink();
986   }
987   #else
988   if (!Cocoa_CVDisplayLinkCreate((void*)DisplayLinkCallBack, reinterpret_cast<void*>(this)))
989   {
990     CLog::Log(LOGDEBUG, "CVideoReferenceClock: Cocoa_CVDisplayLinkCreate failed");
991     return false;
992   }
993   else
994   #endif
995   {
996     UpdateRefreshrate(true);
997     return true;
998   }
999 }
1000
1001 void CVideoReferenceClock::RunCocoa()
1002 {
1003   //because cocoa has a vblank callback, we just keep sleeping until we're asked to stop the thread
1004   while(!m_bStop)
1005   {
1006     Sleep(1000);
1007   }
1008 }
1009
1010 void CVideoReferenceClock::CleanupCocoa()
1011 {
1012   CLog::Log(LOGDEBUG, "CVideoReferenceClock: cleaning up Cocoa");
1013   #if defined(TARGET_DARWIN_IOS)
1014     g_Windowing.DeinitDisplayLink();
1015   #else
1016     Cocoa_CVDisplayLinkRelease();
1017   #endif
1018 }
1019
1020 void CVideoReferenceClock::VblankHandler(int64_t nowtime, double fps)
1021 {
1022   int           NrVBlanks;
1023   double        VBlankTime;
1024   int           RefreshRate = MathUtils::round_int(fps);
1025
1026   CSingleLock SingleLock(m_CritSection);
1027
1028   if (RefreshRate != m_RefreshRate)
1029   {
1030     CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: %f hertz, rounding to %i hertz", fps, RefreshRate);
1031     m_RefreshRate = RefreshRate;
1032   }
1033   m_LastRefreshTime = m_CurrTime;
1034
1035   //calculate how many vblanks happened
1036   VBlankTime = (double)(nowtime - m_LastVBlankTime) / (double)m_SystemFrequency;
1037   NrVBlanks = MathUtils::round_int(VBlankTime * (double)m_RefreshRate);
1038
1039   //save the timestamp of this vblank so we can calculate how many happened next time
1040   m_LastVBlankTime = nowtime;
1041
1042   //update the vblank timestamp, update the clock and send a signal that we got a vblank
1043   m_VblankTime = nowtime;
1044   UpdateClock(NrVBlanks, true);
1045
1046   SingleLock.Leave();
1047
1048   SendVblankSignal();
1049   UpdateRefreshrate();
1050 }
1051 #endif
1052
1053 //this is called from the vblank run function and from CVideoReferenceClock::Wait in case of a late update
1054 void CVideoReferenceClock::UpdateClock(int NrVBlanks, bool CheckMissed)
1055 {
1056   if (CheckMissed) //set to true from the vblank run function, set to false from Wait and GetTime
1057   {
1058     if (NrVBlanks < m_MissedVblanks) //if this is true the vblank detection in the run function is wrong
1059       CLog::Log(LOGDEBUG, "CVideoReferenceClock: detected %i vblanks, missed %i, refreshrate might have changed",
1060                 NrVBlanks, m_MissedVblanks);
1061
1062     NrVBlanks -= m_MissedVblanks; //subtract the vblanks we missed
1063     m_MissedVblanks = 0;
1064   }
1065   else
1066   {
1067     m_MissedVblanks += NrVBlanks;      //tell the vblank clock how many vblanks it missed
1068     m_TotalMissedVblanks += NrVBlanks; //for the codec information screen
1069     m_VblankTime += m_SystemFrequency * (int64_t)NrVBlanks / m_RefreshRate; //set the vblank time forward
1070   }
1071
1072   if (NrVBlanks > 0) //update the clock with the adjusted frequency if we have any vblanks
1073   {
1074     double increment = UpdateInterval() * NrVBlanks;
1075     double integer   = floor(increment);
1076     m_CurrTime      += (int64_t)(integer + 0.5); //make sure it gets correctly converted to int
1077
1078     //accumulate what we lost due to rounding in m_CurrTimeFract, then add the integer part of that to m_CurrTime
1079     m_CurrTimeFract += increment - integer;
1080     integer          = floor(m_CurrTimeFract);
1081     m_CurrTime      += (int64_t)(integer + 0.5);
1082     m_CurrTimeFract -= integer;
1083   }
1084 }
1085
1086 double CVideoReferenceClock::UpdateInterval()
1087 {
1088   return m_ClockSpeed * m_fineadjust / (double)m_RefreshRate * (double)m_SystemFrequency;
1089 }
1090
1091 //called from dvdclock to get the time
1092 int64_t CVideoReferenceClock::GetTime(bool interpolated /* = true*/)
1093 {
1094   CSingleLock SingleLock(m_CritSection);
1095
1096   //when using vblank, get the time from that, otherwise use the systemclock
1097   if (m_UseVblank)
1098   {
1099     int64_t  NextVblank;
1100     int64_t  Now;
1101
1102     Now = CurrentHostCounter();        //get current system time
1103     NextVblank = TimeOfNextVblank();   //get time when the next vblank should happen
1104
1105     while(Now >= NextVblank)  //keep looping until the next vblank is in the future
1106     {
1107       UpdateClock(1, false);           //update clock when next vblank should have happened already
1108       NextVblank = TimeOfNextVblank(); //get time when the next vblank should happen
1109     }
1110
1111     if (interpolated)
1112     {
1113       //interpolate from the last time the clock was updated
1114       double elapsed = (double)(Now - m_VblankTime) * m_ClockSpeed * m_fineadjust;
1115       //don't interpolate more than 2 vblank periods
1116       elapsed = min(elapsed, UpdateInterval() * 2.0);
1117
1118       //make sure the clock doesn't go backwards
1119       int64_t intTime = m_CurrTime + (int64_t)elapsed;
1120       if (intTime > m_LastIntTime)
1121         m_LastIntTime = intTime;
1122
1123       return m_LastIntTime;
1124     }
1125     else
1126     {
1127       return m_CurrTime;
1128     }
1129   }
1130   else
1131   {
1132     return CurrentHostCounter() + m_ClockOffset;
1133   }
1134 }
1135
1136 //called from dvdclock to get the clock frequency
1137 int64_t CVideoReferenceClock::GetFrequency()
1138 {
1139   return m_SystemFrequency;
1140 }
1141
1142 void CVideoReferenceClock::SetSpeed(double Speed)
1143 {
1144   CSingleLock SingleLock(m_CritSection);
1145   //dvdplayer can change the speed to fit the rereshrate
1146   if (m_UseVblank)
1147   {
1148     if (Speed != m_ClockSpeed)
1149     {
1150       m_ClockSpeed = Speed;
1151       CLog::Log(LOGDEBUG, "CVideoReferenceClock: Clock speed %f%%", GetSpeed() * 100.0);
1152     }
1153   }
1154 }
1155
1156 double CVideoReferenceClock::GetSpeed()
1157 {
1158   CSingleLock SingleLock(m_CritSection);
1159
1160   //dvdplayer needs to know the speed for the resampler
1161   if (m_UseVblank)
1162     return m_ClockSpeed;
1163   else
1164     return 1.0;
1165 }
1166
1167 bool CVideoReferenceClock::UpdateRefreshrate(bool Forced /*= false*/)
1168 {
1169   //if the graphicscontext signaled that the refreshrate changed, we check it about one second later
1170   if (m_RefreshChanged == 1 && !Forced)
1171   {
1172     m_LastRefreshTime = m_CurrTime;
1173     m_RefreshChanged = 2;
1174     return false;
1175   }
1176
1177   //update the refreshrate about once a second, or update immediately if a forced update is required
1178   if (m_CurrTime - m_LastRefreshTime < m_SystemFrequency && !Forced)
1179     return false;
1180
1181   if (Forced)
1182     m_LastRefreshTime = 0;
1183   else
1184     m_LastRefreshTime = m_CurrTime;
1185
1186 #if defined(HAS_GLX) && defined(HAS_XRANDR)
1187
1188   //check for RandR events
1189   bool   GotEvent = Forced || m_RefreshChanged == 2;
1190   XEvent Event;
1191   while (XCheckTypedEvent(m_Dpy, m_RREventBase + RRScreenChangeNotify, &Event))
1192   {
1193     if (Event.type == m_RREventBase + RRScreenChangeNotify)
1194     {
1195       CLog::Log(LOGDEBUG, "CVideoReferenceClock: Received RandR event %i", Event.type);
1196       GotEvent = true;
1197     }
1198     XRRUpdateConfiguration(&Event);
1199   }
1200
1201   if (!Forced)
1202     m_RefreshChanged = 0;
1203
1204   if (!GotEvent) //refreshrate did not change
1205     return false;
1206
1207   //the refreshrate can be wrong on nvidia drivers, so read it from nvidia-settings when it's available
1208   if (m_UseNvSettings)
1209   {
1210     int NvRefreshRate;
1211     //if this fails we can't get the refreshrate from nvidia-settings
1212     m_UseNvSettings = ParseNvSettings(NvRefreshRate);
1213
1214     if (m_UseNvSettings)
1215     {
1216       CSingleLock SingleLock(m_CritSection);
1217       m_RefreshRate = NvRefreshRate;
1218       return true;
1219     }
1220
1221     CLog::Log(LOGDEBUG, "CVideoReferenceClock: Using RandR for refreshrate detection");
1222   }
1223
1224   CSingleLock SingleLock(m_CritSection);
1225   m_RefreshRate = GetRandRRate();
1226
1227   CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: %i hertz", (int)m_RefreshRate);
1228
1229   return true;
1230
1231 #elif defined(TARGET_WINDOWS) && defined(HAS_DX)
1232
1233   D3DDISPLAYMODE DisplayMode;
1234   m_D3dDev->GetDisplayMode(0, &DisplayMode);
1235
1236   //0 indicates adapter default
1237   if (DisplayMode.RefreshRate == 0)
1238     DisplayMode.RefreshRate = 60;
1239
1240   if (m_PrevRefreshRate != DisplayMode.RefreshRate || m_Width != DisplayMode.Width || m_Height != DisplayMode.Height ||
1241       m_Interlaced != g_Windowing.Interlaced() || Forced )
1242   {
1243     m_PrevRefreshRate = DisplayMode.RefreshRate;
1244     m_Width = DisplayMode.Width;
1245     m_Height = DisplayMode.Height;
1246     m_Interlaced = g_Windowing.Interlaced();
1247     return true;
1248   }
1249
1250   return false;
1251
1252 #elif defined(TARGET_DARWIN)
1253   #if defined(TARGET_DARWIN_IOS)
1254     int RefreshRate = round(g_Windowing.GetDisplayLinkFPS() + 0.5);
1255   #else
1256     int RefreshRate = MathUtils::round_int(Cocoa_GetCVDisplayLinkRefreshPeriod());
1257   #endif
1258
1259   if (RefreshRate != m_RefreshRate || Forced)
1260   {
1261     CSingleLock SingleLock(m_CritSection);
1262     CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: %i hertz", RefreshRate);
1263     m_RefreshRate = RefreshRate;
1264     return true;
1265   }
1266   return false;
1267 #endif
1268
1269   return false;
1270 }
1271
1272 //dvdplayer needs to know the refreshrate for matching the fps of the video playing to it
1273 int CVideoReferenceClock::GetRefreshRate(double* interval /*= NULL*/)
1274 {
1275   CSingleLock SingleLock(m_CritSection);
1276
1277   if (m_UseVblank)
1278   {
1279     if (interval)
1280       *interval = m_ClockSpeed / m_RefreshRate;
1281
1282     return (int)m_RefreshRate;
1283   }
1284   else
1285     return -1;
1286 }
1287
1288
1289 //this is called from CDVDClock::WaitAbsoluteClock, which is called from CXBMCRenderManager::WaitPresentTime
1290 //it waits until a certain timestamp has passed, used for displaying videoframes at the correct moment
1291 int64_t CVideoReferenceClock::Wait(int64_t Target)
1292 {
1293   int64_t       Now;
1294   int           SleepTime;
1295
1296   CSingleLock SingleLock(m_CritSection);
1297
1298   if (m_UseVblank) //when true the vblank is used as clock source
1299   {
1300     while (m_CurrTime < Target)
1301     {
1302       //calculate how long to sleep before we should have gotten a signal that a vblank happened
1303       Now = CurrentHostCounter();
1304       int64_t NextVblank = TimeOfNextVblank();
1305       SleepTime = (int)((NextVblank - Now) * 1000 / m_SystemFrequency);
1306
1307       int64_t CurrTime = m_CurrTime; //save current value of the clock
1308
1309       bool Late = false;
1310       if (SleepTime <= 0) //if sleeptime is 0 or lower, the vblank clock is already late in updating
1311       {
1312         Late = true;
1313       }
1314       else
1315       {
1316         m_VblankEvent.Reset();
1317         SingleLock.Leave();
1318         if (!m_VblankEvent.WaitMSec(SleepTime)) //if this returns false, it means the vblank event was not set within
1319           Late = true;                          //the required time
1320         SingleLock.Enter();
1321       }
1322
1323       //if the vblank clock was late with its update, we update the clock ourselves
1324       if (Late && CurrTime == m_CurrTime)
1325         UpdateClock(1, false); //update the clock by 1 vblank
1326
1327     }
1328     return m_CurrTime;
1329   }
1330   else
1331   {
1332     int64_t ClockOffset = m_ClockOffset;
1333     SingleLock.Leave();
1334     Now = CurrentHostCounter();
1335     //sleep until the timestamp has passed
1336     SleepTime = (int)((Target - (Now + ClockOffset)) * 1000 / m_SystemFrequency);
1337     if (SleepTime > 0)
1338       ::Sleep(SleepTime);
1339
1340     Now = CurrentHostCounter();
1341     return Now + ClockOffset;
1342   }
1343 }
1344
1345
1346 void CVideoReferenceClock::SendVblankSignal()
1347 {
1348   m_VblankEvent.Set();
1349 }
1350
1351 #define MAXVBLANKDELAY 13LL
1352 //guess when the next vblank should happen,
1353 //based on the refreshrate and when the previous one happened
1354 //increase that by 30% to allow for errors
1355 int64_t CVideoReferenceClock::TimeOfNextVblank()
1356 {
1357   return m_VblankTime + (m_SystemFrequency / m_RefreshRate * MAXVBLANKDELAY / 10LL);
1358 }
1359
1360 //for the codec information screen
1361 bool CVideoReferenceClock::GetClockInfo(int& MissedVblanks, double& ClockSpeed, int& RefreshRate)
1362 {
1363   if (m_UseVblank)
1364   {
1365     MissedVblanks = m_TotalMissedVblanks;
1366     ClockSpeed = m_ClockSpeed * 100.0;
1367     RefreshRate = (int)m_RefreshRate;
1368     return true;
1369   }
1370   return false;
1371 }
1372
1373 void CVideoReferenceClock::SetFineAdjust(double fineadjust)
1374 {
1375   CSingleLock SingleLock(m_CritSection);
1376   m_fineadjust = fineadjust;
1377 }
1378
1379 CVideoReferenceClock g_VideoReferenceClock;