Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_opengl.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Adrian Page <adrian@pagenet.plus.com>
3 * SPDX-FileCopyrightText: 2023 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <tuple>
9
10#include <boost/optional.hpp>
11
12#include <QtGlobal>
13
14#include <QOpenGLContext>
15#include <QOpenGLDebugLogger>
16#include <QOpenGLFunctions>
17
18#include <QApplication>
19#include <QScreen>
20#include <QPixmapCache>
21#include <QColorSpace>
22
23#include <QDir>
24#include <QFile>
25#include <QStandardPaths>
26#include <QVector>
27#include <QWindow>
28#include <QRegularExpression>
29#include <QSettings>
30#include <QScreen>
31
32#include <klocalizedstring.h>
33
34#include <KisRepaintDebugger.h>
35#include <KisUsageLogger.h>
36#include <kis_assert.h>
37#include <kis_config.h>
38#include <kis_debug.h>
39
41#include "KisOpenGLModeProber.h"
42#include "opengl/kis_opengl.h"
43
44#include <config-hdr.h>
45#include <config-use-surface-color-management-api.h>
46
47#ifndef GL_RENDERER
48# define GL_RENDERER 0x1F01
49#endif
50
51#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
54#if !defined APIENTRYP && defined GL_APIENTRYP
55#define APIENTRYP GL_APIENTRYP
56#endif
57
58typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer);
59#else
60typedef void (QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer);
61#endif
62
64
66 qRegisterMetaType<KisOpenGL::XcbGLProviderProtocol>("KisOpenGL::XcbGLProviderProtocol");
67#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
68 QMetaType::registerEqualsComparator<KisOpenGL::XcbGLProviderProtocol>();
69#endif
70}
71
72namespace
73{
74 // config option, set manually by main()
75 bool g_isDebugSynchronous = false;
76
77 bool g_sanityDefaultFormatIsSet = false;
78
79 boost::optional<KisOpenGLModeProber::Result> openGLCheckResult;
80
81 bool g_needsFenceWorkaround = false;
82
83 QString g_surfaceFormatDetectionLog;
84 QString g_debugText("OpenGL Info\n **OpenGL not initialized**");
85
86 QVector<KLocalizedString> g_openglWarningStrings;
87
88 using DetectedRenderer = std::tuple<QString, QString, bool>;
89 QVector<DetectedRenderer> g_detectedRenderers;
90 KisOpenGL::OpenGLRenderers g_supportedRenderers;
91 KisOpenGL::OpenGLRenderer g_rendererPreferredByQt;
92
93 bool g_useBufferInvalidation = false;
94 PFNGLINVALIDATEBUFFERDATAPROC g_glInvalidateBufferData = nullptr;
95
96 bool g_forceDisableTextureBuffers = false;
97
98 void overrideSupportedRenderers(KisOpenGL::OpenGLRenderers supportedRenderers, KisOpenGL::OpenGLRenderer preferredByQt) {
99 g_supportedRenderers = supportedRenderers;
100 g_rendererPreferredByQt = preferredByQt;
101 }
102
103 void appendOpenGLWarningString(KLocalizedString warning)
104 {
105 g_openglWarningStrings << warning;
106 }
107
108 void overrideOpenGLWarningString(QVector<KLocalizedString> warnings)
109 {
110 g_openglWarningStrings = warnings;
111 }
112
113 void openglOnMessageLogged(const QOpenGLDebugMessage& debugMessage) {
114 qDebug() << "OpenGL:" << debugMessage;
115 }
116
117 KisOpenGL::OpenGLRenderer getRendererFromProbeResult(KisOpenGLModeProber::Result info) {
118
120
121 if (info.isOpenGLES()) {
122 const QString rendererString = info.rendererString().toLower();
123
124 if (rendererString.contains("basic render driver") ||
125 rendererString.contains("software")) {
126
128 } else {
130 }
131 }
132
133 return result;
134 }
135}
136
138{
139 if (openGLCheckResult) return;
140
141 KIS_SAFE_ASSERT_RECOVER_NOOP(g_sanityDefaultFormatIsSet);
142
144 config.format = QSurfaceFormat::defaultFormat();
145
146 openGLCheckResult =
148
149#ifdef Q_OS_WIN
150
151 if (!qEnvironmentVariableIsSet("KRITA_UNLOCK_TEXTURE_BUFFERS") &&
152 openGLCheckResult->rendererString().toUpper().contains("ANGLE")) {
153
154 // Angle should always be openGLES...
156
168 g_forceDisableTextureBuffers = true;
169 appendOpenGLWarningString(
170 ki18n("Texture buffers are explicitly disabled on ANGLE renderer due "
171 "to performance issues."));
172 }
173#endif
174
175
176 g_debugText.clear();
177 QDebug debugOut(&g_debugText);
178 debugOut << "OpenGL Info\n";
179
180 if (openGLCheckResult) {
181 debugOut << "\n Qt Platform Name: " << QGuiApplication::platformName();
182 if (openGLCheckResult->xcbGlProviderProtocol()) {
183 debugOut << "\n Qt XCB GL integration plugin: "
184 << (*openGLCheckResult->xcbGlProviderProtocol() == KisOpenGL::XCB_EGL ? "xcb_egl" : "xcb_glx");
185 }
186 debugOut << "\n Vendor: " << openGLCheckResult->vendorString();
187 debugOut << "\n Renderer: " << openGLCheckResult->rendererString();
188 debugOut << "\n Driver version: " << openGLCheckResult->driverVersionString();
189 debugOut << "\n Shading language: " << openGLCheckResult->shadingLanguageString();
190 debugOut << "\n Requested format: " << QSurfaceFormat::defaultFormat();
191 debugOut << "\n Current format: " << openGLCheckResult->format();
192 {
193 QDebugStateSaver saver(debugOut);
194 debugOut.nospace() << "\n GL version: " << openGLCheckResult->glMajorVersion() << "."
195 << openGLCheckResult->glMinorVersion();
196 }
197 debugOut << "\n Supports deprecated functions" << openGLCheckResult->supportsDeprecatedFunctions();
198 debugOut << "\n Is OpenGL ES:" << openGLCheckResult->isOpenGLES();
199 debugOut << "\n supportsBufferMapping:" << openGLCheckResult->supportsBufferMapping();
200 debugOut << "\n supportsBufferInvalidation:" << openGLCheckResult->supportsBufferInvalidation();
201 debugOut << "\n forceDisableTextureBuffers:" << g_forceDisableTextureBuffers;
202 debugOut << "\n Extensions:";
203 {
204 QDebugStateSaver saver(debugOut);
205 Q_FOREACH (const QByteArray &i, openGLCheckResult->extensions()) {
206 debugOut.noquote() << "\n " << QString::fromLatin1(i);
207 }
208 }
209 }
210
211 debugOut << "\n\nQPA OpenGL Detection Info";
212 debugOut << "\n supportsDesktopGL:" << bool(g_supportedRenderers & RendererDesktopGL);
213#ifdef Q_OS_WIN
214 debugOut << "\n supportsAngleD3D11:" << bool(g_supportedRenderers & RendererOpenGLES);
215 debugOut << "\n isQtPreferAngle:" << bool(g_rendererPreferredByQt == RendererOpenGLES);
216#else
217 debugOut << "\n supportsOpenGLES:" << bool(g_supportedRenderers & RendererOpenGLES);
218 debugOut << "\n isQtPreferOpenGLES:" << bool(g_rendererPreferredByQt == RendererOpenGLES);
219#endif
220 debugOut << "\n Detected renderers:";
221 {
222 QDebugStateSaver saver(debugOut);
223 Q_FOREACH (const DetectedRenderer &x, g_detectedRenderers) {
224 debugOut.noquote().nospace() << "\n " << (std::get<2>(x) ? "(Supported)" : "(Unsupported)") << " "
225 << std::get<0>(x) << " (" << std::get<1>(x) << ") ";
226 }
227 }
228
229// debugOut << "\n== log ==\n";
230// debugOut.noquote();
231// debugOut << g_surfaceFormatDetectionLog;
232// debugOut.resetFormat();
233// debugOut << "\n== end log ==";
234
235 dbgOpenGL.noquote().nospace() << g_debugText;
236 KisUsageLogger::writeSysInfo(g_debugText);
237
238 if (!openGLCheckResult) {
239 return;
240 }
241
242
243 // Check if we have a bugged driver that needs fence workaround
244 bool isOnX11 = false;
245#ifdef HAVE_X11
246 isOnX11 = true;
247#endif
248
249 KisConfig cfg(true);
250
251 g_useBufferInvalidation = cfg.readEntry("useBufferInvalidation", false);
252 KisUsageLogger::writeSysInfo(QString("\nuseBufferInvalidation (config option): %1\n").arg(g_useBufferInvalidation ? "true" : "false"));
253
254 if ((isOnX11 && openGLCheckResult->rendererString().startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) {
255 g_needsFenceWorkaround = true;
256 }
257
271 const qreal devicePixelRatio = QGuiApplication::primaryScreen()->devicePixelRatio();
272 const QSize screenSize = QGuiApplication::primaryScreen()->size() * devicePixelRatio;
273 const int minCacheSize = 20 * 1024;
274
275 // reserve space for at least 4 textures
276 const int cacheSize = 2048 + 5 * 4 * screenSize.width() * screenSize.height() / 1024; // KiB
277
278 QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize));
279 }
280}
281
282void KisOpenGL::initializeContext(QOpenGLContext *ctx)
283{
284 KisConfig cfg(true);
285 initialize();
286
287 const bool isDebugEnabled = ctx->format().testOption(QSurfaceFormat::DebugContext);
288
289 dbgUI << "OpenGL: Opening new context";
290 if (isDebugEnabled) {
291 // Passing ctx for ownership management only, not specifying context.
292 // QOpenGLDebugLogger only function on the current active context.
293 // FIXME: Do we need to make sure ctx is the active context?
294 QOpenGLDebugLogger* openglLogger = new QOpenGLDebugLogger(ctx);
295 if (openglLogger->initialize()) {
296 qDebug() << "QOpenGLDebugLogger is initialized. Check whether you get a message below.";
297 QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged);
298 openglLogger->startLogging(g_isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging);
299 openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("QOpenGLDebugLogger is logging.")));
300 } else {
301 qDebug() << "QOpenGLDebugLogger cannot be initialized.";
302 delete openglLogger;
303 }
304 }
305
306 // Double check we were given the version we requested
307 QSurfaceFormat format = ctx->format();
308 QOpenGLFunctions *f = ctx->functions();
309 f->initializeOpenGLFunctions();
310
311 if (openGLCheckResult->supportsBufferInvalidation()) {
312 QOpenGLContext *ctx = QOpenGLContext::currentContext();
313 g_glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC)ctx->getProcAddress("glInvalidateBufferData");
314 }
315
316 QFile log(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/krita-opengl.txt");
317 log.open(QFile::WriteOnly);
318 QString vendor((const char*)f->glGetString(GL_VENDOR));
319 log.write(vendor.toLatin1());
320 log.write(", ");
321 log.write(openGLCheckResult->rendererString().toLatin1());
322 log.write(", ");
323 QString version((const char*)f->glGetString(GL_VERSION));
324 log.write(version.toLatin1());
325 log.close();
326}
327
329{
330 initialize();
331 return g_debugText;
332}
333
335 QStringList strings;
336 Q_FOREACH (const KLocalizedString &item, g_openglWarningStrings) {
337 strings << item.toString();
338 }
339 return strings;
340}
341
343{
344 initialize();
345 if (openGLCheckResult) {
346 return openGLCheckResult->driverVersionString();
347 }
348 return QString();
349}
350
351// Check whether we can allow LoD on OpenGL3 without triggering
352// all of the other 3.2 functionality.
354{
355 initialize();
356 return openGLCheckResult && openGLCheckResult->supportsLoD();
357}
358
360{
361 initialize();
362 return openGLCheckResult && openGLCheckResult->hasOpenGL3();
363}
364
366{
367 initialize();
368 return openGLCheckResult && openGLCheckResult->supportsVAO();
369}
370
372{
373 initialize();
374 return openGLCheckResult && openGLCheckResult->isOpenGLES();
375}
376
378{
379 initialize();
380 return openGLCheckResult && openGLCheckResult->supportsFenceSync();
381}
382
384{
385 initialize();
386 return openGLCheckResult && openGLCheckResult->supportsBufferMapping();
387}
388
390{
391 initialize();
392 return g_forceDisableTextureBuffers;
393}
394
395bool KisOpenGL::shouldUseTextureBuffers(bool userPreference)
396{
397 initialize();
398 return !g_forceDisableTextureBuffers && userPreference;
399}
400
402{
403 initialize();
404 return g_useBufferInvalidation &&
405 openGLCheckResult && openGLCheckResult->supportsBufferInvalidation();
406}
407
409{
410 initialize();
411 return openGLCheckResult && openGLCheckResult->supportsFBO();
412}
413
414std::optional<KisOpenGL::XcbGLProviderProtocol> KisOpenGL::xcbGlProviderProtocol()
415{
416 initialize();
417 return openGLCheckResult ? openGLCheckResult->xcbGlProviderProtocol() : std::nullopt;
418}
419
421{
422 initialize();
423 return g_needsFenceWorkaround;
424}
425
430
432{
433 g_isDebugSynchronous = value;
434}
435
437{
438 g_glInvalidateBufferData(buffer);
439}
440
442{
443 if (!openGLCheckResult) return RendererAuto;
444 return getRendererFromProbeResult(*openGLCheckResult);
445}
446
448{
449 return g_rendererPreferredByQt;
450}
451
452KisOpenGL::OpenGLRenderers KisOpenGL::getSupportedOpenGLRenderers()
453{
454 return g_supportedRenderers;
455}
456
458{
459 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
460 QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
461 return convertConfigToOpenGLRenderer(kritarc.value("OpenGLRenderer", "auto").toString());
462}
463
465{
466 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
467 QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
468 kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer));
469}
470
472{
473 switch (renderer) {
474 case RendererNone:
475 return QStringLiteral("none");
476 case RendererSoftware:
477 return QStringLiteral("software");
479 return QStringLiteral("desktop");
480 case RendererOpenGLES:
481 return QStringLiteral("angle");
482 default:
483 return QStringLiteral("auto");
484 }
485}
486
488{
489 if (renderer == "desktop") {
490 return RendererDesktopGL;
491 } else if (renderer == "angle") {
492 return RendererOpenGLES;
493 } else if (renderer == "software") {
494 return RendererSoftware;
495 } else if (renderer == "none") {
496 return RendererNone;
497 } else {
498 return RendererAuto;
499 }
500}
501
503{
505
506 if (format.renderableType() == QSurfaceFormat::OpenGLES &&
508
509 result = RendererSoftware;
510
511 } else if (format.renderableType() == QSurfaceFormat::OpenGLES) {
512 // If D3D11, D3D9?, Default (which is after probing, if selected)
513 // or the system specifies QT_OPENGL_ES_2
514 result = RendererOpenGLES;
515 } else if (format.renderableType() == QSurfaceFormat::OpenGL) {
516 result = RendererDesktopGL;
517 } else if (format.renderableType() == QSurfaceFormat::DefaultRenderableType &&
519 // noop
520 } else {
521 qWarning() << "WARNING: unsupported combination of OpenGL renderer" << ppVar(format.renderableType()) << ppVar(angleRenderer);
522 }
523
524 return result;
525}
526
527namespace {
528
529typedef std::pair<QSurfaceFormat::RenderableType, KisOpenGL::AngleRenderer> RendererInfo;
530
531RendererInfo getRendererInfo(KisOpenGL::OpenGLRenderer renderer)
532{
533 RendererInfo info = {QSurfaceFormat::DefaultRenderableType,
535
536 switch (renderer) {
538 info = {QSurfaceFormat::DefaultRenderableType, KisOpenGL::AngleRendererDefault};
539 break;
541 break;
543 info = {QSurfaceFormat::OpenGL, KisOpenGL::AngleRendererDefault};
544 break;
546 info = {QSurfaceFormat::OpenGLES, KisOpenGL::AngleRendererD3d11};
547 break;
549 info = {QSurfaceFormat::OpenGLES, KisOpenGL::AngleRendererD3d11Warp};
550 break;
551 }
552
553 return info;
554}
555
556QOpenGLContext::OpenGLModuleType determineOpenGLImplementation(const RendererInfo &info)
557{
558 switch (info.first) {
559 case QSurfaceFormat::OpenGLES:
560#if defined(Q_OS_WINDOWS)
561 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/plugins/platforms/windows/qwindowsintegration.cpp#L425
562 switch (info.second) {
566 return QOpenGLContext::LibGLES;
567 // Assume system OpenGL -- QOpenGLStaticContext
568 default:
569 break;
570 }
571 return QOpenGLContext::LibGL;
572#else
573 // At least Manjaro Qt can perfectly call up a ES context,
574 // while Qt says via macros that it doesn't support that...
575 return QOpenGLContext::LibGLES;
576#endif
577 case QSurfaceFormat::DefaultRenderableType:
578#ifdef Q_OS_WIN
579 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/plugins/platforms/windows/qwindowsglcontext.cpp#L1117
580 return QOpenGLContext::LibGL;
581#else
582 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp#L246
583#if defined(QT_OPENGL_ES_2)
584 return QOpenGLContext::LibGLES;
585#else
586 return QOpenGLContext::LibGL;
587#endif
588#endif
589 case QSurfaceFormat::OpenGL:
590 default:
591 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/plugins/platforms/windows/qwindowsglcontext.cpp#L1117
592 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(info.first != QSurfaceFormat::OpenVG, QOpenGLContext::LibGL);
593 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/gui/kernel/qplatformintegration.cpp#L547
594 return QOpenGLContext::LibGL;
595 };
596}
597
598KisOpenGL::RendererConfig generateSurfaceConfig(KisOpenGL::OpenGLRenderer renderer,
599 std::pair<KisSurfaceColorSpaceWrapper, int> rootSurfaceFormat,
600 bool debugContext,
601 bool inhibitCompatibilityProfile)
602{
603 RendererInfo info = getRendererInfo(renderer);
604
606 config.angleRenderer = info.second;
607
608
609 dbgOpenGL << "Requesting configuration for" << info.first << info.second;
610 dbgOpenGL << "Requesting root surface format" << rootSurfaceFormat;
611
612 QSurfaceFormat &format = config.format;
613 const auto openGLModuleType = determineOpenGLImplementation(info);
614 switch (openGLModuleType) {
615 case QOpenGLContext::LibGL:
616#if defined Q_OS_MACOS
617 format.setVersion(4, 1);
618 format.setProfile(QSurfaceFormat::CoreProfile);
619 Q_UNUSED(inhibitCompatibilityProfile);
620#else
621 // If asked for 3.0 "Core", Qt will instead request
622 // an OpenGL ES context.
623 // NVIDIA's GLX implementation will not allow that and results
624 // in a forced process exit through X11 (NVIDIA bug #3959482).
625 format.setVersion(3, 3);
626 // Make sure to request a Compatibility profile to have NVIDIA
627 // return the maximum supported GL version.
628
629 if (!inhibitCompatibilityProfile) {
630 format.setProfile(QSurfaceFormat::CompatibilityProfile);
631 }
632#ifdef Q_OS_WIN
633 // Some parts of Qt seems to require deprecated functions. On Windows
634 // with the Intel Graphics driver, things like canvas decorations and
635 // the Touch Docker does not render without this option.
636 format.setOptions(QSurfaceFormat::DeprecatedFunctions);
637#endif
638#endif
639 break;
640 case QOpenGLContext::LibGLES:
641 format.setVersion(3, 0);
642 format.setProfile(QSurfaceFormat::NoProfile);
643 break;
644 }
645
646 dbgOpenGL << "Version selected:" << openGLModuleType << format.version();
647
648 format.setDepthBufferSize(24);
649 format.setStencilBufferSize(8);
650
651 KisOpenGLModeProber::initSurfaceFormatFromConfig(rootSurfaceFormat, &format);
652
653 format.setRenderableType(info.first);
654 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
655 format.setSwapInterval(0); // Disable vertical refresh syncing
657 // With repaint debugging, vsync is preferred so that all update regions
658 // can be visible.
659 format.setSwapInterval(1);
660 }
661 if (debugContext) {
662 format.setOption(QSurfaceFormat::DebugContext, true);
663 }
664
665 return config;
666}
667
668bool isOpenGLRendererBlacklisted(const QString &rendererString,
669 const QString &driverVersionString,
670 QVector<KLocalizedString> *warningMessage)
671{
672 bool isBlacklisted = false;
673#ifndef Q_OS_WIN
674 Q_UNUSED(rendererString);
675 Q_UNUSED(driverVersionString);
676 Q_UNUSED(warningMessage);
677#else
678 // Special blacklisting of OpenGL/ANGLE is tracked on:
679 // https://phabricator.kde.org/T7411
680
681 // HACK: Specifically detect for Intel driver build number
682 // See https://www.intel.com/content/www/us/en/support/articles/000005654/graphics.html
683 if (rendererString.startsWith("Intel")) {
684 KLocalizedString knownBadIntelWarning = ki18n("The Intel graphics driver in use is known to have issues with OpenGL.");
685 KLocalizedString grossIntelWarning = ki18n(
686 "Intel graphics drivers tend to have issues with OpenGL so ANGLE will be used by default. "
687 "You may manually switch to OpenGL but it is not guaranteed to work properly."
688 );
689 QRegularExpression regex("\\b\\d{1,2}\\.\\d{1,2}\\.(\\d{1,3})\\.(\\d{4})\\b");
690 QRegularExpressionMatch match = regex.match(driverVersionString);
691 if (match.hasMatch()) {
692 const int thirdPart = match.captured(1).toInt();
693 const int fourthPart = match.captured(2).toInt();
694 int driverBuild;
695 if (thirdPart >= 100) {
696 driverBuild = thirdPart * 10000 + fourthPart;
697 } else {
698 driverBuild = fourthPart;
699 }
700 qDebug() << "Detected Intel driver build number as" << driverBuild;
701 if (driverBuild > 4636 && driverBuild < 4729) {
702 // Make ANGLE the preferred renderer for Intel driver versions
703 // between build 4636 and 4729 (exclusive) due to an UI offset bug.
704 // See https://communities.intel.com/thread/116003
705 // (Build 4636 is known to work from some test results)
706 qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer";
707 isBlacklisted = true;
708 *warningMessage << knownBadIntelWarning;
709 } else if (driverBuild == 4358) {
710 // There are several reports on a bug where the canvas is not being
711 // updated properly which has debug info pointing to this build.
712 qDebug() << "Detected Intel driver build 4358, making ANGLE the preferred renderer";
713 isBlacklisted = true;
714 *warningMessage << knownBadIntelWarning;
715 }
716 } else {
717 // In case Intel changed the driver version format to something that
718 // we don't understand, we still select ANGLE.
719 qDebug() << "Detected Intel driver with unknown version format, making ANGLE the preferred renderer";
720 isBlacklisted = true;
721 *warningMessage << grossIntelWarning;
722 }
723 }
724#endif
725 return isBlacklisted;
726}
727
728boost::optional<bool> orderPreference(bool lhs, bool rhs)
729{
730 if (lhs == rhs) return boost::none;
731 if (lhs && !rhs) return true;
732 if (!lhs && rhs) return false;
733 return false;
734}
735
736#define ORDER_BY(lhs, rhs) if (auto res = orderPreference((lhs), (rhs))) { return *res; }
737
738class FormatPositionLess
739{
740public:
741
742 FormatPositionLess()
743 {
744 }
745
746 bool operator()(const KisOpenGL::RendererConfig &lhs, const KisOpenGL::RendererConfig &rhs) const {
748 if (m_preferredRendererByUser != KisOpenGL::RendererSoftware) {
749 ORDER_BY(!isFallbackOnly(lhs.rendererId()), !isFallbackOnly(rhs.rendererId()));
750 }
751
752 ORDER_BY(isPreferredColorSpace(lhs.format),
753 isPreferredColorSpace(rhs.format));
754
755 if (doPreferHDR()) {
756 ORDER_BY(isHDRFormat(lhs.format), isHDRFormat(rhs.format));
757 } else {
758 ORDER_BY(!isHDRFormat(lhs.format), !isHDRFormat(rhs.format));
759 }
760
761 if (m_preferredRendererByUser != KisOpenGL::RendererAuto) {
762 ORDER_BY(lhs.rendererId() == m_preferredRendererByUser,
763 rhs.rendererId() == m_preferredRendererByUser);
764 }
765
766 ORDER_BY(!isBlacklisted(lhs.rendererId()), !isBlacklisted(rhs.rendererId()));
767
768 if (doPreferHDR() &&
769 m_preferredRendererByHDR != KisOpenGL::RendererAuto) {
770
771 ORDER_BY(lhs.rendererId() == m_preferredRendererByHDR,
772 rhs.rendererId() == m_preferredRendererByHDR);
773
774 }
775
776 KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredRendererByQt != KisOpenGL::RendererAuto);
777
778 ORDER_BY(lhs.rendererId() == m_preferredRendererByQt,
779 rhs.rendererId() == m_preferredRendererByQt);
780
781 ORDER_BY(lhs.format.redBufferSize() == m_userPreferredBitDepth,
782 rhs.format.redBufferSize() == m_userPreferredBitDepth);
783
784 return false;
785 }
786
787
788public:
789 void setPreferredColorSpace(const KisSurfaceColorSpaceWrapper &preferredColorSpace) {
790 m_preferredColorSpace = preferredColorSpace;
791 }
792
793 void setPreferredRendererByQt(const KisOpenGL::OpenGLRenderer &preferredRendererByQt) {
794 m_preferredRendererByQt = preferredRendererByQt;
795 }
796
797 void setPreferredRendererByUser(const KisOpenGL::OpenGLRenderer &preferredRendererByUser) {
798 m_preferredRendererByUser = preferredRendererByUser;
799 }
800
801 void setPreferredRendererByHDR(const KisOpenGL::OpenGLRenderer &preferredRendererByHDR) {
802 m_preferredRendererByHDR = preferredRendererByHDR;
803 }
804
805 void setOpenGLBlacklisted(bool openGLBlacklisted) {
806 m_openGLBlacklisted = openGLBlacklisted;
807 }
808
809 void setOpenGLESBlacklisted(bool openGLESBlacklisted) {
810 m_openGLESBlacklisted = openGLESBlacklisted;
811 }
812
813 void setUserPreferredBitDepth(int value) {
814 m_userPreferredBitDepth = value;
815 }
816
817 bool isOpenGLBlacklisted() const {
818 return m_openGLBlacklisted;
819 }
820
821 bool isOpenGLESBlacklisted() const {
822 return m_openGLESBlacklisted;
823 }
824
825 KisSurfaceColorSpaceWrapper preferredColorSpace() const {
826 return m_preferredColorSpace;
827 }
828
829 KisOpenGL::OpenGLRenderer preferredRendererByUser() const {
830 return m_preferredRendererByUser;
831 }
832
833 int userPreferredBitDepth() const {
834 return m_userPreferredBitDepth;
835 }
836
837private:
838 bool isHDRFormat(const QSurfaceFormat &f) const {
839#ifdef HAVE_HDR
840 return f.colorSpace() == KisSurfaceColorSpaceWrapper::makeBt2020PQColorSpace() ||
842#else
843 Q_UNUSED(f);
844 return false;
845#endif
846 }
847
848 bool isFallbackOnly(KisOpenGL::OpenGLRenderer r) const {
849 return r == KisOpenGL::RendererSoftware;
850 }
851
852 bool isBlacklisted(KisOpenGL::OpenGLRenderer r) const {
858
859 return (r == KisOpenGL::RendererDesktopGL && m_openGLBlacklisted) ||
860 (r == KisOpenGL::RendererOpenGLES && m_openGLESBlacklisted) ||
861 (r == KisOpenGL::RendererSoftware && m_openGLESBlacklisted);
862 }
863
864 bool doPreferHDR() const {
865#ifdef HAVE_HDR
866 return m_preferredColorSpace == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace ||
867 m_preferredColorSpace == KisSurfaceColorSpaceWrapper::scRGBColorSpace;
868#else
869 return false;
870#endif
871 }
872
873 bool isPreferredColorSpace(const QSurfaceFormat & surfaceFormat) const {
875 m_preferredColorSpace,
876 KisSurfaceColorSpaceWrapper::fromQtColorSpace(surfaceFormat.colorSpace()));
877 }
878
879private:
880 KisSurfaceColorSpaceWrapper m_preferredColorSpace;
882 KisOpenGL::OpenGLRenderer m_preferredRendererByUser = KisOpenGL::RendererAuto;
883 KisOpenGL::OpenGLRenderer m_preferredRendererByHDR = KisOpenGL::RendererAuto;
884 bool m_openGLBlacklisted = false;
885 bool m_openGLESBlacklisted = false;
886 int m_userPreferredBitDepth = 8;
887};
888
889struct DetectionDebug : public QDebug
890{
891 DetectionDebug(QString *string)
892 : QDebug(string),
893 m_string(string),
894 m_originalSize(string->size())
895 {}
896 ~DetectionDebug() { dbgOpenGL << m_string->right(m_string->size() - m_originalSize); *this << Qt::endl; }
897
898 QString *m_string;
899 int m_originalSize;
900};
901}
902
903#define dbgDetection() DetectionDebug(&g_surfaceFormatDetectionLog)
904
906 KisConfig::RootSurfaceFormat preferredRootSurfaceFormat,
907 KisConfig::CanvasSurfaceBitDepthMode preferredCanvasSurfaceBitMode,
908 bool enableDebug)
909{
910 QVector<KLocalizedString> warningMessages;
911
912 using Info = boost::optional<KisOpenGLModeProber::Result>;
913
914 QHash<OpenGLRenderer, Info> renderersToTest;
915#ifndef Q_OS_ANDROID
916 renderersToTest.insert(RendererDesktopGL, Info());
917#endif
918 renderersToTest.insert(RendererOpenGLES, Info());
919
920#ifdef Q_OS_WIN
921 renderersToTest.insert(RendererSoftware, Info());
922#endif
923
924 auto makeDefaultSurfaceFormatPair = [] () -> std::pair<KisSurfaceColorSpaceWrapper, int> {
926 };
927
928#if defined HAVE_HDR
929 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
930 {
931 // TODO: check if we can use real sRGB space here
935 });
936#elif KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
937 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
938 {
941 });
942#else
943 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
944 {
946 });
947#endif
948
949 bool shouldInhibitCompatibilityProfile = false;
950 KisOpenGL::RendererConfig defaultConfig = generateSurfaceConfig(KisOpenGL::RendererAuto,
951 makeDefaultSurfaceFormatPair(),
952 false,
953 shouldInhibitCompatibilityProfile);
954 Info info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
955
956#ifndef Q_OS_MACOS
957 // When RendererAuto is active, Qt may perform insane things internally,
958 // e.g. switch from OpenGL to OpenGLES automatically. And the presence of
959 // the compatibility profile flag will cause the context creation process
960 // to fail.
961 //
962 // So, here we request an explicit API again to avoid Qt making decisions
963 // for us.
964
965 if (!info) {
966 dbgOpenGL << "Failed to probe default Qt's openGL format.. Trying DesktopGL with compatibility enabled...";
967 shouldInhibitCompatibilityProfile = false;
968 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererDesktopGL,
969 makeDefaultSurfaceFormatPair(),
970 false,
971 shouldInhibitCompatibilityProfile);
972 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
973 }
974
975 if (!info) {
976 dbgOpenGL << "Failed again.. Trying DesktopGL with compatibility disabled...";
977 shouldInhibitCompatibilityProfile = true;
978 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererDesktopGL,
979 makeDefaultSurfaceFormatPair(),
980 false,
981 shouldInhibitCompatibilityProfile);
982 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
983 }
984
985 if (!info) {
986 dbgOpenGL << "Failed again.. Trying OpenGLES...";
987 shouldInhibitCompatibilityProfile = false;
988 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererOpenGLES,
989 makeDefaultSurfaceFormatPair(),
990 false,
991 true);
992 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
993 }
994
995#endif /* Q_OS_MACOS */
996
997#ifdef Q_OS_WIN
998 if (!info) {
999 // try software rasterizer (WARP)
1000 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererSoftware,
1001 makeDefaultSurfaceFormatPair(),
1002 false,
1003 shouldInhibitCompatibilityProfile);
1004 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
1005
1006 if (!info) {
1007 renderersToTest.remove(RendererSoftware);
1008 }
1009 }
1010#endif
1011
1012 if (!info) {
1013 dbgOpenGL << "Failed to probe default openGL format! No openGL support will be available in Krita";
1015 }
1016
1017 const OpenGLRenderer defaultRenderer = getRendererFromProbeResult(*info);
1018
1022#ifdef Q_OS_WIN
1023 const OpenGLRenderer preferredAutoRenderer = RendererOpenGLES;
1024#else
1025 const OpenGLRenderer preferredAutoRenderer = defaultRenderer;
1026#endif
1027
1028 OpenGLRenderers supportedRenderers = RendererNone;
1029
1030 FormatPositionLess compareOp;
1031 compareOp.setPreferredRendererByQt(preferredAutoRenderer);
1032
1033#ifdef HAVE_HDR
1034 compareOp.setPreferredColorSpace(
1038#else
1039 Q_UNUSED(preferredRootSurfaceFormat);
1040 compareOp.setPreferredColorSpace(KisSurfaceColorSpaceWrapper::sRGBColorSpace);
1041#endif
1042
1043#ifdef Q_OS_WIN
1044 compareOp.setPreferredRendererByHDR(KisOpenGL::RendererOpenGLES);
1045#endif
1046 compareOp.setPreferredRendererByUser(preferredRenderer);
1047 compareOp.setOpenGLESBlacklisted(false); // We cannot blacklist ES drivers atm
1048
1049#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1050 // 10-bit is the default, 8-bit is set explicitly by the user
1051 compareOp.setUserPreferredBitDepth(preferredCanvasSurfaceBitMode == KisConfig::CanvasSurfaceBitDepthMode::Depth8Bit ? 8 : 10);
1052#else
1053 Q_UNUSED(preferredCanvasSurfaceBitMode)
1054#endif
1055
1056 renderersToTest[defaultRenderer] = info;
1057
1058 for (auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) {
1059 Info info = it.value();
1060
1061 if (!info) {
1062 const RendererConfig config = generateSurfaceConfig(it.key(), makeDefaultSurfaceFormatPair(), false, shouldInhibitCompatibilityProfile);
1063 dbgOpenGL << "Probing" << it.key() << "from default:" << config.format << config.angleRenderer
1064 << config.rendererId();
1066 *it = info;
1067 } else {
1068 dbgOpenGL << "Already probed:" << it.key();
1069 }
1070
1071 compareOp.setOpenGLBlacklisted(
1072 !info ||
1073 isOpenGLRendererBlacklisted(info->rendererString(),
1074 info->driverVersionString(),
1075 &warningMessages));
1076
1077 if (info) {
1078 dbgOpenGL << "Result:" << info->rendererString() << info->driverVersionString()
1079 << info->isSupportedVersion();
1080 }
1081
1082 if (info) {
1083 g_detectedRenderers << std::make_tuple(info->rendererString(),
1084 info->driverVersionString(),
1085 info->isSupportedVersion());
1086 }
1087
1088 if (info && info->isSupportedVersion()) {
1089 supportedRenderers |= it.key();
1090 }
1091 }
1092
1093 OpenGLRenderer preferredByQt = preferredAutoRenderer;
1094
1095 if (preferredByQt == RendererDesktopGL &&
1096 supportedRenderers & RendererDesktopGL &&
1097 compareOp.isOpenGLBlacklisted()) {
1098
1099 preferredByQt = RendererOpenGLES;
1100
1101 } else if (preferredByQt == RendererOpenGLES &&
1102 supportedRenderers & RendererOpenGLES &&
1103 compareOp.isOpenGLESBlacklisted()) {
1104
1105 preferredByQt = RendererDesktopGL;
1106 }
1107
1108 QVector<RendererConfig> preferredConfigs;
1109 for (auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) {
1110 // if default mode of the renderer doesn't work, then custom won't either
1111 if (!it.value()) continue;
1112
1113 Q_FOREACH (const auto &formatPair, formatSymbolPairs) {
1114 preferredConfigs << generateSurfaceConfig(it.key(), formatPair, enableDebug, shouldInhibitCompatibilityProfile);
1115 }
1116 }
1117
1118 std::stable_sort(preferredConfigs.begin(), preferredConfigs.end(), compareOp);
1119
1120 dbgDetection() << "Supported renderers:" << supportedRenderers;
1121
1122 dbgDetection() << "Surface format preference list:";
1123 Q_FOREACH (const KisOpenGL::RendererConfig &config, preferredConfigs) {
1124 dbgDetection() << "*" << config.format;
1125 dbgDetection() << " " << config.rendererId();
1126 }
1127
1128 KisOpenGL::RendererConfig resultConfig = defaultConfig;
1129
1130 if (preferredRenderer != RendererNone) {
1131 Q_FOREACH (const KisOpenGL::RendererConfig &config, preferredConfigs) {
1132 dbgDetection() <<"Probing format..." << config.format.colorSpace() << config.rendererId();
1133 Info info = KisOpenGLModeProber::instance()->probeFormat(config);
1134
1135 if (info && info->isSupportedVersion()) {
1136
1137#ifdef Q_OS_WIN
1138 // HACK: Block ANGLE with Direct3D9
1139 // Direct3D9 does not give OpenGL ES 3.0
1140 // Some versions of ANGLE returns OpenGL version 3.0 incorrectly
1141
1142 if (info->isUsingAngle() &&
1143 info->rendererString().contains("Direct3D9", Qt::CaseInsensitive)) {
1144
1145 dbgDetection() << "Skipping Direct3D 9 Angle implementation, it shouldn't have happened.";
1146
1147 continue;
1148 }
1149#endif
1150
1151 dbgDetection() << "Found format:" << config.format;
1152 dbgDetection() << " " << config.rendererId();
1153
1154 resultConfig = config;
1155 break;
1156 }
1157 }
1158
1159 {
1160 const bool colorSpaceIsCorrect =
1161 KisOpenGLModeProber::fuzzyCompareColorSpaces(compareOp.preferredColorSpace(),
1162 KisSurfaceColorSpaceWrapper::fromQtColorSpace(resultConfig.format.colorSpace()));
1163
1164 const bool rendererIsCorrect =
1165 compareOp.preferredRendererByUser() == KisOpenGL::RendererAuto ||
1166 compareOp.preferredRendererByUser() == resultConfig.rendererId();
1167
1168 if (!rendererIsCorrect && colorSpaceIsCorrect) {
1169 warningMessages << ki18n("Preferred renderer doesn't support requested surface format. Another renderer has been selected.");
1170 } else if (!colorSpaceIsCorrect) {
1171 warningMessages << ki18n("Preferred output format is not supported by available renderers");
1172 }
1173
1174 }
1175 } else {
1176 resultConfig.format = QSurfaceFormat();
1177 resultConfig.angleRenderer = AngleRendererDefault;
1178 }
1179
1180 overrideSupportedRenderers(supportedRenderers, preferredByQt);
1181 overrideOpenGLWarningString(warningMessages);
1182
1183 return resultConfig;
1184}
1185
1187{
1188 KIS_SAFE_ASSERT_RECOVER_NOOP(!g_sanityDefaultFormatIsSet);
1189
1190 g_sanityDefaultFormatIsSet = true;
1191 QSurfaceFormat::setDefaultFormat(config.format);
1192
1193 if (config.format.renderableType() == QSurfaceFormat::OpenGLES) {
1194 QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true);
1195#ifdef Q_OS_WIN
1196 if (!qEnvironmentVariableIsSet("QT_ANGLE_PLATFORM")) {
1197 // Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP
1198 // might get weird crashes atm.
1199 qputenv("QT_ANGLE_PLATFORM", KisOpenGLModeProber::angleRendererToString(config.angleRenderer).toLatin1());
1200 }
1201#endif
1202 } else if (config.format.renderableType() == QSurfaceFormat::OpenGL) {
1203 QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true);
1204 }
1205}
1206
1208{
1209 return openGLCheckResult->isSupportedVersion();
1210}
float value(const T *src, size_t ch)
unsigned int uint
@ ASSISTANTS_DRAW_MODE_LARGE_PIXMAP_CACHE
Definition kis_config.h:807
bool forceOpenGLFenceWorkaround(bool defaultValue=false) const
T readEntry(const QString &name, const T &defaultValue=T())
Definition kis_config.h:835
CanvasSurfaceBitDepthMode
Definition kis_config.h:189
AssistantsDrawMode assistantsDrawMode(bool defaultValue=false) const
static KisOpenGLModeProber * instance()
boost::optional< Result > probeFormat(const KisOpenGL::RendererConfig &rendererConfig, bool adjustGlobalState=true)
static void initSurfaceFormatFromConfig(std::pair< KisSurfaceColorSpaceWrapper, int > rootSurfaceFormat, QSurfaceFormat *format)
static QString angleRendererToString(KisOpenGL::AngleRenderer renderer)
static bool fuzzyCompareColorSpaces(const KisSurfaceColorSpaceWrapper &lhs, const KisSurfaceColorSpaceWrapper &rhs)
static bool useFBOForToolOutlineRendering()
supportsRenderToFBO
static OpenGLRenderer getCurrentOpenGLRenderer()
static bool hasOpenGLES()
static RendererConfig selectSurfaceConfig(KisOpenGL::OpenGLRenderer preferredRenderer, KisConfig::RootSurfaceFormat preferredRootSurfaceFormat, KisConfig::CanvasSurfaceBitDepthMode preferredCanvasSurfaceBitMode, bool enableDebug)
static bool shouldUseTextureBuffers(bool userPreference)
static bool needsFenceWorkaround()
static void testingInitializeDefaultSurfaceFormat()
static OpenGLRenderers getSupportedOpenGLRenderers()
static bool useTextureBufferInvalidation()
@ RendererSoftware
Definition kis_opengl.h:45
@ RendererDesktopGL
Definition kis_opengl.h:43
@ RendererOpenGLES
Definition kis_opengl.h:44
static QStringList getOpenGLWarnings()
static void initialize()
Request OpenGL version 3.2.
static void glInvalidateBufferData(uint buffer)
static bool hasOpenGL()
Check for OpenGL.
static bool supportsLoD()
static QString currentDriver()
static void initializeContext(QOpenGLContext *ctx)
Initialize shared OpenGL context.
static OpenGLRenderer getUserPreferredOpenGLRendererConfig()
static OpenGLRenderer getQtPreferredOpenGLRenderer()
static OpenGLRenderer convertConfigToOpenGLRenderer(QString renderer)
static bool supportsFenceSync()
supportsFilter
static bool supportsBufferMapping()
static bool forceDisableTextureBuffers()
static void setDebugSynchronous(bool value)
static QString convertOpenGLRendererToConfig(OpenGLRenderer renderer)
static void setDefaultSurfaceConfig(const RendererConfig &config)
static bool supportsVAO()
static const QString & getDebugText()
static bool hasOpenGL3()
static std::optional< XcbGLProviderProtocol > xcbGlProviderProtocol()
@ AngleRendererD3d11Warp
Definition kis_opengl.h:53
@ AngleRendererD3d11
Definition kis_opengl.h:51
@ AngleRendererD3d9
Definition kis_opengl.h:52
@ AngleRendererDefault
Definition kis_opengl.h:50
static void setUserPreferredOpenGLRendererConfig(OpenGLRenderer renderer)
static constexpr KisSurfaceColorSpaceWrapper makeSCRGBColorSpace()
static KisSurfaceColorSpaceWrapper fromQtColorSpace(const QColorSpace &colorSpace)
static constexpr KisSurfaceColorSpaceWrapper makeBt2020PQColorSpace()
static void writeSysInfo(const QString &message)
Writes to the system information file and Krita log.
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define dbgUI
Definition kis_debug.h:52
#define ppVar(var)
Definition kis_debug.h:155
#define dbgOpenGL
Definition kis_debug.h:60
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
KIS_DECLARE_STATIC_INITIALIZER
#define ORDER_BY(lhs, rhs)
#define dbgDetection()
int size(const Forest< T > &forest)
Definition KisForest.h:1232
AngleRenderer angleRenderer
Definition kis_opengl.h:58
QSurfaceFormat format
Definition kis_opengl.h:57
OpenGLRenderer rendererId() const