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 "DeviceResources.h"
11 #include "DirectXHelper.h"
12 #include "RenderContext.h"
13 #include "ServiceBroker.h"
14 #include "guilib/GUIComponent.h"
15 #include "guilib/GUIWindowManager.h"
16 #include "messaging/ApplicationMessenger.h"
17 #include "settings/Settings.h"
18 #include "settings/SettingsComponent.h"
19 #include "utils/SystemInfo.h"
20 #include "utils/log.h"
21 #include "windowing/GraphicContext.h"
23 #include "platform/win32/CharsetConverter.h"
24 #include "platform/win32/WIN32Util.h"
26 #ifdef TARGET_WINDOWS_STORE
27 #include <winrt/Windows.Graphics.Display.Core.h>
31 #include <libavutil/rational.h>
36 #include <dxgidebug.h>
37 #pragma comment(lib, "dxgi.lib")
40 using namespace DirectX
;
41 using namespace Microsoft::WRL
;
42 using namespace Concurrency
;
45 using namespace Windows::Foundation
;
49 #define breakOnDebug __debugbreak()
54 CLog::LogF(LOGERROR, "function call at line {} ends with error: {}", __LINE__, \
55 CWIN32Util::FormatHRESULT(hr));
56 #define CHECK_ERR() if (FAILED(hr)) { LOG_HR(hr); breakOnDebug; return; }
57 #define RETURN_ERR(ret) if (FAILED(hr)) { LOG_HR(hr); breakOnDebug; return (##ret); }
59 bool DX::DeviceResources::CBackBuffer::Acquire(ID3D11Texture2D
* pTexture
)
64 D3D11_TEXTURE2D_DESC desc
;
65 pTexture
->GetDesc(&desc
);
68 m_height
= desc
.Height
;
69 m_format
= desc
.Format
;
76 std::shared_ptr
<DX::DeviceResources
> DX::DeviceResources::Get()
78 static std::shared_ptr
<DeviceResources
> sDeviceResources(new DeviceResources
);
79 return sDeviceResources
;
82 // Constructor for DeviceResources.
83 DX::DeviceResources::DeviceResources()
85 , m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1
)
88 , m_dpi(DisplayMetrics::Dpi100
)
89 , m_effectiveDpi(DisplayMetrics::Dpi100
)
90 , m_deviceNotify(nullptr)
91 , m_stereoEnabled(false)
92 , m_bDeviceCreated(false)
93 , m_IsHDROutput(false)
94 , m_IsTransferPQ(false)
98 DX::DeviceResources::~DeviceResources() = default;
100 void DX::DeviceResources::Release()
102 if (!m_bDeviceCreated
)
110 m_dxgiFactory
= nullptr;
112 m_deferrContext
= nullptr;
113 m_d3dContext
= nullptr;
114 m_d3dDevice
= nullptr;
115 m_bDeviceCreated
= false;
119 m_d3dDebug
->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY
| D3D11_RLDO_DETAIL
);
120 m_d3dDebug
= nullptr;
125 void DX::DeviceResources::GetOutput(IDXGIOutput
** ppOutput
) const
127 ComPtr
<IDXGIOutput
> pOutput
;
128 if (!m_swapChain
|| FAILED(m_swapChain
->GetContainingOutput(pOutput
.GetAddressOf())) || !pOutput
)
129 m_output
.As(&pOutput
);
130 *ppOutput
= pOutput
.Detach();
133 void DX::DeviceResources::GetCachedOutputAndDesc(IDXGIOutput
** ppOutput
,
134 DXGI_OUTPUT_DESC
* outputDesc
) const
136 ComPtr
<IDXGIOutput
> pOutput
;
139 m_output
.As(&pOutput
);
140 *outputDesc
= m_outputDesc
;
142 else if (m_swapChain
&& SUCCEEDED(m_swapChain
->GetContainingOutput(pOutput
.GetAddressOf())) &&
145 pOutput
->GetDesc(outputDesc
);
149 CLog::LogF(LOGWARNING
, "unable to retrieve current output");
151 *ppOutput
= pOutput
.Detach();
155 DXGI_ADAPTER_DESC
DX::DeviceResources::GetAdapterDesc() const
157 DXGI_ADAPTER_DESC desc
{};
160 m_adapter
->GetDesc(&desc
);
162 // GetDesc() returns VendorId == 0 in Xbox however, we need to know that
163 // GPU is AMD to apply workarounds in DXVA.cpp CheckCompatibility() same as desktop
164 if (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::Xbox
)
165 desc
.VendorId
= PCIV_AMD
;
170 void DX::DeviceResources::GetDisplayMode(DXGI_MODE_DESC
* mode
) const
172 DXGI_OUTPUT_DESC outDesc
;
173 ComPtr
<IDXGIOutput
> pOutput
;
174 DXGI_SWAP_CHAIN_DESC scDesc
;
179 m_swapChain
->GetDesc(&scDesc
);
181 GetOutput(pOutput
.GetAddressOf());
182 pOutput
->GetDesc(&outDesc
);
184 // desktop coords depend on DPI
185 mode
->Width
= DX::ConvertDipsToPixels(outDesc
.DesktopCoordinates
.right
- outDesc
.DesktopCoordinates
.left
, m_dpi
);
186 mode
->Height
= DX::ConvertDipsToPixels(outDesc
.DesktopCoordinates
.bottom
- outDesc
.DesktopCoordinates
.top
, m_dpi
);
187 mode
->Format
= scDesc
.BufferDesc
.Format
;
188 mode
->Scaling
= scDesc
.BufferDesc
.Scaling
;
189 mode
->ScanlineOrdering
= scDesc
.BufferDesc
.ScanlineOrdering
;
191 #ifdef TARGET_WINDOWS_DESKTOP
192 DEVMODEW sDevMode
= {};
193 sDevMode
.dmSize
= sizeof(sDevMode
);
195 // EnumDisplaySettingsW is only one way to detect current refresh rate
196 if (EnumDisplaySettingsW(outDesc
.DeviceName
, ENUM_CURRENT_SETTINGS
, &sDevMode
))
198 int i
= (((sDevMode
.dmDisplayFrequency
+ 1) % 24) == 0 || ((sDevMode
.dmDisplayFrequency
+ 1) % 30) == 0) ? 1 : 0;
199 mode
->RefreshRate
.Numerator
= (sDevMode
.dmDisplayFrequency
+ i
) * 1000;
200 mode
->RefreshRate
.Denominator
= 1000 + i
;
201 if (sDevMode
.dmDisplayFlags
& DM_INTERLACED
)
203 mode
->RefreshRate
.Numerator
*= 2;
204 mode
->ScanlineOrdering
= DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST
; // guessing
207 mode
->ScanlineOrdering
= DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE
;
210 using namespace winrt::Windows::Graphics::Display::Core
;
212 auto hdmiInfo
= HdmiDisplayInformation::GetForCurrentView();
213 if (hdmiInfo
) // Xbox only
215 auto currentMode
= hdmiInfo
.GetCurrentDisplayMode();
216 AVRational refresh
= av_d2q(currentMode
.RefreshRate(), 120000);
217 mode
->RefreshRate
.Numerator
= refresh
.num
;
218 mode
->RefreshRate
.Denominator
= refresh
.den
;
223 void DX::DeviceResources::SetViewPort(D3D11_VIEWPORT
& viewPort
) const
225 // convert logical viewport to real
226 D3D11_VIEWPORT realViewPort
=
236 m_deferrContext
->RSSetViewports(1, &realViewPort
);
239 bool DX::DeviceResources::SetFullScreen(bool fullscreen
, RESOLUTION_INFO
& res
)
241 if (!m_bDeviceCreated
|| !m_swapChain
)
244 critical_section::scoped_lock
lock(m_criticalSection
);
247 m_swapChain
->GetFullscreenState(&bFullScreen
, nullptr);
249 CLog::LogF(LOGDEBUG
, "switching from {}({:.0f} x {:.0f}) to {}({} x {})",
250 bFullScreen
? "fullscreen " : "", m_outputSize
.Width
, m_outputSize
.Height
,
251 fullscreen
? "fullscreen " : "", res
.iWidth
, res
.iHeight
);
253 bool recreate
= m_stereoEnabled
!= (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED
);
254 if (!!bFullScreen
&& !fullscreen
)
256 CLog::LogF(LOGDEBUG
, "switching to windowed");
257 recreate
|= SUCCEEDED(m_swapChain
->SetFullscreenState(false, nullptr));
261 const bool isResValid
= res
.iWidth
> 0 && res
.iHeight
> 0 && res
.fRefreshRate
> 0.f
;
264 DXGI_MODE_DESC currentMode
= {};
265 GetDisplayMode(¤tMode
);
266 DXGI_SWAP_CHAIN_DESC scDesc
;
267 m_swapChain
->GetDesc(&scDesc
);
269 bool is_interlaced
= scDesc
.BufferDesc
.ScanlineOrdering
> DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE
;
270 float refreshRate
= res
.fRefreshRate
;
271 if (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
)
274 if (currentMode
.Width
!= res
.iWidth
275 || currentMode
.Height
!= res
.iHeight
276 || DX::RationalToFloat(currentMode
.RefreshRate
) != refreshRate
277 || is_interlaced
!= (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
? true : false)
278 // force resolution change for stereo mode
279 // some drivers unable to create stereo swapchain if mode does not match @23.976
280 || CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED
)
282 CLog::Log(LOGDEBUG
, __FUNCTION__
": changing display mode to {}x{}@{:0.3f}", res
.iWidth
,
283 res
.iHeight
, res
.fRefreshRate
,
284 res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
? "i" : "");
286 int refresh
= static_cast<int>(res
.fRefreshRate
);
287 int i
= (refresh
+ 1) % 24 == 0 || (refresh
+ 1) % 30 == 0 ? 1 : 0;
289 currentMode
.Width
= res
.iWidth
;
290 currentMode
.Height
= res
.iHeight
;
291 currentMode
.RefreshRate
.Numerator
= (refresh
+ i
) * 1000;
292 currentMode
.RefreshRate
.Denominator
= 1000 + i
;
293 if (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
)
295 currentMode
.RefreshRate
.Numerator
*= 2;
296 currentMode
.ScanlineOrdering
= DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST
; // guessing;
298 // sometimes the OS silently brings Kodi out of full screen mode
299 // in this case switching a resolution has no any effect and
300 // we have to enter into full screen mode before switching
303 ComPtr
<IDXGIOutput
> pOutput
;
304 GetOutput(pOutput
.GetAddressOf());
306 CLog::LogF(LOGDEBUG
, "fixup fullscreen mode before switching resolution");
307 recreate
|= SUCCEEDED(m_swapChain
->SetFullscreenState(true, pOutput
.Get()));
308 m_swapChain
->GetFullscreenState(&bFullScreen
, nullptr);
310 bool resized
= SUCCEEDED(m_swapChain
->ResizeTarget(¤tMode
));
313 // some system doesn't inform windowing about desktop size changes
314 // so we have to change output size before resizing buffers
315 m_outputSize
.Width
= static_cast<float>(currentMode
.Width
);
316 m_outputSize
.Height
= static_cast<float>(currentMode
.Height
);
323 ComPtr
<IDXGIOutput
> pOutput
;
324 GetOutput(pOutput
.GetAddressOf());
326 CLog::LogF(LOGDEBUG
, "switching to fullscreen");
327 recreate
|= SUCCEEDED(m_swapChain
->SetFullscreenState(true, pOutput
.Get()));
331 // resize backbuffer to proper handle fullscreen/stereo transition
335 CLog::LogF(LOGDEBUG
, "switching done.");
340 // Configures resources that don't depend on the Direct3D device.
341 void DX::DeviceResources::CreateDeviceIndependentResources()
345 // Configures the Direct3D device, and stores handles to it and the device context.
346 void DX::DeviceResources::CreateDeviceResources()
348 CLog::LogF(LOGDEBUG
, "creating DirectX 11 device.");
352 UINT creationFlags
= D3D11_CREATE_DEVICE_VIDEO_SUPPORT
;
354 if (DX::SdkLayersAvailable())
356 // If the project is in a debug build, enable debugging via SDK Layers with this flag.
357 creationFlags
|= D3D11_CREATE_DEVICE_DEBUG
;
361 // This array defines the set of DirectX hardware feature levels this app will support.
362 // Note the ordering should be preserved.
363 // Don't forget to declare your application's minimum required feature level in its
364 // description. All applications are assumed to support 9.1 unless otherwise stated.
365 std::vector
<D3D_FEATURE_LEVEL
> featureLevels
;
366 if (CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10
))
368 featureLevels
.push_back(D3D_FEATURE_LEVEL_12_1
);
369 featureLevels
.push_back(D3D_FEATURE_LEVEL_12_0
);
371 featureLevels
.push_back(D3D_FEATURE_LEVEL_11_1
);
372 featureLevels
.push_back(D3D_FEATURE_LEVEL_11_0
);
373 featureLevels
.push_back(D3D_FEATURE_LEVEL_10_1
);
374 featureLevels
.push_back(D3D_FEATURE_LEVEL_10_0
);
375 featureLevels
.push_back(D3D_FEATURE_LEVEL_9_3
);
376 featureLevels
.push_back(D3D_FEATURE_LEVEL_9_2
);
377 featureLevels
.push_back(D3D_FEATURE_LEVEL_9_1
);
379 // Create the Direct3D 11 API device object and a corresponding context.
380 ComPtr
<ID3D11Device
> device
;
381 ComPtr
<ID3D11DeviceContext
> context
;
383 D3D_DRIVER_TYPE drivertType
= m_adapter
!= nullptr ? D3D_DRIVER_TYPE_UNKNOWN
: D3D_DRIVER_TYPE_HARDWARE
;
384 HRESULT hr
= D3D11CreateDevice(
385 m_adapter
.Get(), // Create a device on specified adapter.
386 drivertType
, // Create a device using scepcified driver.
387 nullptr, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
388 creationFlags
, // Set debug and Direct2D compatibility flags.
389 featureLevels
.data(), // List of feature levels this app can support.
390 featureLevels
.size(), // Size of the list above.
391 D3D11_SDK_VERSION
, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
392 &device
, // Returns the Direct3D device created.
393 &m_d3dFeatureLevel
, // Returns feature level of device created.
394 &context
// Returns the device immediate context.
399 CLog::LogF(LOGERROR
, "unable to create hardware device with video support, error {}",
400 CWIN32Util::FormatHRESULT(hr
));
401 CLog::LogF(LOGERROR
, "trying to create hardware device without video support.");
403 creationFlags
&= ~D3D11_CREATE_DEVICE_VIDEO_SUPPORT
;
405 hr
= D3D11CreateDevice(m_adapter
.Get(), drivertType
, nullptr, creationFlags
,
406 featureLevels
.data(), featureLevels
.size(), D3D11_SDK_VERSION
, &device
,
407 &m_d3dFeatureLevel
, &context
);
411 CLog::LogF(LOGERROR
, "unable to create hardware device, error {}",
412 CWIN32Util::FormatHRESULT(hr
));
413 CLog::LogF(LOGERROR
, "trying to create WARP device.");
415 hr
= D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP
, nullptr, creationFlags
,
416 featureLevels
.data(), featureLevels
.size(), D3D11_SDK_VERSION
, &device
,
417 &m_d3dFeatureLevel
, &context
);
421 CLog::LogF(LOGFATAL
, "unable to create WARP device. Rendering is not possible. Error {}",
422 CWIN32Util::FormatHRESULT(hr
));
428 // Store pointers to the Direct3D 11.1 API device and immediate context.
429 hr
= device
.As(&m_d3dDevice
); CHECK_ERR();
431 // Check shared textures support
432 CheckNV12SharedTexturesSupport();
435 if (SUCCEEDED(m_d3dDevice
.As(&m_d3dDebug
)))
437 ComPtr
<ID3D11InfoQueue
> d3dInfoQueue
;
438 if (SUCCEEDED(m_d3dDebug
.As(&d3dInfoQueue
)))
440 std::vector
<D3D11_MESSAGE_ID
> hide
=
442 D3D11_MESSAGE_ID_GETVIDEOPROCESSORFILTERRANGE_UNSUPPORTED
, // avoid GETVIDEOPROCESSORFILTERRANGE_UNSUPPORTED (dx bug)
443 D3D11_MESSAGE_ID_DEVICE_RSSETSCISSORRECTS_NEGATIVESCISSOR
// avoid warning for some labels out of screen
444 // Add more message IDs here as needed
447 D3D11_INFO_QUEUE_FILTER filter
= {};
448 filter
.DenyList
.NumIDs
= hide
.size();
449 filter
.DenyList
.pIDList
= hide
.data();
450 d3dInfoQueue
->AddStorageFilterEntries(&filter
);
455 hr
= context
.As(&m_d3dContext
); CHECK_ERR();
456 hr
= m_d3dDevice
->CreateDeferredContext1(0, &m_deferrContext
); CHECK_ERR();
460 ComPtr
<IDXGIDevice1
> dxgiDevice
;
461 ComPtr
<IDXGIAdapter
> adapter
;
462 hr
= m_d3dDevice
.As(&dxgiDevice
); CHECK_ERR();
463 hr
= dxgiDevice
->GetAdapter(&adapter
); CHECK_ERR();
464 hr
= adapter
.As(&m_adapter
); CHECK_ERR();
467 DXGI_ADAPTER_DESC aDesc
;
468 m_adapter
->GetDesc(&aDesc
);
470 CLog::LogF(LOGINFO
, "device is created on adapter '{}' with {}",
471 KODI::PLATFORM::WINDOWS::FromW(aDesc
.Description
),
472 GetFeatureLevelDescription(m_d3dFeatureLevel
));
474 CheckDXVA2SharedDecoderSurfaces();
476 m_bDeviceCreated
= true;
479 void DX::DeviceResources::ReleaseBackBuffer()
481 CLog::LogF(LOGDEBUG
, "release buffers.");
483 m_backBufferTex
.Release();
484 m_d3dDepthStencilView
= nullptr;
487 // Clear the previous window size specific context.
488 ID3D11RenderTargetView
* nullViews
[] = { nullptr, nullptr, nullptr, nullptr };
489 m_deferrContext
->OMSetRenderTargets(4, nullViews
, nullptr);
490 FinishCommandList(false);
492 m_deferrContext
->Flush();
493 m_d3dContext
->Flush();
497 void DX::DeviceResources::CreateBackBuffer()
499 if (!m_bDeviceCreated
|| !m_swapChain
)
502 CLog::LogF(LOGDEBUG
, "create buffers.");
504 // Get swap chain back buffer.
505 ComPtr
<ID3D11Texture2D
> backBuffer
;
506 HRESULT hr
= m_swapChain
->GetBuffer(0, IID_PPV_ARGS(&backBuffer
)); CHECK_ERR();
508 // Create back buffer texture from swap chain texture
509 if (!m_backBufferTex
.Acquire(backBuffer
.Get()))
511 CLog::LogF(LOGERROR
, "failed to create render target.");
515 // Create a depth stencil view for use with 3D rendering if needed.
516 CD3D11_TEXTURE2D_DESC
depthStencilDesc(
517 DXGI_FORMAT_D24_UNORM_S8_UINT
,
518 lround(m_outputSize
.Width
),
519 lround(m_outputSize
.Height
),
520 1, // This depth stencil view has only one texture.
521 1, // Use a single mipmap level.
522 D3D11_BIND_DEPTH_STENCIL
525 ComPtr
<ID3D11Texture2D
> depthStencil
;
526 hr
= m_d3dDevice
->CreateTexture2D(
532 CD3D11_DEPTH_STENCIL_VIEW_DESC
depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D
);
533 hr
= m_d3dDevice
->CreateDepthStencilView(
535 &depthStencilViewDesc
,
536 &m_d3dDepthStencilView
539 // Set the 3D rendering viewport to target the entire window.
540 m_screenViewport
= CD3D11_VIEWPORT(
547 m_deferrContext
->RSSetViewports(1, &m_screenViewport
);
550 HRESULT
DX::DeviceResources::CreateSwapChain(DXGI_SWAP_CHAIN_DESC1
& desc
, DXGI_SWAP_CHAIN_FULLSCREEN_DESC
& fsDesc
, IDXGISwapChain1
** ppSwapChain
) const
553 #ifdef TARGET_WINDOWS_DESKTOP
554 hr
= m_dxgiFactory
->CreateSwapChainForHwnd(
562 hr
= m_dxgiFactory
->MakeWindowAssociation(m_window
, /*DXGI_MWA_NO_WINDOW_CHANGES |*/ DXGI_MWA_NO_ALT_ENTER
);
564 hr
= m_dxgiFactory
->CreateSwapChainForCoreWindow(
566 winrt::get_unknown(m_coreWindow
),
575 void DX::DeviceResources::DestroySwapChain()
581 m_swapChain
->GetFullscreenState(&bFullcreen
, nullptr);
583 m_swapChain
->SetFullscreenState(false, nullptr); // mandatory before releasing swapchain
584 m_swapChain
= nullptr;
585 m_deferrContext
->Flush();
586 m_d3dContext
->Flush();
587 m_IsTransferPQ
= false;
590 void DX::DeviceResources::ResizeBuffers()
592 if (!m_bDeviceCreated
)
595 CLog::LogF(LOGDEBUG
, "resize buffers.");
597 bool bHWStereoEnabled
= RENDER_STEREO_MODE_HARDWAREBASED
==
598 CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode();
599 bool windowed
= true;
601 DXGI_SWAP_CHAIN_DESC1 scDesc
= {};
606 m_swapChain
->GetFullscreenState(&bFullcreen
, nullptr);
610 m_swapChain
->GetDesc1(&scDesc
);
611 if ((scDesc
.Stereo
== TRUE
) != bHWStereoEnabled
) // check if swapchain needs to be recreated
615 if (m_swapChain
) // If the swap chain already exists, resize it.
617 m_swapChain
->GetDesc1(&scDesc
);
618 hr
= m_swapChain
->ResizeBuffers(scDesc
.BufferCount
, lround(m_outputSize
.Width
),
619 lround(m_outputSize
.Height
), scDesc
.Format
,
620 windowed
? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
);
622 if (hr
== DXGI_ERROR_DEVICE_REMOVED
|| hr
== DXGI_ERROR_DEVICE_RESET
)
624 // If the device was removed for any reason, a new device and swap chain will need to be created.
625 HandleDeviceLost(hr
== DXGI_ERROR_DEVICE_REMOVED
);
627 // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
628 // and correctly set up the new device.
631 else if (hr
== DXGI_ERROR_INVALID_CALL
)
633 // Called when Windows HDR is toggled externally to Kodi.
634 // Is forced to re-create swap chain to avoid crash.
635 CreateWindowSizeDependentResources();
640 else // Otherwise, create a new one using the same adapter as the existing Direct3D device.
642 HDR_STATUS hdrStatus
= CWIN32Util::GetWindowsHDRStatus();
643 const bool isHdrEnabled
= (hdrStatus
== HDR_STATUS::HDR_ON
);
644 bool use10bit
= (hdrStatus
!= HDR_STATUS::HDR_UNSUPPORTED
);
646 // Xbox needs 10 bit swapchain to output true 4K resolution
647 #ifdef TARGET_WINDOWS_DESKTOP
648 // Some AMD graphics has issues with 10 bit in SDR.
649 // Enabled by default only in Intel and NVIDIA with latest drivers/hardware
650 if (m_d3dFeatureLevel
< D3D_FEATURE_LEVEL_12_1
|| GetAdapterDesc().VendorId
== PCIV_AMD
)
654 // 0 = Auto | 1 = Never | 2 = Always
655 int use10bitSetting
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
656 CSettings::SETTING_VIDEOSCREEN_10BITSURFACES
);
658 if (use10bitSetting
== 1)
660 else if (use10bitSetting
== 2)
663 DXGI_SWAP_CHAIN_DESC1 swapChainDesc
= {};
664 swapChainDesc
.Width
= lround(m_outputSize
.Width
);
665 swapChainDesc
.Height
= lround(m_outputSize
.Height
);
666 swapChainDesc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
667 swapChainDesc
.Stereo
= bHWStereoEnabled
;
668 swapChainDesc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
669 #ifdef TARGET_WINDOWS_DESKTOP
670 swapChainDesc
.BufferCount
= 6; // HDR 60 fps needs 6 buffers to avoid frame drops
672 swapChainDesc
.BufferCount
= 3; // Xbox don't like 6 backbuffers (3 is fine even for 4K 60 fps)
674 // FLIP_DISCARD improves performance (needed in some systems for 4K HDR 60 fps)
675 swapChainDesc
.SwapEffect
= CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10
)
676 ? DXGI_SWAP_EFFECT_FLIP_DISCARD
677 : DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
678 swapChainDesc
.Flags
= windowed
? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
;
679 swapChainDesc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
680 swapChainDesc
.SampleDesc
.Count
= 1;
681 swapChainDesc
.SampleDesc
.Quality
= 0;
683 DXGI_SWAP_CHAIN_FULLSCREEN_DESC scFSDesc
= {}; // unused for uwp
684 scFSDesc
.ScanlineOrdering
= DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED
;
685 scFSDesc
.Windowed
= windowed
;
687 ComPtr
<IDXGISwapChain1
> swapChain
;
688 if (m_d3dFeatureLevel
>= D3D_FEATURE_LEVEL_11_0
&& !bHWStereoEnabled
&&
689 (isHdrEnabled
|| use10bit
))
691 swapChainDesc
.Format
= DXGI_FORMAT_R10G10B10A2_UNORM
;
692 hr
= CreateSwapChain(swapChainDesc
, scFSDesc
, &swapChain
);
695 CLog::LogF(LOGWARNING
, "creating 10bit swapchain failed, fallback to 8bit.");
696 swapChainDesc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
701 hr
= CreateSwapChain(swapChainDesc
, scFSDesc
, &swapChain
);
703 if (FAILED(hr
) && bHWStereoEnabled
)
705 // switch to stereo mode failed, create mono swapchain
706 CLog::LogF(LOGERROR
, "creating stereo swap chain failed with error.");
707 CLog::LogF(LOGINFO
, "fallback to monoscopic mode.");
709 swapChainDesc
.Stereo
= false;
710 bHWStereoEnabled
= false;
712 hr
= CreateSwapChain(swapChainDesc
, scFSDesc
, &swapChain
); CHECK_ERR();
714 // fallback to split_horizontal mode.
715 CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoMode(
716 RENDER_STEREO_MODE_SPLIT_HORIZONTAL
);
721 CLog::LogF(LOGERROR
, "unable to create swapchain.");
725 m_IsHDROutput
= (swapChainDesc
.Format
== DXGI_FORMAT_R10G10B10A2_UNORM
) && isHdrEnabled
;
728 LOGINFO
, "{} bit swapchain is used with {} flip {} buffers and {} output (format {})",
729 (swapChainDesc
.Format
== DXGI_FORMAT_R10G10B10A2_UNORM
) ? 10 : 8, swapChainDesc
.BufferCount
,
730 (swapChainDesc
.SwapEffect
== DXGI_SWAP_EFFECT_FLIP_DISCARD
) ? "discard" : "sequential",
731 m_IsHDROutput
? "HDR" : "SDR", DX::DXGIFormatToString(swapChainDesc
.Format
));
733 hr
= swapChain
.As(&m_swapChain
); CHECK_ERR();
734 m_stereoEnabled
= bHWStereoEnabled
;
736 if (CServiceBroker::GetLogging().IsLogLevelLogged(LOGDEBUG
) &&
737 CServiceBroker::GetLogging().CanLogComponent(LOGVIDEO
))
739 std::string colorSpaces
;
740 for (const DXGI_COLOR_SPACE_TYPE
& colorSpace
: GetSwapChainColorSpaces())
742 colorSpaces
.append("\n");
743 colorSpaces
.append(DX::DXGIColorSpaceTypeToString(colorSpace
));
745 CLog::LogFC(LOGDEBUG
, LOGVIDEO
, "Color spaces supported by the swap chain:{}", colorSpaces
);
748 // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
749 // ensures that the application will only render after each VSync, minimizing power consumption.
750 ComPtr
<IDXGIDevice1
> dxgiDevice
;
751 hr
= m_d3dDevice
.As(&dxgiDevice
); CHECK_ERR();
752 dxgiDevice
->SetMaximumFrameLatency(1);
755 SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
);
758 CLog::LogF(LOGDEBUG
, "end resize buffers.");
761 // These resources need to be recreated every time the window size is changed.
762 void DX::DeviceResources::CreateWindowSizeDependentResources()
768 if (!m_dxgiFactory
->IsCurrent()) // HDR toggling requires re-create factory
771 UpdateRenderTargetSize();
777 // Determine the dimensions of the render target and whether it will be scaled down.
778 void DX::DeviceResources::UpdateRenderTargetSize()
780 m_effectiveDpi
= m_dpi
;
782 // To improve battery life on high resolution devices, render to a smaller render target
783 // and allow the GPU to scale the output when it is presented.
784 if (!DisplayMetrics::SupportHighResolutions
&& m_dpi
> DisplayMetrics::DpiThreshold
)
786 float width
= DX::ConvertDipsToPixels(m_logicalSize
.Width
, m_dpi
);
787 float height
= DX::ConvertDipsToPixels(m_logicalSize
.Height
, m_dpi
);
789 // When the device is in portrait orientation, height > width. Compare the
790 // larger dimension against the width threshold and the smaller dimension
791 // against the height threshold.
792 if (std::max(width
, height
) > DisplayMetrics::WidthThreshold
&& std::min(width
, height
) > DisplayMetrics::HeightThreshold
)
794 // To scale the app we change the effective DPI. Logical size does not change.
795 m_effectiveDpi
/= 2.0f
;
799 // Calculate the necessary render target size in pixels.
800 m_outputSize
.Width
= DX::ConvertDipsToPixels(m_logicalSize
.Width
, m_effectiveDpi
);
801 m_outputSize
.Height
= DX::ConvertDipsToPixels(m_logicalSize
.Height
, m_effectiveDpi
);
803 // Prevent zero size DirectX content from being created.
804 m_outputSize
.Width
= std::max(m_outputSize
.Width
, 1.f
);
805 m_outputSize
.Height
= std::max(m_outputSize
.Height
, 1.f
);
808 void DX::DeviceResources::Register(ID3DResource
* resource
)
810 critical_section::scoped_lock
lock(m_resourceSection
);
811 m_resources
.push_back(resource
);
814 void DX::DeviceResources::Unregister(ID3DResource
* resource
)
816 critical_section::scoped_lock
lock(m_resourceSection
);
817 std::vector
<ID3DResource
*>::iterator i
= find(m_resources
.begin(), m_resources
.end(), resource
);
818 if (i
!= m_resources
.end())
819 m_resources
.erase(i
);
822 void DX::DeviceResources::FinishCommandList(bool bExecute
) const
824 if (m_d3dContext
== m_deferrContext
)
827 ComPtr
<ID3D11CommandList
> pCommandList
;
828 if (FAILED(m_deferrContext
->FinishCommandList(true, &pCommandList
)))
830 CLog::LogF(LOGERROR
, "failed to finish command queue.");
835 m_d3dContext
->ExecuteCommandList(pCommandList
.Get(), false);
838 // This method is called in the event handler for the SizeChanged event.
839 void DX::DeviceResources::SetLogicalSize(float width
, float height
)
842 #if defined(TARGET_WINDOWS_DESKTOP)
849 CLog::LogF(LOGDEBUG
, "receive changing logical size to {:f} x {:f}", width
, height
);
851 if (m_logicalSize
.Width
!= width
|| m_logicalSize
.Height
!= height
)
853 CLog::LogF(LOGDEBUG
, "change logical size to {:f} x {:f}", width
, height
);
855 m_logicalSize
= winrt::Size(width
, height
);
857 UpdateRenderTargetSize();
862 // This method is called in the event handler for the DpiChanged event.
863 void DX::DeviceResources::SetDpi(float dpi
)
865 dpi
= std::max(dpi
, DisplayMetrics::Dpi100
);
870 // This method is called in the event handler for the DisplayContentsInvalidated event.
871 void DX::DeviceResources::ValidateDevice()
873 // The D3D Device is no longer valid if the default adapter changed since the device
874 // was created or if the device has been removed.
876 // First, get the information for the default adapter from when the device was created.
877 ComPtr
<IDXGIDevice1
> dxgiDevice
;
878 m_d3dDevice
.As(&dxgiDevice
);
880 ComPtr
<IDXGIAdapter
> deviceAdapter
;
881 dxgiDevice
->GetAdapter(&deviceAdapter
);
883 ComPtr
<IDXGIFactory2
> dxgiFactory
;
884 deviceAdapter
->GetParent(IID_PPV_ARGS(&dxgiFactory
));
886 DXGI_ADAPTER_DESC1 previousDesc
;
888 ComPtr
<IDXGIAdapter1
> previousDefaultAdapter
;
889 dxgiFactory
->EnumAdapters1(0, &previousDefaultAdapter
);
891 previousDefaultAdapter
->GetDesc1(&previousDesc
);
894 // Next, get the information for the current default adapter.
895 DXGI_ADAPTER_DESC1 currentDesc
;
897 ComPtr
<IDXGIFactory1
> currentFactory
;
898 CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory
));
900 ComPtr
<IDXGIAdapter1
> currentDefaultAdapter
;
901 currentFactory
->EnumAdapters1(0, ¤tDefaultAdapter
);
903 currentDefaultAdapter
->GetDesc1(¤tDesc
);
905 // If the adapter LUIDs don't match, or if the device reports that it has been removed,
906 // a new D3D device must be created.
907 HRESULT hr
= m_d3dDevice
->GetDeviceRemovedReason();
908 if ( previousDesc
.AdapterLuid
.LowPart
!= currentDesc
.AdapterLuid
.LowPart
909 || previousDesc
.AdapterLuid
.HighPart
!= currentDesc
.AdapterLuid
.HighPart
912 // Release references to resources related to the old device.
913 dxgiDevice
= nullptr;
914 deviceAdapter
= nullptr;
915 dxgiFactory
= nullptr;
917 // Create a new device and swap chain.
918 HandleDeviceLost(hr
== DXGI_ERROR_DEVICE_REMOVED
);
922 void DX::DeviceResources::OnDeviceLost(bool removed
)
924 auto pGUI
= CServiceBroker::GetGUI();
926 pGUI
->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_RENDERER_LOST
);
928 // tell any shared resources
929 for (auto res
: m_resources
)
931 // the most of resources like textures and buffers try to
932 // receive and save their status from current device.
933 // `removed` means that we have no possibility
934 // to use the device anymore, tell all resources about this.
935 res
->OnDestroyDevice(removed
);
939 void DX::DeviceResources::OnDeviceRestored()
941 // tell any shared resources
942 for (auto res
: m_resources
)
943 res
->OnCreateDevice();
945 auto pGUI
= CServiceBroker::GetGUI();
947 pGUI
->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_RENDERER_RESET
);
950 // Recreate all device resources and set them back to the current state.
951 void DX::DeviceResources::HandleDeviceLost(bool removed
)
953 bool backbuferExists
= m_backBufferTex
.Get() != nullptr;
955 OnDeviceLost(removed
);
956 if (m_deviceNotify
!= nullptr)
957 m_deviceNotify
->OnDXDeviceLost();
964 CreateDeviceResources();
965 UpdateRenderTargetSize();
971 if (m_deviceNotify
!= nullptr)
972 m_deviceNotify
->OnDXDeviceRestored();
976 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN
, -1, -1, nullptr,
980 bool DX::DeviceResources::Begin()
982 HRESULT hr
= m_swapChain
->Present(0, DXGI_PRESENT_TEST
);
984 // If the device was removed either by a disconnection or a driver upgrade, we
985 // must recreate all device resources.
986 if (hr
== DXGI_ERROR_DEVICE_REMOVED
|| hr
== DXGI_ERROR_DEVICE_RESET
)
988 HandleDeviceLost(hr
== DXGI_ERROR_DEVICE_REMOVED
);
993 if (hr
== DXGI_ERROR_INVALID_CALL
)
995 CreateWindowSizeDependentResources();
999 m_deferrContext
->OMSetRenderTargets(1, m_backBufferTex
.GetAddressOfRTV(), m_d3dDepthStencilView
.Get());
1004 // Present the contents of the swap chain to the screen.
1005 void DX::DeviceResources::Present()
1007 FinishCommandList();
1009 // The first argument instructs DXGI to block until VSync, putting the application
1010 // to sleep until the next VSync. This ensures we don't waste any cycles rendering
1011 // frames that will never be displayed to the screen.
1012 DXGI_PRESENT_PARAMETERS parameters
= {};
1013 HRESULT hr
= m_swapChain
->Present1(1, 0, ¶meters
);
1015 // If the device was removed either by a disconnection or a driver upgrade, we
1016 // must recreate all device resources.
1017 if (hr
== DXGI_ERROR_DEVICE_REMOVED
|| hr
== DXGI_ERROR_DEVICE_RESET
)
1019 HandleDeviceLost(hr
== DXGI_ERROR_DEVICE_REMOVED
);
1024 if (hr
== DXGI_ERROR_INVALID_CALL
)
1026 CreateWindowSizeDependentResources();
1030 if (m_d3dContext
== m_deferrContext
)
1032 m_deferrContext
->OMSetRenderTargets(1, m_backBufferTex
.GetAddressOfRTV(), m_d3dDepthStencilView
.Get());
1036 void DX::DeviceResources::ClearDepthStencil() const
1038 m_deferrContext
->ClearDepthStencilView(m_d3dDepthStencilView
.Get(), D3D11_CLEAR_DEPTH
| D3D11_CLEAR_STENCIL
, 1.0, 0);
1041 void DX::DeviceResources::ClearRenderTarget(ID3D11RenderTargetView
* pRTView
, float color
[4]) const
1043 m_deferrContext
->ClearRenderTargetView(pRTView
, color
);
1046 void DX::DeviceResources::HandleOutputChange(const std::function
<bool(DXGI_OUTPUT_DESC
)>& cmpFunc
)
1048 DXGI_ADAPTER_DESC currentDesc
= {};
1049 DXGI_ADAPTER_DESC foundDesc
= {};
1051 ComPtr
<IDXGIFactory1
> factory
;
1053 m_adapter
->GetDesc(¤tDesc
);
1055 CreateDXGIFactory1(IID_IDXGIFactory1
, &factory
);
1057 ComPtr
<IDXGIAdapter1
> adapter
;
1058 for (int i
= 0; factory
->EnumAdapters1(i
, adapter
.ReleaseAndGetAddressOf()) != DXGI_ERROR_NOT_FOUND
; i
++)
1060 adapter
->GetDesc(&foundDesc
);
1061 ComPtr
<IDXGIOutput
> output
;
1062 for (int j
= 0; adapter
->EnumOutputs(j
, output
.ReleaseAndGetAddressOf()) != DXGI_ERROR_NOT_FOUND
; j
++)
1064 DXGI_OUTPUT_DESC outputDesc
;
1065 output
->GetDesc(&outputDesc
);
1066 if (cmpFunc(outputDesc
))
1068 output
.As(&m_output
);
1069 m_outputDesc
= outputDesc
;
1070 // check if adapter is changed
1071 if (currentDesc
.AdapterLuid
.HighPart
!= foundDesc
.AdapterLuid
.HighPart
1072 || currentDesc
.AdapterLuid
.LowPart
!= foundDesc
.AdapterLuid
.LowPart
)
1074 // adapter is changed
1075 m_adapter
= adapter
;
1076 CLog::LogF(LOGDEBUG
, "selected {} adapter. ",
1077 KODI::PLATFORM::WINDOWS::FromW(foundDesc
.Description
));
1078 // (re)init hooks into new driver
1079 Windowing()->InitHooks(output
.Get());
1080 // recreate d3d11 device on new adapter
1082 HandleDeviceLost(false);
1090 bool DX::DeviceResources::CreateFactory()
1093 #if defined(_DEBUG) && defined(TARGET_WINDOWS_STORE)
1094 bool debugDXGI
= false;
1096 ComPtr
<IDXGIInfoQueue
> dxgiInfoQueue
;
1097 if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(dxgiInfoQueue
.GetAddressOf()))))
1101 hr
= CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG
, IID_PPV_ARGS(m_dxgiFactory
.ReleaseAndGetAddressOf())); RETURN_ERR(false);
1103 dxgiInfoQueue
->SetBreakOnSeverity(DXGI_DEBUG_ALL
, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR
, true);
1104 dxgiInfoQueue
->SetBreakOnSeverity(DXGI_DEBUG_ALL
, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION
, true);
1110 hr
= CreateDXGIFactory1(IID_PPV_ARGS(m_dxgiFactory
.ReleaseAndGetAddressOf())); RETURN_ERR(false);
1115 void DX::DeviceResources::SetMonitor(HMONITOR monitor
)
1117 HandleOutputChange([monitor
](DXGI_OUTPUT_DESC outputDesc
) {
1118 return outputDesc
.Monitor
== monitor
;
1122 void DX::DeviceResources::RegisterDeviceNotify(IDeviceNotify
* deviceNotify
)
1124 m_deviceNotify
= deviceNotify
;
1127 HMONITOR
DX::DeviceResources::GetMonitor() const
1131 ComPtr
<IDXGIOutput
> output
;
1132 GetOutput(output
.GetAddressOf());
1135 DXGI_OUTPUT_DESC desc
;
1136 output
->GetDesc(&desc
);
1137 return desc
.Monitor
;
1143 bool DX::DeviceResources::IsStereoAvailable() const
1146 return m_dxgiFactory
->IsWindowedStereoEnabled();
1151 void DX::DeviceResources::CheckNV12SharedTexturesSupport()
1153 if (m_d3dFeatureLevel
< D3D_FEATURE_LEVEL_10_0
||
1154 CSysInfo::GetWindowsDeviceFamily() != CSysInfo::Desktop
)
1157 D3D11_FEATURE_DATA_D3D11_OPTIONS4 op4
= {};
1158 HRESULT hr
= m_d3dDevice
->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS4
, &op4
, sizeof(op4
));
1159 m_NV12SharedTexturesSupport
= SUCCEEDED(hr
) && !!op4
.ExtendedNV12SharedTextureSupported
;
1160 CLog::LogF(LOGINFO
, "extended NV12 shared textures is{}supported",
1161 m_NV12SharedTexturesSupport
? " " : " NOT ");
1164 void DX::DeviceResources::CheckDXVA2SharedDecoderSurfaces()
1166 if (CSysInfo::GetWindowsDeviceFamily() != CSysInfo::Desktop
)
1169 VideoDriverInfo driver
= GetVideoDriverVersion();
1172 if (!m_NV12SharedTexturesSupport
)
1175 const DXGI_ADAPTER_DESC ad
= GetAdapterDesc();
1177 m_DXVA2SharedDecoderSurfaces
=
1178 ad
.VendorId
== PCIV_Intel
||
1179 (ad
.VendorId
== PCIV_NVIDIA
&& driver
.valid
&& driver
.majorVersion
>= 465) ||
1180 (ad
.VendorId
== PCIV_AMD
&& driver
.valid
&& driver
.majorVersion
>= 30 &&
1181 m_d3dFeatureLevel
>= D3D_FEATURE_LEVEL_12_1
);
1183 CLog::LogF(LOGINFO
, "DXVA2 shared decoder surfaces is{}supported",
1184 m_DXVA2SharedDecoderSurfaces
? " " : " NOT ");
1186 m_DXVA2UseFence
= m_DXVA2SharedDecoderSurfaces
&&
1187 (ad
.VendorId
== PCIV_NVIDIA
|| ad
.VendorId
== PCIV_AMD
) &&
1188 CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10_1703
);
1190 if (m_DXVA2SharedDecoderSurfaces
)
1191 CLog::LogF(LOGINFO
, "DXVA2 shared decoder surfaces {} fence synchronization.",
1192 m_DXVA2UseFence
? "WITH" : "WITHOUT");
1194 m_DXVASuperResolutionSupport
=
1195 m_d3dFeatureLevel
>= D3D_FEATURE_LEVEL_12_1
&&
1196 ((ad
.VendorId
== PCIV_Intel
&& driver
.valid
&& driver
.majorVersion
>= 31) ||
1197 (ad
.VendorId
== PCIV_NVIDIA
&& driver
.valid
&& driver
.majorVersion
>= 530));
1199 if (m_DXVASuperResolutionSupport
)
1200 CLog::LogF(LOGINFO
, "DXVA Video Super Resolution is potentially supported");
1203 VideoDriverInfo
DX::DeviceResources::GetVideoDriverVersion() const
1208 VideoDriverInfo driver
{};
1209 const DXGI_ADAPTER_DESC ad
= GetAdapterDesc();
1211 // Version retrieval with DXGI is more modern but requires WDDM >= 2.3 for guarantee of same
1212 // version returned by all driver components. Fallback to older method for older drivers.
1213 LARGE_INTEGER rawVersion
{};
1214 if (SUCCEEDED(m_adapter
->CheckInterfaceSupport(__uuidof(IDXGIDevice
), &rawVersion
)) &&
1215 static_cast<uint16_t>(rawVersion
.QuadPart
>> 48) >= 23)
1216 driver
= CWIN32Util::FormatVideoDriverInfo(ad
.VendorId
, rawVersion
.QuadPart
);
1219 driver
= CWIN32Util::GetVideoDriverInfo(ad
.VendorId
, ad
.Description
);
1222 driver
= CWIN32Util::GetVideoDriverInfoDX(ad
.VendorId
, ad
.AdapterLuid
);
1227 #if defined(TARGET_WINDOWS_DESKTOP)
1228 // This method is called when the window (WND) is created (or re-created).
1229 void DX::DeviceResources::SetWindow(HWND window
)
1233 CreateDeviceIndependentResources();
1234 CreateDeviceResources();
1236 #elif defined(TARGET_WINDOWS_STORE)
1237 // This method is called when the CoreWindow is created (or re-created).
1238 void DX::DeviceResources::SetWindow(const winrt::Windows::UI::Core::CoreWindow
& window
)
1240 using namespace winrt::Windows::UI::Core
;
1241 using namespace winrt::Windows::Graphics::Display
;
1243 m_coreWindow
= window
;
1244 auto dispatcher
= m_coreWindow
.Dispatcher();
1245 DispatchedHandler
handler([&]()
1247 auto coreWindow
= CoreWindow::GetForCurrentThread();
1248 m_logicalSize
= winrt::Size(coreWindow
.Bounds().Width
, coreWindow
.Bounds().Height
);
1249 m_dpi
= DisplayInformation::GetForCurrentView().LogicalDpi();
1250 SetWindowPos(coreWindow
.Bounds());
1252 if (dispatcher
.HasThreadAccess())
1255 dispatcher
.RunAsync(CoreDispatcherPriority::High
, handler
).get();
1257 CreateDeviceIndependentResources();
1258 CreateDeviceResources();
1259 // we have to call this because we will not get initial WM_SIZE
1260 CreateWindowSizeDependentResources();
1263 void DX::DeviceResources::SetWindowPos(winrt::Rect rect
)
1265 int centerX
= rect
.X
+ rect
.Width
/ 2;
1266 int centerY
= rect
.Y
+ rect
.Height
/ 2;
1268 HandleOutputChange([centerX
, centerY
](DXGI_OUTPUT_DESC outputDesc
) {
1269 // DesktopCoordinates depends on the DPI of the desktop
1270 return outputDesc
.DesktopCoordinates
.left
<= centerX
&& outputDesc
.DesktopCoordinates
.right
>= centerX
1271 && outputDesc
.DesktopCoordinates
.top
<= centerY
&& outputDesc
.DesktopCoordinates
.bottom
>= centerY
;
1275 // Call this method when the app suspends. It provides a hint to the driver that the app
1276 // is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
1277 void DX::DeviceResources::Trim() const
1279 ComPtr
<IDXGIDevice3
> dxgiDevice
;
1280 m_d3dDevice
.As(&dxgiDevice
);
1287 void DX::DeviceResources::SetHdrMetaData(DXGI_HDR_METADATA_HDR10
& hdr10
) const
1289 ComPtr
<IDXGISwapChain4
> swapChain4
;
1294 if (SUCCEEDED(m_swapChain
.As(&swapChain4
)))
1296 if (SUCCEEDED(swapChain4
->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10
, sizeof(hdr10
), &hdr10
)))
1298 CLog::LogF(LOGDEBUG
,
1299 "(raw) RP {} {} | GP {} {} | BP {} {} | WP {} {} | Max ML {} | min ML "
1300 "{} | Max CLL {} | Max FALL {}",
1301 hdr10
.RedPrimary
[0], hdr10
.RedPrimary
[1], hdr10
.GreenPrimary
[0],
1302 hdr10
.GreenPrimary
[1], hdr10
.BluePrimary
[0], hdr10
.BluePrimary
[1],
1303 hdr10
.WhitePoint
[0], hdr10
.WhitePoint
[1], hdr10
.MaxMasteringLuminance
,
1304 hdr10
.MinMasteringLuminance
, hdr10
.MaxContentLightLevel
,
1305 hdr10
.MaxFrameAverageLightLevel
);
1307 constexpr double FACTOR_1
= 50000.0;
1308 constexpr double FACTOR_2
= 10000.0;
1309 const double RP_0
= static_cast<double>(hdr10
.RedPrimary
[0]) / FACTOR_1
;
1310 const double RP_1
= static_cast<double>(hdr10
.RedPrimary
[1]) / FACTOR_1
;
1311 const double GP_0
= static_cast<double>(hdr10
.GreenPrimary
[0]) / FACTOR_1
;
1312 const double GP_1
= static_cast<double>(hdr10
.GreenPrimary
[1]) / FACTOR_1
;
1313 const double BP_0
= static_cast<double>(hdr10
.BluePrimary
[0]) / FACTOR_1
;
1314 const double BP_1
= static_cast<double>(hdr10
.BluePrimary
[1]) / FACTOR_1
;
1315 const double WP_0
= static_cast<double>(hdr10
.WhitePoint
[0]) / FACTOR_1
;
1316 const double WP_1
= static_cast<double>(hdr10
.WhitePoint
[1]) / FACTOR_1
;
1317 const double Max_ML
= static_cast<double>(hdr10
.MaxMasteringLuminance
) / FACTOR_2
;
1318 const double min_ML
= static_cast<double>(hdr10
.MinMasteringLuminance
) / FACTOR_2
;
1321 "RP {:.3f} {:.3f} | GP {:.3f} {:.3f} | BP {:.3f} {:.3f} | WP {:.3f} "
1322 "{:.3f} | Max ML {:.0f} | min ML {:.4f} | Max CLL {} | Max FALL {}",
1323 RP_0
, RP_1
, GP_0
, GP_1
, BP_0
, BP_1
, WP_0
, WP_1
, Max_ML
, min_ML
,
1324 hdr10
.MaxContentLightLevel
, hdr10
.MaxFrameAverageLightLevel
);
1328 CLog::LogF(LOGERROR
, "DXGI SetHDRMetaData failed");
1333 void DX::DeviceResources::SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace
)
1335 ComPtr
<IDXGISwapChain3
> swapChain3
;
1340 if (SUCCEEDED(m_swapChain
.As(&swapChain3
)))
1342 if (SUCCEEDED(swapChain3
->SetColorSpace1(colorSpace
)))
1344 m_IsTransferPQ
= (colorSpace
== DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
);
1347 DX::Windowing()->CacheSystemSdrPeakLuminance();
1349 CLog::LogF(LOGDEBUG
, "DXGI SetColorSpace1 {} success",
1350 DX::DXGIColorSpaceTypeToString(colorSpace
));
1354 CLog::LogF(LOGERROR
, "DXGI SetColorSpace1 {} failed",
1355 DX::DXGIColorSpaceTypeToString(colorSpace
));
1360 HDR_STATUS
DX::DeviceResources::ToggleHDR()
1362 DXGI_MODE_DESC md
= {};
1363 GetDisplayMode(&md
);
1365 // Xbox uses only full screen windowed mode and not needs recreate swapchain.
1366 // Recreate swapchain causes native 4K resolution is lost and quality obtained
1367 // is equivalent to 1080p upscaled to 4K (TO DO: investigate root cause).
1368 const bool isXbox
= (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::Xbox
);
1370 DX::Windowing()->SetTogglingHDR(true);
1371 DX::Windowing()->SetAlteringWindow(true);
1373 // Toggle display HDR
1374 HDR_STATUS hdrStatus
= CWIN32Util::ToggleWindowsHDR(md
);
1377 if (!isXbox
&& m_swapChain
&& hdrStatus
!= HDR_STATUS::HDR_TOGGLE_FAILED
)
1379 CLog::LogF(LOGDEBUG
, "Re-create swapchain due HDR <-> SDR switch");
1383 DX::Windowing()->SetAlteringWindow(false);
1385 // Re-create swapchain
1386 if (!isXbox
&& hdrStatus
!= HDR_STATUS::HDR_TOGGLE_FAILED
)
1388 CreateWindowSizeDependentResources();
1390 DX::Windowing()->NotifyAppFocusChange(true);
1393 // On Xbox set new color space in same swapchain
1394 if (isXbox
&& hdrStatus
!= HDR_STATUS::HDR_TOGGLE_FAILED
)
1396 if (hdrStatus
== HDR_STATUS::HDR_ON
)
1398 SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
);
1399 m_IsHDROutput
= true;
1403 SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
);
1404 m_IsHDROutput
= false;
1411 void DX::DeviceResources::ApplyDisplaySettings()
1413 CLog::LogF(LOGDEBUG
, "Re-create swapchain due Display Settings changed");
1416 CreateWindowSizeDependentResources();
1419 DEBUG_INFO_RENDER
DX::DeviceResources::GetDebugInfo() const
1424 DXGI_SWAP_CHAIN_DESC1 desc
= {};
1425 m_swapChain
->GetDesc1(&desc
);
1427 DXGI_MODE_DESC md
= {};
1428 GetDisplayMode(&md
);
1430 const int bits
= (desc
.Format
== DXGI_FORMAT_R10G10B10A2_UNORM
) ? 10 : 8;
1431 const int max
= (desc
.Format
== DXGI_FORMAT_R10G10B10A2_UNORM
) ? 1024 : 256;
1432 const int range_min
= DX::Windowing()->UseLimitedColor() ? (max
* 16) / 256 : 0;
1433 const int range_max
= DX::Windowing()->UseLimitedColor() ? (max
* 235) / 256 : max
- 1;
1435 DEBUG_INFO_RENDER info
;
1437 info
.renderFlags
= StringUtils::Format(
1438 "Swapchain: {} buffers, flip {}, {}, EOTF: {} (Windows HDR {})", desc
.BufferCount
,
1439 (desc
.SwapEffect
== DXGI_SWAP_EFFECT_FLIP_DISCARD
) ? "discard" : "sequential",
1440 Windowing()->IsFullScreen()
1441 ? ((desc
.Flags
& DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
) ? "fullscreen exclusive"
1442 : "fullscreen windowed")
1443 : "windowed screen",
1444 m_IsTransferPQ
? "PQ" : "SDR", m_IsHDROutput
? "on" : "off");
1446 info
.videoOutput
= StringUtils::Format(
1447 "Surfaces: {}x{}{} @ {:.3f} Hz, pixel: {} {}-bit, range: {} ({}-{})", desc
.Width
, desc
.Height
,
1448 (md
.ScanlineOrdering
> DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE
) ? "i" : "p",
1449 static_cast<double>(md
.RefreshRate
.Numerator
) /
1450 static_cast<double>(md
.RefreshRate
.Denominator
),
1451 DXGIFormatToShortString(desc
.Format
), bits
,
1452 DX::Windowing()->UseLimitedColor() ? "limited" : "full", range_min
, range_max
);
1457 std::vector
<DXGI_COLOR_SPACE_TYPE
> DX::DeviceResources::GetSwapChainColorSpaces() const
1462 std::vector
<DXGI_COLOR_SPACE_TYPE
> result
;
1465 ComPtr
<IDXGISwapChain3
> swapChain3
;
1466 if (SUCCEEDED(hr
= m_swapChain
.As(&swapChain3
)))
1468 UINT colorSpaceSupport
= 0;
1469 for (UINT colorSpace
= DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
;
1470 colorSpace
< DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020
; colorSpace
++)
1472 DXGI_COLOR_SPACE_TYPE cs
= static_cast<DXGI_COLOR_SPACE_TYPE
>(colorSpace
);
1473 if (SUCCEEDED(swapChain3
->CheckColorSpaceSupport(cs
, &colorSpaceSupport
)) &&
1474 (colorSpaceSupport
& DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT
))
1475 result
.push_back(cs
);
1480 CLog::LogF(LOGDEBUG
, "IDXGISwapChain3 is not available. Error {}",
1481 CWIN32Util::FormatHRESULT(hr
));
1486 bool DX::DeviceResources::SetMultithreadProtected(bool enabled
) const
1488 BOOL wasEnabled
= FALSE
;
1489 ComPtr
<ID3D11Multithread
> multithread
;
1490 HRESULT hr
= m_d3dDevice
.As(&multithread
);
1492 wasEnabled
= multithread
->SetMultithreadProtected(enabled
? TRUE
: FALSE
);
1494 return (wasEnabled
== TRUE
? true : false);
1497 bool DX::DeviceResources::IsGCNOrOlder() const
1499 if (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::Xbox
)
1502 const VideoDriverInfo driver
= GetVideoDriverVersion();
1504 if (driver
.vendorId
!= PCIV_AMD
)
1507 if (driver
.valid
&& CWIN32Util::IsDriverVersionAtLeast(driver
.version
, "31.0.22000.0"))