Changeset View
Changeset View
Standalone View
Standalone View
libs/ui/opengl/KisScreenInformationAdapter.cpp
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (c) 2019 Dmitry Kazakov <dimula73@gmail.com> | ||||
3 | * | ||||
4 | * This program is free software; you can redistribute it and/or modify | ||||
5 | * it under the terms of the GNU General Public License as published by | ||||
6 | * the Free Software Foundation; either version 2 of the License, or | ||||
7 | * (at your option) any later version. | ||||
8 | * | ||||
9 | * This program is distributed in the hope that it will be useful, | ||||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
12 | * GNU General Public License for more details. | ||||
13 | * | ||||
14 | * You should have received a copy of the GNU General Public License | ||||
15 | * along with this program; if not, write to the Free Software | ||||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
17 | */ | ||||
18 | | ||||
19 | #include "KisScreenInformationAdapter.h" | ||||
20 | | ||||
21 | #include "kis_debug.h" | ||||
22 | #include <QOpenGLContext> | ||||
23 | | ||||
24 | #include <QGuiApplication> | ||||
25 | #include <QWindow> | ||||
26 | | ||||
27 | #include <config-hdr.h> | ||||
28 | | ||||
29 | #ifdef Q_OS_WIN | ||||
30 | #if (QT_VERSION == QT_VERSION_CHECK(5, 11, 2)) | ||||
31 | #include <QtGui/5.11.2/QtGui/qpa/qplatformnativeinterface.h> | ||||
32 | #elif (QT_VERSION == QT_VERSION_CHECK(5, 12, 0)) | ||||
33 | #include <QtGui/5.12.0/QtGui/qpa/qplatformnativeinterface.h> | ||||
34 | #elif (QT_VERSION == QT_VERSION_CHECK(5, 12, 1)) | ||||
35 | #include <QtGui/5.12.1/QtGui/qpa/qplatformnativeinterface.h> | ||||
36 | #elif (QT_VERSION == QT_VERSION_CHECK(5, 12, 2)) | ||||
37 | #include <QtGui/5.12.2/QtGui/qpa/qplatformnativeinterface.h> | ||||
38 | #elif (QT_VERSION == QT_VERSION_CHECK(5, 13, 0)) | ||||
39 | #include <QtGui/5.13.0/QtGui/qpa/qplatformnativeinterface.h> | ||||
40 | #endif | ||||
41 | | ||||
42 | #include <d3d11.h> | ||||
43 | #include <wrl/client.h> | ||||
44 | #include <dxgi1_6.h> | ||||
45 | #include "EGL/egl.h" | ||||
46 | #include "EGL/eglext.h" | ||||
47 | #endif | ||||
48 | | ||||
49 | namespace { | ||||
50 | struct EGLException { | ||||
51 | EGLException() {} | ||||
52 | EGLException(const QString &what) : m_what(what) {} | ||||
53 | | ||||
54 | QString what() const { | ||||
55 | return m_what; | ||||
56 | } | ||||
57 | | ||||
58 | private: | ||||
59 | QString m_what; | ||||
60 | }; | ||||
61 | | ||||
62 | template <typename FuncType> | ||||
63 | void getProcAddressSafe(QOpenGLContext *context, const char *funcName, FuncType &func) | ||||
64 | { | ||||
65 | func = reinterpret_cast<FuncType>(context->getProcAddress(funcName)); | ||||
66 | if (!func) { | ||||
67 | throw EGLException(QString("failed to fetch function %1").arg(funcName)); | ||||
68 | } | ||||
69 | } | ||||
70 | | ||||
71 | #ifdef Q_OS_WIN | ||||
72 | typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name); | ||||
73 | #endif | ||||
74 | } | ||||
75 | | ||||
76 | | ||||
77 | struct KisScreenInformationAdapter::Private | ||||
78 | { | ||||
79 | void initialize(QOpenGLContext *context); | ||||
80 | | ||||
81 | QOpenGLContext *context; | ||||
82 | QString errorString; | ||||
83 | | ||||
84 | #ifdef Q_OS_WIN | ||||
85 | Microsoft::WRL::ComPtr<IDXGIAdapter1> dxgiAdapter; | ||||
86 | #endif | ||||
87 | }; | ||||
88 | | ||||
89 | KisScreenInformationAdapter::KisScreenInformationAdapter(QOpenGLContext *context) | ||||
90 | : m_d(new Private) | ||||
91 | { | ||||
92 | m_d->initialize(context); | ||||
93 | } | ||||
94 | | ||||
95 | KisScreenInformationAdapter::~KisScreenInformationAdapter() | ||||
96 | { | ||||
97 | } | ||||
98 | | ||||
99 | void KisScreenInformationAdapter::Private::initialize(QOpenGLContext *newContext) | ||||
100 | { | ||||
101 | context = newContext; | ||||
102 | errorString.clear(); | ||||
103 | | ||||
104 | try { | ||||
105 | | ||||
106 | #ifdef Q_OS_WIN | ||||
107 | | ||||
108 | if (!context->isOpenGLES()) { | ||||
109 | throw EGLException("the context is not OpenGL ES"); | ||||
110 | } | ||||
111 | | ||||
112 | PFNEGLQUERYSTRINGPROC queryString = nullptr; | ||||
113 | getProcAddressSafe(context, "eglQueryString", queryString); | ||||
114 | | ||||
115 | const char* client_extensions = queryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); | ||||
116 | const QList<QByteArray> extensions = QByteArray(client_extensions).split(' '); | ||||
117 | | ||||
118 | if (!extensions.contains("EGL_ANGLE_platform_angle_d3d") || | ||||
119 | !extensions.contains("EGL_ANGLE_device_creation_d3d11")) { | ||||
120 | | ||||
121 | throw EGLException("the context is not Angle + D3D11"); | ||||
122 | } | ||||
123 | | ||||
124 | PFNEGLQUERYDISPLAYATTRIBEXTPROC queryDisplayAttribEXT = nullptr; | ||||
125 | PFNEGLQUERYDEVICEATTRIBEXTPROC queryDeviceAttribEXT = nullptr; | ||||
126 | | ||||
127 | getProcAddressSafe(context, "eglQueryDisplayAttribEXT", queryDisplayAttribEXT); | ||||
128 | getProcAddressSafe(context, "eglQueryDeviceAttribEXT", queryDeviceAttribEXT); | ||||
129 | | ||||
130 | QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); | ||||
131 | EGLDisplay display = reinterpret_cast<EGLDisplay>(nativeInterface->nativeResourceForContext("egldisplay", context)); | ||||
132 | | ||||
133 | if (!display) { | ||||
134 | throw EGLException( | ||||
135 | QString("couldn't request EGLDisplay handle, display = 0x%1").arg(uintptr_t(display), 0, 16)); | ||||
136 | } | ||||
137 | | ||||
138 | EGLAttrib value = 0; | ||||
139 | EGLBoolean result = false; | ||||
140 | | ||||
141 | result = queryDisplayAttribEXT(display, EGL_DEVICE_EXT, &value); | ||||
142 | | ||||
143 | if (!result || value == EGL_NONE) { | ||||
144 | throw EGLException( | ||||
145 | QString("couldn't request EGLDeviceEXT handle, result = 0x%1, value = 0x%2") | ||||
146 | .arg(result, 0, 16).arg(value, 0, 16)); | ||||
147 | } | ||||
148 | | ||||
149 | EGLDeviceEXT device = reinterpret_cast<EGLDeviceEXT>(value); | ||||
150 | | ||||
151 | result = queryDeviceAttribEXT(device, EGL_D3D11_DEVICE_ANGLE, &value); | ||||
152 | | ||||
153 | if (!result || value == EGL_NONE) { | ||||
154 | throw EGLException( | ||||
155 | QString("couldn't request ID3D11Device pointer, result = 0x%1, value = 0x%2") | ||||
156 | .arg(result, 0, 16).arg(value, 0, 16)); | ||||
157 | } | ||||
158 | ID3D11Device *deviceD3D = reinterpret_cast<ID3D11Device*>(value); | ||||
159 | | ||||
160 | { | ||||
161 | HRESULT result = 0; | ||||
162 | | ||||
163 | Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice; | ||||
164 | result = deviceD3D->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice); | ||||
165 | | ||||
166 | if (FAILED(result)) { | ||||
167 | throw EGLException( | ||||
168 | QString("couldn't request IDXGIDevice pointer, result = 0x%1").arg(result, 0, 16)); | ||||
169 | } | ||||
170 | | ||||
171 | Microsoft::WRL::ComPtr<IDXGIAdapter1> dxgiAdapter; | ||||
172 | result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter1), (void**)&dxgiAdapter); | ||||
173 | | ||||
174 | if (FAILED(result)) { | ||||
175 | throw EGLException( | ||||
176 | QString("couldn't request IDXGIAdapter1 pointer, result = 0x%1").arg(result, 0, 16)); | ||||
177 | } | ||||
178 | | ||||
179 | this->dxgiAdapter = dxgiAdapter; | ||||
180 | } | ||||
181 | | ||||
182 | #else | ||||
183 | throw EGLException("current platform doesn't support fetching display information"); | ||||
184 | #endif | ||||
185 | | ||||
186 | } catch (EGLException &e) { | ||||
187 | this->context = 0; | ||||
188 | this->errorString = e.what(); | ||||
189 | #ifdef Q_OS_WIN | ||||
190 | this->dxgiAdapter.Reset(); | ||||
191 | #endif | ||||
192 | } | ||||
193 | } | ||||
194 | | ||||
195 | bool KisScreenInformationAdapter::isValid() const | ||||
196 | { | ||||
197 | #ifdef Q_OS_WIN | ||||
198 | return m_d->context && m_d->dxgiAdapter; | ||||
199 | #else | ||||
200 | return false; | ||||
201 | #endif | ||||
202 | } | ||||
203 | | ||||
204 | QString KisScreenInformationAdapter::errorString() const | ||||
205 | { | ||||
206 | return m_d->errorString; | ||||
207 | } | ||||
208 | | ||||
209 | KisScreenInformationAdapter::ScreenInfo KisScreenInformationAdapter::infoForScreen(QScreen *screen) const | ||||
210 | { | ||||
211 | ScreenInfo info; | ||||
212 | | ||||
213 | #ifdef Q_OS_WIN | ||||
214 | | ||||
215 | QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); | ||||
216 | HMONITOR monitor = reinterpret_cast<HMONITOR>(nativeInterface->nativeResourceForScreen("handle", screen)); | ||||
217 | | ||||
218 | if (!monitor) { | ||||
219 | qWarning("%s: failed to get HMONITOR handle for screen: screen = 0x%X, monitor = 0x%X", | ||||
220 | __PRETTY_FUNCTION__, screen, monitor); | ||||
221 | } | ||||
222 | | ||||
223 | UINT i = 0; | ||||
224 | Microsoft::WRL::ComPtr<IDXGIOutput> currentOutput; | ||||
225 | | ||||
226 | while (m_d->dxgiAdapter->EnumOutputs(i, ¤tOutput) != DXGI_ERROR_NOT_FOUND) | ||||
227 | { | ||||
228 | | ||||
229 | HRESULT result = 0; | ||||
230 | Microsoft::WRL::ComPtr<IDXGIOutput6> output6; | ||||
231 | result = currentOutput.As(&output6); | ||||
232 | | ||||
233 | if (output6) { | ||||
234 | DXGI_OUTPUT_DESC1 desc; | ||||
235 | result = output6->GetDesc1(&desc); | ||||
236 | | ||||
237 | if (desc.Monitor == monitor) { | ||||
238 | info.screen = screen; | ||||
239 | info.bitsPerColor = desc.BitsPerColor; | ||||
240 | info.redPrimary[0] = desc.RedPrimary[0]; | ||||
241 | info.redPrimary[1] = desc.RedPrimary[1]; | ||||
242 | info.greenPrimary[0] = desc.GreenPrimary[0]; | ||||
243 | info.greenPrimary[1] = desc.GreenPrimary[1]; | ||||
244 | info.bluePrimary[0] = desc.BluePrimary[0]; | ||||
245 | info.bluePrimary[1] = desc.BluePrimary[1]; | ||||
246 | info.whitePoint[0] = desc.WhitePoint[0]; | ||||
247 | info.whitePoint[1] = desc.WhitePoint[1]; | ||||
248 | info.minLuminance = desc.MinLuminance; | ||||
249 | info.maxLuminance = desc.MaxLuminance; | ||||
250 | info.maxFullFrameLuminance = desc.MaxFullFrameLuminance; | ||||
251 | | ||||
252 | info.colorSpace = QSurfaceFormat::DefaultColorSpace; | ||||
253 | | ||||
254 | if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { | ||||
255 | info.colorSpace = QSurfaceFormat::sRGBColorSpace; | ||||
256 | } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { | ||||
257 | #ifdef HAVE_HDR | ||||
258 | info.colorSpace = QSurfaceFormat::scRGBColorSpace; | ||||
259 | #else | ||||
260 | qWarning("WARNING: scRGB display color space is not supported by Qt's build"); | ||||
261 | #endif | ||||
262 | } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { | ||||
263 | #ifdef HAVE_HDR | ||||
264 | info.colorSpace = QSurfaceFormat::bt2020PQColorSpace; | ||||
265 | #else | ||||
266 | qWarning("WARNING: bt2020-pq display color space is not supported by Qt's build"); | ||||
267 | #endif | ||||
268 | } else { | ||||
269 | qWarning("WARNING: unknown display color space! 0x%X", desc.ColorSpace); | ||||
270 | } | ||||
271 | | ||||
272 | break; | ||||
273 | } | ||||
274 | } | ||||
275 | | ||||
276 | i++; | ||||
277 | } | ||||
278 | | ||||
279 | #endif | ||||
280 | Q_UNUSED(screen); | ||||
281 | return info; | ||||
282 | } | ||||
283 | | ||||
284 | QDebug operator<<(QDebug dbg, const KisScreenInformationAdapter::ScreenInfo &info) | ||||
285 | { | ||||
286 | QDebugStateSaver saver(dbg); | ||||
287 | | ||||
288 | if (info.isValid()) { | ||||
289 | dbg.nospace() << "ScreenInfo(" | ||||
290 | << "screen " << info.screen | ||||
291 | << ", bitsPerColor " << info.bitsPerColor | ||||
292 | << ", colorSpace " << info.colorSpace | ||||
293 | << ", redPrimary " << "(" << info.redPrimary[0] << ", " << info.redPrimary[1] << ")" | ||||
294 | << ", greenPrimary " << "(" << info.greenPrimary[0] << ", " << info.greenPrimary[1] << ")" | ||||
295 | << ", bluePrimary " << "(" << info.bluePrimary[0] << ", " << info.bluePrimary[1] << ")" | ||||
296 | << ", whitePoint " << "(" << info.whitePoint[0] << ", " << info.whitePoint[1] << ")" | ||||
297 | << ", minLuminance " << info.minLuminance | ||||
298 | << ", maxLuminance " << info.maxLuminance | ||||
299 | << ", maxFullFrameLuminance " << info.maxFullFrameLuminance | ||||
300 | << ')'; | ||||
301 | } else { | ||||
302 | dbg.nospace() << "ScreenInfo(<invalid>)"; | ||||
303 | } | ||||
304 | | ||||
305 | return dbg; | ||||
306 | } |