[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / rendering / dx / DeviceResources.cpp
blob8fe7d5e412d7caf350960e0ee65f0a0be357953e
1 /*
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.
7 */
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>
30 extern "C"
32 #include <libavutil/rational.h>
34 #endif
36 #ifdef _DEBUG
37 #include <dxgidebug.h>
38 #pragma comment(lib, "dxgi.lib")
39 #endif // _DEBUG
41 using namespace DirectX;
42 using namespace Microsoft::WRL;
43 using namespace Concurrency;
44 namespace winrt
46 using namespace Windows::Foundation;
49 #ifdef _DEBUG
50 #define breakOnDebug __debugbreak()
51 #else
52 #define breakOnDebug
53 #endif
54 #define LOG_HR(hr) \
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)
62 if (!pTexture)
63 return false;
65 D3D11_TEXTURE2D_DESC desc;
66 pTexture->GetDesc(&desc);
68 m_width = desc.Width;
69 m_height = desc.Height;
70 m_format = desc.Format;
71 m_usage = desc.Usage;
73 m_texture = pTexture;
74 return true;
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()
85 : m_screenViewport()
86 , m_d3dFeatureLevel(D3D_FEATURE_LEVEL_9_1)
87 , m_outputSize()
88 , m_logicalSize()
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)
104 return;
106 ReleaseBackBuffer();
107 OnDeviceLost(true);
108 DestroySwapChain();
110 m_adapter = nullptr;
111 m_dxgiFactory = nullptr;
112 m_output = nullptr;
113 m_deferrContext = nullptr;
114 m_d3dContext = nullptr;
115 m_d3dDevice = nullptr;
116 m_bDeviceCreated = false;
117 #ifdef _DEBUG
118 if (m_d3dDebug)
120 m_d3dDebug->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL);
121 m_d3dDebug = nullptr;
123 #endif
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;
138 if (m_output)
140 m_output.As(&pOutput);
141 *outputDesc = m_outputDesc;
143 else if (m_swapChain && SUCCEEDED(m_swapChain->GetContainingOutput(pOutput.GetAddressOf())) &&
144 pOutput)
146 pOutput->GetDesc(outputDesc);
149 if (!pOutput)
150 CLog::LogF(LOGWARNING, "unable to retrieve current output");
152 *ppOutput = pOutput.Detach();
153 return;
156 DXGI_ADAPTER_DESC DX::DeviceResources::GetAdapterDesc() const
158 DXGI_ADAPTER_DESC desc{};
160 if (m_adapter)
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;
168 return desc;
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;
177 if (!m_swapChain)
178 return;
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
207 else
208 mode->ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
210 #else
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;
221 #endif
224 void DX::DeviceResources::SetViewPort(D3D11_VIEWPORT& viewPort) const
226 // convert logical viewport to real
227 D3D11_VIEWPORT realViewPort =
229 viewPort.TopLeftX,
230 viewPort.TopLeftY,
231 viewPort.Width,
232 viewPort.Height,
233 viewPort.MinDepth,
234 viewPort.MinDepth
237 m_deferrContext->RSSetViewports(1, &realViewPort);
240 bool DX::DeviceResources::SetFullScreen(bool fullscreen, RESOLUTION_INFO& res)
242 if (!m_bDeviceCreated || !m_swapChain)
243 return false;
245 critical_section::scoped_lock lock(m_criticalSection);
247 BOOL bFullScreen;
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));
260 else if (fullscreen)
262 const bool isResValid = res.iWidth > 0 && res.iHeight > 0 && res.fRefreshRate > 0.f;
263 if (isResValid)
265 DXGI_MODE_DESC currentMode = {};
266 GetDisplayMode(&currentMode);
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)
273 refreshRate *= 2;
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
302 if (!bFullScreen)
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(&currentMode));
312 if (resized)
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);
319 recreate |= resized;
322 if (!bFullScreen)
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
333 if (recreate)
334 ResizeBuffers();
336 CLog::LogF(LOGDEBUG, "switching done.");
338 return recreate;
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.");
351 CreateFactory();
353 UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
354 #if defined(_DEBUG)
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;
360 #endif
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.
398 if (FAILED(hr))
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);
410 if (FAILED(hr))
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);
420 if (FAILED(hr))
422 CLog::LogF(LOGFATAL, "unable to create WARP device. Rendering is not possible. Error {}",
423 DX::GetErrorDescription(hr));
424 CHECK_ERR();
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();
435 #ifdef _DEBUG
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);
454 #endif
456 hr = context.As(&m_d3dContext); CHECK_ERR();
457 hr = m_d3dDevice->CreateDeferredContext1(0, &m_deferrContext); CHECK_ERR();
459 if (!m_adapter)
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;
486 if (m_deferrContext)
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)
501 return;
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.");
513 return;
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(
528 &depthStencilDesc,
529 nullptr,
530 &depthStencil
531 ); CHECK_ERR();
533 CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
534 hr = m_d3dDevice->CreateDepthStencilView(
535 depthStencil.Get(),
536 &depthStencilViewDesc,
537 &m_d3dDepthStencilView
538 ); CHECK_ERR();
540 // Set the 3D rendering viewport to target the entire window.
541 m_screenViewport = CD3D11_VIEWPORT(
542 0.0f,
543 0.0f,
544 m_outputSize.Width,
545 m_outputSize.Height
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
553 HRESULT hr;
554 #ifdef TARGET_WINDOWS_DESKTOP
555 hr = m_dxgiFactory->CreateSwapChainForHwnd(
556 m_d3dDevice.Get(),
557 m_window,
558 &desc,
559 &fsDesc,
560 nullptr,
561 ppSwapChain
562 ); RETURN_ERR(hr);
563 hr = m_dxgiFactory->MakeWindowAssociation(m_window, /*DXGI_MWA_NO_WINDOW_CHANGES |*/ DXGI_MWA_NO_ALT_ENTER);
564 #else
565 hr = m_dxgiFactory->CreateSwapChainForCoreWindow(
566 m_d3dDevice.Get(),
567 winrt::get_unknown(m_coreWindow),
568 &desc,
569 nullptr,
570 ppSwapChain
571 ); RETURN_ERR(hr);
572 #endif
573 return hr;
576 void DX::DeviceResources::DestroySwapChain()
578 if (!m_swapChain)
579 return;
581 BOOL bFullcreen = 0;
582 m_swapChain->GetFullscreenState(&bFullcreen, nullptr);
583 if (!!bFullcreen)
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)
594 return;
596 CLog::LogF(LOGDEBUG, "resize buffers.");
598 bool bHWStereoEnabled = RENDER_STEREO_MODE_HARDWAREBASED ==
599 CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode();
600 bool windowed = true;
601 HRESULT hr = E_FAIL;
602 DXGI_SWAP_CHAIN_DESC1 scDesc = {};
604 if (m_swapChain)
606 BOOL bFullcreen = 0;
607 m_swapChain->GetFullscreenState(&bFullcreen, nullptr);
608 if (!!bFullcreen)
609 windowed = false;
611 m_swapChain->GetDesc1(&scDesc);
612 if ((scDesc.Stereo == TRUE) != bHWStereoEnabled) // check if swapchain needs to be recreated
613 DestroySwapChain();
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.
630 return;
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();
637 return;
639 CHECK_ERR();
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)
652 use10bit = false;
653 #endif
655 // 0 = Auto | 1 = Never | 2 = Always
656 int use10bitSetting = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
657 CSettings::SETTING_VIDEOSCREEN_10BITSURFACES);
659 if (use10bitSetting == 1)
660 use10bit = false;
661 else if (use10bitSetting == 2)
662 use10bit = true;
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
672 #else
673 swapChainDesc.BufferCount = 3; // Xbox don't like 6 backbuffers (3 is fine even for 4K 60 fps)
674 #endif
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);
694 if (FAILED(hr))
696 CLog::LogF(LOGWARNING, "creating 10bit swapchain failed, fallback to 8bit.");
697 swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
701 if (!swapChain)
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);
720 if (FAILED(hr))
722 CLog::LogF(LOGERROR, "unable to create swapchain.");
723 return;
726 m_IsHDROutput = (swapChainDesc.Format == DXGI_FORMAT_R10G10B10A2_UNORM) && isHdrEnabled;
728 CLog::LogF(
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);
755 if (m_IsHDROutput)
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()
765 ReleaseBackBuffer();
767 DestroySwapChain();
769 if (!m_dxgiFactory->IsCurrent()) // HDR toggling requires re-create factory
770 CreateFactory();
772 UpdateRenderTargetSize();
773 ResizeBuffers();
775 CreateBackBuffer();
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)
826 return;
828 ComPtr<ID3D11CommandList> pCommandList;
829 if (FAILED(m_deferrContext->FinishCommandList(true, &pCommandList)))
831 CLog::LogF(LOGERROR, "failed to finish command queue.");
832 return;
835 if (bExecute)
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)
844 (!m_window)
845 #else
846 (!m_coreWindow)
847 #endif
848 return;
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();
859 ResizeBuffers();
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);
867 if (dpi != m_dpi)
868 m_dpi = dpi;
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(&currentFactory));
901 ComPtr<IDXGIAdapter1> currentDefaultAdapter;
902 currentFactory->EnumAdapters1(0, &currentDefaultAdapter);
904 currentDefaultAdapter->GetDesc1(&currentDesc);
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
911 || FAILED(hr))
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();
926 if (pGUI)
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();
947 if (pGUI)
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();
960 if (backbuferExists)
961 ReleaseBackBuffer();
963 DestroySwapChain();
965 CreateDeviceResources();
966 UpdateRenderTargetSize();
967 ResizeBuffers();
969 if (backbuferExists)
970 CreateBackBuffer();
972 if (m_deviceNotify != nullptr)
973 m_deviceNotify->OnDXDeviceRestored();
974 OnDeviceRestored();
976 if (removed)
977 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
978 "ReloadSkin");
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);
991 else
993 // not fatal errors
994 if (hr == DXGI_ERROR_INVALID_CALL)
996 CreateWindowSizeDependentResources();
1000 m_deferrContext->OMSetRenderTargets(1, m_backBufferTex.GetAddressOfRTV(), m_d3dDepthStencilView.Get());
1002 return true;
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, &parameters);
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);
1022 else
1024 // not fatal errors
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;
1053 if (m_adapter)
1054 m_adapter->GetDesc(&currentDesc);
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
1082 if (m_d3dDevice)
1083 HandleDeviceLost(false);
1085 return;
1091 bool DX::DeviceResources::CreateFactory()
1093 HRESULT hr;
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()))))
1100 debugDXGI = true;
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);
1109 if (!debugDXGI)
1110 #endif
1111 hr = CreateDXGIFactory1(IID_PPV_ARGS(m_dxgiFactory.ReleaseAndGetAddressOf())); RETURN_ERR(false);
1113 return true;
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
1130 if (m_swapChain)
1132 ComPtr<IDXGIOutput> output;
1133 GetOutput(output.GetAddressOf());
1134 if (output)
1136 DXGI_OUTPUT_DESC desc;
1137 output->GetDesc(&desc);
1138 return desc.Monitor;
1141 return nullptr;
1144 bool DX::DeviceResources::IsStereoAvailable() const
1146 if (m_dxgiFactory)
1147 return m_dxgiFactory->IsWindowedStereoEnabled();
1149 return false;
1152 void DX::DeviceResources::CheckNV12SharedTexturesSupport()
1154 if (m_d3dFeatureLevel < D3D_FEATURE_LEVEL_10_0 ||
1155 CSysInfo::GetWindowsDeviceFamily() != CSysInfo::Desktop)
1156 return;
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)
1168 return;
1170 VideoDriverInfo driver = GetVideoDriverVersion();
1171 driver.Log();
1173 if (!m_NV12SharedTexturesSupport)
1174 return;
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
1206 if (!m_adapter)
1207 return {};
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);
1219 if (!driver.valid)
1220 driver = CWIN32Util::GetVideoDriverInfo(ad.VendorId, ad.Description);
1222 if (!driver.valid)
1223 driver = CWIN32Util::GetVideoDriverInfoDX(ad.VendorId, ad.AdapterLuid);
1225 return driver;
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)
1232 m_window = 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())
1254 handler();
1255 else
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);
1283 dxgiDevice->Trim();
1286 #endif
1288 void DX::DeviceResources::SetHdrMetaData(DXGI_HDR_METADATA_HDR10& hdr10) const
1290 ComPtr<IDXGISwapChain4> swapChain4;
1292 if (!m_swapChain)
1293 return;
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;
1321 CLog::LogF(LOGINFO,
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);
1327 else
1329 CLog::LogF(LOGERROR, "DXGI SetHDRMetaData failed");
1334 void DX::DeviceResources::SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace)
1336 ComPtr<IDXGISwapChain3> swapChain3;
1338 if (!m_swapChain)
1339 return;
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);
1347 if (m_IsTransferPQ)
1348 DX::Windowing()->CacheSystemSdrPeakLuminance();
1350 CLog::LogF(LOGDEBUG, "DXGI SetColorSpace1 {} success",
1351 DX::DXGIColorSpaceTypeToString(colorSpace));
1353 else
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);
1377 // Kill swapchain
1378 if (!isXbox && m_swapChain && hdrStatus != HDR_STATUS::HDR_TOGGLE_FAILED)
1380 CLog::LogF(LOGDEBUG, "Re-create swapchain due HDR <-> SDR switch");
1381 DestroySwapChain();
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;
1402 else
1404 SetHdrColorSpace(DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709);
1405 m_IsHDROutput = false;
1409 return hdrStatus;
1412 void DX::DeviceResources::ApplyDisplaySettings()
1414 CLog::LogF(LOGDEBUG, "Re-create swapchain due Display Settings changed");
1416 DestroySwapChain();
1417 CreateWindowSizeDependentResources();
1420 DEBUG_INFO_RENDER DX::DeviceResources::GetDebugInfo() const
1422 if (!m_swapChain)
1423 return {};
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);
1455 return info;
1458 std::vector<DXGI_COLOR_SPACE_TYPE> DX::DeviceResources::GetSwapChainColorSpaces() const
1460 if (!m_swapChain)
1461 return {};
1463 std::vector<DXGI_COLOR_SPACE_TYPE> result;
1464 HRESULT hr;
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);
1479 else
1481 CLog::LogF(LOGDEBUG, "IDXGISwapChain3 is not available. Error {}", DX::GetErrorDescription(hr));
1483 return result;
1486 bool DX::DeviceResources::SetMultithreadProtected(bool enabled) const
1488 BOOL wasEnabled = FALSE;
1489 ComPtr<ID3D11Multithread> multithread;
1490 HRESULT hr = m_d3dDevice.As(&multithread);
1491 if (SUCCEEDED(hr))
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)
1500 return false;
1502 const VideoDriverInfo driver = GetVideoDriverVersion();
1504 if (driver.vendorId != PCIV_AMD)
1505 return false;
1507 if (driver.valid && CWIN32Util::IsDriverVersionAtLeast(driver.version, "31.0.22000.0"))
1508 return false;
1510 return true;