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 "WinSystemWin32DX.h"
11 #include "commons/ilog.h"
12 #include "rendering/dx/RenderContext.h"
13 #include "settings/DisplaySettings.h"
14 #include "settings/Settings.h"
15 #include "settings/SettingsComponent.h"
16 #include "utils/SystemInfo.h"
17 #include "utils/XTimeUtils.h"
18 #include "utils/log.h"
19 #include "windowing/GraphicContext.h"
20 #include "windowing/WindowSystemFactory.h"
22 #include "platform/win32/CharsetConverter.h"
23 #include "platform/win32/WIN32Util.h"
26 #include "utils/SystemInfo.h"
28 #pragma comment(lib, "dxgi.lib")
32 #pragma warning(disable: 4091)
33 #include <d3d10umddi.h>
34 #pragma warning(default: 4091)
37 using KODI::PLATFORM::WINDOWS::FromW
;
39 using namespace std::chrono_literals
;
41 // User Mode Driver hooks definitions
42 void APIENTRY
HookCreateResource(D3D10DDI_HDEVICE hDevice
, const D3D10DDIARG_CREATERESOURCE
* pResource
, D3D10DDI_HRESOURCE hResource
, D3D10DDI_HRTRESOURCE hRtResource
);
43 HRESULT APIENTRY
HookCreateDevice(D3D10DDI_HADAPTER hAdapter
, D3D10DDIARG_CREATEDEVICE
* pCreateData
);
44 HRESULT APIENTRY
HookOpenAdapter10_2(D3D10DDIARG_OPENADAPTER
*pOpenData
);
45 static PFND3D10DDI_OPENADAPTER s_fnOpenAdapter10_2
{ nullptr };
46 static PFND3D10DDI_CREATEDEVICE s_fnCreateDeviceOrig
{ nullptr };
47 static PFND3D10DDI_CREATERESOURCE s_fnCreateResourceOrig
{ nullptr };
49 void CWinSystemWin32DX::Register()
51 KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem
);
54 std::unique_ptr
<CWinSystemBase
> CWinSystemWin32DX::CreateWinSystem()
56 return std::make_unique
<CWinSystemWin32DX
>();
59 CWinSystemWin32DX::CWinSystemWin32DX() : CRenderSystemDX()
60 , m_hDriverModule(nullptr)
64 CWinSystemWin32DX::~CWinSystemWin32DX()
68 void CWinSystemWin32DX::PresentRenderImpl(bool rendered
)
71 m_deviceResources
->Present();
73 if (m_delayDispReset
&& m_dispResetTimer
.IsTimePast())
75 m_delayDispReset
= false;
80 KODI::TIME::Sleep(40ms
);
83 bool CWinSystemWin32DX::CreateNewWindow(const std::string
& name
, bool fullScreen
, RESOLUTION_INFO
& res
)
85 const MONITOR_DETAILS
* monitor
= GetDisplayDetails(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR
));
89 m_hMonitor
= monitor
->hMonitor
;
90 m_deviceResources
= DX::DeviceResources::Get();
91 // setting monitor before creating window for proper hooking into a driver
92 m_deviceResources
->SetMonitor(m_hMonitor
);
94 return CWinSystemWin32::CreateNewWindow(name
, fullScreen
, res
) && m_deviceResources
->HasValidDevice();
97 void CWinSystemWin32DX::SetWindow(HWND hWnd
) const
99 m_deviceResources
->SetWindow(hWnd
);
102 bool CWinSystemWin32DX::DestroyRenderSystem()
104 CRenderSystemDX::DestroyRenderSystem();
106 m_deviceResources
->Release();
107 m_deviceResources
.reset();
111 void CWinSystemWin32DX::SetDeviceFullScreen(bool fullScreen
, RESOLUTION_INFO
& res
)
113 if (m_deviceResources
->SetFullScreen(fullScreen
, res
))
119 bool CWinSystemWin32DX::ResizeWindow(int newWidth
, int newHeight
, int newLeft
, int newTop
)
121 CWinSystemWin32::ResizeWindow(newWidth
, newHeight
, newLeft
, newTop
);
122 CRenderSystemDX::OnResize();
127 void CWinSystemWin32DX::OnMove(int x
, int y
)
129 // do not handle moving at window creation because MonitorFromWindow
130 // returns default system monitor in case of m_hWnd is null
134 HMONITOR newMonitor
= MonitorFromWindow(m_hWnd
, MONITOR_DEFAULTTONEAREST
);
135 if (newMonitor
!= m_hMonitor
)
137 MONITOR_DETAILS
* details
= GetDisplayDetails(newMonitor
);
142 CDisplaySettings::GetInstance().SetMonitor(KODI::PLATFORM::WINDOWS::FromW(details
->MonitorNameW
));
143 m_deviceResources
->SetMonitor(newMonitor
);
144 m_hMonitor
= newMonitor
;
147 // Save window position if not fullscreen
148 if (!IsFullScreen() && (m_nLeft
!= x
|| m_nTop
!= y
))
152 const auto settings
= CServiceBroker::GetSettingsComponent()->GetSettings();
153 settings
->SetInt(SETTING_WINDOW_LEFT
, x
);
154 settings
->SetInt(SETTING_WINDOW_TOP
, y
);
159 bool CWinSystemWin32DX::DPIChanged(WORD dpi
, RECT windowRect
) const
161 // on Win10 FCU the OS keeps window size exactly the same size as it was
162 if (CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10_1709
))
165 m_deviceResources
->SetDpi(dpi
);
166 if (!IsAlteringWindow())
167 return __super::DPIChanged(dpi
, windowRect
);
172 void CWinSystemWin32DX::ReleaseBackBuffer()
174 m_deviceResources
->ReleaseBackBuffer();
177 void CWinSystemWin32DX::CreateBackBuffer()
179 m_deviceResources
->CreateBackBuffer();
182 void CWinSystemWin32DX::ResizeDeviceBuffers()
184 m_deviceResources
->ResizeBuffers();
187 bool CWinSystemWin32DX::IsStereoEnabled()
189 return m_deviceResources
->IsStereoEnabled();
192 void CWinSystemWin32DX::OnScreenChange(HMONITOR monitor
)
194 m_deviceResources
->SetMonitor(monitor
);
197 bool CWinSystemWin32DX::ChangeResolution(const RESOLUTION_INFO
&res
, bool forceChange
)
199 bool changed
= CWinSystemWin32::ChangeResolution(res
, forceChange
);
200 // this is a try to fix FCU issue after changing resolution
201 if (m_deviceResources
&& changed
)
202 m_deviceResources
->ResizeBuffers();
206 void CWinSystemWin32DX::OnResize(int width
, int height
)
208 if (!m_IsAlteringWindow
)
211 m_deviceResources
->SetLogicalSize(static_cast<float>(width
), static_cast<float>(height
));
213 if (!m_IsAlteringWindow
)
217 bool CWinSystemWin32DX::SetFullScreen(bool fullScreen
, RESOLUTION_INFO
& res
, bool blankOtherDisplays
)
219 bool const result
= CWinSystemWin32::SetFullScreen(fullScreen
, res
, blankOtherDisplays
);
220 CRenderSystemDX::OnResize();
224 void CWinSystemWin32DX::UninitHooks()
227 if (!s_fnOpenAdapter10_2
)
230 DetourTransactionBegin();
231 DetourUpdateThread(GetCurrentThread());
232 DetourDetach(reinterpret_cast<void**>(&s_fnOpenAdapter10_2
), HookOpenAdapter10_2
);
233 DetourTransactionCommit();
236 FreeLibrary(m_hDriverModule
);
237 m_hDriverModule
= nullptr;
241 void CWinSystemWin32DX::InitHooks(IDXGIOutput
* pOutput
)
243 DXGI_OUTPUT_DESC outputDesc
;
244 if (!pOutput
|| FAILED(pOutput
->GetDesc(&outputDesc
)))
247 DISPLAY_DEVICEW displayDevice
;
248 displayDevice
.cb
= sizeof(DISPLAY_DEVICEW
);
250 bool deviceFound
= false;
252 // delete exiting hooks.
253 if (s_fnOpenAdapter10_2
)
256 // enum devices to find matched
257 while (EnumDisplayDevicesW(nullptr, adapter
, &displayDevice
, 0))
259 if (wcscmp(displayDevice
.DeviceName
, outputDesc
.DeviceName
) == 0)
269 CLog::LogF(LOGDEBUG
, "Hooking into UserModeDriver on device {}. ",
270 FromW(displayDevice
.DeviceKey
));
271 const wchar_t* keyName
=
273 // on x64 system and x32 build use UserModeDriverNameWow key
274 CSysInfo::GetKernelBitness() == 64 ? keyName
= L
"UserModeDriverNameWow" :
276 L
"UserModeDriverName";
278 DWORD dwType
= REG_MULTI_SZ
;
281 DWORD valueLength
= sizeof(value
);
284 // to void \Registry\Machine at the beginning, we use shifted pointer at 18
285 if (ERROR_SUCCESS
== (lstat
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, displayDevice
.DeviceKey
+ 18, 0, KEY_READ
, &hKey
))
286 && ERROR_SUCCESS
== (lstat
= RegQueryValueExW(hKey
, keyName
, nullptr, &dwType
, (LPBYTE
)&value
, &valueLength
)))
288 // 1. registry value has a list of drivers for each API with the following format: dx9\0dx10\0dx11\0dx12\0\0
289 // 2. we split the value by \0
290 std::vector
<std::wstring
> drivers
;
291 const wchar_t* pValue
= value
;
294 drivers
.push_back(std::wstring(pValue
));
295 pValue
+= drivers
.back().size() + 1;
297 // no entries in the registry
300 // 3. we take only first three values (dx12 driver isn't needed if it exists ofc)
301 if (drivers
.size() > 3)
302 drivers
= std::vector
<std::wstring
>(drivers
.begin(), drivers
.begin() + 3);
303 // 4. and then iterate with reverse order to start iterate with the best candidate for d3d11 driver
304 for (auto it
= drivers
.rbegin(); it
!= drivers
.rend(); ++it
)
306 m_hDriverModule
= LoadLibraryW(it
->c_str());
307 if (m_hDriverModule
!= nullptr)
309 s_fnOpenAdapter10_2
= reinterpret_cast<PFND3D10DDI_OPENADAPTER
>(GetProcAddress(m_hDriverModule
, "OpenAdapter10_2"));
310 if (s_fnOpenAdapter10_2
!= nullptr)
312 DetourTransactionBegin();
313 DetourUpdateThread(GetCurrentThread());
314 DetourAttach(reinterpret_cast<void**>(&s_fnOpenAdapter10_2
), HookOpenAdapter10_2
);
315 if (NO_ERROR
== DetourTransactionCommit())
316 // install and activate hook into a driver
318 CLog::LogF(LOGDEBUG
, "D3D11 hook installed and activated.");
323 CLog::Log(LOGDEBUG
, __FUNCTION__
": Unable to install and activate D3D11 hook.");
324 s_fnOpenAdapter10_2
= nullptr;
325 FreeLibrary(m_hDriverModule
);
326 m_hDriverModule
= nullptr;
333 if (lstat
!= ERROR_SUCCESS
)
334 CLog::LogF(LOGDEBUG
, "error open registry key with error {}.", lstat
);
340 void CWinSystemWin32DX::FixRefreshRateIfNecessary(const D3D10DDIARG_CREATERESOURCE
* pResource
) const
342 if (pResource
&& pResource
->pPrimaryDesc
)
344 float refreshRate
= RATIONAL_TO_FLOAT(pResource
->pPrimaryDesc
->ModeDesc
.RefreshRate
);
345 if (refreshRate
> 10.0f
&& refreshRate
< 300.0f
)
348 if (pResource
->pPrimaryDesc
->ModeDesc
.ScanlineOrdering
> DXGI_DDI_MODE_SCANLINE_ORDER_PROGRESSIVE
)
351 uint32_t refreshNum
, refreshDen
;
352 DX::GetRefreshRatio(static_cast<uint32_t>(floor(m_fRefreshRate
)), &refreshNum
, &refreshDen
);
353 float diff
= fabs(refreshRate
- static_cast<float>(refreshNum
) / static_cast<float>(refreshDen
)) / refreshRate
;
355 "refreshRate: {:0.4f}, desired: {:0.4f}, deviation: {:.5f}, fixRequired: {}, {}",
356 refreshRate
, m_fRefreshRate
, diff
, (diff
> 0.0005 && diff
< 0.1) ? "yes" : "no",
357 pResource
->pPrimaryDesc
->Flags
);
358 if (diff
> 0.0005 && diff
< 0.1)
360 pResource
->pPrimaryDesc
->ModeDesc
.RefreshRate
.Numerator
= refreshNum
;
361 pResource
->pPrimaryDesc
->ModeDesc
.RefreshRate
.Denominator
= refreshDen
;
362 if (pResource
->pPrimaryDesc
->ModeDesc
.ScanlineOrdering
> DXGI_DDI_MODE_SCANLINE_ORDER_PROGRESSIVE
)
363 pResource
->pPrimaryDesc
->ModeDesc
.RefreshRate
.Numerator
*= 2;
364 CLog::LogF(LOGDEBUG
, "refreshRate fix applied -> {:0.3f}",
365 RATIONAL_TO_FLOAT(pResource
->pPrimaryDesc
->ModeDesc
.RefreshRate
));
371 void APIENTRY
HookCreateResource(D3D10DDI_HDEVICE hDevice
, const D3D10DDIARG_CREATERESOURCE
* pResource
, D3D10DDI_HRESOURCE hResource
, D3D10DDI_HRTRESOURCE hRtResource
)
373 if (pResource
&& pResource
->pPrimaryDesc
)
375 DX::Windowing()->FixRefreshRateIfNecessary(pResource
);
377 s_fnCreateResourceOrig(hDevice
, pResource
, hResource
, hRtResource
);
380 HRESULT APIENTRY
HookCreateDevice(D3D10DDI_HADAPTER hAdapter
, D3D10DDIARG_CREATEDEVICE
* pCreateData
)
382 HRESULT hr
= s_fnCreateDeviceOrig(hAdapter
, pCreateData
);
383 if (pCreateData
->pDeviceFuncs
->pfnCreateResource
)
385 CLog::LogF(LOGDEBUG
, "hook into pCreateData->pDeviceFuncs->pfnCreateResource");
386 s_fnCreateResourceOrig
= pCreateData
->pDeviceFuncs
->pfnCreateResource
;
387 pCreateData
->pDeviceFuncs
->pfnCreateResource
= HookCreateResource
;
392 HRESULT APIENTRY
HookOpenAdapter10_2(D3D10DDIARG_OPENADAPTER
*pOpenData
)
394 HRESULT hr
= s_fnOpenAdapter10_2(pOpenData
);
395 if (pOpenData
->pAdapterFuncs
->pfnCreateDevice
)
397 CLog::LogF(LOGDEBUG
, "hook into pOpenData->pAdapterFuncs->pfnCreateDevice");
398 s_fnCreateDeviceOrig
= pOpenData
->pAdapterFuncs
->pfnCreateDevice
;
399 pOpenData
->pAdapterFuncs
->pfnCreateDevice
= HookCreateDevice
;
404 bool CWinSystemWin32DX::IsHDRDisplay()
406 return (CWIN32Util::GetWindowsHDRStatus() != HDR_STATUS::HDR_UNSUPPORTED
);
409 HDR_STATUS
CWinSystemWin32DX::GetOSHDRStatus()
411 return CWIN32Util::GetWindowsHDRStatus();
414 HDR_STATUS
CWinSystemWin32DX::ToggleHDR()
416 return m_deviceResources
->ToggleHDR();
419 bool CWinSystemWin32DX::IsHDROutput() const
421 return m_deviceResources
->IsHDROutput();
424 bool CWinSystemWin32DX::IsTransferPQ() const
426 return m_deviceResources
->IsTransferPQ();
429 void CWinSystemWin32DX::SetHdrMetaData(DXGI_HDR_METADATA_HDR10
& hdr10
) const
431 m_deviceResources
->SetHdrMetaData(hdr10
);
434 void CWinSystemWin32DX::SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace
) const
436 m_deviceResources
->SetHdrColorSpace(colorSpace
);
439 DEBUG_INFO_RENDER
CWinSystemWin32DX::GetDebugInfo()
441 return m_deviceResources
->GetDebugInfo();
444 bool CWinSystemWin32DX::SupportsVideoSuperResolution()
446 return m_deviceResources
->IsSuperResolutionSupported();