diff options
author | CrystalP <crystalp@kodi.tv> | 2023-10-15 09:35:05 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-15 09:35:05 -0400 |
commit | 09de11fa3eab4d4741b1db53b60d7af70d6533e1 (patch) | |
tree | cd3c7c3251daec54953ea005971839aab03d091f | |
parent | cf223f413fd62ea24db42af9719709a59f33c09a (diff) | |
parent | aad05083b5d7c767e71f51eafcf859ab82bdfae7 (diff) | |
download | xbmc-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.cpp | 23 | ||||
-rw-r--r-- | xbmc/rendering/dx/DeviceResources.h | 8 | ||||
-rw-r--r-- | xbmc/windowing/windows/VideoSyncD3D.cpp | 82 | ||||
-rw-r--r-- | xbmc/windowing/windows/VideoSyncD3D.h | 7 |
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; }; |