File indexing completed on 2024-06-09 04:24:39
0001 /* 0002 * SPDX-FileCopyrightText: 2017 Alvin Wong <alvinhochun@gmail.com> 0003 * SPDX-FileCopyrightText: 2019 Dmitry Kazakov <dimula73@gmail.com> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "KisOpenGLModeProber.h" 0009 0010 #include <config-hdr.h> 0011 #include <QApplication> 0012 #include <QOpenGLContext> 0013 #include <QOpenGLFunctions> 0014 #include <QWindow> 0015 0016 #include <QGlobalStatic> 0017 Q_GLOBAL_STATIC(KisOpenGLModeProber, s_instance) 0018 0019 0020 KisOpenGLModeProber::KisOpenGLModeProber() 0021 { 0022 } 0023 0024 KisOpenGLModeProber::~KisOpenGLModeProber() 0025 { 0026 0027 } 0028 0029 KisOpenGLModeProber *KisOpenGLModeProber::instance() 0030 { 0031 return s_instance; 0032 } 0033 0034 bool KisOpenGLModeProber::useHDRMode() const 0035 { 0036 return isFormatHDR(QSurfaceFormat::defaultFormat()); 0037 } 0038 0039 QSurfaceFormat KisOpenGLModeProber::surfaceformatInUse() const 0040 { 0041 // TODO: use information provided by KisOpenGL instead 0042 QOpenGLContext *sharedContext = QOpenGLContext::globalShareContext(); 0043 QSurfaceFormat format = sharedContext ? sharedContext->format() : QSurfaceFormat::defaultFormat(); 0044 return format; 0045 } 0046 0047 const KoColorProfile *KisOpenGLModeProber::rootSurfaceColorProfile() const 0048 { 0049 const KoColorProfile *profile = KoColorSpaceRegistry::instance()->p709SRGBProfile(); 0050 0051 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 0052 0053 const KisSurfaceColorSpace surfaceColorSpace = surfaceformatInUse().colorSpace(); 0054 if (surfaceColorSpace == KisSurfaceColorSpace::sRGBColorSpace) { 0055 // use the default one! 0056 #ifdef HAVE_HDR 0057 } else if (surfaceColorSpace == KisSurfaceColorSpace::scRGBColorSpace) { 0058 profile = KoColorSpaceRegistry::instance()->p709G10Profile(); 0059 } else if (surfaceColorSpace == KisSurfaceColorSpace::bt2020PQColorSpace) { 0060 profile = KoColorSpaceRegistry::instance()->p2020PQProfile(); 0061 #endif 0062 } 0063 0064 #endif 0065 0066 return profile; 0067 } 0068 0069 namespace { 0070 struct AppAttributeSetter 0071 { 0072 AppAttributeSetter(Qt::ApplicationAttribute attribute, bool useOpenGLES) 0073 : m_attribute(attribute), 0074 m_oldValue(QCoreApplication::testAttribute(attribute)) 0075 { 0076 QCoreApplication::setAttribute(attribute, useOpenGLES); 0077 } 0078 0079 ~AppAttributeSetter() { 0080 QCoreApplication::setAttribute(m_attribute, m_oldValue); 0081 } 0082 0083 private: 0084 Qt::ApplicationAttribute m_attribute; 0085 bool m_oldValue = false; 0086 }; 0087 0088 struct SurfaceFormatSetter 0089 { 0090 SurfaceFormatSetter(const QSurfaceFormat &format) 0091 : m_oldFormat(QSurfaceFormat::defaultFormat()) 0092 { 0093 QSurfaceFormat::setDefaultFormat(format); 0094 } 0095 0096 ~SurfaceFormatSetter() { 0097 QSurfaceFormat::setDefaultFormat(m_oldFormat); 0098 } 0099 0100 private: 0101 QSurfaceFormat m_oldFormat; 0102 }; 0103 0104 0105 #if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) 0106 QString qEnvironmentVariable(const char *varName) { 0107 return qgetenv(varName); 0108 } 0109 #endif 0110 0111 struct EnvironmentSetter 0112 { 0113 EnvironmentSetter(const QLatin1String &env, const QString &value) 0114 : m_env(env) 0115 { 0116 if (qEnvironmentVariableIsEmpty(m_env.latin1())) { 0117 m_oldValue = qgetenv(env.latin1()); 0118 } 0119 if (!value.isEmpty()) { 0120 qputenv(env.latin1(), value.toLatin1()); 0121 } else { 0122 qunsetenv(env.latin1()); 0123 } 0124 } 0125 0126 ~EnvironmentSetter() { 0127 if (m_oldValue) { 0128 qputenv(m_env.latin1(), (*m_oldValue).toLatin1()); 0129 } else { 0130 qunsetenv(m_env.latin1()); 0131 } 0132 } 0133 0134 private: 0135 const QLatin1String m_env; 0136 boost::optional<QString> m_oldValue; 0137 }; 0138 0139 } 0140 0141 boost::optional<KisOpenGLModeProber::Result> 0142 KisOpenGLModeProber::probeFormat(const KisOpenGL::RendererConfig &rendererConfig, 0143 bool adjustGlobalState) 0144 { 0145 const QSurfaceFormat &format = rendererConfig.format; 0146 0147 dbgOpenGL << "Probing format" << rendererConfig.rendererId() << rendererConfig.angleRenderer 0148 << rendererConfig.format; 0149 0150 QScopedPointer<AppAttributeSetter> sharedContextSetter; 0151 QScopedPointer<AppAttributeSetter> glSetter; 0152 QScopedPointer<AppAttributeSetter> glesSetter; 0153 QScopedPointer<SurfaceFormatSetter> formatSetter; 0154 QScopedPointer<EnvironmentSetter> rendererSetter; 0155 QScopedPointer<EnvironmentSetter> portalSetter; 0156 QScopedPointer<QGuiApplication> application; 0157 0158 int argc = 1; 0159 QByteArray probeAppName("krita"); 0160 char *argv = probeAppName.data(); 0161 0162 0163 0164 if (adjustGlobalState) { 0165 sharedContextSetter.reset(new AppAttributeSetter(Qt::AA_ShareOpenGLContexts, false)); 0166 0167 if (format.renderableType() != QSurfaceFormat::DefaultRenderableType) { 0168 glSetter.reset(new AppAttributeSetter(Qt::AA_UseDesktopOpenGL, format.renderableType() != QSurfaceFormat::OpenGLES)); 0169 glesSetter.reset(new AppAttributeSetter(Qt::AA_UseOpenGLES, format.renderableType() == QSurfaceFormat::OpenGLES)); 0170 } 0171 0172 rendererSetter.reset(new EnvironmentSetter(QLatin1String("QT_ANGLE_PLATFORM"), angleRendererToString(rendererConfig.angleRenderer))); 0173 portalSetter.reset(new EnvironmentSetter(QLatin1String("QT_NO_XDG_DESKTOP_PORTAL"), QLatin1String("1"))); 0174 formatSetter.reset(new SurfaceFormatSetter(format)); 0175 0176 // Disable this workaround for plasma (BUG:408015), because it causes 0177 // a crash on Windows with Qt 5.15.7 0178 //QGuiApplication::setDesktopSettingsAware(false); 0179 application.reset(new QGuiApplication(argc, &argv)); 0180 //QGuiApplication::setDesktopSettingsAware(true); 0181 } 0182 0183 QWindow surface; 0184 surface.setFormat(format); 0185 surface.setSurfaceType(QSurface::OpenGLSurface); 0186 surface.create(); 0187 QOpenGLContext context; 0188 context.setFormat(format); 0189 0190 0191 if (!context.create()) { 0192 dbgOpenGL << "OpenGL context cannot be created"; 0193 return boost::none; 0194 } 0195 if (!context.isValid()) { 0196 dbgOpenGL << "OpenGL context is not valid while checking Qt's OpenGL status"; 0197 return boost::none; 0198 } 0199 if (!context.makeCurrent(&surface)) { 0200 dbgOpenGL << "OpenGL context cannot be made current"; 0201 return boost::none; 0202 } 0203 0204 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 0205 if (!fuzzyCompareColorSpaces(context.format().colorSpace(), format.colorSpace())) { 0206 dbgOpenGL << "Failed to create an OpenGL context with requested color space. Requested:" << format.colorSpace() << "Actual:" << context.format().colorSpace(); 0207 return boost::none; 0208 } 0209 #endif 0210 0211 Result result(context); 0212 0213 dbgOpenGL << "Probe returned" << result.rendererString() << result.driverVersionString() << result.isOpenGLES(); 0214 0215 return result; 0216 } 0217 0218 bool KisOpenGLModeProber::fuzzyCompareColorSpaces(const KisSurfaceColorSpace &lhs, const KisSurfaceColorSpace &rhs) 0219 { 0220 return lhs == rhs || 0221 ((lhs == KisSurfaceColorSpace::DefaultColorSpace || 0222 lhs == KisSurfaceColorSpace::sRGBColorSpace) && 0223 (rhs == KisSurfaceColorSpace::DefaultColorSpace || 0224 rhs == KisSurfaceColorSpace::sRGBColorSpace)); 0225 } 0226 0227 void KisOpenGLModeProber::initSurfaceFormatFromConfig(KisConfig::RootSurfaceFormat config, 0228 QSurfaceFormat *format) 0229 { 0230 #ifdef HAVE_HDR 0231 if (config == KisConfig::BT2020_PQ) { 0232 0233 format->setRedBufferSize(10); 0234 format->setGreenBufferSize(10); 0235 format->setBlueBufferSize(10); 0236 format->setAlphaBufferSize(2); 0237 format->setColorSpace(KisSurfaceColorSpace::bt2020PQColorSpace); 0238 } else if (config == KisConfig::BT709_G10) { 0239 format->setRedBufferSize(16); 0240 format->setGreenBufferSize(16); 0241 format->setBlueBufferSize(16); 0242 format->setAlphaBufferSize(16); 0243 format->setColorSpace(KisSurfaceColorSpace::scRGBColorSpace); 0244 } else 0245 #else 0246 if (config == KisConfig::BT2020_PQ) { 0247 qWarning() << "WARNING: Bt.2020 PQ surface type is not supported by this build of Krita"; 0248 } else if (config == KisConfig::BT709_G10) { 0249 qWarning() << "WARNING: scRGB surface type is not supported by this build of Krita"; 0250 } 0251 #endif 0252 0253 { 0254 format->setRedBufferSize(8); 0255 format->setGreenBufferSize(8); 0256 format->setBlueBufferSize(8); 0257 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 0258 format->setAlphaBufferSize(8); 0259 #else 0260 format->setAlphaBufferSize(0); 0261 #endif 0262 0263 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) 0264 // TODO: check if we can use real sRGB space here 0265 format->setColorSpace(KisSurfaceColorSpace::DefaultColorSpace); 0266 #endif 0267 } 0268 } 0269 0270 bool KisOpenGLModeProber::isFormatHDR(const QSurfaceFormat &format) 0271 { 0272 #ifdef HAVE_HDR 0273 0274 bool isBt2020PQ = 0275 format.colorSpace() == KisSurfaceColorSpace::bt2020PQColorSpace && 0276 format.redBufferSize() == 10 && 0277 format.greenBufferSize() == 10 && 0278 format.blueBufferSize() == 10 && 0279 format.alphaBufferSize() == 2; 0280 0281 bool isBt709G10 = 0282 format.colorSpace() == KisSurfaceColorSpace::scRGBColorSpace && 0283 format.redBufferSize() == 16 && 0284 format.greenBufferSize() == 16 && 0285 format.blueBufferSize() == 16 && 0286 format.alphaBufferSize() == 16; 0287 0288 return isBt2020PQ || isBt709G10; 0289 #else 0290 Q_UNUSED(format); 0291 return false; 0292 #endif 0293 } 0294 0295 QString KisOpenGLModeProber::angleRendererToString(KisOpenGL::AngleRenderer renderer) 0296 { 0297 QString value; 0298 0299 switch (renderer) { 0300 case KisOpenGL::AngleRendererDefault: 0301 break; 0302 case KisOpenGL::AngleRendererD3d9: 0303 value = "d3d9"; 0304 break; 0305 case KisOpenGL::AngleRendererD3d11: 0306 value = "d3d11"; 0307 break; 0308 case KisOpenGL::AngleRendererD3d11Warp: 0309 value = "warp"; 0310 break; 0311 }; 0312 0313 return value; 0314 } 0315 0316 KisOpenGLModeProber::Result::Result(QOpenGLContext &context) { 0317 if (!context.isValid()) { 0318 return; 0319 } 0320 0321 QOpenGLFunctions *funcs = context.functions(); // funcs is ready to be used 0322 0323 m_rendererString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_RENDERER))); 0324 m_driverVersionString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VERSION))); 0325 m_vendorString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_VENDOR))); 0326 m_shadingLanguageString = QString(reinterpret_cast<const char *>(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION))); 0327 m_glMajorVersion = context.format().majorVersion(); 0328 m_glMinorVersion = context.format().minorVersion(); 0329 m_supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions); 0330 m_isOpenGLES = context.isOpenGLES(); 0331 m_format = context.format(); 0332 m_supportsFBO = context.functions()->hasOpenGLFeature(QOpenGLFunctions::Framebuffers); 0333 0334 m_supportsBufferMapping = !m_isOpenGLES || 0335 m_glMajorVersion >= 3 || 0336 context.hasExtension("GL_OES_mapbuffer") || 0337 context.hasExtension("GL_EXT_map_buffer_range") || 0338 context.hasExtension("GL_ARB_map_buffer_range"); 0339 0340 m_supportsBufferInvalidation = !m_isOpenGLES && 0341 ((m_glMajorVersion >= 4 && m_glMinorVersion >= 3) || 0342 context.hasExtension("GL_ARB_invalidate_subdata")); 0343 m_supportsLod = context.format().majorVersion() >= 3 || (m_isOpenGLES && context.hasExtension("GL_EXT_shader_texture_lod")); 0344 0345 m_extensions = context.extensions(); 0346 // Remove empty name extension that sometimes appears on NVIDIA output 0347 m_extensions.remove(""); 0348 }