From da61af8065a75dd1508d8231e2885c827b9d72c3 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Fri, 15 Nov 2024 16:01:12 +0100 Subject: [PATCH] rhi: Make DXGI HDR query usable with Vulkan Also moves the relevant code into one place, that is usable internally from other places in the future. (the interface might change still) Most importantly, this now makes the Vulkan backend to report the same information as the D3D11/12 ones, as long as the Vulkan implementation reports the deviceLUID in the extended physical device properties. Change-Id: I6aa8adaf40bbc3199959019d323910abd27ec473 Reviewed-by: Kristoffer Skau Reviewed-by: Andy Nichols --- src/gui/CMakeLists.txt | 1 + src/gui/rhi/qdxgihdrinfo.cpp | 203 +++++++++++++++++++++++++++++++++ src/gui/rhi/qdxgihdrinfo_p.h | 69 +++++++++++ src/gui/rhi/qrhid3d11.cpp | 25 ++-- src/gui/rhi/qrhid3d12.cpp | 23 +--- src/gui/rhi/qrhid3dhelpers.cpp | 81 ------------- src/gui/rhi/qrhid3dhelpers_p.h | 7 +- src/gui/rhi/qrhivulkan.cpp | 41 +++++++ src/gui/rhi/qrhivulkan_p.h | 11 ++ 9 files changed, 339 insertions(+), 122 deletions(-) create mode 100644 src/gui/rhi/qdxgihdrinfo.cpp create mode 100644 src/gui/rhi/qdxgihdrinfo_p.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index b22be1aec79..2c72123db11 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -465,6 +465,7 @@ qt_internal_extend_target(Gui CONDITION WIN32 ../3rdparty/D3D12MemoryAllocator/D3D12MemAlloc.h ../3rdparty/D3D12MemoryAllocator/D3D12MemAlloc.cpp rhi/qdxgivsyncservice_p.h rhi/qdxgivsyncservice.cpp + rhi/qdxgihdrinfo_p.h rhi/qdxgihdrinfo.cpp text/windows/qwindowsfontdatabase.cpp text/windows/qwindowsfontdatabase_p.h text/windows/qwindowsfontdatabasebase.cpp text/windows/qwindowsfontdatabasebase_p.h text/windows/qwindowsfontengine.cpp text/windows/qwindowsfontengine_p.h diff --git a/src/gui/rhi/qdxgihdrinfo.cpp b/src/gui/rhi/qdxgihdrinfo.cpp new file mode 100644 index 00000000000..023ca654e04 --- /dev/null +++ b/src/gui/rhi/qdxgihdrinfo.cpp @@ -0,0 +1,203 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qdxgihdrinfo_p.h" +#include + +QT_BEGIN_NAMESPACE + +QDxgiHdrInfo::QDxgiHdrInfo() +{ + HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast(&m_factory)); + if (FAILED(hr)) { + qWarning("QDxgiHdrInfo: CreateDXGIFactory2 failed: %s", qPrintable(QSystemError::windowsComString(hr))); + return; + } + + // Create the factory but leave m_adapter set to null, indicating that all + // outputs of all adapters should be enumerated (if this is a good idea, not + // sure, but there is no choice here, when someone wants to use this outside + // of QRhi's scope and control) +} + +QDxgiHdrInfo::QDxgiHdrInfo(LUID luid) +{ + HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast(&m_factory)); + if (FAILED(hr)) { + qWarning("QDxgiHdrInfo: CreateDXGIFactory2 failed: %s", qPrintable(QSystemError::windowsComString(hr))); + return; + } + + IDXGIAdapter1 *ad; + for (int adapterIndex = 0; m_factory->EnumAdapters1(UINT(adapterIndex), &ad) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { + DXGI_ADAPTER_DESC1 desc; + ad->GetDesc1(&desc); + if (desc.AdapterLuid.LowPart == luid.LowPart && desc.AdapterLuid.HighPart == luid.HighPart) { + m_adapter = ad; + break; + } + ad->Release(); + } + + if (!m_adapter) + qWarning("QDxgiHdrInfo: No adapter found"); +} + +QDxgiHdrInfo::QDxgiHdrInfo(IDXGIAdapter1 *adapter) + : m_adapter(adapter) +{ + m_adapter->AddRef(); // so that the dtor does not destroy it +} + +QDxgiHdrInfo::~QDxgiHdrInfo() +{ + if (m_adapter) + m_adapter->Release(); + + if (m_factory) + m_factory->Release(); +} + +bool QDxgiHdrInfo::isHdrCapable(QWindow *w) +{ + if (!m_adapter && m_factory) { + IDXGIAdapter1 *adapter; + for (int adapterIndex = 0; m_factory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { + DXGI_OUTPUT_DESC1 desc1; + const bool ok = outputDesc1ForWindow(w, adapter, &desc1) && desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; + adapter->Release(); + if (ok) + return true; + } + } else if (m_adapter) { + DXGI_OUTPUT_DESC1 desc1; + if (outputDesc1ForWindow(w, m_adapter, &desc1)) { + // as per https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range + if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { + // "HDR display with all Advanced Color capabilities" + return true; + } + } + } + + return false; +} + +QRhiSwapChainHdrInfo QDxgiHdrInfo::queryHdrInfo(QWindow *w) +{ + QRhiSwapChainHdrInfo info; + info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits; + info.limits.luminanceInNits.minLuminance = 0.0f; + info.limits.luminanceInNits.maxLuminance = 1000.0f; + info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; + info.sdrWhiteLevel = 200.0f; + + DXGI_OUTPUT_DESC1 hdrOutputDesc; + bool ok = false; + if (!m_adapter && m_factory) { + IDXGIAdapter1 *adapter; + for (int adapterIndex = 0; m_factory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) { + ok = outputDesc1ForWindow(w, adapter, &hdrOutputDesc); + adapter->Release(); + if (ok) + break; + } + } else if (m_adapter) { + ok = outputDesc1ForWindow(w, m_adapter, &hdrOutputDesc); + } + if (ok) { + info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits; + info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance; + info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance; + info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits + info.sdrWhiteLevel = sdrWhiteLevelInNits(hdrOutputDesc); + } + + return info; +} + +bool QDxgiHdrInfo::output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result) +{ + if (!adapter) + return false; + + bool ok = false; + QRect wr = w->geometry(); + wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio()); + const QPoint center = wr.center(); + IDXGIOutput *currentOutput = nullptr; + IDXGIOutput *output = nullptr; + for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) { + if (!output) + continue; + DXGI_OUTPUT_DESC desc; + output->GetDesc(&desc); + const RECT r = desc.DesktopCoordinates; + const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1)); + if (dr.contains(center)) { + currentOutput = output; + break; + } else { + output->Release(); + } + } + if (currentOutput) { + ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast(result))); + currentOutput->Release(); + } + return ok; +} + +bool QDxgiHdrInfo::outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result) +{ + bool ok = false; + IDXGIOutput6 *out6 = nullptr; + if (output6ForWindow(w, adapter, &out6)) { + ok = SUCCEEDED(out6->GetDesc1(result)); + out6->Release(); + } + return ok; +} + +float QDxgiHdrInfo::sdrWhiteLevelInNits(const DXGI_OUTPUT_DESC1 &outputDesc) +{ + QVector pathInfos; + uint32_t pathInfoCount, modeInfoCount; + LONG result; + do { + if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathInfoCount, &modeInfoCount) == ERROR_SUCCESS) { + pathInfos.resize(pathInfoCount); + QVector modeInfos(modeInfoCount); + result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathInfoCount, pathInfos.data(), &modeInfoCount, modeInfos.data(), nullptr); + } else { + return 200.0f; + } + } while (result == ERROR_INSUFFICIENT_BUFFER); + + MONITORINFOEX monitorInfo = {}; + monitorInfo.cbSize = sizeof(monitorInfo); + GetMonitorInfo(outputDesc.Monitor, &monitorInfo); + + for (const DISPLAYCONFIG_PATH_INFO &info : pathInfos) { + DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName = {}; + deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; + deviceName.header.size = sizeof(deviceName); + deviceName.header.adapterId = info.sourceInfo.adapterId; + deviceName.header.id = info.sourceInfo.id; + if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) { + if (!wcscmp(monitorInfo.szDevice, deviceName.viewGdiDeviceName)) { + DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {}; + whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL; + whiteLevel.header.size = sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL); + whiteLevel.header.adapterId = info.targetInfo.adapterId; + whiteLevel.header.id = info.targetInfo.id; + if (DisplayConfigGetDeviceInfo(&whiteLevel.header) == ERROR_SUCCESS) + return whiteLevel.SDRWhiteLevel * 80 / 1000.0f; + } + } + } + + return 200.0f; +} + +QT_END_NAMESPACE diff --git a/src/gui/rhi/qdxgihdrinfo_p.h b/src/gui/rhi/qdxgihdrinfo_p.h new file mode 100644 index 00000000000..d19f87bc55a --- /dev/null +++ b/src/gui/rhi/qdxgihdrinfo_p.h @@ -0,0 +1,69 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QDXGIHDRINFO_P_H +#define QDXGIHDRINFO_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QDxgiHdrInfo +{ +public: + // When already having an adapter, i.e. in a D3D11/12 backend of QRhi; this + // is very cheap to construct. + QDxgiHdrInfo(IDXGIAdapter1 *adapter); + + // When having the adapter LUID from somewhere (e.g. + // VkPhysicalDeviceIDProperties::deviceLUID); This has to create a DXGI + // factory and enumarate to find the matching IDXGIAdapter, so less cheap. + QDxgiHdrInfo(LUID luid); + + // All functions below will enumerate all adapters and all their outputs + // every time they are called. Probably not nice. Use when there is no + // knowledge which GPUs will be used with QRhi. + QDxgiHdrInfo(); + + ~QDxgiHdrInfo(); + + // True if the window belongs to an output where HDR is enabled. Moving a + // window to another screen may make this function return a different value. + // + // If/how DXGI outputs map to screens, is out of our hands, but they will + // typically match. However, this will not be functional when WARP is used + // since the WARP adapter never has any outputs associated (even though + // there are certainly screens still). + // + bool isHdrCapable(QWindow *w); + + // HDR info and white level. Or default, dummy values when the window is not + // on a HDR-enabled output. + QRhiSwapChainHdrInfo queryHdrInfo(QWindow *w); + +private: + static bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result); + static bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result); + static float sdrWhiteLevelInNits(const DXGI_OUTPUT_DESC1 &outputDesc); + + IDXGIFactory2 *m_factory = nullptr; + IDXGIAdapter1 *m_adapter = nullptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 1e211d98265..8019b585f71 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -385,7 +385,7 @@ bool QRhiD3D11::create(QRhi::Flags flags) adapter1->GetDesc1(&desc); adapterLuid = desc.AdapterLuid; QRhiD3D::fillDriverInfo(&driverInfoStruct, desc); - adapter1->Release(); + activeAdapter = adapter1; } adapter->Release(); } @@ -5071,11 +5071,8 @@ bool QD3D11SwapChain::isFormatSupported(Format f) } QRHI_RES_RHI(QRhiD3D11); - DXGI_OUTPUT_DESC1 desc1; - if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) { - if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) - return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10; - } + if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window)) + return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10; return false; } @@ -5086,14 +5083,7 @@ QRhiSwapChainHdrInfo QD3D11SwapChain::hdrInfo() // Must use m_window, not window, given this may be called before createOrResize(). if (m_window) { QRHI_RES_RHI(QRhiD3D11); - DXGI_OUTPUT_DESC1 hdrOutputDesc; - if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) { - info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits; - info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance; - info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance; - info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits - info.sdrWhiteLevel = QRhiD3D::sdrWhiteLevelInNits(hdrOutputDesc); - } + info = QDxgiHdrInfo(rhiD->activeAdapter).queryHdrInfo(m_window); } return info; } @@ -5230,10 +5220,9 @@ bool QD3D11SwapChain::createOrResize() srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT; DXGI_COLOR_SPACE_TYPE hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR - DXGI_OUTPUT_DESC1 hdrOutputDesc; - if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) { - // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range - if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { + if (m_format != SDR) { + if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window)) { + // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range switch (m_format) { case HDRExtendedSrgbLinear: colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp index be78d532394..bdf84f54d79 100644 --- a/src/gui/rhi/qrhid3d12.cpp +++ b/src/gui/rhi/qrhid3d12.cpp @@ -6400,11 +6400,8 @@ bool QD3D12SwapChain::isFormatSupported(Format f) } QRHI_RES_RHI(QRhiD3D12); - DXGI_OUTPUT_DESC1 desc1; - if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &desc1)) { - if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) - return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10; - } + if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window)) + return f == QRhiSwapChain::HDRExtendedSrgbLinear || f == QRhiSwapChain::HDR10; return false; } @@ -6415,14 +6412,7 @@ QRhiSwapChainHdrInfo QD3D12SwapChain::hdrInfo() // Must use m_window, not window, given this may be called before createOrResize(). if (m_window) { QRHI_RES_RHI(QRhiD3D12); - DXGI_OUTPUT_DESC1 hdrOutputDesc; - if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc)) { - info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits; - info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance; - info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance; - info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits - info.sdrWhiteLevel = QRhiD3D::sdrWhiteLevelInNits(hdrOutputDesc); - } + info = QDxgiHdrInfo(rhiD->activeAdapter).queryHdrInfo(m_window); } return info; } @@ -6465,11 +6455,10 @@ void QD3D12SwapChain::chooseFormats() colorFormat = DEFAULT_FORMAT; srgbAdjustedColorFormat = m_flags.testFlag(sRGB) ? DEFAULT_SRGB_FORMAT : DEFAULT_FORMAT; hdrColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // SDR - DXGI_OUTPUT_DESC1 hdrOutputDesc; QRHI_RES_RHI(QRhiD3D12); - if (QRhiD3D::outputDesc1ForWindow(m_window, rhiD->activeAdapter, &hdrOutputDesc) && m_format != SDR) { - // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range - if (hdrOutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { + if (m_format != SDR) { + if (QDxgiHdrInfo(rhiD->activeAdapter).isHdrCapable(m_window)) { + // https://docs.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range switch (m_format) { case HDRExtendedSrgbLinear: colorFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; diff --git a/src/gui/rhi/qrhid3dhelpers.cpp b/src/gui/rhi/qrhid3dhelpers.cpp index 920446fbd9b..4e42411ea1e 100644 --- a/src/gui/rhi/qrhid3dhelpers.cpp +++ b/src/gui/rhi/qrhid3dhelpers.cpp @@ -9,87 +9,6 @@ QT_BEGIN_NAMESPACE namespace QRhiD3D { -bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result) -{ - bool ok = false; - QRect wr = w->geometry(); - wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio()); - const QPoint center = wr.center(); - IDXGIOutput *currentOutput = nullptr; - IDXGIOutput *output = nullptr; - for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) { - if (!output) - continue; - DXGI_OUTPUT_DESC desc; - output->GetDesc(&desc); - const RECT r = desc.DesktopCoordinates; - const QRect dr(QPoint(r.left, r.top), QPoint(r.right - 1, r.bottom - 1)); - if (dr.contains(center)) { - currentOutput = output; - break; - } else { - output->Release(); - } - } - if (currentOutput) { - ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast(result))); - currentOutput->Release(); - } - return ok; -} - -bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result) -{ - bool ok = false; - IDXGIOutput6 *out6 = nullptr; - if (output6ForWindow(w, adapter, &out6)) { - ok = SUCCEEDED(out6->GetDesc1(result)); - out6->Release(); - } - return ok; -} - -float sdrWhiteLevelInNits(const DXGI_OUTPUT_DESC1 &outputDesc) -{ - QVector pathInfos; - uint32_t pathInfoCount, modeInfoCount; - LONG result; - do { - if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathInfoCount, &modeInfoCount) == ERROR_SUCCESS) { - pathInfos.resize(pathInfoCount); - QVector modeInfos(modeInfoCount); - result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathInfoCount, pathInfos.data(), &modeInfoCount, modeInfos.data(), nullptr); - } else { - return 200.0f; - } - } while (result == ERROR_INSUFFICIENT_BUFFER); - - MONITORINFOEX monitorInfo = {}; - monitorInfo.cbSize = sizeof(monitorInfo); - GetMonitorInfo(outputDesc.Monitor, &monitorInfo); - - for (const DISPLAYCONFIG_PATH_INFO &info : pathInfos) { - DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName = {}; - deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; - deviceName.header.size = sizeof(deviceName); - deviceName.header.adapterId = info.sourceInfo.adapterId; - deviceName.header.id = info.sourceInfo.id; - if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) { - if (!wcscmp(monitorInfo.szDevice, deviceName.viewGdiDeviceName)) { - DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {}; - whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL; - whiteLevel.header.size = sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL); - whiteLevel.header.adapterId = info.targetInfo.adapterId; - whiteLevel.header.id = info.targetInfo.id; - if (DisplayConfigGetDeviceInfo(&whiteLevel.header) == ERROR_SUCCESS) - return whiteLevel.SDRWhiteLevel * 80 / 1000.0f; - } - } - } - - return 200.0f; -} - pD3DCompile resolveD3DCompile() { for (const wchar_t *libraryName : {L"D3DCompiler_47", L"D3DCompiler_43"}) { diff --git a/src/gui/rhi/qrhid3dhelpers_p.h b/src/gui/rhi/qrhid3dhelpers_p.h index 814824d774c..2d07620c0b4 100644 --- a/src/gui/rhi/qrhid3dhelpers_p.h +++ b/src/gui/rhi/qrhid3dhelpers_p.h @@ -17,8 +17,6 @@ #include -#include - #include #include #include @@ -29,15 +27,12 @@ #endif #include "qdxgivsyncservice_p.h" +#include "qdxgihdrinfo_p.h" QT_BEGIN_NAMESPACE namespace QRhiD3D { -bool output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result); -bool outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result); -float sdrWhiteLevelInNits(const DXGI_OUTPUT_DESC1 &outputDesc); - pD3DCompile resolveD3DCompile(); IDCompositionDevice *createDirectCompositionDevice(); diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 3046e310188..be2554bda43 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -968,6 +968,30 @@ bool QRhiVulkan::create(QRhi::Flags flags) } #endif + // On Windows, figure out the DXGI adapter LUID. +#ifdef Q_OS_WIN + adapterLuidValid = false; + adapterLuid = {}; +#ifdef VK_VERSION_1_2 + if (caps.apiVersion >= QVersionNumber(1, 2)) { + VkPhysicalDeviceVulkan11Properties v11props = {}; + v11props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES; + VkPhysicalDeviceProperties2 props2 = {}; + props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + props2.pNext = &v11props; + f->vkGetPhysicalDeviceProperties2(physDev, &props2); + if (v11props.deviceLUIDValid) { + const LUID *luid = reinterpret_cast(v11props.deviceLUID); + memcpy(&adapterLuid, luid, VK_LUID_SIZE); + adapterLuidValid = true; + dxgiHdrInfo = new QDxgiHdrInfo(adapterLuid); + qCDebug(QRHI_LOG_INFO, "DXGI adapter LUID for physical device is %lu, %lu", + adapterLuid.LowPart, adapterLuid.HighPart); + } + } +#endif +#endif + if (!importedAllocator) { VmaVulkanFunctions funcs = {}; funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr; @@ -1047,6 +1071,11 @@ void QRhiVulkan::destroy() executeDeferredReleases(true); finishActiveReadbacks(true); +#ifdef Q_OS_WIN + delete dxgiHdrInfo; + dxgiHdrInfo = nullptr; +#endif + if (ofr.cmdFence) { df->vkDestroyFence(dev, ofr.cmdFence, nullptr); ofr.cmdFence = VK_NULL_HANDLE; @@ -8551,6 +8580,18 @@ bool QVkSwapChain::isFormatSupported(Format f) return false; } +QRhiSwapChainHdrInfo QVkSwapChain::hdrInfo() +{ + QRhiSwapChainHdrInfo info = QRhiSwapChain::hdrInfo(); +#ifdef Q_OS_WIN + QRHI_RES_RHI(QRhiVulkan); + // Must use m_window, not window, given this may be called before createOrResize(). + if (m_window && rhiD->adapterLuidValid) + info = rhiD->dxgiHdrInfo->queryHdrInfo(m_window); +#endif + return info; +} + QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor() { // not yet built so cannot rely on data computed in createOrResize() diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h index ad9ca8cb66c..14936a60ec5 100644 --- a/src/gui/rhi/qrhivulkan_p.h +++ b/src/gui/rhi/qrhivulkan_p.h @@ -17,6 +17,10 @@ #include "qrhi_p.h" +#ifdef Q_OS_WIN +#include "qdxgihdrinfo_p.h" +#endif + QT_BEGIN_NAMESPACE class QVulkanFunctions; @@ -605,6 +609,7 @@ struct QVkSwapChain : public QRhiSwapChain QSize surfacePixelSize() override; bool isFormatSupported(Format f) override; + QRhiSwapChainHdrInfo hdrInfo() override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() override; bool createOrResize() override; @@ -895,6 +900,12 @@ public: bool deviceLost = false; bool releaseCachedResourcesCalledBeforeFrameStart = false; +#ifdef Q_OS_WIN + bool adapterLuidValid = false; + LUID adapterLuid; + QDxgiHdrInfo *dxgiHdrInfo = nullptr; +#endif + #ifdef VK_EXT_debug_utils PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = nullptr; PFN_vkCmdBeginDebugUtilsLabelEXT vkCmdBeginDebugUtilsLabelEXT = nullptr;