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#else
620 // If asked for 3.0 "Core", Qt will instead request
621 // an OpenGL ES context.
622 // NVIDIA's GLX implementation will not allow that and results
623 // in a forced process exit through X11 (NVIDIA bug #3959482).
624 format.setVersion(3, 3);
625 // Make sure to request a Compatibility profile to have NVIDIA
626 // return the maximum supported GL version.
627
628 if (!inhibitCompatibilityProfile) {
629 format.setProfile(QSurfaceFormat::CompatibilityProfile);
630 }
631#ifdef Q_OS_WIN
632 // Some parts of Qt seems to require deprecated functions. On Windows
633 // with the Intel Graphics driver, things like canvas decorations and
634 // the Touch Docker does not render without this option.
635 format.setOptions(QSurfaceFormat::DeprecatedFunctions);
636#endif
637#endif
638 break;
639 case QOpenGLContext::LibGLES:
640 format.setVersion(3, 0);
641 format.setProfile(QSurfaceFormat::NoProfile);
642 break;
643 }
644
645 dbgOpenGL << "Version selected:" << openGLModuleType << format.version();
646
647 format.setDepthBufferSize(24);
648 format.setStencilBufferSize(8);
649
650 KisOpenGLModeProber::initSurfaceFormatFromConfig(rootSurfaceFormat, &format);
651
652 format.setRenderableType(info.first);
653 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
654 format.setSwapInterval(0); // Disable vertical refresh syncing
656 // With repaint debugging, vsync is preferred so that all update regions
657 // can be visible.
658 format.setSwapInterval(1);
659 }
660 if (debugContext) {
661 format.setOption(QSurfaceFormat::DebugContext, true);
662 }
663
664 return config;
665}
666
667bool isOpenGLRendererBlacklisted(const QString &rendererString,
668 const QString &driverVersionString,
669 QVector<KLocalizedString> *warningMessage)
670{
671 bool isBlacklisted = false;
672#ifndef Q_OS_WIN
673 Q_UNUSED(rendererString);
674 Q_UNUSED(driverVersionString);
675 Q_UNUSED(warningMessage);
676#else
677 // Special blacklisting of OpenGL/ANGLE is tracked on:
678 // https://phabricator.kde.org/T7411
679
680 // HACK: Specifically detect for Intel driver build number
681 // See https://www.intel.com/content/www/us/en/support/articles/000005654/graphics.html
682 if (rendererString.startsWith("Intel")) {
683 KLocalizedString knownBadIntelWarning = ki18n("The Intel graphics driver in use is known to have issues with OpenGL.");
684 KLocalizedString grossIntelWarning = ki18n(
685 "Intel graphics drivers tend to have issues with OpenGL so ANGLE will be used by default. "
686 "You may manually switch to OpenGL but it is not guaranteed to work properly."
687 );
688 QRegularExpression regex("\\b\\d{1,2}\\.\\d{1,2}\\.(\\d{1,3})\\.(\\d{4})\\b");
689 QRegularExpressionMatch match = regex.match(driverVersionString);
690 if (match.hasMatch()) {
691 const int thirdPart = match.captured(1).toInt();
692 const int fourthPart = match.captured(2).toInt();
693 int driverBuild;
694 if (thirdPart >= 100) {
695 driverBuild = thirdPart * 10000 + fourthPart;
696 } else {
697 driverBuild = fourthPart;
698 }
699 qDebug() << "Detected Intel driver build number as" << driverBuild;
700 if (driverBuild > 4636 && driverBuild < 4729) {
701 // Make ANGLE the preferred renderer for Intel driver versions
702 // between build 4636 and 4729 (exclusive) due to an UI offset bug.
703 // See https://communities.intel.com/thread/116003
704 // (Build 4636 is known to work from some test results)
705 qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer";
706 isBlacklisted = true;
707 *warningMessage << knownBadIntelWarning;
708 } else if (driverBuild == 4358) {
709 // There are several reports on a bug where the canvas is not being
710 // updated properly which has debug info pointing to this build.
711 qDebug() << "Detected Intel driver build 4358, making ANGLE the preferred renderer";
712 isBlacklisted = true;
713 *warningMessage << knownBadIntelWarning;
714 }
715 } else {
716 // In case Intel changed the driver version format to something that
717 // we don't understand, we still select ANGLE.
718 qDebug() << "Detected Intel driver with unknown version format, making ANGLE the preferred renderer";
719 isBlacklisted = true;
720 *warningMessage << grossIntelWarning;
721 }
722 }
723#endif
724 return isBlacklisted;
725}
726
727boost::optional<bool> orderPreference(bool lhs, bool rhs)
728{
729 if (lhs == rhs) return boost::none;
730 if (lhs && !rhs) return true;
731 if (!lhs && rhs) return false;
732 return false;
733}
734
735#define ORDER_BY(lhs, rhs) if (auto res = orderPreference((lhs), (rhs))) { return *res; }
736
737class FormatPositionLess
738{
739public:
740
741 FormatPositionLess()
742 {
743 }
744
745 bool operator()(const KisOpenGL::RendererConfig &lhs, const KisOpenGL::RendererConfig &rhs) const {
747 if (m_preferredRendererByUser != KisOpenGL::RendererSoftware) {
748 ORDER_BY(!isFallbackOnly(lhs.rendererId()), !isFallbackOnly(rhs.rendererId()));
749 }
750
751 ORDER_BY(isPreferredColorSpace(lhs.format),
752 isPreferredColorSpace(rhs.format));
753
754 if (doPreferHDR()) {
755 ORDER_BY(isHDRFormat(lhs.format), isHDRFormat(rhs.format));
756 } else {
757 ORDER_BY(!isHDRFormat(lhs.format), !isHDRFormat(rhs.format));
758 }
759
760 if (m_preferredRendererByUser != KisOpenGL::RendererAuto) {
761 ORDER_BY(lhs.rendererId() == m_preferredRendererByUser,
762 rhs.rendererId() == m_preferredRendererByUser);
763 }
764
765 ORDER_BY(!isBlacklisted(lhs.rendererId()), !isBlacklisted(rhs.rendererId()));
766
767 if (doPreferHDR() &&
768 m_preferredRendererByHDR != KisOpenGL::RendererAuto) {
769
770 ORDER_BY(lhs.rendererId() == m_preferredRendererByHDR,
771 rhs.rendererId() == m_preferredRendererByHDR);
772
773 }
774
775 KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredRendererByQt != KisOpenGL::RendererAuto);
776
777 ORDER_BY(lhs.rendererId() == m_preferredRendererByQt,
778 rhs.rendererId() == m_preferredRendererByQt);
779
780 ORDER_BY(lhs.format.redBufferSize() == m_userPreferredBitDepth,
781 rhs.format.redBufferSize() == m_userPreferredBitDepth);
782
783 return false;
784 }
785
786
787public:
788 void setPreferredColorSpace(const KisSurfaceColorSpaceWrapper &preferredColorSpace) {
789 m_preferredColorSpace = preferredColorSpace;
790 }
791
792 void setPreferredRendererByQt(const KisOpenGL::OpenGLRenderer &preferredRendererByQt) {
793 m_preferredRendererByQt = preferredRendererByQt;
794 }
795
796 void setPreferredRendererByUser(const KisOpenGL::OpenGLRenderer &preferredRendererByUser) {
797 m_preferredRendererByUser = preferredRendererByUser;
798 }
799
800 void setPreferredRendererByHDR(const KisOpenGL::OpenGLRenderer &preferredRendererByHDR) {
801 m_preferredRendererByHDR = preferredRendererByHDR;
802 }
803
804 void setOpenGLBlacklisted(bool openGLBlacklisted) {
805 m_openGLBlacklisted = openGLBlacklisted;
806 }
807
808 void setOpenGLESBlacklisted(bool openGLESBlacklisted) {
809 m_openGLESBlacklisted = openGLESBlacklisted;
810 }
811
812 void setUserPreferredBitDepth(int value) {
813 m_userPreferredBitDepth = value;
814 }
815
816 bool isOpenGLBlacklisted() const {
817 return m_openGLBlacklisted;
818 }
819
820 bool isOpenGLESBlacklisted() const {
821 return m_openGLESBlacklisted;
822 }
823
824 KisSurfaceColorSpaceWrapper preferredColorSpace() const {
825 return m_preferredColorSpace;
826 }
827
828 KisOpenGL::OpenGLRenderer preferredRendererByUser() const {
829 return m_preferredRendererByUser;
830 }
831
832 int userPreferredBitDepth() const {
833 return m_userPreferredBitDepth;
834 }
835
836private:
837 bool isHDRFormat(const QSurfaceFormat &f) const {
838#ifdef HAVE_HDR
839 return f.colorSpace() == KisSurfaceColorSpaceWrapper::makeBt2020PQColorSpace() ||
841#else
842 Q_UNUSED(f);
843 return false;
844#endif
845 }
846
847 bool isFallbackOnly(KisOpenGL::OpenGLRenderer r) const {
848 return r == KisOpenGL::RendererSoftware;
849 }
850
851 bool isBlacklisted(KisOpenGL::OpenGLRenderer r) const {
857
858 return (r == KisOpenGL::RendererDesktopGL && m_openGLBlacklisted) ||
859 (r == KisOpenGL::RendererOpenGLES && m_openGLESBlacklisted) ||
860 (r == KisOpenGL::RendererSoftware && m_openGLESBlacklisted);
861 }
862
863 bool doPreferHDR() const {
864#ifdef HAVE_HDR
865 return m_preferredColorSpace == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace ||
866 m_preferredColorSpace == KisSurfaceColorSpaceWrapper::scRGBColorSpace;
867#else
868 return false;
869#endif
870 }
871
872 bool isPreferredColorSpace(const QSurfaceFormat & surfaceFormat) const {
874 m_preferredColorSpace,
875 KisSurfaceColorSpaceWrapper::fromQtColorSpace(surfaceFormat.colorSpace()));
876 }
877
878private:
879 KisSurfaceColorSpaceWrapper m_preferredColorSpace;
881 KisOpenGL::OpenGLRenderer m_preferredRendererByUser = KisOpenGL::RendererAuto;
882 KisOpenGL::OpenGLRenderer m_preferredRendererByHDR = KisOpenGL::RendererAuto;
883 bool m_openGLBlacklisted = false;
884 bool m_openGLESBlacklisted = false;
885 int m_userPreferredBitDepth = 8;
886};
887
888struct DetectionDebug : public QDebug
889{
890 DetectionDebug(QString *string)
891 : QDebug(string),
892 m_string(string),
893 m_originalSize(string->size())
894 {}
895 ~DetectionDebug() { dbgOpenGL << m_string->right(m_string->size() - m_originalSize); *this << Qt::endl; }
896
897 QString *m_string;
898 int m_originalSize;
899};
900}
901
902#define dbgDetection() DetectionDebug(&g_surfaceFormatDetectionLog)
903
905 KisConfig::RootSurfaceFormat preferredRootSurfaceFormat,
906 KisConfig::CanvasSurfaceBitDepthMode preferredCanvasSurfaceBitMode,
907 bool enableDebug)
908{
909 QVector<KLocalizedString> warningMessages;
910
911 using Info = boost::optional<KisOpenGLModeProber::Result>;
912
913 QHash<OpenGLRenderer, Info> renderersToTest;
914#ifndef Q_OS_ANDROID
915 renderersToTest.insert(RendererDesktopGL, Info());
916#endif
917 renderersToTest.insert(RendererOpenGLES, Info());
918
919#ifdef Q_OS_WIN
920 renderersToTest.insert(RendererSoftware, Info());
921#endif
922
923 auto makeDefaultSurfaceFormatPair = [] () -> std::pair<KisSurfaceColorSpaceWrapper, int> {
925 };
926
927#if defined HAVE_HDR
928 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
929 {
930 // TODO: check if we can use real sRGB space here
934 });
935#elif KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
936 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
937 {
940 });
941#else
942 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
943 {
945 });
946#endif
947
948 bool shouldInhibitCompatibilityProfile = false;
949 KisOpenGL::RendererConfig defaultConfig = generateSurfaceConfig(KisOpenGL::RendererAuto,
950 makeDefaultSurfaceFormatPair(),
951 false,
952 shouldInhibitCompatibilityProfile);
953 Info info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
954
955#ifndef Q_OS_MACOS
956 // When RendererAuto is active, Qt may perform insane things internally,
957 // e.g. switch from OpenGL to OpenGLES automatically. And the presence of
958 // the compatibility profile flag will cause the context creation process
959 // to fail.
960 //
961 // So, here we request an explicit API again to avoid Qt making decisions
962 // for us.
963
964 if (!info) {
965 dbgOpenGL << "Failed to probe default Qt's openGL format.. Trying DesktopGL with compatibility enabled...";
966 shouldInhibitCompatibilityProfile = false;
967 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererDesktopGL,
968 makeDefaultSurfaceFormatPair(),
969 false,
970 shouldInhibitCompatibilityProfile);
971 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
972 }
973
974 if (!info) {
975 dbgOpenGL << "Failed again.. Trying DesktopGL with compatibility disabled...";
976 shouldInhibitCompatibilityProfile = true;
977 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererDesktopGL,
978 makeDefaultSurfaceFormatPair(),
979 false,
980 shouldInhibitCompatibilityProfile);
981 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
982 }
983
984 if (!info) {
985 dbgOpenGL << "Failed again.. Trying OpenGLES...";
986 shouldInhibitCompatibilityProfile = false;
987 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererOpenGLES,
988 makeDefaultSurfaceFormatPair(),
989 false,
990 true);
991 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
992 }
993
994#endif /* Q_OS_MACOS */
995
996#ifdef Q_OS_WIN
997 if (!info) {
998 // try software rasterizer (WARP)
999 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererSoftware,
1000 makeDefaultSurfaceFormatPair(),
1001 false,
1002 shouldInhibitCompatibilityProfile);
1003 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
1004
1005 if (!info) {
1006 renderersToTest.remove(RendererSoftware);
1007 }
1008 }
1009#endif
1010
1011 if (!info) {
1012 dbgOpenGL << "Failed to probe default openGL format! No openGL support will be available in Krita";
1014 }
1015
1016 const OpenGLRenderer defaultRenderer = getRendererFromProbeResult(*info);
1017
1021#ifdef Q_OS_WIN
1022 const OpenGLRenderer preferredAutoRenderer = RendererOpenGLES;
1023#else
1024 const OpenGLRenderer preferredAutoRenderer = defaultRenderer;
1025#endif
1026
1027 OpenGLRenderers supportedRenderers = RendererNone;
1028
1029 FormatPositionLess compareOp;
1030 compareOp.setPreferredRendererByQt(preferredAutoRenderer);
1031
1032#ifdef HAVE_HDR
1033 compareOp.setPreferredColorSpace(
1037#else
1038 Q_UNUSED(preferredRootSurfaceFormat);
1039 compareOp.setPreferredColorSpace(KisSurfaceColorSpaceWrapper::sRGBColorSpace);
1040#endif
1041
1042#ifdef Q_OS_WIN
1043 compareOp.setPreferredRendererByHDR(KisOpenGL::RendererOpenGLES);
1044#endif
1045 compareOp.setPreferredRendererByUser(preferredRenderer);
1046 compareOp.setOpenGLESBlacklisted(false); // We cannot blacklist ES drivers atm
1047
1048#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1049 // 10-bit is the default, 8-bit is set explicitly by the user
1050 compareOp.setUserPreferredBitDepth(preferredCanvasSurfaceBitMode == KisConfig::CanvasSurfaceBitDepthMode::Depth8Bit ? 8 : 10);
1051#else
1052 Q_UNUSED(preferredCanvasSurfaceBitMode)
1053#endif
1054
1055 renderersToTest[defaultRenderer] = info;
1056
1057 for (auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) {
1058 Info info = it.value();
1059
1060 if (!info) {
1061 const RendererConfig config = generateSurfaceConfig(it.key(), makeDefaultSurfaceFormatPair(), false, shouldInhibitCompatibilityProfile);
1062 dbgOpenGL << "Probing" << it.key() << "from default:" << config.format << config.angleRenderer
1063 << config.rendererId();
1065 *it = info;
1066 } else {
1067 dbgOpenGL << "Already probed:" << it.key();
1068 }
1069
1070 compareOp.setOpenGLBlacklisted(
1071 !info ||
1072 isOpenGLRendererBlacklisted(info->rendererString(),
1073 info->driverVersionString(),
1074 &warningMessages));
1075
1076 if (info) {
1077 dbgOpenGL << "Result:" << info->rendererString() << info->driverVersionString()
1078 << info->isSupportedVersion();
1079 }
1080
1081 if (info) {
1082 g_detectedRenderers << std::make_tuple(info->rendererString(),
1083 info->driverVersionString(),
1084 info->isSupportedVersion());
1085 }
1086
1087 if (info && info->isSupportedVersion()) {
1088 supportedRenderers |= it.key();
1089 }
1090 }
1091
1092 OpenGLRenderer preferredByQt = preferredAutoRenderer;
1093
1094 if (preferredByQt == RendererDesktopGL &&
1095 supportedRenderers & RendererDesktopGL &&
1096 compareOp.isOpenGLBlacklisted()) {
1097
1098 preferredByQt = RendererOpenGLES;
1099
1100 } else if (preferredByQt == RendererOpenGLES &&
1101 supportedRenderers & RendererOpenGLES &&
1102 compareOp.isOpenGLESBlacklisted()) {
1103
1104 preferredByQt = RendererDesktopGL;
1105 }
1106
1107 QVector<RendererConfig> preferredConfigs;
1108 for (auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) {
1109 // if default mode of the renderer doesn't work, then custom won't either
1110 if (!it.value()) continue;
1111
1112 Q_FOREACH (const auto &formatPair, formatSymbolPairs) {
1113 preferredConfigs << generateSurfaceConfig(it.key(), formatPair, enableDebug, shouldInhibitCompatibilityProfile);
1114 }
1115 }
1116
1117 std::stable_sort(preferredConfigs.begin(), preferredConfigs.end(), compareOp);
1118
1119 dbgDetection() << "Supported renderers:" << supportedRenderers;
1120
1121 dbgDetection() << "Surface format preference list:";
1122 Q_FOREACH (const KisOpenGL::RendererConfig &config, preferredConfigs) {
1123 dbgDetection() << "*" << config.format;
1124 dbgDetection() << " " << config.rendererId();
1125 }
1126
1127 KisOpenGL::RendererConfig resultConfig = defaultConfig;
1128
1129 if (preferredRenderer != RendererNone) {
1130 Q_FOREACH (const KisOpenGL::RendererConfig &config, preferredConfigs) {
1131 dbgDetection() <<"Probing format..." << config.format.colorSpace() << config.rendererId();
1132 Info info = KisOpenGLModeProber::instance()->probeFormat(config);
1133
1134 if (info && info->isSupportedVersion()) {
1135
1136#ifdef Q_OS_WIN
1137 // HACK: Block ANGLE with Direct3D9
1138 // Direct3D9 does not give OpenGL ES 3.0
1139 // Some versions of ANGLE returns OpenGL version 3.0 incorrectly
1140
1141 if (info->isUsingAngle() &&
1142 info->rendererString().contains("Direct3D9", Qt::CaseInsensitive)) {
1143
1144 dbgDetection() << "Skipping Direct3D 9 Angle implementation, it shouldn't have happened.";
1145
1146 continue;
1147 }
1148#endif
1149
1150 dbgDetection() << "Found format:" << config.format;
1151 dbgDetection() << " " << config.rendererId();
1152
1153 resultConfig = config;
1154 break;
1155 }
1156 }
1157
1158 {
1159 const bool colorSpaceIsCorrect =
1160 KisOpenGLModeProber::fuzzyCompareColorSpaces(compareOp.preferredColorSpace(),
1161 KisSurfaceColorSpaceWrapper::fromQtColorSpace(resultConfig.format.colorSpace()));
1162
1163 const bool rendererIsCorrect =
1164 compareOp.preferredRendererByUser() == KisOpenGL::RendererAuto ||
1165 compareOp.preferredRendererByUser() == resultConfig.rendererId();
1166
1167 if (!rendererIsCorrect && colorSpaceIsCorrect) {
1168 warningMessages << ki18n("Preferred renderer doesn't support requested surface format. Another renderer has been selected.");
1169 } else if (!colorSpaceIsCorrect) {
1170 warningMessages << ki18n("Preferred output format is not supported by available renderers");
1171 }
1172
1173 }
1174 } else {
1175 resultConfig.format = QSurfaceFormat();
1176 resultConfig.angleRenderer = AngleRendererDefault;
1177 }
1178
1179 overrideSupportedRenderers(supportedRenderers, preferredByQt);
1180 overrideOpenGLWarningString(warningMessages);
1181
1182 return resultConfig;
1183}
1184
1186{
1187 KIS_SAFE_ASSERT_RECOVER_NOOP(!g_sanityDefaultFormatIsSet);
1188
1189 g_sanityDefaultFormatIsSet = true;
1190 QSurfaceFormat::setDefaultFormat(config.format);
1191
1192 if (config.format.renderableType() == QSurfaceFormat::OpenGLES) {
1193 QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true);
1194#ifdef Q_OS_WIN
1195 if (!qEnvironmentVariableIsSet("QT_ANGLE_PLATFORM")) {
1196 // Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP
1197 // might get weird crashes atm.
1198 qputenv("QT_ANGLE_PLATFORM", KisOpenGLModeProber::angleRendererToString(config.angleRenderer).toLatin1());
1199 }
1200#endif
1201 } else if (config.format.renderableType() == QSurfaceFormat::OpenGL) {
1202 QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true);
1203 }
1204}
1205
1207{
1208 return openGLCheckResult->isSupportedVersion();
1209}
float value(const T *src, size_t ch)
unsigned int uint
@ ASSISTANTS_DRAW_MODE_LARGE_PIXMAP_CACHE
Definition kis_config.h:804
bool forceOpenGLFenceWorkaround(bool defaultValue=false) const
T readEntry(const QString &name, const T &defaultValue=T())
Definition kis_config.h:832
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