aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCrystalP <crystalp@kodi.tv>2023-10-15 09:35:05 -0400
committerGitHub <noreply@github.com>2023-10-15 09:35:05 -0400
commit09de11fa3eab4d4741b1db53b60d7af70d6533e1 (patch)
treecd3c7c3251daec54953ea005971839aab03d091f
parentcf223f413fd62ea24db42af9719709a59f33c09a (diff)
parentaad05083b5d7c767e71f51eafcf859ab82bdfae7 (diff)
downloadxbmc-09de11fa3eab4d4741b1db53b60d7af70d6533e1.tar.xz
Merge pull request #23893 from CrystalP/fix-videosyncd3d
[Windows][UWP] VSync Detection Fixes and Improvements
-rw-r--r--xbmc/rendering/dx/DeviceResources.cpp23
-rw-r--r--xbmc/rendering/dx/DeviceResources.h8
-rw-r--r--xbmc/windowing/windows/VideoSyncD3D.cpp82
-rw-r--r--xbmc/windowing/windows/VideoSyncD3D.h7
4 files changed, 101 insertions, 19 deletions
diff --git a/xbmc/rendering/dx/DeviceResources.cpp b/xbmc/rendering/dx/DeviceResources.cpp
index 103809d591..9a00894f57 100644
--- a/xbmc/rendering/dx/DeviceResources.cpp
+++ b/xbmc/rendering/dx/DeviceResources.cpp
@@ -131,6 +131,28 @@ void DX::DeviceResources::GetOutput(IDXGIOutput** ppOutput) const
*ppOutput = pOutput.Detach();
}
+void DX::DeviceResources::GetCachedOutputAndDesc(IDXGIOutput** ppOutput,
+ DXGI_OUTPUT_DESC* outputDesc) const
+{
+ ComPtr<IDXGIOutput> pOutput;
+ if (m_output)
+ {
+ m_output.As(&pOutput);
+ *outputDesc = m_outputDesc;
+ }
+ else if (m_swapChain && SUCCEEDED(m_swapChain->GetContainingOutput(pOutput.GetAddressOf())) &&
+ pOutput)
+ {
+ pOutput->GetDesc(outputDesc);
+ }
+
+ if (!pOutput)
+ CLog::LogF(LOGWARNING, "unable to retrieve current output");
+
+ *ppOutput = pOutput.Detach();
+ return;
+}
+
DXGI_ADAPTER_DESC DX::DeviceResources::GetAdapterDesc() const
{
DXGI_ADAPTER_DESC desc{};
@@ -1045,6 +1067,7 @@ void DX::DeviceResources::HandleOutputChange(const std::function<bool(DXGI_OUTPU
if (cmpFunc(outputDesc))
{
output.As(&m_output);
+ m_outputDesc = outputDesc;
// check if adapter is changed
if (currentDesc.AdapterLuid.HighPart != foundDesc.AdapterLuid.HighPart
|| currentDesc.AdapterLuid.LowPart != foundDesc.AdapterLuid.LowPart)
diff --git a/xbmc/rendering/dx/DeviceResources.h b/xbmc/rendering/dx/DeviceResources.h
index 8d325ee6dc..0d9937c864 100644
--- a/xbmc/rendering/dx/DeviceResources.h
+++ b/xbmc/rendering/dx/DeviceResources.h
@@ -68,6 +68,13 @@ namespace DX
CD3DTexture& GetBackBuffer() { return m_backBufferTex; }
void GetOutput(IDXGIOutput** ppOutput) const;
+ /*!
+ * \brief Retrieve current output and output description. Use cached data first to avoid delays due
+ * to dxgi internal multithreading synchronization.
+ * \param output The output
+ * \param outputDesc The output description
+ */
+ void GetCachedOutputAndDesc(IDXGIOutput** output, DXGI_OUTPUT_DESC* outputDesc) const;
DXGI_ADAPTER_DESC GetAdapterDesc() const;
void GetDisplayMode(DXGI_MODE_DESC *mode) const;
@@ -152,6 +159,7 @@ namespace DX
Microsoft::WRL::ComPtr<IDXGIFactory2> m_dxgiFactory;
Microsoft::WRL::ComPtr<IDXGIAdapter1> m_adapter;
Microsoft::WRL::ComPtr<IDXGIOutput1> m_output;
+ DXGI_OUTPUT_DESC m_outputDesc{};
Microsoft::WRL::ComPtr<ID3D11Device1> m_d3dDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1> m_d3dContext;
diff --git a/xbmc/windowing/windows/VideoSyncD3D.cpp b/xbmc/windowing/windows/VideoSyncD3D.cpp
index 564df55fdd..084c34e0e0 100644
--- a/xbmc/windowing/windows/VideoSyncD3D.cpp
+++ b/xbmc/windowing/windows/VideoSyncD3D.cpp
@@ -20,6 +20,10 @@
#include <mutex>
+#ifdef TARGET_WINDOWS_STORE
+#include <winrt/Windows.Graphics.Display.Core.h>
+#endif
+
using namespace std::chrono_literals;
void CVideoSyncD3D::OnLostDisplay()
@@ -54,6 +58,11 @@ bool CVideoSyncD3D::Setup()
if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
CLog::Log(LOGDEBUG, "CVideoSyncD3D: SetThreadPriority failed");
+ CreateDXGIFactory1(IID_PPV_ARGS(m_factory.ReleaseAndGetAddressOf()));
+
+ Microsoft::WRL::ComPtr<IDXGIOutput> pOutput;
+ DX::DeviceResources::Get()->GetCachedOutputAndDesc(&pOutput, &m_outputDesc);
+
return true;
}
@@ -63,18 +72,44 @@ void CVideoSyncD3D::Run(CEvent& stopEvent)
int64_t LastVBlankTime;
int NrVBlanks;
double VBlankTime;
- int64_t systemFrequency = CurrentHostFrequency();
+ const int64_t systemFrequency = CurrentHostFrequency();
+ bool validVBlank{true};
// 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);
- pOutput->WaitForVBlank();
+ DX::DeviceResources::Get()->GetCachedOutputAndDesc(pOutput.ReleaseAndGetAddressOf(),
+ &m_outputDesc);
+
+ const int64_t WaitForVBlankStartTime = CurrentHostCounter();
+ const HRESULT hr = pOutput ? pOutput->WaitForVBlank() : E_INVALIDARG;
+ const int64_t WaitForVBlankElapsedTime = CurrentHostCounter() - WaitForVBlankStartTime;
+
+ // WaitForVBlank() can return very quickly due to errors or screen sleeping
+ if (!SUCCEEDED(hr) || WaitForVBlankElapsedTime - (systemFrequency / 1000) <= 0)
+ {
+ if (SUCCEEDED(hr) && validVBlank)
+ CLog::LogF(LOGWARNING, "failed to detect vblank - screen asleep?");
+
+ if (!SUCCEEDED(hr))
+ CLog::LogF(LOGERROR, "error waiting for vblank, {}", DX::GetErrorDescription(hr));
+
+ validVBlank = false;
+
+ // Wait a while, until vblank may have come back. No need for accurate sleep.
+ ::Sleep(250);
+ continue;
+ }
+ else if (!validVBlank)
+ {
+ CLog::LogF(LOGWARNING, "vblank detected - resuming reference clock updates");
+ validVBlank = true;
+ }
// calculate how many vblanks happened
Now = CurrentHostCounter();
@@ -87,20 +122,14 @@ void CVideoSyncD3D::Run(CEvent& stopEvent)
// save the timestamp of this vblank so we can calculate how many vblanks happened next time
LastVBlankTime = Now;
- if ((Now - m_lastUpdateTime) >= systemFrequency)
+ if (!m_factory->IsCurrent())
{
+ CreateDXGIFactory1(IID_PPV_ARGS(m_factory.ReleaseAndGetAddressOf()));
+
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();
@@ -120,14 +149,33 @@ void CVideoSyncD3D::Cleanup()
float CVideoSyncD3D::GetFps()
{
- DXGI_MODE_DESC DisplayMode = {};
- DX::DeviceResources::Get()->GetDisplayMode(&DisplayMode);
+#ifdef TARGET_WINDOWS_DESKTOP
+ DEVMODEW sDevMode = {};
+ sDevMode.dmSize = sizeof(sDevMode);
- m_fps = (DisplayMode.RefreshRate.Denominator != 0) ? (float)DisplayMode.RefreshRate.Numerator / (float)DisplayMode.RefreshRate.Denominator : 0.0f;
+ if (EnumDisplaySettingsW(m_outputDesc.DeviceName, ENUM_CURRENT_SETTINGS, &sDevMode))
+ {
+ if ((sDevMode.dmDisplayFrequency + 1) % 24 == 0 || (sDevMode.dmDisplayFrequency + 1) % 30 == 0)
+ m_fps = static_cast<float>(sDevMode.dmDisplayFrequency + 1) / 1.001f;
+ else
+ m_fps = static_cast<float>(sDevMode.dmDisplayFrequency);
+
+ if (sDevMode.dmDisplayFlags & DM_INTERLACED)
+ m_fps *= 2;
+ }
+#else
+ using namespace winrt::Windows::Graphics::Display::Core;
+
+ auto hdmiInfo = HdmiDisplayInformation::GetForCurrentView();
+ if (hdmiInfo) // Xbox only
+ {
+ auto currentMode = hdmiInfo.GetCurrentDisplayMode();
+ m_fps = static_cast<float>(currentMode.RefreshRate());
+ }
+#endif
if (m_fps == 0.0)
m_fps = 60.0f;
return m_fps;
}
-
diff --git a/xbmc/windowing/windows/VideoSyncD3D.h b/xbmc/windowing/windows/VideoSyncD3D.h
index 8c706dd88d..01664c08d4 100644
--- a/xbmc/windowing/windows/VideoSyncD3D.h
+++ b/xbmc/windowing/windows/VideoSyncD3D.h
@@ -12,11 +12,13 @@
#include "threads/Event.h"
#include "windowing/VideoSync.h"
+#include <dxgi1_5.h>
+
class CVideoSyncD3D : public CVideoSync, IDispResource
{
public:
CVideoSyncD3D(CVideoReferenceClock* clock)
- : CVideoSync(clock), m_displayLost(false), m_displayReset(false), m_lastUpdateTime(0)
+ : CVideoSync(clock), m_displayLost(false), m_displayReset(false)
{
}
bool Setup() override;
@@ -32,6 +34,7 @@ private:
volatile bool m_displayLost;
volatile bool m_displayReset;
CEvent m_lostEvent;
- int64_t m_lastUpdateTime;
+ DXGI_OUTPUT_DESC m_outputDesc{};
+ Microsoft::WRL::ComPtr<IDXGIFactory2> m_factory;
};