File indexing completed on 2024-12-22 04:12:49
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Dmitry Kazakov <dimula73@gmail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "KisScreenInformationAdapter.h" 0008 0009 #include "kis_debug.h" 0010 #include <QOpenGLContext> 0011 0012 #include <QGuiApplication> 0013 #include <QWindow> 0014 0015 #include <config-hdr.h> 0016 0017 0018 0019 #ifdef Q_OS_WIN 0020 #include <qpa/qplatformnativeinterface.h> 0021 #include <d3d11.h> 0022 #include <wrl/client.h> 0023 #include <dxgi1_6.h> 0024 #include "EGL/egl.h" 0025 #include "EGL/eglext.h" 0026 #endif 0027 0028 namespace { 0029 struct EGLException { 0030 EGLException() {} 0031 EGLException(const QString &what) : m_what(what) {} 0032 0033 QString what() const { 0034 return m_what; 0035 } 0036 0037 private: 0038 QString m_what; 0039 }; 0040 0041 template <typename FuncType> 0042 void getProcAddressSafe(QOpenGLContext *context, const char *funcName, FuncType &func) 0043 { 0044 func = reinterpret_cast<FuncType>(context->getProcAddress(funcName)); 0045 if (!func) { 0046 throw EGLException(QString("failed to fetch function %1").arg(funcName)); 0047 } 0048 } 0049 0050 #ifdef Q_OS_WIN 0051 typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGPROC) (EGLDisplay dpy, EGLint name); 0052 #endif 0053 } 0054 0055 0056 struct KisScreenInformationAdapter::Private 0057 { 0058 void initialize(QOpenGLContext *context); 0059 0060 QOpenGLContext *context; 0061 QString errorString; 0062 0063 #ifdef Q_OS_WIN 0064 Microsoft::WRL::ComPtr<IDXGIAdapter1> dxgiAdapter; 0065 #endif 0066 }; 0067 0068 KisScreenInformationAdapter::KisScreenInformationAdapter(QOpenGLContext *context) 0069 : m_d(new Private) 0070 { 0071 if (context) { 0072 m_d->initialize(context); 0073 } 0074 } 0075 0076 KisScreenInformationAdapter::~KisScreenInformationAdapter() 0077 { 0078 } 0079 0080 void KisScreenInformationAdapter::Private::initialize(QOpenGLContext *newContext) 0081 { 0082 context = newContext; 0083 errorString.clear(); 0084 0085 try { 0086 0087 #if defined Q_OS_WIN && QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 0088 0089 if (!context->isOpenGLES()) { 0090 throw EGLException("the context is not OpenGL ES"); 0091 } 0092 0093 PFNEGLQUERYSTRINGPROC queryString = nullptr; 0094 getProcAddressSafe(context, "eglQueryString", queryString); 0095 0096 const char* client_extensions = queryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); 0097 const QList<QByteArray> extensions = QByteArray(client_extensions).split(' '); 0098 0099 if (!extensions.contains("EGL_ANGLE_platform_angle_d3d") || 0100 !extensions.contains("EGL_ANGLE_device_creation_d3d11")) { 0101 0102 throw EGLException("the context is not Angle + D3D11"); 0103 } 0104 0105 PFNEGLQUERYDISPLAYATTRIBEXTPROC queryDisplayAttribEXT = nullptr; 0106 PFNEGLQUERYDEVICEATTRIBEXTPROC queryDeviceAttribEXT = nullptr; 0107 0108 getProcAddressSafe(context, "eglQueryDisplayAttribEXT", queryDisplayAttribEXT); 0109 getProcAddressSafe(context, "eglQueryDeviceAttribEXT", queryDeviceAttribEXT); 0110 0111 QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); 0112 EGLDisplay display = reinterpret_cast<EGLDisplay>(nativeInterface->nativeResourceForContext("egldisplay", context)); 0113 0114 if (!display) { 0115 throw EGLException( 0116 QString("couldn't request EGLDisplay handle, display = 0x%1").arg(uintptr_t(display), 0, 16)); 0117 } 0118 0119 EGLAttrib value = 0; 0120 EGLBoolean result = false; 0121 0122 result = queryDisplayAttribEXT(display, EGL_DEVICE_EXT, &value); 0123 0124 if (!result || value == EGL_NONE) { 0125 throw EGLException( 0126 QString("couldn't request EGLDeviceEXT handle, result = 0x%1, value = 0x%2") 0127 .arg(result, 0, 16).arg(value, 0, 16)); 0128 } 0129 0130 EGLDeviceEXT device = reinterpret_cast<EGLDeviceEXT>(value); 0131 0132 result = queryDeviceAttribEXT(device, EGL_D3D11_DEVICE_ANGLE, &value); 0133 0134 if (!result || value == EGL_NONE) { 0135 throw EGLException( 0136 QString("couldn't request ID3D11Device pointer, result = 0x%1, value = 0x%2") 0137 .arg(result, 0, 16).arg(value, 0, 16)); 0138 } 0139 ID3D11Device *deviceD3D = reinterpret_cast<ID3D11Device*>(value); 0140 0141 { 0142 HRESULT result = 0; 0143 0144 Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice; 0145 result = deviceD3D->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgiDevice); 0146 0147 if (FAILED(result)) { 0148 throw EGLException( 0149 QString("couldn't request IDXGIDevice pointer, result = 0x%1").arg(result, 0, 16)); 0150 } 0151 0152 Microsoft::WRL::ComPtr<IDXGIAdapter1> dxgiAdapter; 0153 result = dxgiDevice->GetParent(__uuidof(IDXGIAdapter1), (void**)&dxgiAdapter); 0154 0155 if (FAILED(result)) { 0156 throw EGLException( 0157 QString("couldn't request IDXGIAdapter1 pointer, result = 0x%1").arg(result, 0, 16)); 0158 } 0159 0160 this->dxgiAdapter = dxgiAdapter; 0161 } 0162 0163 #else 0164 throw EGLException("current platform doesn't support fetching display information"); 0165 #endif 0166 0167 } catch (EGLException &e) { 0168 this->context = 0; 0169 this->errorString = e.what(); 0170 #ifdef Q_OS_WIN 0171 this->dxgiAdapter.Reset(); 0172 #endif 0173 } 0174 } 0175 0176 bool KisScreenInformationAdapter::isValid() const 0177 { 0178 #ifdef Q_OS_WIN 0179 return m_d->context && m_d->dxgiAdapter; 0180 #else 0181 return false; 0182 #endif 0183 } 0184 0185 QString KisScreenInformationAdapter::errorString() const 0186 { 0187 return m_d->errorString; 0188 } 0189 0190 KisScreenInformationAdapter::ScreenInfo KisScreenInformationAdapter::infoForScreen(QScreen *screen) const 0191 { 0192 ScreenInfo info; 0193 0194 #if defined Q_OS_WIN && QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 0195 0196 QPlatformNativeInterface *nativeInterface = qGuiApp->platformNativeInterface(); 0197 HMONITOR monitor = reinterpret_cast<HMONITOR>(nativeInterface->nativeResourceForScreen("handle", screen)); 0198 0199 UINT i = 0; 0200 Microsoft::WRL::ComPtr<IDXGIOutput> currentOutput; 0201 0202 while (m_d->dxgiAdapter->EnumOutputs(i, ¤tOutput) != DXGI_ERROR_NOT_FOUND) 0203 { 0204 0205 HRESULT result = 0; 0206 Microsoft::WRL::ComPtr<IDXGIOutput6> output6; 0207 result = currentOutput.As(&output6); 0208 0209 if (output6) { 0210 DXGI_OUTPUT_DESC1 desc; 0211 result = output6->GetDesc1(&desc); 0212 0213 if (desc.Monitor == monitor) { 0214 info.screen = screen; 0215 info.bitsPerColor = desc.BitsPerColor; 0216 info.redPrimary[0] = desc.RedPrimary[0]; 0217 info.redPrimary[1] = desc.RedPrimary[1]; 0218 info.greenPrimary[0] = desc.GreenPrimary[0]; 0219 info.greenPrimary[1] = desc.GreenPrimary[1]; 0220 info.bluePrimary[0] = desc.BluePrimary[0]; 0221 info.bluePrimary[1] = desc.BluePrimary[1]; 0222 info.whitePoint[0] = desc.WhitePoint[0]; 0223 info.whitePoint[1] = desc.WhitePoint[1]; 0224 info.minLuminance = desc.MinLuminance; 0225 info.maxLuminance = desc.MaxLuminance; 0226 info.maxFullFrameLuminance = desc.MaxFullFrameLuminance; 0227 0228 info.colorSpace = KisSurfaceColorSpace::DefaultColorSpace; 0229 0230 if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { 0231 info.colorSpace = KisSurfaceColorSpace::sRGBColorSpace; 0232 } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709) { 0233 #ifdef HAVE_HDR 0234 info.colorSpace = KisSurfaceColorSpace::scRGBColorSpace; 0235 #else 0236 qWarning("WARNING: scRGB display color space is not supported by Qt's build"); 0237 #endif 0238 } else if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { 0239 #ifdef HAVE_HDR 0240 info.colorSpace = KisSurfaceColorSpace::bt2020PQColorSpace; 0241 #else 0242 qWarning("WARNING: bt2020-pq display color space is not supported by Qt's build"); 0243 #endif 0244 } else { 0245 qWarning("WARNING: unknown display color space! 0x%X", desc.ColorSpace); 0246 } 0247 0248 break; 0249 } 0250 } 0251 0252 Q_UNUSED(result); 0253 0254 i++; 0255 } 0256 0257 #endif 0258 Q_UNUSED(screen); 0259 return info; 0260 } 0261 0262 QDebug operator<<(QDebug dbg, const KisScreenInformationAdapter::ScreenInfo &info) 0263 { 0264 QDebugStateSaver saver(dbg); 0265 0266 if (info.isValid()) { 0267 dbg.nospace() << "ScreenInfo(" 0268 << "screen " << info.screen 0269 << ", bitsPerColor " << info.bitsPerColor 0270 << ", colorSpace " << info.colorSpace 0271 << ", redPrimary " << "(" << info.redPrimary[0] << ", " << info.redPrimary[1] << ")" 0272 << ", greenPrimary " << "(" << info.greenPrimary[0] << ", " << info.greenPrimary[1] << ")" 0273 << ", bluePrimary " << "(" << info.bluePrimary[0] << ", " << info.bluePrimary[1] << ")" 0274 << ", whitePoint " << "(" << info.whitePoint[0] << ", " << info.whitePoint[1] << ")" 0275 << ", minLuminance " << info.minLuminance 0276 << ", maxLuminance " << info.maxLuminance 0277 << ", maxFullFrameLuminance " << info.maxFullFrameLuminance 0278 << ')'; 0279 } else { 0280 dbg.nospace() << "ScreenInfo(<invalid>)"; 0281 } 0282 0283 return dbg; 0284 }