diff options
author | Rainer Hochecker <fernetmenta@online.de> | 2014-10-17 08:55:16 +0200 |
---|---|---|
committer | Rainer Hochecker <fernetmenta@online.de> | 2014-10-31 21:45:03 +0100 |
commit | ce924e98a4f6be2e31dec8fa5846307cbe56f273 (patch) | |
tree | 08feb0577fc90f9bae9abe3874a2d21fc108db5a | |
parent | eda7cc530f9770879a771252179fe4872b878ddf (diff) |
videorefclock: refactoring
-rw-r--r-- | Kodi.xcodeproj/project.pbxproj | 20 | ||||
-rw-r--r-- | Makefile.in | 1 | ||||
-rw-r--r-- | project/VS2010Express/XBMC.vcxproj | 5 | ||||
-rw-r--r-- | project/VS2010Express/XBMC.vcxproj.filters | 14 | ||||
-rw-r--r-- | xbmc/cores/dvdplayer/DVDPlayerVideo.cpp | 5 | ||||
-rw-r--r-- | xbmc/osx/IOSEAGLView.mm | 3 | ||||
-rw-r--r-- | xbmc/settings/AdvancedSettings.cpp | 4 | ||||
-rw-r--r-- | xbmc/settings/AdvancedSettings.h | 3 | ||||
-rw-r--r-- | xbmc/video/VideoReferenceClock.cpp | 879 | ||||
-rw-r--r-- | xbmc/video/VideoReferenceClock.h | 91 | ||||
-rw-r--r-- | xbmc/video/videosync/Makefile | 7 | ||||
-rw-r--r-- | xbmc/video/videosync/VideoSync.h | 35 | ||||
-rw-r--r-- | xbmc/video/videosync/VideoSyncCocoa.cpp | 150 | ||||
-rw-r--r-- | xbmc/video/videosync/VideoSyncCocoa.h | 37 | ||||
-rw-r--r-- | xbmc/video/videosync/VideoSyncD3D.cpp | 208 | ||||
-rw-r--r-- | xbmc/video/videosync/VideoSyncD3D.h | 50 | ||||
-rw-r--r-- | xbmc/video/videosync/VideoSyncGLX.cpp | 287 | ||||
-rw-r--r-- | xbmc/video/videosync/VideoSyncGLX.h | 55 | ||||
-rw-r--r-- | xbmc/windowing/osx/WinSystemIOS.h | 5 | ||||
-rw-r--r-- | xbmc/windowing/osx/WinSystemIOS.mm | 11 |
20 files changed, 935 insertions, 935 deletions
diff --git a/Kodi.xcodeproj/project.pbxproj b/Kodi.xcodeproj/project.pbxproj index 82e3cea046..bf49f6402f 100644 --- a/Kodi.xcodeproj/project.pbxproj +++ b/Kodi.xcodeproj/project.pbxproj @@ -1285,6 +1285,9 @@ DF93D7F61444B568007C6459 /* HDHomeRunDirectory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF93D7F51444B568007C6459 /* HDHomeRunDirectory.cpp */; }; DF98D98C1434F47D00A6EBE1 /* SkinVariable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF98D98A1434F47D00A6EBE1 /* SkinVariable.cpp */; }; DF9A71EE1639C8F6005ECB2E /* HTTPFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DF9A71EC1639C8F6005ECB2E /* HTTPFile.cpp */; }; + DFA0E8C519FD6BEE00269A92 /* VideoSyncCocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFA0E8C319FD6BEE00269A92 /* VideoSyncCocoa.cpp */; }; + DFA0E8C719FD7BD100269A92 /* VideoSyncCocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFA0E8C319FD6BEE00269A92 /* VideoSyncCocoa.cpp */; }; + DFA0E8C819FD7BD100269A92 /* VideoSyncCocoa.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFA0E8C319FD6BEE00269A92 /* VideoSyncCocoa.cpp */; }; DFA8157E16713B1200E4E597 /* WakeOnAccess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFA8157C16713B1200E4E597 /* WakeOnAccess.cpp */; }; DFAB049813F8376700B70BFB /* InertialScrollingHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAB049613F8376700B70BFB /* InertialScrollingHandler.cpp */; }; DFAF6A4F16EBAE3800D6AE12 /* RssManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFAF6A4D16EBAE3800D6AE12 /* RssManager.cpp */; }; @@ -5012,6 +5015,9 @@ DF98D98B1434F47D00A6EBE1 /* SkinVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SkinVariable.h; sourceTree = "<group>"; }; DF9A71EC1639C8F6005ECB2E /* HTTPFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTTPFile.cpp; sourceTree = "<group>"; }; DF9A71ED1639C8F6005ECB2E /* HTTPFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPFile.h; sourceTree = "<group>"; }; + DFA0E8C219FD6BEE00269A92 /* VideoSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VideoSync.h; path = videosync/VideoSync.h; sourceTree = "<group>"; }; + DFA0E8C319FD6BEE00269A92 /* VideoSyncCocoa.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = VideoSyncCocoa.cpp; path = videosync/VideoSyncCocoa.cpp; sourceTree = "<group>"; }; + DFA0E8C419FD6BEE00269A92 /* VideoSyncCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VideoSyncCocoa.h; path = videosync/VideoSyncCocoa.h; sourceTree = "<group>"; }; DFA8157C16713B1200E4E597 /* WakeOnAccess.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WakeOnAccess.cpp; sourceTree = "<group>"; }; DFA8157D16713B1200E4E597 /* WakeOnAccess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WakeOnAccess.h; sourceTree = "<group>"; }; DFAB049613F8376700B70BFB /* InertialScrollingHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InertialScrollingHandler.cpp; sourceTree = "<group>"; }; @@ -7213,6 +7219,7 @@ isa = PBXGroup; children = ( 43FAC88612D6363B00F67914 /* dialogs */, + DFA0E8C619FD6BFC00269A92 /* videosync */, 43FAC88812D6364800F67914 /* windows */, 7C62F24010505BC7002AD2C1 /* Bookmark.cpp */, 7C62F24110505BC7002AD2C1 /* Bookmark.h */, @@ -8307,6 +8314,16 @@ path = view; sourceTree = "<group>"; }; + DFA0E8C619FD6BFC00269A92 /* videosync */ = { + isa = PBXGroup; + children = ( + DFA0E8C219FD6BEE00269A92 /* VideoSync.h */, + DFA0E8C319FD6BEE00269A92 /* VideoSyncCocoa.cpp */, + DFA0E8C419FD6BEE00269A92 /* VideoSyncCocoa.h */, + ); + name = videosync; + sourceTree = "<group>"; + }; DFB15B1F15F8FB8100CDF0DE /* osx */ = { isa = PBXGroup; children = ( @@ -11646,6 +11663,7 @@ 7CCDA209192753E30074CF51 /* ContentDirectorywSearchSCPD.cpp in Sources */, 7CCDA214192753E30074CF51 /* PltDidl.cpp in Sources */, 7CCDA21D192753E30074CF51 /* PltFileMediaServer.cpp in Sources */, + DFA0E8C519FD6BEE00269A92 /* VideoSyncCocoa.cpp in Sources */, 7CCDA226192753E30074CF51 /* PltMediaBrowser.cpp in Sources */, 7CCDA22F192753E30074CF51 /* PltMediaCache.cpp in Sources */, 7CCDA238192753E30074CF51 /* PltMediaItem.cpp in Sources */, @@ -11978,6 +11996,7 @@ DFF0F16F17528350002DA3A4 /* DVDDemuxPVRClient.cpp in Sources */, DFF0F17017528350002DA3A4 /* DVDDemuxShoutcast.cpp in Sources */, DFF0F17117528350002DA3A4 /* DVDDemuxUtils.cpp in Sources */, + DFA0E8C819FD7BD100269A92 /* VideoSyncCocoa.cpp in Sources */, DFF0F17217528350002DA3A4 /* DVDDemuxVobsub.cpp in Sources */, DFF0F17317528350002DA3A4 /* DVDFactoryDemuxer.cpp in Sources */, DFF0F17417528350002DA3A4 /* DVDFactoryInputStream.cpp in Sources */, @@ -13661,6 +13680,7 @@ E49913DA174E5F8D00741B6D /* Picture.cpp in Sources */, E49913DB174E5F8D00741B6D /* PictureInfoLoader.cpp in Sources */, E49913DC174E5F8D00741B6D /* PictureInfoTag.cpp in Sources */, + DFA0E8C719FD7BD100269A92 /* VideoSyncCocoa.cpp in Sources */, E49913DD174E5F8D00741B6D /* PictureThumbLoader.cpp in Sources */, E49913DE174E5F8D00741B6D /* SlideShowPicture.cpp in Sources */, E49913DF174E5F8D00741B6D /* PlayList.cpp in Sources */, diff --git a/Makefile.in b/Makefile.in index bd28e52ff2..1ce9039ac6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -96,6 +96,7 @@ DIRECTORY_ARCHIVES=$(DVDPLAYER_ARCHIVES) \ xbmc/storage/storage.a \ xbmc/utils/utils.a \ xbmc/video/dialogs/videodialogs.a \ + xbmc/video/videosync/videosync.a \ xbmc/video/video.a \ xbmc/video/windows/videowindows.a \ xbmc/view/view.a \ diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index b33e533115..c29fb2f17b 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -1139,6 +1139,7 @@ <ClCompile Include="..\..\xbmc\utils\win32\Win32Log.cpp" /> <ClCompile Include="..\..\xbmc\utils\XSLTUtils.cpp" /> <ClCompile Include="..\..\xbmc\video\PlayerController.cpp" /> + <ClCompile Include="..\..\xbmc\video\videosync\VideoSyncD3D.cpp" /> <ClCompile Include="..\..\xbmc\video\VideoThumbLoader.cpp" /> <ClCompile Include="..\..\xbmc\music\MusicThumbLoader.cpp" /> <ClCompile Include="..\..\xbmc\ThumbnailCache.cpp" /> @@ -2025,6 +2026,8 @@ <ClInclude Include="..\..\xbmc\DatabaseManager.h" /> <ClInclude Include="..\..\xbmc\ThumbLoader.h" /> <ClInclude Include="..\..\xbmc\video\PlayerController.h" /> + <ClInclude Include="..\..\xbmc\video\videosync\VideoSync.h" /> + <ClInclude Include="..\..\xbmc\video\videosync\VideoSyncD3D.h" /> <ClInclude Include="..\..\xbmc\video\VideoThumbLoader.h" /> <ClInclude Include="..\..\xbmc\music\MusicThumbLoader.h" /> <ClInclude Include="..\..\xbmc\ThumbnailCache.h" /> @@ -2551,4 +2554,4 @@ </VisualStudio> </ProjectExtensions> <Import Project="$(SolutionDir)\$(ProjectFileName).targets.user" Condition="Exists('$(SolutionDir)\$(ProjectFileName).targets.user')" /> -</Project> +</Project>
\ No newline at end of file diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index ca16ec8c1a..dbb9b9d41f 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -319,6 +319,9 @@ <Filter Include="utils\win32"> <UniqueIdentifier>{3adbba6a-6fbf-4192-b215-108d94bde1e0}</UniqueIdentifier> </Filter> + <Filter Include="video\videosync"> + <UniqueIdentifier>{9775d5c0-c640-4606-a625-e6cdcf9f959e}</UniqueIdentifier> + </Filter> </ItemGroup> <ItemGroup> <ClCompile Include="..\..\xbmc\win32\pch.cpp"> @@ -3077,6 +3080,9 @@ <ClCompile Include="..\..\xbmc\filesystem\BlurayFile.cpp"> <Filter>filesystem</Filter> </ClCompile> + <ClCompile Include="..\..\xbmc\video\videosync\VideoSyncD3D.cpp"> + <Filter>video\videosync</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\..\xbmc\win32\pch.h"> @@ -6022,6 +6028,12 @@ <ClInclude Include="..\..\xbmc\filesystem\BlurayFile.h"> <Filter>filesystem</Filter> </ClInclude> + <ClInclude Include="..\..\xbmc\video\videosync\VideoSyncD3D.h"> + <Filter>video\videosync</Filter> + </ClInclude> + <ClInclude Include="..\..\xbmc\video\videosync\VideoSync.h"> + <Filter>video\videosync</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc"> @@ -6067,4 +6079,4 @@ <Filter>interfaces\swig</Filter> </None> </ItemGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp index e039c2b938..5a758ab714 100644 --- a/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayerVideo.cpp @@ -164,7 +164,7 @@ CDVDPlayerVideo::CDVDPlayerVideo( CDVDClock* pClock CDVDPlayerVideo::~CDVDPlayerVideo() { StopThread(); - g_VideoReferenceClock.StopThread(); + g_VideoReferenceClock.Stop(); } double CDVDPlayerVideo::GetOutputDelay() @@ -205,9 +205,6 @@ bool CDVDPlayerVideo::OpenStream( CDVDStreamInfo &hint ) if(CSettings::Get().GetBool("videoplayer.usedisplayasclock") && !g_VideoReferenceClock.IsRunning()) { g_VideoReferenceClock.Create(); - //we have to wait for the clock to start otherwise alsa can cause trouble - if (!g_VideoReferenceClock.WaitStarted(2000)) - CLog::Log(LOGDEBUG, "g_VideoReferenceClock didn't start in time"); } if(m_messageQueue.IsInited()) diff --git a/xbmc/osx/IOSEAGLView.mm b/xbmc/osx/IOSEAGLView.mm index bf09e90833..50cdf80986 100644 --- a/xbmc/osx/IOSEAGLView.mm +++ b/xbmc/osx/IOSEAGLView.mm @@ -36,6 +36,7 @@ #include "utils/TimeUtils.h" #include "Util.h" #include "XbmcContext.h" +#include "WindowingFactory.h" #undef BOOL #import <QuartzCore/QuartzCore.h> @@ -457,7 +458,7 @@ if (animationThread && [animationThread isExecuting] == YES) { if (g_VideoReferenceClock.IsRunning()) - g_VideoReferenceClock.VblankHandler(CurrentHostCounter(), displayFPS); + g_Windowing.VblankHandler(CurrentHostCounter(), displayFPS); } [pool release]; } diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp index 3aff9f61ad..7d048723ca 100644 --- a/xbmc/settings/AdvancedSettings.cpp +++ b/xbmc/settings/AdvancedSettings.cpp @@ -363,8 +363,6 @@ void CAdvancedSettings::Initialize() m_bPVRAutoScanIconsUserSet = false; m_iPVRNumericChannelSwitchTimeout = 1000; - m_measureRefreshrate = false; - m_cacheMemBufferSize = 1024 * 1024 * 20; m_networkBufferMode = 0; // Default (buffer all internet streams/filesystems) // the following setting determines the readRate of a player data @@ -1093,8 +1091,6 @@ void CAdvancedSettings::ParseSettingsFile(const CStdString &file) XMLUtils::GetInt(pPVR, "numericchannelswitchtimeout", m_iPVRNumericChannelSwitchTimeout, 50, 60000); } - XMLUtils::GetBoolean(pRootElement, "measurerefreshrate", m_measureRefreshrate); - TiXmlElement* pDatabase = pRootElement->FirstChildElement("videodatabase"); if (pDatabase) { diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h index 1d9021e28a..7df586e444 100644 --- a/xbmc/settings/AdvancedSettings.h +++ b/xbmc/settings/AdvancedSettings.h @@ -365,9 +365,6 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler bool m_bPVRAutoScanIconsUserSet; /*!< @brief mark channel icons populated by auto scan as "user set" */ int m_iPVRNumericChannelSwitchTimeout; /*!< @brief time in ms before the numeric dialog auto closes when confirmchannelswitch is disabled */ - bool m_measureRefreshrate; //when true the videoreferenceclock will measure the refreshrate when direct3d is used - //otherwise it will use the windows refreshrate - DatabaseSettings m_databaseMusic; // advanced music database setup DatabaseSettings m_databaseVideo; // advanced video database setup DatabaseSettings m_databaseTV; // advanced tv database setup diff --git a/xbmc/video/VideoReferenceClock.cpp b/xbmc/video/VideoReferenceClock.cpp index 6d84b60613..fb10282f81 100644 --- a/xbmc/video/VideoReferenceClock.cpp +++ b/xbmc/video/VideoReferenceClock.cpp @@ -26,127 +26,60 @@ #include "utils/TimeUtils.h" #include "utils/StringUtils.h" #include "threads/SingleLock.h" +#include "guilib/GraphicContext.h" +#include "video/videosync/VideoSync.h" -#if defined(HAS_GLX) && defined(HAS_XRANDR) - #include <sstream> - #include <X11/extensions/Xrandr.h> - #include "windowing/WindowingFactory.h" - #include "guilib/GraphicContext.h" -#elif defined(TARGET_DARWIN_OSX) - #include <QuartzCore/CVDisplayLink.h> - #include "osx/CocoaInterface.h" -#elif defined(TARGET_DARWIN_IOS) - #include "windowing/WindowingFactory.h" -#elif defined(TARGET_WINDOWS) && defined(HAS_DX) - #pragma comment (lib,"d3d9.lib") - #if (D3DX_SDK_VERSION >= 42) //aug 2009 sdk and up there is no dxerr9 anymore - #include <Dxerr.h> - #pragma comment (lib,"DxErr.lib") - #else - #include <dxerr9.h> - #define DXGetErrorString(hr) DXGetErrorString9(hr) - #define DXGetErrorDescription(hr) DXGetErrorDescription9(hr) - #pragma comment (lib,"Dxerr9.lib") - #endif - #include "windowing/WindowingFactory.h" - #include "settings/AdvancedSettings.h" +#if defined(HAS_GLX) +#include "video/videosync/VideoSyncGLX.h" +#endif +#if defined(TARGET_WINDOWS) +#include "video/videosync/VideoSyncD3D.h" +#endif +#if defined(TARGET_DARWIN) +#include "video/videosync/VideoSyncCocoa.h" #endif using namespace std; -#if defined(TARGET_WINDOWS) && defined(HAS_DX) - - void CD3DCallback::Reset() - { - m_devicevalid = true; - m_deviceused = false; - } - - void CD3DCallback::OnDestroyDevice() - { - CSingleLock lock(m_critsection); - m_devicevalid = false; - while (m_deviceused) - { - lock.Leave(); - m_releaseevent.Wait(); - lock.Enter(); - } - } - - void CD3DCallback::OnCreateDevice() - { - CSingleLock lock(m_critsection); - m_devicevalid = true; - m_createevent.Set(); - } - - void CD3DCallback::Aquire() - { - CSingleLock lock(m_critsection); - while(!m_devicevalid) - { - lock.Leave(); - m_createevent.Wait(); - lock.Enter(); - } - m_deviceused = true; - } - - void CD3DCallback::Release() - { - CSingleLock lock(m_critsection); - m_deviceused = false; - m_releaseevent.Set(); - } - - bool CD3DCallback::IsValid() - { - return m_devicevalid; - } - -#endif - -CVideoReferenceClock::CVideoReferenceClock() : CThread("VideoReferenceClock") +CVideoReferenceClock::CVideoReferenceClock() : CThread("RefClock") { m_SystemFrequency = CurrentHostFrequency(); m_ClockSpeed = 1.0; m_ClockOffset = 0; m_TotalMissedVblanks = 0; m_UseVblank = false; - m_Started.Reset(); m_CurrTime = 0; m_LastIntTime = 0; m_CurrTimeFract = 0.0; - m_LastRefreshTime = 0; m_fineadjust = 0.0; m_RefreshRate = 0.0; - m_PrevRefreshRate = 0; m_MissedVblanks = 0; m_RefreshChanged = 0; m_VblankTime = 0; -#if defined(HAS_GLX) && defined(HAS_XRANDR) - m_glXWaitVideoSyncSGI = NULL; - m_glXGetVideoSyncSGI = NULL; - m_Dpy = NULL; - m_vInfo = NULL; - m_Window = 0; - m_Context = NULL; -#endif + m_pVideoSync = NULL; } CVideoReferenceClock::~CVideoReferenceClock() { -#if defined(HAS_GLX) - // some ATI voodoo, if we don't close the display, we crash on exit - if (m_Dpy) +} + +void CVideoReferenceClock::Stop() +{ + CSingleExit lock(g_graphicsContext); + StopThread(); +} + +void CVideoReferenceClock::CBUpdateClock(int NrVBlanks, uint64_t time) +{ { - XCloseDisplay(m_Dpy); - m_Dpy = NULL; + CSingleLock lock(g_VideoReferenceClock.m_CritSection); + g_VideoReferenceClock.m_VblankTime = time; + g_VideoReferenceClock.UpdateClock(NrVBlanks, true); } -#endif + + g_VideoReferenceClock.SendVblankSignal(); } void CVideoReferenceClock::Process() @@ -154,33 +87,23 @@ void CVideoReferenceClock::Process() bool SetupSuccess = false; int64_t Now; -#if defined(TARGET_WINDOWS) && defined(HAS_DX) - //register callback - m_D3dCallback.Reset(); - g_Windowing.Register(&m_D3dCallback); -#endif -#if defined(HAS_GLX) && defined(HAS_XRANDR) - g_Windowing.Register(this); - m_xrrEvent = false; -#endif - while(!m_bStop) { //set up the vblank clock -#if defined(HAS_GLX) && defined(HAS_XRANDR) - SetupSuccess = SetupGLX(); -#elif defined(TARGET_WINDOWS) && defined(HAS_DX) - SetupSuccess = SetupD3D(); -#elif defined(TARGET_DARWIN) - SetupSuccess = SetupCocoa(); -#elif defined(HAS_GLX) - CLog::Log(LOGDEBUG, "CVideoReferenceClock: compiled without RandR support"); +#if defined(HAS_GLX) + m_pVideoSync = new CVideoSyncGLX(); #elif defined(TARGET_WINDOWS) - CLog::Log(LOGDEBUG, "CVideoReferenceClock: only available on directx build"); -#else - CLog::Log(LOGDEBUG, "CVideoReferenceClock: no implementation available"); + m_pVideoSync = new CVideoSyncD3D(); +#elif defined(TARGET_DARWIN) + m_pVideoSync = new CVideoSyncCocoa(); #endif + if (m_pVideoSync) + { + SetupSuccess = m_pVideoSync->Setup(CBUpdateClock); + UpdateRefreshrate(); + } + CSingleLock SingleLock(m_CritSection); Now = CurrentHostCounter(); m_CurrTime = Now + m_ClockOffset; //add the clock offset from the previous time we stopped @@ -190,7 +113,7 @@ void CVideoReferenceClock::Process() m_TotalMissedVblanks = 0; m_fineadjust = 1.0; m_RefreshChanged = 0; - m_Started.Set(); + m_MissedVblanks = 0; if (SetupSuccess) { @@ -199,14 +122,7 @@ void CVideoReferenceClock::Process() SingleLock.Leave(); //run the clock -#if defined(HAS_GLX) && defined(HAS_XRANDR) - RunGLX(); -#elif defined(TARGET_WINDOWS) && defined(HAS_DX) - RunD3D(); -#elif defined(TARGET_DARWIN) - RunCocoa(); -#endif - + m_pVideoSync->Run(m_bStop); } else { @@ -221,650 +137,14 @@ void CVideoReferenceClock::Process() SingleLock.Leave(); //clean up the vblank clock -#if defined(HAS_GLX) && defined(HAS_XRANDR) - CleanupGLX(); - if (m_xrrEvent) - { - m_releaseEvent.Set(); - while (!m_bStop) - { - if (m_resetEvent.WaitMSec(100)) - break; - } - m_xrrEvent = false; - } -#elif defined(TARGET_WINDOWS) && defined(HAS_DX) - CleanupD3D(); -#elif defined(TARGET_DARWIN) - CleanupCocoa(); -#endif - if (!SetupSuccess) break; - } - -#if defined(TARGET_WINDOWS) && defined(HAS_DX) - g_Windowing.Unregister(&m_D3dCallback); -#endif -#if defined(HAS_GLX) - g_Windowing.Unregister(this); -#endif -} - -bool CVideoReferenceClock::WaitStarted(int MSecs) -{ - //not waiting here can cause issues with alsa - return m_Started.WaitMSec(MSecs); -} - -#if defined(HAS_GLX) && defined(HAS_XRANDR) - -void CVideoReferenceClock::OnLostDevice() -{ - if (!m_xrrEvent) - { - m_releaseEvent.Reset(); - m_resetEvent.Reset(); - m_xrrEvent = true; - m_releaseEvent.Wait(); - } -} - -void CVideoReferenceClock::OnResetDevice() -{ - m_xrrEvent = false; - m_resetEvent.Set(); -} - -bool CVideoReferenceClock::SetupGLX() -{ - int singleBufferAttributes[] = { - GLX_RGBA, - GLX_RED_SIZE, 0, - GLX_GREEN_SIZE, 0, - GLX_BLUE_SIZE, 0, - None - }; - - int ReturnV, SwaMask; - unsigned int GlxTest; - XSetWindowAttributes Swa; - - m_vInfo = NULL; - m_Context = NULL; - m_Window = 0; - - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setting up GLX"); - - if (!m_Dpy) - { - m_Dpy = XOpenDisplay(NULL); - if (!m_Dpy) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Unable to open display"); - return false; - } - } - - if (!glXQueryExtension(m_Dpy, NULL, NULL)) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: X server does not support GLX"); - return false; - } - - bool ExtensionFound = false; - istringstream Extensions(glXQueryExtensionsString(m_Dpy, g_Windowing.GetCurrentScreen())); - string ExtensionStr; - - while (!ExtensionFound) - { - Extensions >> ExtensionStr; - if (Extensions.fail()) + m_pVideoSync->Cleanup(); + delete m_pVideoSync; + m_pVideoSync = NULL; + if (!SetupSuccess) break; - - if (ExtensionStr == "GLX_SGI_video_sync") - ExtensionFound = true; - } - - if (!ExtensionFound) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: X server does not support GLX_SGI_video_sync"); - return false; - } - - m_vInfo = glXChooseVisual(m_Dpy, g_Windowing.GetCurrentScreen(), singleBufferAttributes); - if (!m_vInfo) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXChooseVisual returned NULL"); - return false; - } - - Swa.border_pixel = 0; - Swa.event_mask = StructureNotifyMask; - Swa.colormap = XCreateColormap(m_Dpy, g_Windowing.GetWindow(), m_vInfo->visual, AllocNone ); - SwaMask = CWBorderPixel | CWColormap | CWEventMask; - - m_Window = XCreateWindow(m_Dpy, g_Windowing.GetWindow(), 0, 0, 256, 256, 0, - m_vInfo->depth, InputOutput, m_vInfo->visual, SwaMask, &Swa); - - m_Context = glXCreateContext(m_Dpy, m_vInfo, NULL, True); - if (!m_Context) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXCreateContext returned NULL"); - return false; - } - - ReturnV = glXMakeCurrent(m_Dpy, m_Window, m_Context); - if (ReturnV != True) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV); - return false; - } - - m_glXWaitVideoSyncSGI = (int (*)(int, int, unsigned int*))glXGetProcAddress((const GLubyte*)"glXWaitVideoSyncSGI"); - if (!m_glXWaitVideoSyncSGI) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI not found"); - return false; - } - - ReturnV = m_glXWaitVideoSyncSGI(2, 0, &GlxTest); - if (ReturnV) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI returned %i", ReturnV); - return false; - } - - m_glXGetVideoSyncSGI = (int (*)(unsigned int*))glXGetProcAddress((const GLubyte*)"glXGetVideoSyncSGI"); - if (!m_glXGetVideoSyncSGI) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXGetVideoSyncSGI not found"); - return false; - } - - ReturnV = m_glXGetVideoSyncSGI(&GlxTest); - if (ReturnV) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXGetVideoSyncSGI returned %i", ReturnV); - return false; - } - - XRRSizes(m_Dpy, m_vInfo->screen, &ReturnV); - if (ReturnV == 0) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: RandR not supported"); - return false; - } - - UpdateRefreshrate(true); //forced refreshrate update - m_MissedVblanks = 0; - - return true; -} - -void CVideoReferenceClock::CleanupGLX() -{ - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Cleaning up GLX"); - - if (m_vInfo) - { - XFree(m_vInfo); - m_vInfo = NULL; - } - if (m_Context) - { - glXMakeCurrent(m_Dpy, None, NULL); - glXDestroyContext(m_Dpy, m_Context); - m_Context = NULL; - } - if (m_Window) - { - XDestroyWindow(m_Dpy, m_Window); - m_Window = 0; - } - - //ati saves the Display* in their libGL, if we close it here, we crash - if (m_Dpy) - { - XCloseDisplay(m_Dpy); - m_Dpy = NULL; } } -void CVideoReferenceClock::RunGLX() -{ - unsigned int PrevVblankCount; - unsigned int VblankCount; - int ReturnV; - bool IsReset = false; - int64_t Now; - - CSingleLock SingleLock(m_CritSection); - SingleLock.Leave(); - - //get the current vblank counter - m_glXGetVideoSyncSGI(&VblankCount); - PrevVblankCount = VblankCount; - - while(!m_bStop) - { - if (m_xrrEvent) - return; - - //wait for the next vblank - ReturnV = m_glXWaitVideoSyncSGI(2, (VblankCount + 1) % 2, &VblankCount); - m_glXGetVideoSyncSGI(&VblankCount); //the vblank count returned by glXWaitVideoSyncSGI is not always correct - Now = CurrentHostCounter(); //get the timestamp of this vblank - - if(ReturnV) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI returned %i", ReturnV); - return; - } - - if (VblankCount > PrevVblankCount) - { - //update the vblank timestamp, update the clock and send a signal that we got a vblank - SingleLock.Enter(); - m_VblankTime = Now; - UpdateClock((int)(VblankCount - PrevVblankCount), true); - SingleLock.Leave(); - SendVblankSignal(); - IsReset = false; - } - else - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Vblank counter has reset"); - - //only try reattaching once - if (IsReset) - return; - - //because of a bug in the nvidia driver, glXWaitVideoSyncSGI breaks when the vblank counter resets - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detaching glX context"); - ReturnV = glXMakeCurrent(m_Dpy, None, NULL); - if (ReturnV != True) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV); - return; - } - - //sleep here so we don't busy spin when this constantly happens, for example when the display went to sleep - Sleep(1000); - - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Attaching glX context"); - ReturnV = glXMakeCurrent(m_Dpy, m_Window, m_Context); - if (ReturnV != True) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV); - return; - } - - m_glXGetVideoSyncSGI(&VblankCount); - - IsReset = true; - } - PrevVblankCount = VblankCount; - } -} - -#elif defined(TARGET_WINDOWS) && defined(HAS_DX) - -void CVideoReferenceClock::RunD3D() -{ - D3DRASTER_STATUS RasterStatus; - int64_t Now; - int64_t LastVBlankTime; - unsigned int LastLine; - int NrVBlanks; - double VBlankTime; - int ReturnV; - - CSingleLock SingleLock(m_CritSection); - SingleLock.Leave(); - - //get the scanline we're currently at - m_D3dDev->GetRasterStatus(0, &RasterStatus); - if (RasterStatus.InVBlank) LastLine = 0; - else LastLine = RasterStatus.ScanLine; - - //init the vblanktime - Now = CurrentHostCounter(); - LastVBlankTime = Now; - - while(!m_bStop && m_D3dCallback.IsValid()) - { - //get the scanline we're currently at - ReturnV = m_D3dDev->GetRasterStatus(0, &RasterStatus); - if (ReturnV != D3D_OK) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetRasterStatus returned returned %s: %s", - DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); - return; - } - - //if InVBlank is set, or the current scanline is lower than the previous scanline, a vblank happened - if ((RasterStatus.InVBlank && LastLine > 0) || (RasterStatus.ScanLine < LastLine)) - { - //calculate how many vblanks happened - Now = CurrentHostCounter() - m_SystemFrequency * RasterStatus.ScanLine / (m_Height * MathUtils::round_int(m_RefreshRate)); - VBlankTime = (double)(Now - LastVBlankTime) / (double)m_SystemFrequency; - NrVBlanks = MathUtils::round_int(VBlankTime * m_RefreshRate); - - //update the vblank timestamp, update the clock and send a signal that we got a vblank - SingleLock.Enter(); - m_VblankTime = Now; - UpdateClock(NrVBlanks, true); - SingleLock.Leave(); - SendVblankSignal(); - - if (UpdateRefreshrate()) - { - //we have to measure the refreshrate again - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Displaymode changed"); - return; - } - - //save the timestamp of this vblank so we can calculate how many vblanks happened next time - LastVBlankTime = Now; - - //because we had a vblank, sleep until half the refreshrate period - Now = CurrentHostCounter(); - int SleepTime = (int)((LastVBlankTime + (m_SystemFrequency / MathUtils::round_int(m_RefreshRate) / 2) - Now) * 1000 / m_SystemFrequency); - if (SleepTime > 100) SleepTime = 100; //failsafe - if (SleepTime > 0) ::Sleep(SleepTime); - } - else - { - ::Sleep(1); - } - - if (RasterStatus.InVBlank) LastLine = 0; - else LastLine = RasterStatus.ScanLine; - } -} - -//how many times we measure the refreshrate -#define NRMEASURES 6 -//how long to measure in milliseconds -#define MEASURETIME 250 - -bool CVideoReferenceClock::SetupD3D() -{ - int ReturnV; - - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setting up Direct3d"); - - m_D3dCallback.Aquire(); - - //get d3d device - m_D3dDev = g_Windowing.Get3DDevice(); - - //we need a high priority thread to get accurate timing - if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) - CLog::Log(LOGDEBUG, "CVideoReferenceClock: SetThreadPriority failed"); - - D3DCAPS9 DevCaps; - ReturnV = m_D3dDev->GetDeviceCaps(&DevCaps); - if (ReturnV != D3D_OK) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetDeviceCaps returned %s: %s", - DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); - return false; - } - - if ((DevCaps.Caps & D3DCAPS_READ_SCANLINE) != D3DCAPS_READ_SCANLINE) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Hardware does not support GetRasterStatus"); - return false; - } - - D3DRASTER_STATUS RasterStatus; - ReturnV = m_D3dDev->GetRasterStatus(0, &RasterStatus); - if (ReturnV != D3D_OK) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetRasterStatus returned returned %s: %s", - DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); - return false; - } - - D3DDISPLAYMODE DisplayMode; - ReturnV = m_D3dDev->GetDisplayMode(0, &DisplayMode); - if (ReturnV != D3D_OK) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetDisplayMode returned returned %s: %s", - DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); - return false; - } - - //forced update of windows refreshrate - UpdateRefreshrate(true); - - if (g_advancedSettings.m_measureRefreshrate) - { - //measure the refreshrate a couple times - list<double> Measures; - for (int i = 0; i < NRMEASURES; i++) - Measures.push_back(MeasureRefreshrate(MEASURETIME)); - - //build up a string of measured rates - CStdString StrRates; - for (list<double>::iterator it = Measures.begin(); it != Measures.end(); it++) - StrRates += StringUtils::Format("%.2f ", *it); - - //get the top half of the measured rates - Measures.sort(); - double RefreshRate = 0.0; - int NrMeasurements = 0; - while (NrMeasurements < NRMEASURES / 2 && !Measures.empty()) - { - if (Measures.back() > 0.0) - { - RefreshRate += Measures.back(); - NrMeasurements++; - } - Measures.pop_back(); - } - - if (NrMeasurements < NRMEASURES / 2) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: refreshrate measurements: %s, unable to get a good measurement", - StrRates.c_str(), m_RefreshRate); - return false; - } - - RefreshRate /= NrMeasurements; - m_RefreshRate = RefreshRate; - - CLog::Log(LOGDEBUG, "CVideoReferenceClock: refreshrate measurements: %s, assuming %i hertz", StrRates.c_str(), m_RefreshRate); - } - else - { - m_RefreshRate = m_PrevRefreshRate; - if (MathUtils::round_int(m_RefreshRate) == 23 || - MathUtils::round_int(m_RefreshRate) == 29 || - MathUtils::round_int(m_RefreshRate) == 59) - m_RefreshRate++; - - if (m_Interlaced) - { - m_RefreshRate *= 2; - CLog::Log(LOGDEBUG, "CVideoReferenceClock: display is interlaced"); - } - - CLog::Log(LOGDEBUG, "CVideoReferenceClock: detected refreshrate: %i hertz, assuming %i hertz", m_PrevRefreshRate, MathUtils::round_int(m_RefreshRate)); - } - - m_MissedVblanks = 0; - - return true; -} - -double CVideoReferenceClock::MeasureRefreshrate(int MSecs) -{ - D3DRASTER_STATUS RasterStatus; - int64_t Now; - int64_t Target; - int64_t Prev; - int64_t AvgInterval; - int64_t MeasureCount; - unsigned int LastLine; - int ReturnV; - - Now = CurrentHostCounter(); - Target = Now + (m_SystemFrequency * MSecs / 1000); - Prev = -1; - AvgInterval = 0; - MeasureCount = 0; - - //start measuring vblanks - LastLine = 0; - while(Now <= Target) - { - ReturnV = m_D3dDev->GetRasterStatus(0, &RasterStatus); - Now = CurrentHostCounter(); - if (ReturnV != D3D_OK) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: GetRasterStatus returned returned %s: %s", - DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); - return -1.0; - } - - if ((RasterStatus.InVBlank && LastLine != 0) || (!RasterStatus.InVBlank && RasterStatus.ScanLine < LastLine)) - { //we got a vblank - if (Prev != -1) //need two for a measurement - { - AvgInterval += Now - Prev; //save how long this vblank lasted - MeasureCount++; - } - Prev = Now; //save this time for the next measurement - } - - //save the current scanline - if (RasterStatus.InVBlank) - LastLine = 0; - else - LastLine = RasterStatus.ScanLine; - - ::Sleep(1); - } - - if (MeasureCount < 1) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Didn't measure any vblanks"); - return -1.0; - } - - double fRefreshRate = 1.0 / ((double)AvgInterval / (double)MeasureCount / (double)m_SystemFrequency); - - return fRefreshRate; -} - -void CVideoReferenceClock::CleanupD3D() -{ - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Cleaning up Direct3d"); - m_D3dCallback.Release(); -} - -#elif defined(TARGET_DARWIN) -#if defined(TARGET_DARWIN_OSX) -// Called by the Core Video Display Link whenever it's appropriate to render a frame. -static CVReturn DisplayLinkCallBack(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) -{ - double fps = 60.0; - - if (inNow->videoRefreshPeriod > 0) - fps = (double)inOutputTime->videoTimeScale / (double)inOutputTime->videoRefreshPeriod; - - // Create an autorelease pool (necessary to call into non-Obj-C code from Obj-C code) - void* pool = Cocoa_Create_AutoReleasePool(); - - CVideoReferenceClock *VideoReferenceClock = reinterpret_cast<CVideoReferenceClock*>(displayLinkContext); - VideoReferenceClock->VblankHandler(inOutputTime->hostTime, fps); - - // Destroy the autorelease pool - Cocoa_Destroy_AutoReleasePool(pool); - - return kCVReturnSuccess; -} -#endif -bool CVideoReferenceClock::SetupCocoa() -{ - CLog::Log(LOGDEBUG, "CVideoReferenceClock: setting up Cocoa"); - - //init the vblank timestamp - m_LastVBlankTime = CurrentHostCounter(); - m_MissedVblanks = 0; - m_RefreshRate = 60; //init the refreshrate so we don't get any division by 0 errors - - #if defined(TARGET_DARWIN_IOS) - { - g_Windowing.InitDisplayLink(); - } - #else - if (!Cocoa_CVDisplayLinkCreate((void*)DisplayLinkCallBack, reinterpret_cast<void*>(this))) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Cocoa_CVDisplayLinkCreate failed"); - return false; - } - else - #endif - { - UpdateRefreshrate(true); - return true; - } -} - -void CVideoReferenceClock::RunCocoa() -{ - //because cocoa has a vblank callback, we just keep sleeping until we're asked to stop the thread - while(!m_bStop) - { - Sleep(1000); - } -} - -void CVideoReferenceClock::CleanupCocoa() -{ - CLog::Log(LOGDEBUG, "CVideoReferenceClock: cleaning up Cocoa"); - #if defined(TARGET_DARWIN_IOS) - g_Windowing.DeinitDisplayLink(); - #else - Cocoa_CVDisplayLinkRelease(); - #endif -} - -void CVideoReferenceClock::VblankHandler(int64_t nowtime, double fps) -{ - int NrVBlanks; - double VBlankTime; - int RefreshRate = MathUtils::round_int(fps); - - CSingleLock SingleLock(m_CritSection); - - if (RefreshRate != MathUtils::round_int(m_RefreshRate)) - { - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: %f hertz, rounding to %i hertz", fps, RefreshRate); - m_RefreshRate = RefreshRate; - } - m_LastRefreshTime = m_CurrTime; - - //calculate how many vblanks happened - VBlankTime = (double)(nowtime - m_LastVBlankTime) / (double)m_SystemFrequency; - NrVBlanks = MathUtils::round_int(VBlankTime * m_RefreshRate); - - //save the timestamp of this vblank so we can calculate how many happened next time - m_LastVBlankTime = nowtime; - - //update the vblank timestamp, update the clock and send a signal that we got a vblank - m_VblankTime = nowtime; - UpdateClock(NrVBlanks, true); - - SingleLock.Leave(); - - SendVblankSignal(); - UpdateRefreshrate(); -} -#endif - //this is called from the vblank run function and from CVideoReferenceClock::Wait in case of a late update void CVideoReferenceClock::UpdateClock(int NrVBlanks, bool CheckMissed) { @@ -979,79 +259,12 @@ double CVideoReferenceClock::GetSpeed() return 1.0; } -bool CVideoReferenceClock::UpdateRefreshrate(bool Forced /*= false*/) +void CVideoReferenceClock::UpdateRefreshrate() { - //if the graphicscontext signaled that the refreshrate changed, we check it about one second later - if (m_RefreshChanged == 1 && !Forced) - { - m_LastRefreshTime = m_CurrTime; - m_RefreshChanged = 2; - return false; - } - - //update the refreshrate about once a second, or update immediately if a forced update is required - if (m_CurrTime - m_LastRefreshTime < m_SystemFrequency && !Forced) - return false; - - if (Forced) - m_LastRefreshTime = 0; - else - m_LastRefreshTime = m_CurrTime; - -#if defined(HAS_GLX) && defined(HAS_XRANDR) - - if (!Forced) - m_RefreshChanged = 0; - - if (!Forced) //refreshrate did not change - return false; - CSingleLock SingleLock(m_CritSection); - m_RefreshRate = g_graphicsContext.GetFPS(); + m_RefreshRate = m_pVideoSync->GetFps(); CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: %.3f hertz", m_RefreshRate); - - return true; - -#elif defined(TARGET_WINDOWS) && defined(HAS_DX) - - D3DDISPLAYMODE DisplayMode; - m_D3dDev->GetDisplayMode(0, &DisplayMode); - - //0 indicates adapter default - if (DisplayMode.RefreshRate == 0) - DisplayMode.RefreshRate = 60; - - if (m_PrevRefreshRate != DisplayMode.RefreshRate || m_Width != DisplayMode.Width || m_Height != DisplayMode.Height || - m_Interlaced != g_Windowing.Interlaced() || Forced ) - { - m_PrevRefreshRate = DisplayMode.RefreshRate; - m_Width = DisplayMode.Width; - m_Height = DisplayMode.Height; - m_Interlaced = g_Windowing.Interlaced(); - return true; - } - - return false; - -#elif defined(TARGET_DARWIN) - #if defined(TARGET_DARWIN_IOS) - int RefreshRate = round(g_Windowing.GetDisplayLinkFPS() + 0.5); - #else - int RefreshRate = MathUtils::round_int(Cocoa_GetCVDisplayLinkRefreshPeriod()); - #endif - - if (RefreshRate != MathUtils::round_int(m_RefreshRate) || Forced) - { - CSingleLock SingleLock(m_CritSection); - CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detected refreshrate: %i hertz", RefreshRate); - m_RefreshRate = RefreshRate; - return true; - } - return false; -#endif - - return false; } //dvdplayer needs to know the refreshrate for matching the fps of the video playing to it diff --git a/xbmc/video/VideoReferenceClock.h b/xbmc/video/VideoReferenceClock.h index 6027031449..33a0adf568 100644 --- a/xbmc/video/VideoReferenceClock.h +++ b/xbmc/video/VideoReferenceClock.h @@ -19,47 +19,12 @@ * */ -#include "system.h" // for HAS_XRANDR, and Win32 types #include "threads/Thread.h" #include "threads/CriticalSection.h" -//TODO: get rid of #ifdef hell, abstract implementations in separate classes - -#if defined(HAS_GLX) && defined(HAS_XRANDR) - #include "system_gl.h" - #include <X11/X.h> - #include <X11/Xlib.h> - #include <GL/glx.h> - #include "guilib/DispResource.h" -#elif defined(TARGET_WINDOWS) && defined(HAS_DX) - #include <d3d9.h> - #include "guilib/D3DResource.h" - -class CD3DCallback : public ID3DResource -{ - public: - void Reset(); - void OnDestroyDevice(); - void OnCreateDevice(); - void Aquire(); - void Release(); - bool IsValid(); - - private: - bool m_devicevalid; - bool m_deviceused; - - CCriticalSection m_critsection; - CEvent m_createevent; - CEvent m_releaseevent; -}; - -#endif +class CVideoSync; class CVideoReferenceClock : public CThread -#if defined(HAS_GLX) && defined(HAS_XRANDR) - ,public IDispResource -#endif { public: CVideoReferenceClock(); @@ -71,86 +36,40 @@ class CVideoReferenceClock : public CThread double GetSpeed(); double GetRefreshRate(double* interval = NULL); int64_t Wait(int64_t Target); - bool WaitStarted(int MSecs); bool GetClockInfo(int& MissedVblanks, double& ClockSpeed, double& RefreshRate); void SetFineAdjust(double fineadjust); void RefreshChanged() { m_RefreshChanged = 1; } - -#if defined(TARGET_DARWIN) - void VblankHandler(int64_t nowtime, double fps); -#endif - -#if defined(HAS_GLX) && defined(HAS_XRANDR) - virtual void OnLostDevice(); - virtual void OnResetDevice(); -#endif + void Stop(); private: void Process(); - bool UpdateRefreshrate(bool Forced = false); + void UpdateRefreshrate(); void SendVblankSignal(); void UpdateClock(int NrVBlanks, bool CheckMissed); double UpdateInterval(); int64_t TimeOfNextVblank(); + static void CBUpdateClock(int NrVBlanks, uint64_t time); int64_t m_CurrTime; //the current time of the clock when using vblank as clock source int64_t m_LastIntTime; //last interpolated clock value, to make sure the clock doesn't go backwards double m_CurrTimeFract; //fractional part that is lost due to rounding when updating the clock double m_ClockSpeed; //the frequency of the clock set by dvdplayer int64_t m_ClockOffset; //the difference between the vblank clock and systemclock, set when vblank clock is stopped - int64_t m_LastRefreshTime; //last time we updated the refreshrate int64_t m_SystemFrequency; //frequency of the systemclock double m_fineadjust; bool m_UseVblank; //set to true when vblank is used as clock source double m_RefreshRate; //current refreshrate - int m_PrevRefreshRate; //previous refreshrate, used for log printing and getting refreshrate from nvidia-settings int m_MissedVblanks; //number of clock updates missed by the vblank clock int m_RefreshChanged; //1 = we changed the refreshrate, 2 = we should check the refreshrate forced int m_TotalMissedVblanks;//total number of clock updates missed, used by codec information screen int64_t m_VblankTime; //last time the clock was updated when using vblank as clock - CEvent m_Started; //set when the vblank clock is started CEvent m_VblankEvent; //set when a vblank happens CCriticalSection m_CritSection; -#if defined(HAS_GLX) && defined(HAS_XRANDR) - bool SetupGLX(); - void RunGLX(); - void CleanupGLX(); - - int (*m_glXWaitVideoSyncSGI) (int, int, unsigned int*); - int (*m_glXGetVideoSyncSGI) (unsigned int*); - - Display* m_Dpy; - XVisualInfo *m_vInfo; - Window m_Window; - GLXContext m_Context; - bool m_xrrEvent; - CEvent m_releaseEvent, m_resetEvent; - -#elif defined(TARGET_WINDOWS) && defined(HAS_DX) - bool SetupD3D(); - double MeasureRefreshrate(int MSecs); - void RunD3D(); - void CleanupD3D(); - - LPDIRECT3DDEVICE9 m_D3dDev; - CD3DCallback m_D3dCallback; - - unsigned int m_Width; - unsigned int m_Height; - bool m_Interlaced; - -#elif defined(TARGET_DARWIN) - bool SetupCocoa(); - void RunCocoa(); - void CleanupCocoa(); - - int64_t m_LastVBlankTime; //timestamp of the last vblank, used for calculating how many vblanks happened - //not the same as m_VblankTime -#endif + CVideoSync *m_pVideoSync; }; extern CVideoReferenceClock g_VideoReferenceClock; diff --git a/xbmc/video/videosync/Makefile b/xbmc/video/videosync/Makefile new file mode 100644 index 0000000000..ce748403f8 --- /dev/null +++ b/xbmc/video/videosync/Makefile @@ -0,0 +1,7 @@ +SRCS=VideoSyncGLX.cpp \ + VideoSyncCocoa.cpp \ + +LIB=videosync.a + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS)))
\ No newline at end of file diff --git a/xbmc/video/videosync/VideoSync.h b/xbmc/video/videosync/VideoSync.h new file mode 100644 index 0000000000..0b03f7dcbc --- /dev/null +++ b/xbmc/video/videosync/VideoSync.h @@ -0,0 +1,35 @@ +#pragma once +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +typedef void (*PUPDATECLOCK)(int NrVBlanks, uint64_t time); + +class CVideoSync +{ +public: + virtual ~CVideoSync() {}; + virtual bool Setup(PUPDATECLOCK func) = 0; + virtual void Run(volatile bool& stop) = 0; + virtual void Cleanup() = 0; + virtual float GetFps() = 0; +protected: + PUPDATECLOCK UpdateClock; + float m_fps; +}; diff --git a/xbmc/video/videosync/VideoSyncCocoa.cpp b/xbmc/video/videosync/VideoSyncCocoa.cpp new file mode 100644 index 0000000000..1e28183c5a --- /dev/null +++ b/xbmc/video/videosync/VideoSyncCocoa.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "system.h" + +#if defined(TARGET_DARWIN) +#include "utils/log.h" +#include "VideoSyncCocoa.h" +#include "utils/MathUtils.h" +#include "video/VideoReferenceClock.h" +#include "utils/TimeUtils.h" + +//ios specifics +#if defined(TARGET_DARWIN_IOS) +#include "windowing/WindowingFactory.h" +#endif + +//osx specifics +#if defined(TARGET_DARWIN_OSX) +#include <QuartzCore/CVDisplayLink.h> +#include "osx/CocoaInterface.h" +// Called by the Core Video Display Link whenever it's appropriate to render a frame. +static CVReturn DisplayLinkCallBack(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) +{ + double fps = 60.0; + + if (inNow->videoRefreshPeriod > 0) + fps = (double)inOutputTime->videoTimeScale / (double)inOutputTime->videoRefreshPeriod; + + // Create an autorelease pool (necessary to call into non-Obj-C code from Obj-C code) + void* pool = Cocoa_Create_AutoReleasePool(); + + CVideoSyncCocoa *VideoSyncCocoa = reinterpret_cast<CVideoSyncCocoa*>(displayLinkContext); + VideoSyncCocoa->VblankHandler(inOutputTime->hostTime, fps); + + // Destroy the autorelease pool + Cocoa_Destroy_AutoReleasePool(pool); + + return kCVReturnSuccess; +} +#endif + +void CVideoSyncCocoa::VblankHandler(int64_t nowtime, double fps) +{ + int NrVBlanks; + double VBlankTime; + int RefreshRate = MathUtils::round_int(fps); + + if (RefreshRate != MathUtils::round_int(m_fps)) + { + CLog::Log(LOGDEBUG, "CVideoSyncCocoa: Detected refreshrate: %f hertz, rounding to %i hertz", fps, RefreshRate); + UpdateFPS(fps); + } + + //calculate how many vblanks happened + VBlankTime = (double)(nowtime - m_LastVBlankTime) / (double)g_VideoReferenceClock.GetFrequency(); + NrVBlanks = MathUtils::round_int(VBlankTime * m_fps); + + //save the timestamp of this vblank so we can calculate how many happened next time + m_LastVBlankTime = nowtime; + + //update the vblank timestamp, update the clock and send a signal that we got a vblank + UpdateClock(NrVBlanks, nowtime); +} + +bool CVideoSyncCocoa::Setup(PUPDATECLOCK func) +{ + CLog::Log(LOGDEBUG, "CVideoSyncCocoa: setting up Cocoa"); + + //init the vblank timestamp + m_LastVBlankTime = CurrentHostCounter(); + UpdateClock = func; + +#if defined(TARGET_DARWIN_IOS) + { + g_Windowing.InitDisplayLink(this); + } +#else + if (!Cocoa_CVDisplayLinkCreate((void*)DisplayLinkCallBack, reinterpret_cast<void*>(this))) + { + CLog::Log(LOGDEBUG, "CVideoSyncCocoa: Cocoa_CVDisplayLinkCreate failed"); + return false; + } + else +#endif + { + GetFps();//UpdateRefreshrate(true); - FIXME?NEEDED? + return true; + } +} + +void CVideoSyncCocoa::Run(volatile bool& stop) +{ + //because cocoa has a vblank callback, we just keep sleeping until we're asked to stop the thread + while(!stop) + { + Sleep(1000); + } +} + +void CVideoSyncCocoa::Cleanup() +{ + CLog::Log(LOGDEBUG, "CVideoSyncCocoa: cleaning up Cocoa"); +#if defined(TARGET_DARWIN_IOS) + g_Windowing.DeinitDisplayLink(); +#else + Cocoa_CVDisplayLinkRelease(); +#endif +} + +void CVideoSyncCocoa::UpdateFPS(double fps) +{ + int fpsInt = MathUtils::round_int(fps); + + if (fpsInt != MathUtils::round_int(m_fps)) + { + CLog::Log(LOGDEBUG, "CVideoSyncCocoa: Detected refreshrate: %i hertz", fpsInt); + m_fps = fpsInt; + } +} + +float CVideoSyncCocoa::GetFps() +{ +#if defined(TARGET_DARWIN_IOS) + UpdateFPS(g_Windowing.GetDisplayLinkFPS() + 0.5); +#else + UpdateFPS(Cocoa_GetCVDisplayLinkRefreshPeriod()); +#endif + + return m_fps; +} + +#endif//TARGET_DARWIN diff --git a/xbmc/video/videosync/VideoSyncCocoa.h b/xbmc/video/videosync/VideoSyncCocoa.h new file mode 100644 index 0000000000..a7ddc73e56 --- /dev/null +++ b/xbmc/video/videosync/VideoSyncCocoa.h @@ -0,0 +1,37 @@ +#pragma once +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#if defined(TARGET_DARWIN) +#include "VideoSync.h" +class CVideoSyncCocoa : public CVideoSync +{ +public: + virtual bool Setup(PUPDATECLOCK func); + virtual void Run(volatile bool& stop); + virtual void Cleanup(); + virtual float GetFps(); + void VblankHandler(int64_t nowtime, double fps); +private: + void UpdateFPS(double fps); + int64_t m_LastVBlankTime; //timestamp of the last vblank, used for calculating how many vblanks happened +}; + +#endif diff --git a/xbmc/video/videosync/VideoSyncD3D.cpp b/xbmc/video/videosync/VideoSyncD3D.cpp new file mode 100644 index 0000000000..166635f724 --- /dev/null +++ b/xbmc/video/videosync/VideoSyncD3D.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "system.h" + +#if defined(TARGET_WINDOWS) + +#pragma comment (lib,"d3d9.lib") +#pragma comment (lib,"DxErr.lib") +#include <Dxerr.h> +#include "utils/log.h" +#include "Utils/TimeUtils.h" +#include "Utils/MathUtils.h" +#include "windowing\WindowingFactory.h" +#include "video/videosync/VideoSyncD3D.h" +#include "guilib/GraphicContext.h" + +void CVideoSyncD3D::OnDestroyDevice() +{ + if (!m_displayLost) + { + m_displayLost = true; + m_lostEvent.Wait(); + } +} + +void CVideoSyncD3D::OnResetDevice() +{ + m_displayReset = true; +} + +bool CVideoSyncD3D::Setup(PUPDATECLOCK func) +{ + int ReturnV; + CLog::Log(LOGDEBUG, "CVideoSyncD3D: Setting up Direct3d"); + CSingleLock lock(g_graphicsContext); + g_Windowing.Register(this); + m_displayLost = false; + m_displayReset = false; + m_lostEvent.Reset(); + UpdateClock = func; + + //get d3d device + m_D3dDev = g_Windowing.Get3DDevice(); + //we need a high priority thread to get accurate timing + if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) + CLog::Log(LOGDEBUG, "CVideoSyncD3D: SetThreadPriority failed"); + + D3DCAPS9 DevCaps; + ReturnV = m_D3dDev->GetDeviceCaps(&DevCaps); + + if (ReturnV != D3D_OK) + { + CLog::Log(LOGDEBUG, "CVideoSyncD3D: GetDeviceCaps returned %s: %s", + DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); + return false; + } + + if ((DevCaps.Caps & D3DCAPS_READ_SCANLINE) != D3DCAPS_READ_SCANLINE) + { + CLog::Log(LOGDEBUG, "CVideoSyncD3D: Hardware does not support GetRasterStatus"); + return false; + } + + D3DRASTER_STATUS RasterStatus; + ReturnV = m_D3dDev->GetRasterStatus(0, &RasterStatus); + if (ReturnV != D3D_OK) + { + CLog::Log(LOGDEBUG, "CVideoSyncD3D: GetRasterStatus returned returned %s: %s", + DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); + return false; + } + + D3DDISPLAYMODE DisplayMode; + ReturnV = m_D3dDev->GetDisplayMode(0, &DisplayMode); + if (ReturnV != D3D_OK) + { + CLog::Log(LOGDEBUG, "CVideoSyncD3D: GetDisplayMode returned returned %s: %s", + DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); + return false; + } + m_height = DisplayMode.Height; + + return true; +} + +void CVideoSyncD3D::Run(volatile bool& stop) +{ + D3DRASTER_STATUS RasterStatus; + int64_t Now; + int64_t LastVBlankTime; + unsigned int LastLine; + int NrVBlanks; + double VBlankTime; + int ReturnV; + int64_t systemFrequency = CurrentHostFrequency(); + + //get the scanline we're currently at + m_D3dDev->GetRasterStatus(0, &RasterStatus); + if (RasterStatus.InVBlank) + LastLine = 0; + else + LastLine = RasterStatus.ScanLine; + + //init the vblanktime + Now = CurrentHostCounter(); + LastVBlankTime = Now; + m_lastUpdateTime = Now - systemFrequency; + while (!stop && !m_displayLost && !m_displayReset) + { + //get the scanline we're currently at + ReturnV = m_D3dDev->GetRasterStatus(0, &RasterStatus); + if (ReturnV != D3D_OK) + { + CLog::Log(LOGDEBUG, "CVideoSyncD3D: GetRasterStatus returned returned %s: %s", + DXGetErrorString(ReturnV), DXGetErrorDescription(ReturnV)); + return; + } + + //if InVBlank is set, or the current scanline is lower than the previous scanline, a vblank happened + if ((RasterStatus.InVBlank && LastLine > 0) || (RasterStatus.ScanLine < LastLine)) + { + //calculate how many vblanks happened + Now = CurrentHostCounter() - systemFrequency * RasterStatus.ScanLine / (m_height * MathUtils::round_int(m_fps)); + VBlankTime = (double)(Now - LastVBlankTime) / (double)systemFrequency; + NrVBlanks = MathUtils::round_int(VBlankTime * m_fps); + //update the vblank timestamp, update the clock and send a signal that we got a vblank + UpdateClock(NrVBlanks, Now); + + //save the timestamp of this vblank so we can calculate how many vblanks happened next time + LastVBlankTime = Now; + //because we had a vblank, sleep until half the refreshrate period + Now = CurrentHostCounter(); + + if ((Now - m_lastUpdateTime) >= systemFrequency) + { + if (m_fps != GetFps()) + break; + } + + int SleepTime = (int)((LastVBlankTime + (systemFrequency / MathUtils::round_int(m_fps) / 2) - Now) * 1000 / systemFrequency); + if (SleepTime > 100) + SleepTime = 100; //failsafe + if (SleepTime > 0) + ::Sleep(SleepTime); + } + else + { + ::Sleep(1); + } + + if (RasterStatus.InVBlank) + LastLine = 0; + else + LastLine = RasterStatus.ScanLine; + } + + m_lostEvent.Set(); + while (!stop && m_displayLost && !m_displayReset) + { + Sleep(10); + } +} + +void CVideoSyncD3D::Cleanup() +{ + CLog::Log(LOGDEBUG, "CVideoSyncD3D: Cleaning up Direct3d"); + CSingleLock lock(g_graphicsContext); + + m_lostEvent.Set(); + g_Windowing.Unregister(this); +} + +float CVideoSyncD3D::GetFps() +{ + D3DDISPLAYMODE DisplayMode; + m_D3dDev->GetDisplayMode(0, &DisplayMode); + m_fps = DisplayMode.RefreshRate; + if (m_fps == 0) + m_fps = 60; + + if (m_fps == 23 || m_fps == 29 || m_fps == 59) + m_fps++; + + if (g_Windowing.Interlaced()) + { + m_fps *= 2; + } + return m_fps; +} +#endif diff --git a/xbmc/video/videosync/VideoSyncD3D.h b/xbmc/video/videosync/VideoSyncD3D.h new file mode 100644 index 0000000000..5ccbdd24f9 --- /dev/null +++ b/xbmc/video/videosync/VideoSyncD3D.h @@ -0,0 +1,50 @@ +#pragma once +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#if defined(TARGET_WINDOWS) + +#include "video/videosync/VideoSync.h" +#include <d3d9.h> +#include "guilib/D3DResource.h" +#include "threads/CriticalSection.h" +#include "threads/Event.h" + +class CVideoSyncD3D : public CVideoSync, ID3DResource +{ +public: + virtual bool Setup(PUPDATECLOCK func); + virtual void Run(volatile bool& stop); + virtual void Cleanup(); + virtual float GetFps(); + + virtual void OnCreateDevice() {} + virtual void OnDestroyDevice(); + virtual void OnResetDevice(); +private: + LPDIRECT3DDEVICE9 m_D3dDev; + int m_height; + volatile bool m_displayLost; + volatile bool m_displayReset; + CEvent m_lostEvent; + int64_t m_lastUpdateTime; +}; + +#endif diff --git a/xbmc/video/videosync/VideoSyncGLX.cpp b/xbmc/video/videosync/VideoSyncGLX.cpp new file mode 100644 index 0000000000..fe839045d3 --- /dev/null +++ b/xbmc/video/videosync/VideoSyncGLX.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "system.h" + +#if defined(HAS_GLX) + +#include "video/videosync/VideoSyncGLX.h" +#include <sstream> +#include <X11/extensions/Xrandr.h> +#include "windowing/WindowingFactory.h" +#include "guilib/GraphicContext.h" +#include "threads/SingleLock.h" +#include "utils/log.h" +#include "utils/TimeUtils.h" +#include <string> + +using namespace std; + +Display* CVideoSyncGLX::m_Dpy = NULL; + +void CVideoSyncGLX::OnLostDevice() +{ + if (!m_displayLost) + { + m_displayLost = true; + m_lostEvent.Wait(); + } +} + +void CVideoSyncGLX::OnResetDevice() +{ + m_displayReset = true; +} + +bool CVideoSyncGLX::Setup(PUPDATECLOCK func) +{ + CSingleLock lock(g_graphicsContext); + + m_glXWaitVideoSyncSGI = NULL; + m_glXGetVideoSyncSGI = NULL; + m_vInfo = NULL; + m_Window = 0; + m_Context = NULL; + UpdateClock = func; + + int singleBufferAttributes[] = { + GLX_RGBA, + GLX_RED_SIZE, 0, + GLX_GREEN_SIZE, 0, + GLX_BLUE_SIZE, 0, + None + }; + + int ReturnV, SwaMask; + unsigned int GlxTest; + XSetWindowAttributes Swa; + + m_vInfo = NULL; + m_Context = NULL; + m_Window = 0; + + CLog::Log(LOGDEBUG, "CVideoReferenceClock: Setting up GLX"); + + g_Windowing.Register(this); + + m_displayLost = false; + m_displayReset = false; + m_lostEvent.Reset(); + + if (!m_Dpy) + { + m_Dpy = XOpenDisplay(NULL); + if (!m_Dpy) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: Unable to open display"); + return false; + } + } + + if (!glXQueryExtension(m_Dpy, NULL, NULL)) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: X server does not support GLX"); + return false; + } + + bool ExtensionFound = false; + istringstream Extensions(glXQueryExtensionsString(m_Dpy, g_Windowing.GetCurrentScreen())); + string ExtensionStr; + + while (!ExtensionFound) + { + Extensions >> ExtensionStr; + if (Extensions.fail()) + break; + + if (ExtensionStr == "GLX_SGI_video_sync") + ExtensionFound = true; + } + + if (!ExtensionFound) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: X server does not support GLX_SGI_video_sync"); + return false; + } + + m_vInfo = glXChooseVisual(m_Dpy, g_Windowing.GetCurrentScreen(), singleBufferAttributes); + if (!m_vInfo) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXChooseVisual returned NULL"); + return false; + } + + Swa.border_pixel = 0; + Swa.event_mask = StructureNotifyMask; + Swa.colormap = XCreateColormap(m_Dpy, g_Windowing.GetWindow(), m_vInfo->visual, AllocNone ); + SwaMask = CWBorderPixel | CWColormap | CWEventMask; + + m_Window = XCreateWindow(m_Dpy, g_Windowing.GetWindow(), 0, 0, 256, 256, 0, + m_vInfo->depth, InputOutput, m_vInfo->visual, SwaMask, &Swa); + + m_Context = glXCreateContext(m_Dpy, m_vInfo, NULL, True); + if (!m_Context) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXCreateContext returned NULL"); + return false; + } + + ReturnV = glXMakeCurrent(m_Dpy, m_Window, m_Context); + if (ReturnV != True) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV); + return false; + } + + m_glXWaitVideoSyncSGI = (int (*)(int, int, unsigned int*))glXGetProcAddress((const GLubyte*)"glXWaitVideoSyncSGI"); + if (!m_glXWaitVideoSyncSGI) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI not found"); + return false; + } + + ReturnV = m_glXWaitVideoSyncSGI(2, 0, &GlxTest); + if (ReturnV) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI returned %i", ReturnV); + return false; + } + + m_glXGetVideoSyncSGI = (int (*)(unsigned int*))glXGetProcAddress((const GLubyte*)"glXGetVideoSyncSGI"); + if (!m_glXGetVideoSyncSGI) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXGetVideoSyncSGI not found"); + return false; + } + + ReturnV = m_glXGetVideoSyncSGI(&GlxTest); + if (ReturnV) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXGetVideoSyncSGI returned %i", ReturnV); + return false; + } + + return true; +} + +void CVideoSyncGLX::Run(volatile bool& stop) +{ + unsigned int PrevVblankCount; + unsigned int VblankCount; + int ReturnV; + bool IsReset = false; + int64_t Now; + + //get the current vblank counter + m_glXGetVideoSyncSGI(&VblankCount); + PrevVblankCount = VblankCount; + + while(!stop && !m_displayLost && !m_displayReset) + { + //wait for the next vblank + ReturnV = m_glXWaitVideoSyncSGI(2, (VblankCount + 1) % 2, &VblankCount); + m_glXGetVideoSyncSGI(&VblankCount); //the vblank count returned by glXWaitVideoSyncSGI is not always correct + Now = CurrentHostCounter(); //get the timestamp of this vblank + + if(ReturnV) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXWaitVideoSyncSGI returned %i", ReturnV); + return; + } + + if (VblankCount > PrevVblankCount) + { + UpdateClock((int)(VblankCount - PrevVblankCount), Now); + IsReset = false; + } + else + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: Vblank counter has reset"); + + //only try reattaching once + if (IsReset) + return; + + //because of a bug in the nvidia driver, glXWaitVideoSyncSGI breaks when the vblank counter resets + CLog::Log(LOGDEBUG, "CVideoReferenceClock: Detaching glX context"); + ReturnV = glXMakeCurrent(m_Dpy, None, NULL); + if (ReturnV != True) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV); + return; + } + + //sleep here so we don't busy spin when this constantly happens, for example when the display went to sleep + Sleep(1000); + + CLog::Log(LOGDEBUG, "CVideoReferenceClock: Attaching glX context"); + ReturnV = glXMakeCurrent(m_Dpy, m_Window, m_Context); + if (ReturnV != True) + { + CLog::Log(LOGDEBUG, "CVideoReferenceClock: glXMakeCurrent returned %i", ReturnV); + return; + } + + m_glXGetVideoSyncSGI(&VblankCount); + + IsReset = true; + } + PrevVblankCount = VblankCount; + } + m_lostEvent.Set(); + while(!stop && m_displayLost && !m_displayReset) + { + Sleep(10); + } +} + +void CVideoSyncGLX::Cleanup() +{ + CLog::Log(LOGDEBUG, "CVideoReferenceClock: Cleaning up GLX"); + CSingleLock lock(g_graphicsContext); + + if (m_vInfo) + { + XFree(m_vInfo); + m_vInfo = NULL; + } + if (m_Context) + { + glXMakeCurrent(m_Dpy, None, NULL); + glXDestroyContext(m_Dpy, m_Context); + m_Context = NULL; + } + if (m_Window) + { + XDestroyWindow(m_Dpy, m_Window); + m_Window = 0; + } + + m_lostEvent.Set(); + g_Windowing.Unregister(this); +} + +float CVideoSyncGLX::GetFps() +{ + m_fps = g_graphicsContext.GetFPS(); + return m_fps; +} + +#endif diff --git a/xbmc/video/videosync/VideoSyncGLX.h b/xbmc/video/videosync/VideoSyncGLX.h new file mode 100644 index 0000000000..38d719defc --- /dev/null +++ b/xbmc/video/videosync/VideoSyncGLX.h @@ -0,0 +1,55 @@ +#pragma once +/* + * Copyright (C) 2005-2014 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#if defined(HAS_GLX) + +#include "video/videosync/VideoSync.h" +#include "system_gl.h" +#include <X11/X.h> +#include <X11/Xlib.h> +#include <GL/glx.h> +#include "guilib/DispResource.h" +#include "threads/Event.h" + +class CVideoSyncGLX : public CVideoSync, IDispResource +{ +public: + virtual bool Setup(PUPDATECLOCK func); + virtual void Run(volatile bool& stop); + virtual void Cleanup(); + virtual float GetFps(); + virtual void OnLostDevice(); + virtual void OnResetDevice(); + +private: + int (*m_glXWaitVideoSyncSGI) (int, int, unsigned int*); + int (*m_glXGetVideoSyncSGI) (unsigned int*); + + static Display* m_Dpy; + XVisualInfo *m_vInfo; + Window m_Window; + GLXContext m_Context; + volatile bool m_displayLost; + volatile bool m_displayReset; + CEvent m_lostEvent; +}; + +#endif diff --git a/xbmc/windowing/osx/WinSystemIOS.h b/xbmc/windowing/osx/WinSystemIOS.h index 049e0c8291..c34110a6e6 100644 --- a/xbmc/windowing/osx/WinSystemIOS.h +++ b/xbmc/windowing/osx/WinSystemIOS.h @@ -30,6 +30,7 @@ #include "threads/CriticalSection.h" class IDispResource; +class CVideoSyncCocoa; class CWinSystemIOS : public CWinSystemBase, public CRenderSystemGLES { @@ -67,7 +68,8 @@ public: virtual int GetNumScreens(); virtual int GetCurrentScreen(); - void InitDisplayLink(void); + void InitDisplayLink(CVideoSyncCocoa *syncImpl); + void VblankHandler(int64_t nowtime, double fps); void DeinitDisplayLink(void); double GetDisplayLinkFPS(void); void OnAppFocusChange(bool focus); @@ -85,6 +87,7 @@ protected: CCriticalSection m_resourceSection; std::vector<IDispResource*> m_resources; bool m_bIsBackgrounded; + CVideoSyncCocoa *m_VideoSync; private: bool GetScreenResolution(int* w, int* h, double* fps, int screenIdx); diff --git a/xbmc/windowing/osx/WinSystemIOS.mm b/xbmc/windowing/osx/WinSystemIOS.mm index d1c7d64b94..5639a9604d 100644 --- a/xbmc/windowing/osx/WinSystemIOS.mm +++ b/xbmc/windowing/osx/WinSystemIOS.mm @@ -36,6 +36,7 @@ #include "utils/StringUtils.h" #include "guilib/DispResource.h" #include "threads/SingleLock.h" +#include "video/videosync/VideoSyncCocoa.h" #include <vector> #undef BOOL @@ -57,6 +58,7 @@ CWinSystemIOS::CWinSystemIOS() : CWinSystemBase() m_iVSyncErrors = 0; m_bIsBackgrounded = false; + m_VideoSync = NULL; } CWinSystemIOS::~CWinSystemIOS() @@ -344,8 +346,15 @@ void CWinSystemIOS::OnAppFocusChange(bool focus) (*i)->OnAppFocusChange(focus); } -void CWinSystemIOS::InitDisplayLink(void) +void CWinSystemIOS::VblankHandler(int64_t nowtime, double fps) { + if (m_VideoSync) + m_VideoSync->VblankHandler(nowtime, fps); +} + +void CWinSystemIOS::InitDisplayLink(CVideoSyncCocoa *syncImpl) +{ + m_VideoSync = syncImpl; } void CWinSystemIOS::DeinitDisplayLink(void) { |