blob: d95374f1bd79370efec045e30eaa276c597193b1 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
/*
* Copyright (C) 2005-2018 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSES/README.md for more information.
*/
#include "VideoSyncD3D.h"
#include "Utils/MathUtils.h"
#include "Utils/TimeUtils.h"
#include "rendering/dx/DeviceResources.h"
#include "rendering/dx/RenderContext.h"
#include "utils/StringUtils.h"
#include "utils/XTimeUtils.h"
#include "utils/log.h"
#include "windowing/GraphicContext.h"
#include <mutex>
using namespace std::chrono_literals;
void CVideoSyncD3D::OnLostDisplay()
{
if (!m_displayLost)
{
m_displayLost = true;
m_lostEvent.Wait();
}
}
void CVideoSyncD3D::OnResetDisplay()
{
m_displayReset = true;
}
void CVideoSyncD3D::RefreshChanged()
{
m_displayReset = true;
}
bool CVideoSyncD3D::Setup(PUPDATECLOCK func)
{
CLog::Log(LOGDEBUG, "CVideoSyncD3D: Setting up Direct3d");
std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext());
DX::Windowing()->Register(this);
m_displayLost = false;
m_displayReset = false;
m_lostEvent.Reset();
UpdateClock = func;
// we need a high priority thread to get accurate timing
if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
CLog::Log(LOGDEBUG, "CVideoSyncD3D: SetThreadPriority failed");
return true;
}
void CVideoSyncD3D::Run(CEvent& stopEvent)
{
int64_t Now;
int64_t LastVBlankTime;
int NrVBlanks;
double VBlankTime;
int64_t systemFrequency = CurrentHostFrequency();
// init the vblanktime
Now = CurrentHostCounter();
LastVBlankTime = Now;
m_lastUpdateTime = Now - systemFrequency;
while (!stopEvent.Signaled() && !m_displayLost && !m_displayReset)
{
// sleep until vblank
Microsoft::WRL::ComPtr<IDXGIOutput> pOutput;
DX::DeviceResources::Get()->GetOutput(&pOutput);
HRESULT hr = pOutput->WaitForVBlank();
// calculate how many vblanks happened
Now = CurrentHostCounter();
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, m_refClock);
// save the timestamp of this vblank so we can calculate how many vblanks happened next time
LastVBlankTime = Now;
if ((Now - m_lastUpdateTime) >= systemFrequency)
{
float fps = m_fps;
if (fps != GetFps())
break;
}
// because we had a vblank, sleep until half the refreshrate period because i think WaitForVBlank block any rendering stuf
// without sleeping we have freeze rendering
int SleepTime = (int)((LastVBlankTime + (systemFrequency / MathUtils::round_int(m_fps) / 2) - Now) * 1000 / systemFrequency);
if (SleepTime > 50)
SleepTime = 50; //failsafe
if (SleepTime > 0)
::Sleep(SleepTime);
}
m_lostEvent.Set();
while (!stopEvent.Signaled() && m_displayLost && !m_displayReset)
{
KODI::TIME::Sleep(10ms);
}
}
void CVideoSyncD3D::Cleanup()
{
CLog::Log(LOGDEBUG, "CVideoSyncD3D: Cleaning up Direct3d");
m_lostEvent.Set();
DX::Windowing()->Unregister(this);
}
float CVideoSyncD3D::GetFps()
{
DXGI_MODE_DESC DisplayMode = {};
DX::DeviceResources::Get()->GetDisplayMode(&DisplayMode);
m_fps = (DisplayMode.RefreshRate.Denominator != 0) ? (float)DisplayMode.RefreshRate.Numerator / (float)DisplayMode.RefreshRate.Denominator : 0.0f;
if (m_fps == 0.0)
m_fps = 60.0f;
if (DX::Windowing()->Interlaced())
{
m_fps *= 2;
}
return m_fps;
}
|