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, &currentOutput) != 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 }