summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/windows/VideoSyncD3D.cpp
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;
}