Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdxgihdrinfo.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <QtCore/qt_windows.h>
7#include <QtCore/private/qsystemerror_p.h>
8
10
11QDxgiHdrInfo::QDxgiHdrInfo()
12{
13 HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&m_factory));
14 if (FAILED(hr)) {
15 qWarning("QDxgiHdrInfo: CreateDXGIFactory2 failed: %s", qPrintable(QSystemError::windowsComString(hr)));
16 return;
17 }
18
19 // Create the factory but leave m_adapter set to null, indicating that all
20 // outputs of all adapters should be enumerated (if this is a good idea, not
21 // sure, but there is no choice here, when someone wants to use this outside
22 // of QRhi's scope and control)
23}
24
25QDxgiHdrInfo::QDxgiHdrInfo(LUID luid)
26{
27 HRESULT hr = CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), reinterpret_cast<void **>(&m_factory));
28 if (FAILED(hr)) {
29 qWarning("QDxgiHdrInfo: CreateDXGIFactory2 failed: %s", qPrintable(QSystemError::windowsComString(hr)));
30 return;
31 }
32
33 IDXGIAdapter1 *ad;
34 for (int adapterIndex = 0; m_factory->EnumAdapters1(UINT(adapterIndex), &ad) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
35 DXGI_ADAPTER_DESC1 desc;
36 ad->GetDesc1(&desc);
37 if (desc.AdapterLuid.LowPart == luid.LowPart && desc.AdapterLuid.HighPart == luid.HighPart) {
38 m_adapter = ad;
39 break;
40 }
41 ad->Release();
42 }
43
44 if (!m_adapter)
45 qWarning("QDxgiHdrInfo: No adapter found");
46}
47
48QDxgiHdrInfo::QDxgiHdrInfo(IDXGIAdapter1 *adapter)
49 : m_adapter(adapter)
50{
51 m_adapter->AddRef(); // so that the dtor does not destroy it
52}
53
54QDxgiHdrInfo::~QDxgiHdrInfo()
55{
56 if (m_adapter)
57 m_adapter->Release();
58
59 if (m_factory)
60 m_factory->Release();
61}
62
63bool QDxgiHdrInfo::isHdrCapable(QWindow *w)
64{
65 if (!m_adapter && m_factory) {
66 IDXGIAdapter1 *adapter;
67 for (int adapterIndex = 0; m_factory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
68 DXGI_OUTPUT_DESC1 desc1;
69 const bool ok = outputDesc1ForWindow(w, adapter, &desc1) && desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
70 adapter->Release();
71 if (ok)
72 return true;
73 }
74 } else if (m_adapter) {
75 DXGI_OUTPUT_DESC1 desc1;
76 if (outputDesc1ForWindow(w, m_adapter, &desc1)) {
77 // as per https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range
78 if (desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) {
79 // "HDR display with all Advanced Color capabilities"
80 return true;
81 }
82 }
83 }
84
85 return false;
86}
87
88QRhiSwapChainHdrInfo QDxgiHdrInfo::queryHdrInfo(QWindow *w)
89{
90 QRhiSwapChainHdrInfo info;
91 info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits;
92 info.limits.luminanceInNits.minLuminance = 0.0f;
93 info.limits.luminanceInNits.maxLuminance = 1000.0f;
94 info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred;
95 info.sdrWhiteLevel = 200.0f;
96
97 DXGI_OUTPUT_DESC1 hdrOutputDesc;
98 bool ok = false;
99 if (!m_adapter && m_factory) {
100 IDXGIAdapter1 *adapter;
101 for (int adapterIndex = 0; m_factory->EnumAdapters1(UINT(adapterIndex), &adapter) != DXGI_ERROR_NOT_FOUND; ++adapterIndex) {
102 ok = outputDesc1ForWindow(w, adapter, &hdrOutputDesc);
103 adapter->Release();
104 if (ok)
105 break;
106 }
107 } else if (m_adapter) {
108 ok = outputDesc1ForWindow(w, m_adapter, &hdrOutputDesc);
109 }
110 if (ok) {
111 info.limitsType = QRhiSwapChainHdrInfo::LuminanceInNits;
112 info.limits.luminanceInNits.minLuminance = hdrOutputDesc.MinLuminance;
113 info.limits.luminanceInNits.maxLuminance = hdrOutputDesc.MaxLuminance;
114 info.luminanceBehavior = QRhiSwapChainHdrInfo::SceneReferred; // 1.0 = 80 nits
115 info.sdrWhiteLevel = sdrWhiteLevelInNits(hdrOutputDesc);
116 }
117
118 return info;
119}
120
121bool QDxgiHdrInfo::output6ForWindow(QWindow *w, IDXGIAdapter1 *adapter, IDXGIOutput6 **result)
122{
123 if (!adapter)
124 return false;
125
126 bool ok = false;
127 HMONITOR monitor = nullptr;
128 QPoint center{};
129 if (w->handle()) {
130 // The returned HMONITOR will never be null unless using MONITOR_DEFAULTTONULL.
131 monitor = MonitorFromWindow(reinterpret_cast<HWND>(w->winId()), MONITOR_DEFAULTTONEAREST);
132 } else {
133 QRect wr = w->geometry();
134 wr = QRect(wr.topLeft() * w->devicePixelRatio(), wr.size() * w->devicePixelRatio());
135 center = wr.center();
136 }
137 IDXGIOutput *currentOutput = nullptr;
138 IDXGIOutput *output = nullptr;
139 for (UINT i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; ++i) {
140 if (!output)
141 continue;
142 DXGI_OUTPUT_DESC desc;
143 if (FAILED(output->GetDesc(&desc))) {
144 output->Release();
145 continue;
146 }
147 const RECT r = desc.DesktopCoordinates;
148 const QRect dr(QPoint(r.left, r.top), QSize(r.right - r.left, r.bottom - r.top));
149 if ((monitor && desc.Monitor == monitor) || (!monitor && dr.contains(center))) {
150 currentOutput = output;
151 break;
152 } else {
153 output->Release();
154 }
155 }
156 if (currentOutput) {
157 ok = SUCCEEDED(currentOutput->QueryInterface(__uuidof(IDXGIOutput6), reinterpret_cast<void **>(result)));
158 currentOutput->Release();
159 }
160 return ok;
161}
162
163bool QDxgiHdrInfo::outputDesc1ForWindow(QWindow *w, IDXGIAdapter1 *adapter, DXGI_OUTPUT_DESC1 *result)
164{
165 bool ok = false;
166 IDXGIOutput6 *out6 = nullptr;
167 if (output6ForWindow(w, adapter, &out6)) {
168 ok = SUCCEEDED(out6->GetDesc1(result));
169 out6->Release();
170 }
171 return ok;
172}
173
174float QDxgiHdrInfo::sdrWhiteLevelInNits(const DXGI_OUTPUT_DESC1 &outputDesc)
175{
176 QVector<DISPLAYCONFIG_PATH_INFO> pathInfos;
177 uint32_t pathInfoCount, modeInfoCount;
178 LONG result;
179 do {
180 if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathInfoCount, &modeInfoCount) == ERROR_SUCCESS) {
181 pathInfos.resize(pathInfoCount);
182 QVector<DISPLAYCONFIG_MODE_INFO> modeInfos(modeInfoCount);
183 result = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathInfoCount, pathInfos.data(), &modeInfoCount, modeInfos.data(), nullptr);
184 } else {
185 return 200.0f;
186 }
187 } while (result == ERROR_INSUFFICIENT_BUFFER);
188
189 MONITORINFOEX monitorInfo = {};
190 monitorInfo.cbSize = sizeof(monitorInfo);
191 GetMonitorInfo(outputDesc.Monitor, &monitorInfo);
192
193 for (const DISPLAYCONFIG_PATH_INFO &info : pathInfos) {
194 DISPLAYCONFIG_SOURCE_DEVICE_NAME deviceName = {};
195 deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
196 deviceName.header.size = sizeof(deviceName);
197 deviceName.header.adapterId = info.sourceInfo.adapterId;
198 deviceName.header.id = info.sourceInfo.id;
199 if (DisplayConfigGetDeviceInfo(&deviceName.header) == ERROR_SUCCESS) {
200 if (!wcscmp(monitorInfo.szDevice, deviceName.viewGdiDeviceName)) {
201 DISPLAYCONFIG_SDR_WHITE_LEVEL whiteLevel = {};
202 whiteLevel.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
203 whiteLevel.header.size = sizeof(DISPLAYCONFIG_SDR_WHITE_LEVEL);
204 whiteLevel.header.adapterId = info.targetInfo.adapterId;
205 whiteLevel.header.id = info.targetInfo.id;
206 if (DisplayConfigGetDeviceInfo(&whiteLevel.header) == ERROR_SUCCESS)
207 return whiteLevel.SDRWhiteLevel * 80 / 1000.0f;
208 }
209 }
210 }
211
212 return 200.0f;
213}
214
215QT_END_NAMESPACE
Combined button and popup list for selecting options.