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 "rendering/dx/DirectXHelper.h"
18 #include "settings/Settings.h"
19 #include "settings/SettingsComponent.h"
20 #include "utils/SystemInfo.h"
21 #include "utils/log.h"
22 #include "windowing/GraphicContext.h"
24 #include "platform/win32/CharsetConverter.h"
25 #include "platform/win32/WIN32Util.h"
27 #ifdef TARGET_WINDOWS_STORE
28 #include <winrt/Windows.Graphics.Display.Core.h>
32 #include <libavutil/rational.h>
37 #include <dxgidebug.h>
38 #pragma comment(lib, "dxgi.lib")
41 using namespace DirectX
;
42 using namespace Microsoft::WRL
;
43 using namespace Concurrency
;
46 using namespace Windows::Foundation
;
50 #define breakOnDebug __debugbreak()
55 CLog::LogF(LOGERROR, "function call at line {} ends with error: {}", __LINE__, \
56 DX::GetErrorDescription(hr));
57 #define CHECK_ERR() if (FAILED(hr)) { LOG_HR(hr); breakOnDebug; return; }
58 #define RETURN_ERR(ret) if (FAILED(hr)) { LOG_HR(hr); breakOnDebug; return (##ret); }
60 bool DX::DeviceResources::CBackBuffer::Acquire(ID3D11Texture2D
* pTexture
)
65 D3D11_TEXTURE2D_DESC desc
;
66 pTexture
->GetDesc(&desc
);
69 m_height
= desc
.Height
;
70 m_format
= desc
.Format
;
77 std::shared_ptr
<DX::DeviceResources
> DX::DeviceResources::Get()
79 static std::shared_ptr
<DeviceResources
> sDeviceResources(new DeviceResources
);
80 return sDeviceResources
;
83 // Constructor for DeviceResources.
84 DX::DeviceResources::DeviceResources()
86 , m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1
)
89 , m_dpi(DisplayMetrics::Dpi100
)
90 , m_effectiveDpi(DisplayMetrics::Dpi100
)
91 , m_deviceNotify(nullptr)
92 , m_stereoEnabled(false)
93 , m_bDeviceCreated(false)
94 , m_IsHDROutput(false)
95 , m_IsTransferPQ(false)
99 DX::DeviceResources::~DeviceResources() = default;
101 void DX::DeviceResources::Release()
103 if (!m_bDeviceCreated
)
111 m_dxgiFactory
= nullptr;
113 m_deferrContext
= nullptr;
114 m_d3dContext
= nullptr;
115 m_d3dDevice
= nullptr;
116 m_bDeviceCreated
= false;
120 m_d3dDebug
->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY
| D3D11_RLDO_DETAIL
);
121 m_d3dDebug
= nullptr;
126 void DX::DeviceResources::GetOutput(IDXGIOutput
** ppOutput
) const
128 ComPtr
<IDXGIOutput
> pOutput
;
129 if (!m_swapChain
|| FAILED(m_swapChain
->GetContainingOutput(pOutput
.GetAddressOf())) || !pOutput
)
130 m_output
.As(&pOutput
);
131 *ppOutput
= pOutput
.Detach();
134 void DX::DeviceResources::GetCachedOutputAndDesc(IDXGIOutput
** ppOutput
,
135 DXGI_OUTPUT_DESC
* outputDesc
) const
137 ComPtr
<IDXGIOutput
> pOutput
;
140 m_output
.As(&pOutput
);
141 *outputDesc
= m_outputDesc
;
143 else if (m_swapChain
&& SUCCEEDED(m_swapChain
->GetContainingOutput(pOutput
.GetAddressOf())) &&
146 pOutput
->GetDesc(outputDesc
);
150 CLog::LogF(LOGWARNING
, "unable to retrieve current output");
152 *ppOutput
= pOutput
.Detach();
156 DXGI_ADAPTER_DESC
DX::DeviceResources::GetAdapterDesc() const
158 DXGI_ADAPTER_DESC desc
{};
161 m_adapter
->GetDesc(&desc
);
163 // GetDesc() returns VendorId == 0 in Xbox however, we need to know that
164 // GPU is AMD to apply workarounds in DXVA.cpp CheckCompatibility() same as desktop
165 if (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::Xbox
)
166 desc
.VendorId
= PCIV_AMD
;
171 void DX::DeviceResources::GetDisplayMode(DXGI_MODE_DESC
* mode
) const
173 DXGI_OUTPUT_DESC outDesc
;
174 ComPtr
<IDXGIOutput
> pOutput
;
175 DXGI_SWAP_CHAIN_DESC scDesc
;
180 m_swapChain
->GetDesc(&scDesc
);
182 GetOutput(pOutput
.GetAddressOf());
183 pOutput
->GetDesc(&outDesc
);
185 // desktop coords depend on DPI
186 mode
->Width
= DX::ConvertDipsToPixels(outDesc
.DesktopCoordinates
.right
- outDesc
.DesktopCoordinates
.left
, m_dpi
);
187 mode
->Height
= DX::ConvertDipsToPixels(outDesc
.DesktopCoordinates
.bottom
- outDesc
.DesktopCoordinates
.top
, m_dpi
);
188 mode
->Format
= scDesc
.BufferDesc
.Format
;
189 mode
->Scaling
= scDesc
.BufferDesc
.Scaling
;
190 mode
->ScanlineOrdering
= scDesc
.BufferDesc
.ScanlineOrdering
;
192 #ifdef TARGET_WINDOWS_DESKTOP
193 DEVMODEW sDevMode
= {};
194 sDevMode
.dmSize
= sizeof(sDevMode
);
196 // EnumDisplaySettingsW is only one way to detect current refresh rate
197 if (EnumDisplaySettingsW(outDesc
.DeviceName
, ENUM_CURRENT_SETTINGS
, &sDevMode
))
199 int i
= (((sDevMode
.dmDisplayFrequency
+ 1) % 24) == 0 || ((sDevMode
.dmDisplayFrequency
+ 1) % 30) == 0) ? 1 : 0;
200 mode
->RefreshRate
.Numerator
= (sDevMode
.dmDisplayFrequency
+ i
) * 1000;
201 mode
->RefreshRate
.Denominator
= 1000 + i
;
202 if (sDevMode
.dmDisplayFlags
& DM_INTERLACED
)
204 mode
->RefreshRate
.Numerator
*= 2;
205 mode
->ScanlineOrdering
= DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST
; // guessing
208 mode
->ScanlineOrdering
= DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE
;
211 using namespace winrt::Windows::Graphics::Display::Core
;
213 auto hdmiInfo
= HdmiDisplayInformation::GetForCurrentView();
214 if (hdmiInfo
) // Xbox only
216 auto currentMode
= hdmiInfo
.GetCurrentDisplayMode();
217 AVRational refresh
= av_d2q(currentMode
.RefreshRate(), 120000);
218 mode
->RefreshRate
.Numerator
= refresh
.num
;
219 mode
->RefreshRate
.Denominator
= refresh
.den
;
224 void DX::DeviceResources::SetViewPort(D3D11_VIEWPORT
& viewPort
) const
226 // convert logical viewport to real
227 D3D11_VIEWPORT realViewPort
=
237 m_deferrContext
->RSSetViewports(1, &realViewPort
);
240 bool DX::DeviceResources::SetFullScreen(bool fullscreen
, RESOLUTION_INFO
& res
)
242 if (!m_bDeviceCreated
|| !m_swapChain
)
245 critical_section::scoped_lock
lock(m_criticalSection
);
248 m_swapChain
->GetFullscreenState(&bFullScreen
, nullptr);
250 CLog::LogF(LOGDEBUG
, "switching from {}({:.0f} x {:.0f}) to {}({} x {})",
251 bFullScreen
? "fullscreen " : "", m_outputSize
.Width
, m_outputSize
.Height
,
252 fullscreen
? "fullscreen " : "", res
.iWidth
, res
.iHeight
);
254 bool recreate
= m_stereoEnabled
!= (CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED
);
255 if (!!bFullScreen
&& !fullscreen
)
257 CLog::LogF(LOGDEBUG
, "switching to windowed");
258 recreate
|= SUCCEEDED(m_swapChain
->SetFullscreenState(false, nullptr));
262 const bool isResValid
= res
.iWidth
> 0 && res
.iHeight
> 0 && res
.fRefreshRate
> 0.f
;
265 DXGI_MODE_DESC currentMode
= {};
266 GetDisplayMode(¤tMode
);
267 DXGI_SWAP_CHAIN_DESC scDesc
;
268 m_swapChain
->GetDesc(&scDesc
);
270 bool is_interlaced
= scDesc
.BufferDesc
.ScanlineOrdering
> DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE
;
271 float refreshRate
= res
.fRefreshRate
;
272 if (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
)
275 if (currentMode
.Width
!= res
.iWidth
276 || currentMode
.Height
!= res
.iHeight
277 || DX::RationalToFloat(currentMode
.RefreshRate
) != refreshRate
278 || is_interlaced
!= (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
? true : false)
279 // force resolution change for stereo mode
280 // some drivers unable to create stereo swapchain if mode does not match @23.976
281 || CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED
)
283 CLog::Log(LOGDEBUG
, __FUNCTION__
": changing display mode to {}x{}@{:0.3f}", res
.iWidth
,
284 res
.iHeight
, res
.fRefreshRate
,
285 res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
? "i" : "");
287 int refresh
= static_cast<int>(res
.fRefreshRate
);
288 int i
= (refresh
+ 1) % 24 == 0 || (refresh
+ 1) % 30 == 0 ? 1 : 0;
290 currentMode
.Width
= res
.iWidth
;
291 currentMode
.Height
= res
.iHeight
;
292 currentMode
.RefreshRate
.Numerator
= (refresh
+ i
) * 1000;
293 currentMode
.RefreshRate
.Denominator
= 1000 + i
;
294 if (res
.dwFlags
& D3DPRESENTFLAG_INTERLACED
)
296 currentMode
.RefreshRate
.Numerator
*= 2;
297 currentMode
.ScanlineOrdering
= DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST
; // guessing;
299 // sometimes the OS silently brings Kodi out of full screen mode
300 // in this case switching a resolution has no any effect and
301 // we have to enter into full screen mode before switching
304 ComPtr
<IDXGIOutput
> pOutput
;
305 GetOutput(pOutput
.GetAddressOf());
307 CLog::LogF(LOGDEBUG
, "fixup fullscreen mode before switching resolution");
308 recreate
|= SUCCEEDED(m_swapChain
->SetFullscreenState(true, pOutput
.Get()));
309 m_swapChain
->GetFullscreenState(&bFullScreen
, nullptr);
311 bool resized
= SUCCEEDED(m_swapChain
->ResizeTarget(¤tMode
));
314 // some system doesn't inform windowing about desktop size changes
315 // so we have to change output size before resizing buffers
316 m_outputSize
.Width
= static_cast<float>(currentMode
.Width
);
317 m_outputSize
.Height
= static_cast<float>(currentMode
.Height
);
324 ComPtr
<IDXGIOutput
> pOutput
;
325 GetOutput(pOutput
.GetAddressOf());
327 CLog::LogF(LOGDEBUG
, "switching to fullscreen");
328 recreate
|= SUCCEEDED(m_swapChain
->SetFullscreenState(true, pOutput
.Get()));
332 // resize backbuffer to proper handle fullscreen/stereo transition
336 CLog::LogF(LOGDEBUG
, "switching done.");
341 // Configures resources that don't depend on the Direct3D device.
342 void DX::DeviceResources::CreateDeviceIndependentResources()
346 // Configures the Direct3D device, and stores handles to it and the device context.
347 void DX::DeviceResources::CreateDeviceResources()
349 CLog::LogF(LOGDEBUG
, "creating DirectX 11 device.");
353 UINT creationFlags
= D3D11_CREATE_DEVICE_VIDEO_SUPPORT
;
355 if (DX::SdkLayersAvailable())
357 // If the project is in a debug build, enable debugging via SDK Layers with this flag.
358 creationFlags
|= D3D11_CREATE_DEVICE_DEBUG
;
362 // This array defines the set of DirectX hardware feature levels this app will support.
363 // Note the ordering should be preserved.
364 // Don't forget to declare your application's minimum required feature level in its
365 // description. All applications are assumed to support 9.1 unless otherwise stated.
366 std::vector
<D3D_FEATURE_LEVEL
> featureLevels
;
367 if (CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10
))
369 featureLevels
.push_back(D3D_FEATURE_LEVEL_12_1
);
370 featureLevels
.push_back(D3D_FEATURE_LEVEL_12_0
);
372 featureLevels
.push_back(D3D_FEATURE_LEVEL_11_1
);
373 featureLevels
.push_back(D3D_FEATURE_LEVEL_11_0
);
374 featureLevels
.push_back(D3D_FEATURE_LEVEL_10_1
);
375 featureLevels
.push_back(D3D_FEATURE_LEVEL_10_0
);
376 featureLevels
.push_back(D3D_FEATURE_LEVEL_9_3
);
377 featureLevels
.push_back(D3D_FEATURE_LEVEL_9_2
);
378 featureLevels
.push_back(D3D_FEATURE_LEVEL_9_1
);
380 // Create the Direct3D 11 API device object and a corresponding context.
381 ComPtr
<ID3D11Device
> device
;
382 ComPtr
<ID3D11DeviceContext
> context
;
384 D3D_DRIVER_TYPE drivertType
= m_adapter
!= nullptr ? D3D_DRIVER_TYPE_UNKNOWN
: D3D_DRIVER_TYPE_HARDWARE
;
385 HRESULT hr
= D3D11CreateDevice(
386 m_adapter
.Get(), // Create a device on specified adapter.
387 drivertType
, // Create a device using scepcified driver.
388 nullptr, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
389 creationFlags
, // Set debug and Direct2D compatibility flags.
390 featureLevels
.data(), // List of feature levels this app can support.
391 featureLevels
.size(), // Size of the list above.
392 D3D11_SDK_VERSION
, // Always set this to D3D11_SDK_VERSION for Windows Store apps.
393 &device
, // Returns the Direct3D device created.
394 &m_d3dFeatureLevel
, // Returns feature level of device created.
395 &context
// Returns the device immediate context.
400 CLog::LogF(LOGERROR
, "unable to create hardware device with video support, error {}",
401 DX::GetErrorDescription(hr
));
402 CLog::LogF(LOGERROR
, "trying to create hardware device without video support.");
404 creationFlags
&= ~D3D11_CREATE_DEVICE_VIDEO_SUPPORT
;
406 hr
= D3D11CreateDevice(m_adapter
.Get(), drivertType
, nullptr, creationFlags
,
407 featureLevels
.data(), featureLevels
.size(), D3D11_SDK_VERSION
, &device
,
408 &m_d3dFeatureLevel
, &context
);
412 CLog::LogF(LOGERROR
, "unable to create hardware device, error {}",
413 DX::GetErrorDescription(hr
));
414 CLog::LogF(LOGERROR
, "trying to create WARP device.");
416 hr
= D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP
, nullptr, creationFlags
,
417 featureLevels
.data(), featureLevels
.size(), D3D11_SDK_VERSION
, &device
,
418 &m_d3dFeatureLevel
, &context
);
422 CLog::LogF(LOGFATAL
, "unable to create WARP device. Rendering is not possible. Error {}",
423 DX::GetErrorDescription(hr
));
429 // Store pointers to the Direct3D 11.1 API device and immediate context.
430 hr
= device
.As(&m_d3dDevice
); CHECK_ERR();
432 // Check shared textures support
433 CheckNV12SharedTexturesSupport();
436 if (SUCCEEDED(m_d3dDevice
.As(&m_d3dDebug
)))
438 ComPtr
<ID3D11InfoQueue
> d3dInfoQueue
;
439 if (SUCCEEDED(m_d3dDebug
.As(&d3dInfoQueue
)))
441 std::vector
<D3D11_MESSAGE_ID
> hide
=
443 D3D11_MESSAGE_ID_GETVIDEOPROCESSORFILTERRANGE_UNSUPPORTED
, // avoid GETVIDEOPROCESSORFILTERRANGE_UNSUPPORTED (dx bug)
444 D3D11_MESSAGE_ID_DEVICE_RSSETSCISSORRECTS_NEGATIVESCISSOR
// avoid warning for some labels out of screen
445 // Add more message IDs here as needed
448 D3D11_INFO_QUEUE_FILTER filter
= {};
449 filter
.DenyList
.NumIDs
= hide
.size();
450 filter
.DenyList
.pIDList
= hide
.data();
451 d3dInfoQueue
->AddStorageFilterEntries(&filter
);
456 hr
= context
.As(&m_d3dContext
); CHECK_ERR();
457 hr
= m_d3dDevice
->CreateDeferredContext1(0, &m_deferrContext
); CHECK_ERR();
461 ComPtr
<IDXGIDevice1
> dxgiDevice
;
462 ComPtr
<IDXGIAdapter
> adapter
;
463 hr
= m_d3dDevice
.As(&dxgiDevice
); CHECK_ERR();
464 hr
= dxgiDevice
->GetAdapter(&adapter
); CHECK_ERR();
465 hr
= adapter
.As(&m_adapter
); CHECK_ERR();
468 DXGI_ADAPTER_DESC aDesc
;
469 m_adapter
->GetDesc(&aDesc
);
471 CLog::LogF(LOGINFO
, "device is created on adapter '{}' with {}",
472 KODI::PLATFORM::WINDOWS::FromW(aDesc
.Description
),
473 GetFeatureLevelDescription(m_d3dFeatureLevel
));
475 CheckDXVA2SharedDecoderSurfaces();
477 m_bDeviceCreated
= true;
480 void DX::DeviceResources::ReleaseBackBuffer()
482 CLog::LogF(LOGDEBUG
, "release buffers.");
484 m_backBufferTex
.Release();
485 m_d3dDepthStencilView
= nullptr;
488 // Clear the previous window size specific context.
489 ID3D11RenderTargetView
* nullViews
[] = { nullptr, nullptr, nullptr, nullptr };
490 m_deferrContext
->OMSetRenderTargets(4, nullViews
, nullptr);
491 FinishCommandList(false);
493 m_deferrContext
->Flush();
494 m_d3dContext
->Flush();
498 void DX::DeviceResources::CreateBackBuffer()
500 if (!m_bDeviceCreated
|| !m_swapChain
)
503 CLog::LogF(LOGDEBUG
, "create buffers.");
505 // Get swap chain back buffer.
506 ComPtr
<ID3D11Texture2D
> backBuffer
;
507 HRESULT hr
= m_swapChain
->GetBuffer(0, IID_PPV_ARGS(&backBuffer
)); CHECK_ERR();
509 // Create back buffer texture from swap chain texture
510 if (!m_backBufferTex
.Acquire(backBuffer
.Get()))
512 CLog::LogF(LOGERROR
, "failed to create render target.");
516 // Create a depth stencil view for use with 3D rendering if needed.
517 CD3D11_TEXTURE2D_DESC
depthStencilDesc(
518 DXGI_FORMAT_D24_UNORM_S8_UINT
,
519 lround(m_outputSize
.Width
),
520 lround(m_outputSize
.Height
),
521 1, // This depth stencil view has only one texture.
522 1, // Use a single mipmap level.
523 D3D11_BIND_DEPTH_STENCIL
526 ComPtr
<ID3D11Texture2D
> depthStencil
;
527 hr
= m_d3dDevice
->CreateTexture2D(
533 CD3D11_DEPTH_STENCIL_VIEW_DESC
depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D
);
534 hr
= m_d3dDevice
->CreateDepthStencilView(
536 &depthStencilViewDesc
,
537 &m_d3dDepthStencilView
540 // Set the 3D rendering viewport to target the entire window.
541 m_screenViewport
= CD3D11_VIEWPORT(
548 m_deferrContext
->RSSetViewports(1, &m_screenViewport
);
551 HRESULT
DX::DeviceResources::CreateSwapChain(DXGI_SWAP_CHAIN_DESC1
& desc
, DXGI_SWAP_CHAIN_FULLSCREEN_DESC
& fsDesc
, IDXGISwapChain1
** ppSwapChain
) const
554 #ifdef TARGET_WINDOWS_DESKTOP
555 hr
= m_dxgiFactory
->CreateSwapChainForHwnd(
563 hr
= m_dxgiFactory
->MakeWindowAssociation(m_window
, /*DXGI_MWA_NO_WINDOW_CHANGES |*/ DXGI_MWA_NO_ALT_ENTER
);
565 hr
= m_dxgiFactory
->CreateSwapChainForCoreWindow(
567 winrt::get_unknown(m_coreWindow
),
576 void DX::DeviceResources::DestroySwapChain()
582 m_swapChain
->GetFullscreenState(&bFullcreen
, nullptr);
584 m_swapChain
->SetFullscreenState(false, nullptr); // mandatory before releasing swapchain
585 m_swapChain
= nullptr;
586 m_deferrContext
->Flush();
587 m_d3dContext
->Flush();
588 m_IsTransferPQ
= false;
591 void DX::DeviceResources::ResizeBuffers()
593 if (!m_bDeviceCreated
)
596 CLog::LogF(LOGDEBUG
, "resize buffers.");
598 bool bHWStereoEnabled
= RENDER_STEREO_MODE_HARDWAREBASED
==
599 CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode();
600 bool windowed
= true;
602 DXGI_SWAP_CHAIN_DESC1 scDesc
= {};
607 m_swapChain
->GetFullscreenState(&bFullcreen
, nullptr);
611 m_swapChain
->GetDesc1(&scDesc
);
612 if ((scDesc
.Stereo
== TRUE
) != bHWStereoEnabled
) // check if swapchain needs to be recreated
616 if (m_swapChain
) // If the swap chain already exists, resize it.
618 m_swapChain
->GetDesc1(&scDesc
);
619 hr
= m_swapChain
->ResizeBuffers(scDesc
.BufferCount
, lround(m_outputSize
.Width
),
620 lround(m_outputSize
.Height
), scDesc
.Format
,
621 windowed
? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
);
623 if (hr
== DXGI_ERROR_DEVICE_REMOVED
|| hr
== DXGI_ERROR_DEVICE_RESET
)
625 // If the device was removed for any reason, a new device and swap chain will need to be created.
626 HandleDeviceLost(hr
== DXGI_ERROR_DEVICE_REMOVED
);
628 // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
629 // and correctly set up the new device.
632 else if (hr
== DXGI_ERROR_INVALID_CALL
)
634 // Called when Windows HDR is toggled externally to Kodi.
635 // Is forced to re-create swap chain to avoid crash.
636 CreateWindowSizeDependentResources();
641 else // Otherwise, create a new one using the same adapter as the existing Direct3D device.
643 HDR_STATUS hdrStatus
= CWIN32Util::GetWindowsHDRStatus();
644 const bool isHdrEnabled
= (hdrStatus
== HDR_STATUS::HDR_ON
);
645 bool use10bit
= (hdrStatus
!= HDR_STATUS::HDR_UNSUPPORTED
);
647 // Xbox needs 10 bit swapchain to output true 4K resolution
648 #ifdef TARGET_WINDOWS_DESKTOP
649 // Some AMD graphics has issues with 10 bit in SDR.
650 // Enabled by default only in Intel and NVIDIA with latest drivers/hardware
651 if (m_d3dFeatureLevel
< D3D_FEATURE_LEVEL_12_1
|| GetAdapterDesc().VendorId
== PCIV_AMD
)
655 // 0 = Auto | 1 = Never | 2 = Always
656 int use10bitSetting
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
657 CSettings::SETTING_VIDEOSCREEN_10BITSURFACES
);
659 if (use10bitSetting
== 1)
661 else if (use10bitSetting
== 2)
664 DXGI_SWAP_CHAIN_DESC1 swapChainDesc
= {};
665 swapChainDesc
.Width
= lround(m_outputSize
.Width
);
666 swapChainDesc
.Height
= lround(m_outputSize
.Height
);
667 swapChainDesc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
668 swapChainDesc
.Stereo
= bHWStereoEnabled
;
669 swapChainDesc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
670 #ifdef TARGET_WINDOWS_DESKTOP
671 swapChainDesc
.BufferCount
= 6; // HDR 60 fps needs 6 buffers to avoid frame drops
673 swapChainDesc
.BufferCount
= 3; // Xbox don't like 6 backbuffers (3 is fine even for 4K 60 fps)
675 // FLIP_DISCARD improves performance (needed in some systems for 4K HDR 60 fps)
676 swapChainDesc
.SwapEffect
= CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10
)
677 ? DXGI_SWAP_EFFECT_FLIP_DISCARD
678 : DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
679 swapChainDesc
.Flags
= windowed
? 0 : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
;
680 swapChainDesc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
681 swapChainDesc
.SampleDesc
.Count
= 1;
682 swapChainDesc
.SampleDesc
.Quality
= 0;
684 DXGI_SWAP_CHAIN_FULLSCREEN_DESC scFSDesc
= {}; // unused for uwp
685 scFSDesc
.ScanlineOrdering
= DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED
;
686 scFSDesc
.Windowed
= windowed
;
688 ComPtr
<IDXGISwapChain1
> swapChain
;
689 if (m_d3dFeatureLevel
>= D3D_FEATURE_LEVEL_11_0
&& !bHWStereoEnabled
&&
690 (isHdrEnabled
|| use10bit
))
692 swapChainDesc
.Format
= DXGI_FORMAT_R10G10B10A2_UNORM
;
693 hr
= CreateSwapChain(swapChainDesc
, scFSDesc
, &swapChain
);
696 CLog::LogF(LOGWARNING
, "creating 10bit swapchain failed, fallback to 8bit.");
697 swapChainDesc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
702 hr
= CreateSwapChain(swapChainDesc
, scFSDesc
, &swapChain
);
704 if (FAILED(hr
) && bHWStereoEnabled
)
706 // switch to stereo mode failed, create mono swapchain
707 CLog::LogF(LOGERROR
, "creating stereo swap chain failed with error.");
708 CLog::LogF(LOGINFO
, "fallback to monoscopic mode.");
710 swapChainDesc
.Stereo
= false;
711 bHWStereoEnabled
= false;
713 hr
= CreateSwapChain(swapChainDesc
, scFSDesc
, &swapChain
); CHECK_ERR();
715 // fallback to split_horizontal mode.
716 CServiceBroker::GetWinSystem()->GetGfxContext().SetStereoMode(
717 RENDER_STEREO_MODE_SPLIT_HORIZONTAL
);
722 CLog::LogF(LOGERROR
, "unable to create swapchain.");
726 m_IsHDROutput
= (swapChainDesc
.Format
== DXGI_FORMAT_R10G10B10A2_UNORM
) && isHdrEnabled
;
729 LOGINFO
, "{} bit swapchain is used with {} flip {} buffers and {} output (format {})",
730 (swapChainDesc
.Format
== DXGI_FORMAT_R10G10B10A2_UNORM
) ? 10 : 8, swapChainDesc
.BufferCount
,
731 (swapChainDesc
.SwapEffect
== DXGI_SWAP_EFFECT_FLIP_DISCARD
) ? "discard" : "sequential",
732 m_IsHDROutput
? "HDR" : "SDR", DX::DXGIFormatToString(swapChainDesc
.Format
));
734 hr
= swapChain
.As(&m_swapChain
); CHECK_ERR();
735 m_stereoEnabled
= bHWStereoEnabled
;
737 if (CServiceBroker::GetLogging().IsLogLevelLogged(LOGDEBUG
) &&
738 CServiceBroker::GetLogging().CanLogComponent(LOGVIDEO
))
740 std::string colorSpaces
;
741 for (const DXGI_COLOR_SPACE_TYPE
& colorSpace
: GetSwapChainColorSpaces())
743 colorSpaces
.append("\n");
744 colorSpaces
.append(DX::DXGIColorSpaceTypeToString(colorSpace
));
746 CLog::LogFC(LOGDEBUG
, LOGVIDEO
, "Color spaces supported by the swap chain:{}", colorSpaces
);
749 // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and
750 // ensures that the application will only render after each VSync, minimizing power consumption.
751 ComPtr
<IDXGIDevice1
> dxgiDevice
;
752 hr
= m_d3dDevice
.As(&dxgiDevice
); CHECK_ERR();
753 dxgiDevice
->SetMaximumFrameLatency(1);
756 SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
);
759 CLog::LogF(LOGDEBUG
, "end resize buffers.");
762 // These resources need to be recreated every time the window size is changed.
763 void DX::DeviceResources::CreateWindowSizeDependentResources()
769 if (!m_dxgiFactory
->IsCurrent()) // HDR toggling requires re-create factory
772 UpdateRenderTargetSize();
778 // Determine the dimensions of the render target and whether it will be scaled down.
779 void DX::DeviceResources::UpdateRenderTargetSize()
781 m_effectiveDpi
= m_dpi
;
783 // To improve battery life on high resolution devices, render to a smaller render target
784 // and allow the GPU to scale the output when it is presented.
785 if (!DisplayMetrics::SupportHighResolutions
&& m_dpi
> DisplayMetrics::DpiThreshold
)
787 float width
= DX::ConvertDipsToPixels(m_logicalSize
.Width
, m_dpi
);
788 float height
= DX::ConvertDipsToPixels(m_logicalSize
.Height
, m_dpi
);
790 // When the device is in portrait orientation, height > width. Compare the
791 // larger dimension against the width threshold and the smaller dimension
792 // against the height threshold.
793 if (std::max(width
, height
) > DisplayMetrics::WidthThreshold
&& std::min(width
, height
) > DisplayMetrics::HeightThreshold
)
795 // To scale the app we change the effective DPI. Logical size does not change.
796 m_effectiveDpi
/= 2.0f
;
800 // Calculate the necessary render target size in pixels.
801 m_outputSize
.Width
= DX::ConvertDipsToPixels(m_logicalSize
.Width
, m_effectiveDpi
);
802 m_outputSize
.Height
= DX::ConvertDipsToPixels(m_logicalSize
.Height
, m_effectiveDpi
);
804 // Prevent zero size DirectX content from being created.
805 m_outputSize
.Width
= std::max(m_outputSize
.Width
, 1.f
);
806 m_outputSize
.Height
= std::max(m_outputSize
.Height
, 1.f
);
809 void DX::DeviceResources::Register(ID3DResource
* resource
)
811 critical_section::scoped_lock
lock(m_resourceSection
);
812 m_resources
.push_back(resource
);
815 void DX::DeviceResources::Unregister(ID3DResource
* resource
)
817 critical_section::scoped_lock
lock(m_resourceSection
);
818 std::vector
<ID3DResource
*>::iterator i
= find(m_resources
.begin(), m_resources
.end(), resource
);
819 if (i
!= m_resources
.end())
820 m_resources
.erase(i
);
823 void DX::DeviceResources::FinishCommandList(bool bExecute
) const
825 if (m_d3dContext
== m_deferrContext
)
828 ComPtr
<ID3D11CommandList
> pCommandList
;
829 if (FAILED(m_deferrContext
->FinishCommandList(true, &pCommandList
)))
831 CLog::LogF(LOGERROR
, "failed to finish command queue.");
836 m_d3dContext
->ExecuteCommandList(pCommandList
.Get(), false);
839 // This method is called in the event handler for the SizeChanged event.
840 void DX::DeviceResources::SetLogicalSize(float width
, float height
)
843 #if defined(TARGET_WINDOWS_DESKTOP)
850 CLog::LogF(LOGDEBUG
, "receive changing logical size to {:f} x {:f}", width
, height
);
852 if (m_logicalSize
.Width
!= width
|| m_logicalSize
.Height
!= height
)
854 CLog::LogF(LOGDEBUG
, "change logical size to {:f} x {:f}", width
, height
);
856 m_logicalSize
= winrt::Size(width
, height
);
858 UpdateRenderTargetSize();
863 // This method is called in the event handler for the DpiChanged event.
864 void DX::DeviceResources::SetDpi(float dpi
)
866 dpi
= std::max(dpi
, DisplayMetrics::Dpi100
);
871 // This method is called in the event handler for the DisplayContentsInvalidated event.
872 void DX::DeviceResources::ValidateDevice()
874 // The D3D Device is no longer valid if the default adapter changed since the device
875 // was created or if the device has been removed.
877 // First, get the information for the default adapter from when the device was created.
878 ComPtr
<IDXGIDevice1
> dxgiDevice
;
879 m_d3dDevice
.As(&dxgiDevice
);
881 ComPtr
<IDXGIAdapter
> deviceAdapter
;
882 dxgiDevice
->GetAdapter(&deviceAdapter
);
884 ComPtr
<IDXGIFactory2
> dxgiFactory
;
885 deviceAdapter
->GetParent(IID_PPV_ARGS(&dxgiFactory
));
887 DXGI_ADAPTER_DESC1 previousDesc
;
889 ComPtr
<IDXGIAdapter1
> previousDefaultAdapter
;
890 dxgiFactory
->EnumAdapters1(0, &previousDefaultAdapter
);
892 previousDefaultAdapter
->GetDesc1(&previousDesc
);
895 // Next, get the information for the current default adapter.
896 DXGI_ADAPTER_DESC1 currentDesc
;
898 ComPtr
<IDXGIFactory1
> currentFactory
;
899 CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory
));
901 ComPtr
<IDXGIAdapter1
> currentDefaultAdapter
;
902 currentFactory
->EnumAdapters1(0, ¤tDefaultAdapter
);
904 currentDefaultAdapter
->GetDesc1(¤tDesc
);
906 // If the adapter LUIDs don't match, or if the device reports that it has been removed,
907 // a new D3D device must be created.
908 HRESULT hr
= m_d3dDevice
->GetDeviceRemovedReason();
909 if ( previousDesc
.AdapterLuid
.LowPart
!= currentDesc
.AdapterLuid
.LowPart
910 || previousDesc
.AdapterLuid
.HighPart
!= currentDesc
.AdapterLuid
.HighPart
913 // Release references to resources related to the old device.
914 dxgiDevice
= nullptr;
915 deviceAdapter
= nullptr;
916 dxgiFactory
= nullptr;
918 // Create a new device and swap chain.
919 HandleDeviceLost(hr
== DXGI_ERROR_DEVICE_REMOVED
);
923 void DX::DeviceResources::OnDeviceLost(bool removed
)
925 auto pGUI
= CServiceBroker::GetGUI();
927 pGUI
->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_RENDERER_LOST
);
929 // tell any shared resources
930 for (auto res
: m_resources
)
932 // the most of resources like textures and buffers try to
933 // receive and save their status from current device.
934 // `removed` means that we have no possibility
935 // to use the device anymore, tell all resources about this.
936 res
->OnDestroyDevice(removed
);
940 void DX::DeviceResources::OnDeviceRestored()
942 // tell any shared resources
943 for (auto res
: m_resources
)
944 res
->OnCreateDevice();
946 auto pGUI
= CServiceBroker::GetGUI();
948 pGUI
->GetWindowManager().SendMessage(GUI_MSG_NOTIFY_ALL
, 0, 0, GUI_MSG_RENDERER_RESET
);
951 // Recreate all device resources and set them back to the current state.
952 void DX::DeviceResources::HandleDeviceLost(bool removed
)
954 bool backbuferExists
= m_backBufferTex
.Get() != nullptr;
956 OnDeviceLost(removed
);
957 if (m_deviceNotify
!= nullptr)
958 m_deviceNotify
->OnDXDeviceLost();
965 CreateDeviceResources();
966 UpdateRenderTargetSize();
972 if (m_deviceNotify
!= nullptr)
973 m_deviceNotify
->OnDXDeviceRestored();
977 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN
, -1, -1, nullptr,
981 bool DX::DeviceResources::Begin()
983 HRESULT hr
= m_swapChain
->Present(0, DXGI_PRESENT_TEST
);
985 // If the device was removed either by a disconnection or a driver upgrade, we
986 // must recreate all device resources.
987 if (hr
== DXGI_ERROR_DEVICE_REMOVED
|| hr
== DXGI_ERROR_DEVICE_RESET
)
989 HandleDeviceLost(hr
== DXGI_ERROR_DEVICE_REMOVED
);
994 if (hr
== DXGI_ERROR_INVALID_CALL
)
996 CreateWindowSizeDependentResources();
1000 m_deferrContext
->OMSetRenderTargets(1, m_backBufferTex
.GetAddressOfRTV(), m_d3dDepthStencilView
.Get());
1005 // Present the contents of the swap chain to the screen.
1006 void DX::DeviceResources::Present()
1008 FinishCommandList();
1010 // The first argument instructs DXGI to block until VSync, putting the application
1011 // to sleep until the next VSync. This ensures we don't waste any cycles rendering
1012 // frames that will never be displayed to the screen.
1013 DXGI_PRESENT_PARAMETERS parameters
= {};
1014 HRESULT hr
= m_swapChain
->Present1(1, 0, ¶meters
);
1016 // If the device was removed either by a disconnection or a driver upgrade, we
1017 // must recreate all device resources.
1018 if (hr
== DXGI_ERROR_DEVICE_REMOVED
|| hr
== DXGI_ERROR_DEVICE_RESET
)
1020 HandleDeviceLost(hr
== DXGI_ERROR_DEVICE_REMOVED
);
1025 if (hr
== DXGI_ERROR_INVALID_CALL
)
1027 CreateWindowSizeDependentResources();
1031 if (m_d3dContext
== m_deferrContext
)
1033 m_deferrContext
->OMSetRenderTargets(1, m_backBufferTex
.GetAddressOfRTV(), m_d3dDepthStencilView
.Get());
1037 void DX::DeviceResources::ClearDepthStencil() const
1039 m_deferrContext
->ClearDepthStencilView(m_d3dDepthStencilView
.Get(), D3D11_CLEAR_DEPTH
| D3D11_CLEAR_STENCIL
, 1.0, 0);
1042 void DX::DeviceResources::ClearRenderTarget(ID3D11RenderTargetView
* pRTView
, float color
[4]) const
1044 m_deferrContext
->ClearRenderTargetView(pRTView
, color
);
1047 void DX::DeviceResources::HandleOutputChange(const std::function
<bool(DXGI_OUTPUT_DESC
)>& cmpFunc
)
1049 DXGI_ADAPTER_DESC currentDesc
= {};
1050 DXGI_ADAPTER_DESC foundDesc
= {};
1052 ComPtr
<IDXGIFactory1
> factory
;
1054 m_adapter
->GetDesc(¤tDesc
);
1056 CreateDXGIFactory1(IID_IDXGIFactory1
, &factory
);
1058 ComPtr
<IDXGIAdapter1
> adapter
;
1059 for (int i
= 0; factory
->EnumAdapters1(i
, adapter
.ReleaseAndGetAddressOf()) != DXGI_ERROR_NOT_FOUND
; i
++)
1061 adapter
->GetDesc(&foundDesc
);
1062 ComPtr
<IDXGIOutput
> output
;
1063 for (int j
= 0; adapter
->EnumOutputs(j
, output
.ReleaseAndGetAddressOf()) != DXGI_ERROR_NOT_FOUND
; j
++)
1065 DXGI_OUTPUT_DESC outputDesc
;
1066 output
->GetDesc(&outputDesc
);
1067 if (cmpFunc(outputDesc
))
1069 output
.As(&m_output
);
1070 m_outputDesc
= outputDesc
;
1071 // check if adapter is changed
1072 if (currentDesc
.AdapterLuid
.HighPart
!= foundDesc
.AdapterLuid
.HighPart
1073 || currentDesc
.AdapterLuid
.LowPart
!= foundDesc
.AdapterLuid
.LowPart
)
1075 // adapter is changed
1076 m_adapter
= adapter
;
1077 CLog::LogF(LOGDEBUG
, "selected {} adapter. ",
1078 KODI::PLATFORM::WINDOWS::FromW(foundDesc
.Description
));
1079 // (re)init hooks into new driver
1080 Windowing()->InitHooks(output
.Get());
1081 // recreate d3d11 device on new adapter
1083 HandleDeviceLost(false);
1091 bool DX::DeviceResources::CreateFactory()
1094 #if defined(_DEBUG) && defined(TARGET_WINDOWS_STORE)
1095 bool debugDXGI
= false;
1097 ComPtr
<IDXGIInfoQueue
> dxgiInfoQueue
;
1098 if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(dxgiInfoQueue
.GetAddressOf()))))
1102 hr
= CreateDXGIFactory2(DXGI_CREATE_FACTORY_DEBUG
, IID_PPV_ARGS(m_dxgiFactory
.ReleaseAndGetAddressOf())); RETURN_ERR(false);
1104 dxgiInfoQueue
->SetBreakOnSeverity(DXGI_DEBUG_ALL
, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR
, true);
1105 dxgiInfoQueue
->SetBreakOnSeverity(DXGI_DEBUG_ALL
, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION
, true);
1111 hr
= CreateDXGIFactory1(IID_PPV_ARGS(m_dxgiFactory
.ReleaseAndGetAddressOf())); RETURN_ERR(false);
1116 void DX::DeviceResources::SetMonitor(HMONITOR monitor
)
1118 HandleOutputChange([monitor
](DXGI_OUTPUT_DESC outputDesc
) {
1119 return outputDesc
.Monitor
== monitor
;
1123 void DX::DeviceResources::RegisterDeviceNotify(IDeviceNotify
* deviceNotify
)
1125 m_deviceNotify
= deviceNotify
;
1128 HMONITOR
DX::DeviceResources::GetMonitor() const
1132 ComPtr
<IDXGIOutput
> output
;
1133 GetOutput(output
.GetAddressOf());
1136 DXGI_OUTPUT_DESC desc
;
1137 output
->GetDesc(&desc
);
1138 return desc
.Monitor
;
1144 bool DX::DeviceResources::IsStereoAvailable() const
1147 return m_dxgiFactory
->IsWindowedStereoEnabled();
1152 void DX::DeviceResources::CheckNV12SharedTexturesSupport()
1154 if (m_d3dFeatureLevel
< D3D_FEATURE_LEVEL_10_0
||
1155 CSysInfo::GetWindowsDeviceFamily() != CSysInfo::Desktop
)
1158 D3D11_FEATURE_DATA_D3D11_OPTIONS4 op4
= {};
1159 HRESULT hr
= m_d3dDevice
->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS4
, &op4
, sizeof(op4
));
1160 m_NV12SharedTexturesSupport
= SUCCEEDED(hr
) && !!op4
.ExtendedNV12SharedTextureSupported
;
1161 CLog::LogF(LOGINFO
, "extended NV12 shared textures is{}supported",
1162 m_NV12SharedTexturesSupport
? " " : " NOT ");
1165 void DX::DeviceResources::CheckDXVA2SharedDecoderSurfaces()
1167 if (CSysInfo::GetWindowsDeviceFamily() != CSysInfo::Desktop
)
1170 VideoDriverInfo driver
= GetVideoDriverVersion();
1173 if (!m_NV12SharedTexturesSupport
)
1176 const DXGI_ADAPTER_DESC ad
= GetAdapterDesc();
1178 m_DXVA2SharedDecoderSurfaces
=
1179 ad
.VendorId
== PCIV_Intel
||
1180 (ad
.VendorId
== PCIV_NVIDIA
&& driver
.valid
&& driver
.majorVersion
>= 465) ||
1181 (ad
.VendorId
== PCIV_AMD
&& driver
.valid
&& driver
.majorVersion
>= 30 &&
1182 m_d3dFeatureLevel
>= D3D_FEATURE_LEVEL_12_1
);
1184 CLog::LogF(LOGINFO
, "DXVA2 shared decoder surfaces is{}supported",
1185 m_DXVA2SharedDecoderSurfaces
? " " : " NOT ");
1187 m_DXVA2UseFence
= m_DXVA2SharedDecoderSurfaces
&&
1188 (ad
.VendorId
== PCIV_NVIDIA
|| ad
.VendorId
== PCIV_AMD
) &&
1189 CSysInfo::IsWindowsVersionAtLeast(CSysInfo::WindowsVersionWin10_1703
);
1191 if (m_DXVA2SharedDecoderSurfaces
)
1192 CLog::LogF(LOGINFO
, "DXVA2 shared decoder surfaces {} fence synchronization.",
1193 m_DXVA2UseFence
? "WITH" : "WITHOUT");
1195 m_DXVASuperResolutionSupport
=
1196 m_d3dFeatureLevel
>= D3D_FEATURE_LEVEL_12_1
&&
1197 ((ad
.VendorId
== PCIV_Intel
&& driver
.valid
&& driver
.majorVersion
>= 31) ||
1198 (ad
.VendorId
== PCIV_NVIDIA
&& driver
.valid
&& driver
.majorVersion
>= 530));
1200 if (m_DXVASuperResolutionSupport
)
1201 CLog::LogF(LOGINFO
, "DXVA Video Super Resolution is potentially supported");
1204 VideoDriverInfo
DX::DeviceResources::GetVideoDriverVersion() const
1209 VideoDriverInfo driver
{};
1210 const DXGI_ADAPTER_DESC ad
= GetAdapterDesc();
1212 // Version retrieval with DXGI is more modern but requires WDDM >= 2.3 for guarantee of same
1213 // version returned by all driver components. Fallback to older method for older drivers.
1214 LARGE_INTEGER rawVersion
{};
1215 if (SUCCEEDED(m_adapter
->CheckInterfaceSupport(__uuidof(IDXGIDevice
), &rawVersion
)) &&
1216 static_cast<uint16_t>(rawVersion
.QuadPart
>> 48) >= 23)
1217 driver
= CWIN32Util::FormatVideoDriverInfo(ad
.VendorId
, rawVersion
.QuadPart
);
1220 driver
= CWIN32Util::GetVideoDriverInfo(ad
.VendorId
, ad
.Description
);
1223 driver
= CWIN32Util::GetVideoDriverInfoDX(ad
.VendorId
, ad
.AdapterLuid
);
1228 #if defined(TARGET_WINDOWS_DESKTOP)
1229 // This method is called when the window (WND) is created (or re-created).
1230 void DX::DeviceResources::SetWindow(HWND window
)
1234 CreateDeviceIndependentResources();
1235 CreateDeviceResources();
1237 #elif defined(TARGET_WINDOWS_STORE)
1238 // This method is called when the CoreWindow is created (or re-created).
1239 void DX::DeviceResources::SetWindow(const winrt::Windows::UI::Core::CoreWindow
& window
)
1241 using namespace winrt::Windows::UI::Core
;
1242 using namespace winrt::Windows::Graphics::Display
;
1244 m_coreWindow
= window
;
1245 auto dispatcher
= m_coreWindow
.Dispatcher();
1246 DispatchedHandler
handler([&]()
1248 auto coreWindow
= CoreWindow::GetForCurrentThread();
1249 m_logicalSize
= winrt::Size(coreWindow
.Bounds().Width
, coreWindow
.Bounds().Height
);
1250 m_dpi
= DisplayInformation::GetForCurrentView().LogicalDpi();
1251 SetWindowPos(coreWindow
.Bounds());
1253 if (dispatcher
.HasThreadAccess())
1256 dispatcher
.RunAsync(CoreDispatcherPriority::High
, handler
).get();
1258 CreateDeviceIndependentResources();
1259 CreateDeviceResources();
1260 // we have to call this because we will not get initial WM_SIZE
1261 CreateWindowSizeDependentResources();
1264 void DX::DeviceResources::SetWindowPos(winrt::Rect rect
)
1266 int centerX
= rect
.X
+ rect
.Width
/ 2;
1267 int centerY
= rect
.Y
+ rect
.Height
/ 2;
1269 HandleOutputChange([centerX
, centerY
](DXGI_OUTPUT_DESC outputDesc
) {
1270 // DesktopCoordinates depends on the DPI of the desktop
1271 return outputDesc
.DesktopCoordinates
.left
<= centerX
&& outputDesc
.DesktopCoordinates
.right
>= centerX
1272 && outputDesc
.DesktopCoordinates
.top
<= centerY
&& outputDesc
.DesktopCoordinates
.bottom
>= centerY
;
1276 // Call this method when the app suspends. It provides a hint to the driver that the app
1277 // is entering an idle state and that temporary buffers can be reclaimed for use by other apps.
1278 void DX::DeviceResources::Trim() const
1280 ComPtr
<IDXGIDevice3
> dxgiDevice
;
1281 m_d3dDevice
.As(&dxgiDevice
);
1288 void DX::DeviceResources::SetHdrMetaData(DXGI_HDR_METADATA_HDR10
& hdr10
) const
1290 ComPtr
<IDXGISwapChain4
> swapChain4
;
1295 if (SUCCEEDED(m_swapChain
.As(&swapChain4
)))
1297 if (SUCCEEDED(swapChain4
->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10
, sizeof(hdr10
), &hdr10
)))
1299 CLog::LogF(LOGDEBUG
,
1300 "(raw) RP {} {} | GP {} {} | BP {} {} | WP {} {} | Max ML {} | min ML "
1301 "{} | Max CLL {} | Max FALL {}",
1302 hdr10
.RedPrimary
[0], hdr10
.RedPrimary
[1], hdr10
.GreenPrimary
[0],
1303 hdr10
.GreenPrimary
[1], hdr10
.BluePrimary
[0], hdr10
.BluePrimary
[1],
1304 hdr10
.WhitePoint
[0], hdr10
.WhitePoint
[1], hdr10
.MaxMasteringLuminance
,
1305 hdr10
.MinMasteringLuminance
, hdr10
.MaxContentLightLevel
,
1306 hdr10
.MaxFrameAverageLightLevel
);
1308 constexpr double FACTOR_1
= 50000.0;
1309 constexpr double FACTOR_2
= 10000.0;
1310 const double RP_0
= static_cast<double>(hdr10
.RedPrimary
[0]) / FACTOR_1
;
1311 const double RP_1
= static_cast<double>(hdr10
.RedPrimary
[1]) / FACTOR_1
;
1312 const double GP_0
= static_cast<double>(hdr10
.GreenPrimary
[0]) / FACTOR_1
;
1313 const double GP_1
= static_cast<double>(hdr10
.GreenPrimary
[1]) / FACTOR_1
;
1314 const double BP_0
= static_cast<double>(hdr10
.BluePrimary
[0]) / FACTOR_1
;
1315 const double BP_1
= static_cast<double>(hdr10
.BluePrimary
[1]) / FACTOR_1
;
1316 const double WP_0
= static_cast<double>(hdr10
.WhitePoint
[0]) / FACTOR_1
;
1317 const double WP_1
= static_cast<double>(hdr10
.WhitePoint
[1]) / FACTOR_1
;
1318 const double Max_ML
= static_cast<double>(hdr10
.MaxMasteringLuminance
) / FACTOR_2
;
1319 const double min_ML
= static_cast<double>(hdr10
.MinMasteringLuminance
) / FACTOR_2
;
1322 "RP {:.3f} {:.3f} | GP {:.3f} {:.3f} | BP {:.3f} {:.3f} | WP {:.3f} "
1323 "{:.3f} | Max ML {:.0f} | min ML {:.4f} | Max CLL {} | Max FALL {}",
1324 RP_0
, RP_1
, GP_0
, GP_1
, BP_0
, BP_1
, WP_0
, WP_1
, Max_ML
, min_ML
,
1325 hdr10
.MaxContentLightLevel
, hdr10
.MaxFrameAverageLightLevel
);
1329 CLog::LogF(LOGERROR
, "DXGI SetHDRMetaData failed");
1334 void DX::DeviceResources::SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace
)
1336 ComPtr
<IDXGISwapChain3
> swapChain3
;
1341 if (SUCCEEDED(m_swapChain
.As(&swapChain3
)))
1343 if (SUCCEEDED(swapChain3
->SetColorSpace1(colorSpace
)))
1345 m_IsTransferPQ
= (colorSpace
== DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
);
1348 DX::Windowing()->CacheSystemSdrPeakLuminance();
1350 CLog::LogF(LOGDEBUG
, "DXGI SetColorSpace1 {} success",
1351 DX::DXGIColorSpaceTypeToString(colorSpace
));
1355 CLog::LogF(LOGERROR
, "DXGI SetColorSpace1 {} failed",
1356 DX::DXGIColorSpaceTypeToString(colorSpace
));
1361 HDR_STATUS
DX::DeviceResources::ToggleHDR()
1363 DXGI_MODE_DESC md
= {};
1364 GetDisplayMode(&md
);
1366 // Xbox uses only full screen windowed mode and not needs recreate swapchain.
1367 // Recreate swapchain causes native 4K resolution is lost and quality obtained
1368 // is equivalent to 1080p upscaled to 4K (TO DO: investigate root cause).
1369 const bool isXbox
= (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::Xbox
);
1371 DX::Windowing()->SetTogglingHDR(true);
1372 DX::Windowing()->SetAlteringWindow(true);
1374 // Toggle display HDR
1375 HDR_STATUS hdrStatus
= CWIN32Util::ToggleWindowsHDR(md
);
1378 if (!isXbox
&& m_swapChain
&& hdrStatus
!= HDR_STATUS::HDR_TOGGLE_FAILED
)
1380 CLog::LogF(LOGDEBUG
, "Re-create swapchain due HDR <-> SDR switch");
1384 DX::Windowing()->SetAlteringWindow(false);
1386 // Re-create swapchain
1387 if (!isXbox
&& hdrStatus
!= HDR_STATUS::HDR_TOGGLE_FAILED
)
1389 CreateWindowSizeDependentResources();
1391 DX::Windowing()->NotifyAppFocusChange(true);
1394 // On Xbox set new color space in same swapchain
1395 if (isXbox
&& hdrStatus
!= HDR_STATUS::HDR_TOGGLE_FAILED
)
1397 if (hdrStatus
== HDR_STATUS::HDR_ON
)
1399 SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020
);
1400 m_IsHDROutput
= true;
1404 SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
);
1405 m_IsHDROutput
= false;
1412 void DX::DeviceResources::ApplyDisplaySettings()
1414 CLog::LogF(LOGDEBUG
, "Re-create swapchain due Display Settings changed");
1417 CreateWindowSizeDependentResources();
1420 DEBUG_INFO_RENDER
DX::DeviceResources::GetDebugInfo() const
1425 DXGI_SWAP_CHAIN_DESC1 desc
= {};
1426 m_swapChain
->GetDesc1(&desc
);
1428 DXGI_MODE_DESC md
= {};
1429 GetDisplayMode(&md
);
1431 const int bits
= (desc
.Format
== DXGI_FORMAT_R10G10B10A2_UNORM
) ? 10 : 8;
1432 const int max
= (desc
.Format
== DXGI_FORMAT_R10G10B10A2_UNORM
) ? 1024 : 256;
1433 const int range_min
= DX::Windowing()->UseLimitedColor() ? (max
* 16) / 256 : 0;
1434 const int range_max
= DX::Windowing()->UseLimitedColor() ? (max
* 235) / 256 : max
- 1;
1436 DEBUG_INFO_RENDER info
;
1438 info
.renderFlags
= StringUtils::Format(
1439 "Swapchain: {} buffers, flip {}, {}, EOTF: {} (Windows HDR {})", desc
.BufferCount
,
1440 (desc
.SwapEffect
== DXGI_SWAP_EFFECT_FLIP_DISCARD
) ? "discard" : "sequential",
1441 Windowing()->IsFullScreen()
1442 ? ((desc
.Flags
& DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH
) ? "fullscreen exclusive"
1443 : "fullscreen windowed")
1444 : "windowed screen",
1445 m_IsTransferPQ
? "PQ" : "SDR", m_IsHDROutput
? "on" : "off");
1447 info
.videoOutput
= StringUtils::Format(
1448 "Surfaces: {}x{}{} @ {:.3f} Hz, pixel: {} {}-bit, range: {} ({}-{})", desc
.Width
, desc
.Height
,
1449 (md
.ScanlineOrdering
> DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE
) ? "i" : "p",
1450 static_cast<double>(md
.RefreshRate
.Numerator
) /
1451 static_cast<double>(md
.RefreshRate
.Denominator
),
1452 DXGIFormatToShortString(desc
.Format
), bits
,
1453 DX::Windowing()->UseLimitedColor() ? "limited" : "full", range_min
, range_max
);
1458 std::vector
<DXGI_COLOR_SPACE_TYPE
> DX::DeviceResources::GetSwapChainColorSpaces() const
1463 std::vector
<DXGI_COLOR_SPACE_TYPE
> result
;
1466 ComPtr
<IDXGISwapChain3
> swapChain3
;
1467 if (SUCCEEDED(hr
= m_swapChain
.As(&swapChain3
)))
1469 UINT colorSpaceSupport
= 0;
1470 for (UINT colorSpace
= DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709
;
1471 colorSpace
< DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020
; colorSpace
++)
1473 DXGI_COLOR_SPACE_TYPE cs
= static_cast<DXGI_COLOR_SPACE_TYPE
>(colorSpace
);
1474 if (SUCCEEDED(swapChain3
->CheckColorSpaceSupport(cs
, &colorSpaceSupport
)) &&
1475 (colorSpaceSupport
& DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT
))
1476 result
.push_back(cs
);
1481 CLog::LogF(LOGDEBUG
, "IDXGISwapChain3 is not available. Error {}", DX::GetErrorDescription(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"))