2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "VideoSyncD3D.h"
11 #include "Utils/MathUtils.h"
12 #include "Utils/TimeUtils.h"
13 #include "cores/VideoPlayer/VideoReferenceClock.h"
14 #include "rendering/dx/DeviceResources.h"
15 #include "rendering/dx/RenderContext.h"
16 #include "utils/StringUtils.h"
17 #include "utils/XTimeUtils.h"
18 #include "utils/log.h"
19 #include "windowing/GraphicContext.h"
23 #ifdef TARGET_WINDOWS_STORE
24 #include <winrt/Windows.Graphics.Display.Core.h>
27 using namespace std::chrono_literals
;
29 void CVideoSyncD3D::OnLostDisplay()
38 void CVideoSyncD3D::OnResetDisplay()
40 m_displayReset
= true;
43 void CVideoSyncD3D::RefreshChanged()
45 m_displayReset
= true;
48 bool CVideoSyncD3D::Setup()
50 CLog::Log(LOGDEBUG
, "CVideoSyncD3D: Setting up Direct3d");
51 std::unique_lock
<CCriticalSection
> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
52 DX::Windowing()->Register(this);
53 m_displayLost
= false;
54 m_displayReset
= false;
57 // we need a high priority thread to get accurate timing
58 if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
))
59 CLog::Log(LOGDEBUG
, "CVideoSyncD3D: SetThreadPriority failed");
61 CreateDXGIFactory1(IID_PPV_ARGS(m_factory
.ReleaseAndGetAddressOf()));
63 Microsoft::WRL::ComPtr
<IDXGIOutput
> pOutput
;
64 DX::DeviceResources::Get()->GetCachedOutputAndDesc(&pOutput
, &m_outputDesc
);
69 void CVideoSyncD3D::Run(CEvent
& stopEvent
)
72 int64_t LastVBlankTime
;
75 const int64_t systemFrequency
= CurrentHostFrequency();
76 bool validVBlank
{true};
78 // init the vblanktime
79 Now
= CurrentHostCounter();
82 while (!stopEvent
.Signaled() && !m_displayLost
&& !m_displayReset
)
85 Microsoft::WRL::ComPtr
<IDXGIOutput
> pOutput
;
86 DX::DeviceResources::Get()->GetCachedOutputAndDesc(pOutput
.ReleaseAndGetAddressOf(),
89 const int64_t WaitForVBlankStartTime
= CurrentHostCounter();
90 const HRESULT hr
= pOutput
? pOutput
->WaitForVBlank() : E_INVALIDARG
;
91 const int64_t WaitForVBlankElapsedTime
= CurrentHostCounter() - WaitForVBlankStartTime
;
93 // WaitForVBlank() can return very quickly due to errors or screen sleeping
94 if (!SUCCEEDED(hr
) || WaitForVBlankElapsedTime
- (systemFrequency
/ 1000) <= 0)
96 if (SUCCEEDED(hr
) && validVBlank
)
97 CLog::LogF(LOGWARNING
, "failed to detect vblank - screen asleep?");
100 CLog::LogF(LOGERROR
, "error waiting for vblank, {}", DX::GetErrorDescription(hr
));
104 // Wait a while, until vblank may have come back. No need for accurate sleep.
108 else if (!validVBlank
)
110 CLog::LogF(LOGWARNING
, "vblank detected - resuming reference clock updates");
114 // calculate how many vblanks happened
115 Now
= CurrentHostCounter();
116 VBlankTime
= (double)(Now
- LastVBlankTime
) / (double)systemFrequency
;
117 NrVBlanks
= MathUtils::round_int(VBlankTime
* m_fps
);
119 // update the vblank timestamp, update the clock and send a signal that we got a vblank
120 m_refClock
->UpdateClock(NrVBlanks
, Now
);
122 // save the timestamp of this vblank so we can calculate how many vblanks happened next time
123 LastVBlankTime
= Now
;
125 if (!m_factory
->IsCurrent())
127 CreateDXGIFactory1(IID_PPV_ARGS(m_factory
.ReleaseAndGetAddressOf()));
136 while (!stopEvent
.Signaled() && m_displayLost
&& !m_displayReset
)
138 KODI::TIME::Sleep(10ms
);
142 void CVideoSyncD3D::Cleanup()
144 CLog::Log(LOGDEBUG
, "CVideoSyncD3D: Cleaning up Direct3d");
147 DX::Windowing()->Unregister(this);
150 float CVideoSyncD3D::GetFps()
152 #ifdef TARGET_WINDOWS_DESKTOP
153 DEVMODEW sDevMode
= {};
154 sDevMode
.dmSize
= sizeof(sDevMode
);
156 if (EnumDisplaySettingsW(m_outputDesc
.DeviceName
, ENUM_CURRENT_SETTINGS
, &sDevMode
))
158 if ((sDevMode
.dmDisplayFrequency
+ 1) % 24 == 0 || (sDevMode
.dmDisplayFrequency
+ 1) % 30 == 0)
159 m_fps
= static_cast<float>(sDevMode
.dmDisplayFrequency
+ 1) / 1.001f
;
161 m_fps
= static_cast<float>(sDevMode
.dmDisplayFrequency
);
163 if (sDevMode
.dmDisplayFlags
& DM_INTERLACED
)
167 using namespace winrt::Windows::Graphics::Display::Core
;
169 auto hdmiInfo
= HdmiDisplayInformation::GetForCurrentView();
170 if (hdmiInfo
) // Xbox only
172 auto currentMode
= hdmiInfo
.GetCurrentDisplayMode();
173 m_fps
= static_cast<float>(currentMode
.RefreshRate());