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