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 if (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 } else {
327 qWarning() << "Could not open" << log.fileName() << "for writing:" << log.errorString();
328 }
329}
330
332{
333 initialize();
334 return g_debugText;
335}
336
338 QStringList strings;
339 Q_FOREACH (const KLocalizedString &item, g_openglWarningStrings) {
340 strings << item.toString();
341 }
342 return strings;
343}
344
346{
347 initialize();
348 if (openGLCheckResult) {
349 return openGLCheckResult->driverVersionString();
350 }
351 return QString();
352}
353
354// Check whether we can allow LoD on OpenGL3 without triggering
355// all of the other 3.2 functionality.
357{
358 initialize();
359 return openGLCheckResult && openGLCheckResult->supportsLoD();
360}
361
363{
364 initialize();
365 return openGLCheckResult && openGLCheckResult->hasOpenGL3();
366}
367
369{
370 initialize();
371 return openGLCheckResult && openGLCheckResult->supportsVAO();
372}
373
375{
376 initialize();
377 return openGLCheckResult && openGLCheckResult->isOpenGLES();
378}
379
381{
382 initialize();
383 return openGLCheckResult && openGLCheckResult->supportsFenceSync();
384}
385
387{
388 initialize();
389 return openGLCheckResult && openGLCheckResult->supportsBufferMapping();
390}
391
393{
394 initialize();
395 return g_forceDisableTextureBuffers;
396}
397
398bool KisOpenGL::shouldUseTextureBuffers(bool userPreference)
399{
400 initialize();
401 return !g_forceDisableTextureBuffers && userPreference;
402}
403
405{
406 initialize();
407 return g_useBufferInvalidation &&
408 openGLCheckResult && openGLCheckResult->supportsBufferInvalidation();
409}
410
412{
413 initialize();
414 return openGLCheckResult && openGLCheckResult->supportsFBO();
415}
416
417std::optional<KisOpenGL::XcbGLProviderProtocol> KisOpenGL::xcbGlProviderProtocol()
418{
419 initialize();
420 return openGLCheckResult ? openGLCheckResult->xcbGlProviderProtocol() : std::nullopt;
421}
422
424{
425 initialize();
426 return g_needsFenceWorkaround;
427}
428
433
435{
436 g_isDebugSynchronous = value;
437}
438
440{
441 g_glInvalidateBufferData(buffer);
442}
443
445{
446 if (!openGLCheckResult) return RendererAuto;
447 return getRendererFromProbeResult(*openGLCheckResult);
448}
449
451{
452 return g_rendererPreferredByQt;
453}
454
455KisOpenGL::OpenGLRenderers KisOpenGL::getSupportedOpenGLRenderers()
456{
457 return g_supportedRenderers;
458}
459
461{
462 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
463 QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
464 return convertConfigToOpenGLRenderer(kritarc.value("OpenGLRenderer", "auto").toString());
465}
466
468{
469 const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
470 QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat);
471 kritarc.setValue("OpenGLRenderer", KisOpenGL::convertOpenGLRendererToConfig(renderer));
472}
473
475{
476 switch (renderer) {
477 case RendererNone:
478 return QStringLiteral("none");
479 case RendererSoftware:
480 return QStringLiteral("software");
482 return QStringLiteral("desktop");
483 case RendererOpenGLES:
484 return QStringLiteral("angle");
485 default:
486 return QStringLiteral("auto");
487 }
488}
489
491{
492 if (renderer == "desktop") {
493 return RendererDesktopGL;
494 } else if (renderer == "angle") {
495 return RendererOpenGLES;
496 } else if (renderer == "software") {
497 return RendererSoftware;
498 } else if (renderer == "none") {
499 return RendererNone;
500 } else {
501 return RendererAuto;
502 }
503}
504
506{
508
509 if (format.renderableType() == QSurfaceFormat::OpenGLES &&
511
512 result = RendererSoftware;
513
514 } else if (format.renderableType() == QSurfaceFormat::OpenGLES) {
515 // If D3D11, D3D9?, Default (which is after probing, if selected)
516 // or the system specifies QT_OPENGL_ES_2
517 result = RendererOpenGLES;
518 } else if (format.renderableType() == QSurfaceFormat::OpenGL) {
519 result = RendererDesktopGL;
520 } else if (format.renderableType() == QSurfaceFormat::DefaultRenderableType &&
522 // noop
523 } else {
524 qWarning() << "WARNING: unsupported combination of OpenGL renderer" << ppVar(format.renderableType()) << ppVar(angleRenderer);
525 }
526
527 return result;
528}
529
530namespace {
531
532typedef std::pair<QSurfaceFormat::RenderableType, KisOpenGL::AngleRenderer> RendererInfo;
533
534RendererInfo getRendererInfo(KisOpenGL::OpenGLRenderer renderer)
535{
536 RendererInfo info = {QSurfaceFormat::DefaultRenderableType,
538
539 switch (renderer) {
541 info = {QSurfaceFormat::DefaultRenderableType, KisOpenGL::AngleRendererDefault};
542 break;
544 break;
546 info = {QSurfaceFormat::OpenGL, KisOpenGL::AngleRendererDefault};
547 break;
549 info = {QSurfaceFormat::OpenGLES, KisOpenGL::AngleRendererD3d11};
550 break;
552 info = {QSurfaceFormat::OpenGLES, KisOpenGL::AngleRendererD3d11Warp};
553 break;
554 }
555
556 return info;
557}
558
559QOpenGLContext::OpenGLModuleType determineOpenGLImplementation(const RendererInfo &info)
560{
561 switch (info.first) {
562 case QSurfaceFormat::OpenGLES:
563#if defined(Q_OS_WINDOWS)
564 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/plugins/platforms/windows/qwindowsintegration.cpp#L425
565 switch (info.second) {
569 return QOpenGLContext::LibGLES;
570 // Assume system OpenGL -- QOpenGLStaticContext
571 default:
572 break;
573 }
574 return QOpenGLContext::LibGL;
575#else
576 // At least Manjaro Qt can perfectly call up a ES context,
577 // while Qt says via macros that it doesn't support that...
578 return QOpenGLContext::LibGLES;
579#endif
580 case QSurfaceFormat::DefaultRenderableType:
581#ifdef Q_OS_WIN
582 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/plugins/platforms/windows/qwindowsglcontext.cpp#L1117
583 return QOpenGLContext::LibGL;
584#else
585 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp#L246
586#if defined(QT_OPENGL_ES_2)
587 return QOpenGLContext::LibGLES;
588#else
589 return QOpenGLContext::LibGL;
590#endif
591#endif
592 case QSurfaceFormat::OpenGL:
593 default:
594 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/plugins/platforms/windows/qwindowsglcontext.cpp#L1117
595 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(info.first != QSurfaceFormat::OpenVG, QOpenGLContext::LibGL);
596 // https://invent.kde.org/szaman/qtbase/-/blob/krita/5.15/src/gui/kernel/qplatformintegration.cpp#L547
597 return QOpenGLContext::LibGL;
598 };
599}
600
601KisOpenGL::RendererConfig generateSurfaceConfig(KisOpenGL::OpenGLRenderer renderer,
602 std::pair<KisSurfaceColorSpaceWrapper, int> rootSurfaceFormat,
603 bool debugContext,
604 bool inhibitCompatibilityProfile)
605{
606 RendererInfo info = getRendererInfo(renderer);
607
609 config.angleRenderer = info.second;
610
611
612 dbgOpenGL << "Requesting configuration for" << info.first << info.second;
613 dbgOpenGL << "Requesting root surface format" << rootSurfaceFormat;
614
615 QSurfaceFormat &format = config.format;
616 const auto openGLModuleType = determineOpenGLImplementation(info);
617 switch (openGLModuleType) {
618 case QOpenGLContext::LibGL:
619#if defined Q_OS_MACOS
620 format.setVersion(4, 1);
621 format.setProfile(QSurfaceFormat::CoreProfile);
622 Q_UNUSED(inhibitCompatibilityProfile);
623#else
624 // If asked for 3.0 "Core", Qt will instead request
625 // an OpenGL ES context.
626 // NVIDIA's GLX implementation will not allow that and results
627 // in a forced process exit through X11 (NVIDIA bug #3959482).
628 format.setVersion(3, 3);
629 // Make sure to request a Compatibility profile to have NVIDIA
630 // return the maximum supported GL version.
631
632 if (!inhibitCompatibilityProfile) {
633 format.setProfile(QSurfaceFormat::CompatibilityProfile);
634 }
635#ifdef Q_OS_WIN
636 // Some parts of Qt seems to require deprecated functions. On Windows
637 // with the Intel Graphics driver, things like canvas decorations and
638 // the Touch Docker does not render without this option.
639 format.setOptions(QSurfaceFormat::DeprecatedFunctions);
640#endif
641#endif
642 break;
643 case QOpenGLContext::LibGLES:
644 format.setVersion(3, 0);
645 format.setProfile(QSurfaceFormat::NoProfile);
646 break;
647 }
648
649 dbgOpenGL << "Version selected:" << openGLModuleType << format.version();
650
651 format.setDepthBufferSize(24);
652 format.setStencilBufferSize(8);
653
654 KisOpenGLModeProber::initSurfaceFormatFromConfig(rootSurfaceFormat, &format);
655
656 format.setRenderableType(info.first);
657 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
658 format.setSwapInterval(0); // Disable vertical refresh syncing
660 // With repaint debugging, vsync is preferred so that all update regions
661 // can be visible.
662 format.setSwapInterval(1);
663 }
664 if (debugContext) {
665 format.setOption(QSurfaceFormat::DebugContext, true);
666 }
667
668 return config;
669}
670
671bool isOpenGLRendererBlacklisted(const QString &rendererString,
672 const QString &driverVersionString,
673 QVector<KLocalizedString> *warningMessage)
674{
675 bool isBlacklisted = false;
676#ifndef Q_OS_WIN
677 Q_UNUSED(rendererString);
678 Q_UNUSED(driverVersionString);
679 Q_UNUSED(warningMessage);
680#else
681 // Special blacklisting of OpenGL/ANGLE is tracked on:
682 // https://phabricator.kde.org/T7411
683
684 // HACK: Specifically detect for Intel driver build number
685 // See https://www.intel.com/content/www/us/en/support/articles/000005654/graphics.html
686 if (rendererString.startsWith("Intel")) {
687 KLocalizedString knownBadIntelWarning = ki18n("The Intel graphics driver in use is known to have issues with OpenGL.");
688 KLocalizedString grossIntelWarning = ki18n(
689 "Intel graphics drivers tend to have issues with OpenGL so ANGLE will be used by default. "
690 "You may manually switch to OpenGL but it is not guaranteed to work properly."
691 );
692 QRegularExpression regex("\\b\\d{1,2}\\.\\d{1,2}\\.(\\d{1,3})\\.(\\d{4})\\b");
693 QRegularExpressionMatch match = regex.match(driverVersionString);
694 if (match.hasMatch()) {
695 const int thirdPart = match.captured(1).toInt();
696 const int fourthPart = match.captured(2).toInt();
697 int driverBuild;
698 if (thirdPart >= 100) {
699 driverBuild = thirdPart * 10000 + fourthPart;
700 } else {
701 driverBuild = fourthPart;
702 }
703 qDebug() << "Detected Intel driver build number as" << driverBuild;
704 if (driverBuild > 4636 && driverBuild < 4729) {
705 // Make ANGLE the preferred renderer for Intel driver versions
706 // between build 4636 and 4729 (exclusive) due to an UI offset bug.
707 // See https://communities.intel.com/thread/116003
708 // (Build 4636 is known to work from some test results)
709 qDebug() << "Detected Intel driver build between 4636 and 4729, making ANGLE the preferred renderer";
710 isBlacklisted = true;
711 *warningMessage << knownBadIntelWarning;
712 } else if (driverBuild == 4358) {
713 // There are several reports on a bug where the canvas is not being
714 // updated properly which has debug info pointing to this build.
715 qDebug() << "Detected Intel driver build 4358, making ANGLE the preferred renderer";
716 isBlacklisted = true;
717 *warningMessage << knownBadIntelWarning;
718 }
719 } else {
720 // In case Intel changed the driver version format to something that
721 // we don't understand, we still select ANGLE.
722 qDebug() << "Detected Intel driver with unknown version format, making ANGLE the preferred renderer";
723 isBlacklisted = true;
724 *warningMessage << grossIntelWarning;
725 }
726 }
727#endif
728 return isBlacklisted;
729}
730
731boost::optional<bool> orderPreference(bool lhs, bool rhs)
732{
733 if (lhs == rhs) return boost::none;
734 if (lhs && !rhs) return true;
735 if (!lhs && rhs) return false;
736 return false;
737}
738
739#define ORDER_BY(lhs, rhs) if (auto res = orderPreference((lhs), (rhs))) { return *res; }
740
741class FormatPositionLess
742{
743public:
744
745 FormatPositionLess()
746 {
747 }
748
749 bool operator()(const KisOpenGL::RendererConfig &lhs, const KisOpenGL::RendererConfig &rhs) const {
751 if (m_preferredRendererByUser != KisOpenGL::RendererSoftware) {
752 ORDER_BY(!isFallbackOnly(lhs.rendererId()), !isFallbackOnly(rhs.rendererId()));
753 }
754
755 ORDER_BY(isPreferredColorSpace(lhs.format),
756 isPreferredColorSpace(rhs.format));
757
758 if (doPreferHDR()) {
759 ORDER_BY(isHDRFormat(lhs.format), isHDRFormat(rhs.format));
760 } else {
761 ORDER_BY(!isHDRFormat(lhs.format), !isHDRFormat(rhs.format));
762 }
763
764 if (m_preferredRendererByUser != KisOpenGL::RendererAuto) {
765 ORDER_BY(lhs.rendererId() == m_preferredRendererByUser,
766 rhs.rendererId() == m_preferredRendererByUser);
767 }
768
769 ORDER_BY(!isBlacklisted(lhs.rendererId()), !isBlacklisted(rhs.rendererId()));
770
771 if (doPreferHDR() &&
772 m_preferredRendererByHDR != KisOpenGL::RendererAuto) {
773
774 ORDER_BY(lhs.rendererId() == m_preferredRendererByHDR,
775 rhs.rendererId() == m_preferredRendererByHDR);
776
777 }
778
779 KIS_SAFE_ASSERT_RECOVER_NOOP(m_preferredRendererByQt != KisOpenGL::RendererAuto);
780
781 ORDER_BY(lhs.rendererId() == m_preferredRendererByQt,
782 rhs.rendererId() == m_preferredRendererByQt);
783
784 ORDER_BY(lhs.format.redBufferSize() == m_userPreferredBitDepth,
785 rhs.format.redBufferSize() == m_userPreferredBitDepth);
786
787 return false;
788 }
789
790
791public:
792 void setPreferredColorSpace(const KisSurfaceColorSpaceWrapper &preferredColorSpace) {
793 m_preferredColorSpace = preferredColorSpace;
794 }
795
796 void setPreferredRendererByQt(const KisOpenGL::OpenGLRenderer &preferredRendererByQt) {
797 m_preferredRendererByQt = preferredRendererByQt;
798 }
799
800 void setPreferredRendererByUser(const KisOpenGL::OpenGLRenderer &preferredRendererByUser) {
801 m_preferredRendererByUser = preferredRendererByUser;
802 }
803
804 void setPreferredRendererByHDR(const KisOpenGL::OpenGLRenderer &preferredRendererByHDR) {
805 m_preferredRendererByHDR = preferredRendererByHDR;
806 }
807
808 void setOpenGLBlacklisted(bool openGLBlacklisted) {
809 m_openGLBlacklisted = openGLBlacklisted;
810 }
811
812 void setOpenGLESBlacklisted(bool openGLESBlacklisted) {
813 m_openGLESBlacklisted = openGLESBlacklisted;
814 }
815
816 void setUserPreferredBitDepth(int value) {
817 m_userPreferredBitDepth = value;
818 }
819
820 bool isOpenGLBlacklisted() const {
821 return m_openGLBlacklisted;
822 }
823
824 bool isOpenGLESBlacklisted() const {
825 return m_openGLESBlacklisted;
826 }
827
828 KisSurfaceColorSpaceWrapper preferredColorSpace() const {
829 return m_preferredColorSpace;
830 }
831
832 KisOpenGL::OpenGLRenderer preferredRendererByUser() const {
833 return m_preferredRendererByUser;
834 }
835
836 int userPreferredBitDepth() const {
837 return m_userPreferredBitDepth;
838 }
839
840private:
841 bool isHDRFormat(const QSurfaceFormat &f) const {
842#ifdef HAVE_HDR
843 return f.colorSpace() == KisSurfaceColorSpaceWrapper::makeBt2020PQColorSpace() ||
845#else
846 Q_UNUSED(f);
847 return false;
848#endif
849 }
850
851 bool isFallbackOnly(KisOpenGL::OpenGLRenderer r) const {
852 return r == KisOpenGL::RendererSoftware;
853 }
854
855 bool isBlacklisted(KisOpenGL::OpenGLRenderer r) const {
861
862 return (r == KisOpenGL::RendererDesktopGL && m_openGLBlacklisted) ||
863 (r == KisOpenGL::RendererOpenGLES && m_openGLESBlacklisted) ||
864 (r == KisOpenGL::RendererSoftware && m_openGLESBlacklisted);
865 }
866
867 bool doPreferHDR() const {
868#ifdef HAVE_HDR
869 return m_preferredColorSpace == KisSurfaceColorSpaceWrapper::bt2020PQColorSpace ||
870 m_preferredColorSpace == KisSurfaceColorSpaceWrapper::scRGBColorSpace;
871#else
872 return false;
873#endif
874 }
875
876 bool isPreferredColorSpace(const QSurfaceFormat & surfaceFormat) const {
878 m_preferredColorSpace,
879 KisSurfaceColorSpaceWrapper::fromQtColorSpace(surfaceFormat.colorSpace()));
880 }
881
882private:
883 KisSurfaceColorSpaceWrapper m_preferredColorSpace;
885 KisOpenGL::OpenGLRenderer m_preferredRendererByUser = KisOpenGL::RendererAuto;
886 KisOpenGL::OpenGLRenderer m_preferredRendererByHDR = KisOpenGL::RendererAuto;
887 bool m_openGLBlacklisted = false;
888 bool m_openGLESBlacklisted = false;
889 int m_userPreferredBitDepth = 8;
890};
891
892struct DetectionDebug : public QDebug
893{
894 DetectionDebug(QString *string)
895 : QDebug(string),
896 m_string(string),
897 m_originalSize(string->size())
898 {}
899 ~DetectionDebug() { dbgOpenGL << m_string->right(m_string->size() - m_originalSize); *this << Qt::endl; }
900
901 QString *m_string;
902 int m_originalSize;
903};
904}
905
906#define dbgDetection() DetectionDebug(&g_surfaceFormatDetectionLog)
907
909 KisConfig::RootSurfaceFormat preferredRootSurfaceFormat,
910 KisConfig::CanvasSurfaceBitDepthMode preferredCanvasSurfaceBitMode,
911 bool enableDebug)
912{
913 QVector<KLocalizedString> warningMessages;
914
915 using Info = boost::optional<KisOpenGLModeProber::Result>;
916
917 QHash<OpenGLRenderer, Info> renderersToTest;
918#ifndef Q_OS_ANDROID
919 renderersToTest.insert(RendererDesktopGL, Info());
920#endif
921 renderersToTest.insert(RendererOpenGLES, Info());
922
923#ifdef Q_OS_WIN
924 renderersToTest.insert(RendererSoftware, Info());
925#endif
926
927 auto makeDefaultSurfaceFormatPair = [] () -> std::pair<KisSurfaceColorSpaceWrapper, int> {
929 };
930
931#if defined HAVE_HDR
932 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
933 {
934 // TODO: check if we can use real sRGB space here
938 });
939#elif KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
940 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
941 {
944 });
945#else
946 std::vector<std::pair<KisSurfaceColorSpaceWrapper, int>> formatSymbolPairs(
947 {
949 });
950#endif
951
952 bool shouldInhibitCompatibilityProfile = false;
953 KisOpenGL::RendererConfig defaultConfig = generateSurfaceConfig(KisOpenGL::RendererAuto,
954 makeDefaultSurfaceFormatPair(),
955 false,
956 shouldInhibitCompatibilityProfile);
957 Info info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
958
959#ifndef Q_OS_MACOS
960 // When RendererAuto is active, Qt may perform insane things internally,
961 // e.g. switch from OpenGL to OpenGLES automatically. And the presence of
962 // the compatibility profile flag will cause the context creation process
963 // to fail.
964 //
965 // So, here we request an explicit API again to avoid Qt making decisions
966 // for us.
967
968 if (!info) {
969 dbgOpenGL << "Failed to probe default Qt's openGL format.. Trying DesktopGL with compatibility enabled...";
970 shouldInhibitCompatibilityProfile = false;
971 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererDesktopGL,
972 makeDefaultSurfaceFormatPair(),
973 false,
974 shouldInhibitCompatibilityProfile);
975 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
976 }
977
978 if (!info) {
979 dbgOpenGL << "Failed again.. Trying DesktopGL with compatibility disabled...";
980 shouldInhibitCompatibilityProfile = true;
981 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererDesktopGL,
982 makeDefaultSurfaceFormatPair(),
983 false,
984 shouldInhibitCompatibilityProfile);
985 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
986 }
987
988 if (!info) {
989 dbgOpenGL << "Failed again.. Trying OpenGLES...";
990 shouldInhibitCompatibilityProfile = false;
991 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererOpenGLES,
992 makeDefaultSurfaceFormatPair(),
993 false,
994 true);
995 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
996 }
997
998#endif /* Q_OS_MACOS */
999
1000#ifdef Q_OS_WIN
1001 if (!info) {
1002 // try software rasterizer (WARP)
1003 defaultConfig = generateSurfaceConfig(KisOpenGL::RendererSoftware,
1004 makeDefaultSurfaceFormatPair(),
1005 false,
1006 shouldInhibitCompatibilityProfile);
1007 info = KisOpenGLModeProber::instance()->probeFormat(defaultConfig);
1008
1009 if (!info) {
1010 renderersToTest.remove(RendererSoftware);
1011 }
1012 }
1013#endif
1014
1015 if (!info) {
1016 dbgOpenGL << "Failed to probe default openGL format! No openGL support will be available in Krita";
1018 }
1019
1020 const OpenGLRenderer defaultRenderer = getRendererFromProbeResult(*info);
1021
1025#ifdef Q_OS_WIN
1026 const OpenGLRenderer preferredAutoRenderer = RendererOpenGLES;
1027#else
1028 const OpenGLRenderer preferredAutoRenderer = defaultRenderer;
1029#endif
1030
1031 OpenGLRenderers supportedRenderers = RendererNone;
1032
1033 FormatPositionLess compareOp;
1034 compareOp.setPreferredRendererByQt(preferredAutoRenderer);
1035
1036#ifdef HAVE_HDR
1037 compareOp.setPreferredColorSpace(
1041#else
1042 Q_UNUSED(preferredRootSurfaceFormat);
1043 compareOp.setPreferredColorSpace(KisSurfaceColorSpaceWrapper::sRGBColorSpace);
1044#endif
1045
1046#ifdef Q_OS_WIN
1047 compareOp.setPreferredRendererByHDR(KisOpenGL::RendererOpenGLES);
1048#endif
1049 compareOp.setPreferredRendererByUser(preferredRenderer);
1050 compareOp.setOpenGLESBlacklisted(false); // We cannot blacklist ES drivers atm
1051
1052#if KRITA_USE_SURFACE_COLOR_MANAGEMENT_API
1053 // 10-bit is the default, 8-bit is set explicitly by the user
1054 compareOp.setUserPreferredBitDepth(preferredCanvasSurfaceBitMode == KisConfig::CanvasSurfaceBitDepthMode::Depth8Bit ? 8 : 10);
1055#else
1056 Q_UNUSED(preferredCanvasSurfaceBitMode)
1057#endif
1058
1059 renderersToTest[defaultRenderer] = info;
1060
1061 for (auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) {
1062 Info info = it.value();
1063
1064 if (!info) {
1065 const RendererConfig config = generateSurfaceConfig(it.key(), makeDefaultSurfaceFormatPair(), false, shouldInhibitCompatibilityProfile);
1066 dbgOpenGL << "Probing" << it.key() << "from default:" << config.format << config.angleRenderer
1067 << config.rendererId();
1069 *it = info;
1070 } else {
1071 dbgOpenGL << "Already probed:" << it.key();
1072 }
1073
1074 compareOp.setOpenGLBlacklisted(
1075 !info ||
1076 isOpenGLRendererBlacklisted(info->rendererString(),
1077 info->driverVersionString(),
1078 &warningMessages));
1079
1080 if (info) {
1081 dbgOpenGL << "Result:" << info->rendererString() << info->driverVersionString()
1082 << info->isSupportedVersion();
1083 }
1084
1085 if (info) {
1086 g_detectedRenderers << std::make_tuple(info->rendererString(),
1087 info->driverVersionString(),
1088 info->isSupportedVersion());
1089 }
1090
1091 if (info && info->isSupportedVersion()) {
1092 supportedRenderers |= it.key();
1093 }
1094 }
1095
1096 OpenGLRenderer preferredByQt = preferredAutoRenderer;
1097
1098 if (preferredByQt == RendererDesktopGL &&
1099 supportedRenderers & RendererDesktopGL &&
1100 compareOp.isOpenGLBlacklisted()) {
1101
1102 preferredByQt = RendererOpenGLES;
1103
1104 } else if (preferredByQt == RendererOpenGLES &&
1105 supportedRenderers & RendererOpenGLES &&
1106 compareOp.isOpenGLESBlacklisted()) {
1107
1108 preferredByQt = RendererDesktopGL;
1109 }
1110
1111 QVector<RendererConfig> preferredConfigs;
1112 for (auto it = renderersToTest.begin(); it != renderersToTest.end(); ++it) {
1113 // if default mode of the renderer doesn't work, then custom won't either
1114 if (!it.value()) continue;
1115
1116 Q_FOREACH (const auto &formatPair, formatSymbolPairs) {
1117 preferredConfigs << generateSurfaceConfig(it.key(), formatPair, enableDebug, shouldInhibitCompatibilityProfile);
1118 }
1119 }
1120
1121 std::stable_sort(preferredConfigs.begin(), preferredConfigs.end(), compareOp);
1122
1123 dbgDetection() << "Supported renderers:" << supportedRenderers;
1124
1125 dbgDetection() << "Surface format preference list:";
1126 Q_FOREACH (const KisOpenGL::RendererConfig &config, preferredConfigs) {
1127 dbgDetection() << "*" << config.format;
1128 dbgDetection() << " " << config.rendererId();
1129 }
1130
1131 KisOpenGL::RendererConfig resultConfig = defaultConfig;
1132
1133 if (preferredRenderer != RendererNone) {
1134 Q_FOREACH (const KisOpenGL::RendererConfig &config, preferredConfigs) {
1135 dbgDetection() <<"Probing format..." << config.format.colorSpace() << config.rendererId();
1136 Info info = KisOpenGLModeProber::instance()->probeFormat(config);
1137
1138 if (info && info->isSupportedVersion()) {
1139
1140#ifdef Q_OS_WIN
1141 // HACK: Block ANGLE with Direct3D9
1142 // Direct3D9 does not give OpenGL ES 3.0
1143 // Some versions of ANGLE returns OpenGL version 3.0 incorrectly
1144
1145 if (info->isUsingAngle() &&
1146 info->rendererString().contains("Direct3D9", Qt::CaseInsensitive)) {
1147
1148 dbgDetection() << "Skipping Direct3D 9 Angle implementation, it shouldn't have happened.";
1149
1150 continue;
1151 }
1152#endif
1153
1154 dbgDetection() << "Found format:" << config.format;
1155 dbgDetection() << " " << config.rendererId();
1156
1157 resultConfig = config;
1158 break;
1159 }
1160 }
1161
1162 {
1163 const bool colorSpaceIsCorrect =
1164 KisOpenGLModeProber::fuzzyCompareColorSpaces(compareOp.preferredColorSpace(),
1165 KisSurfaceColorSpaceWrapper::fromQtColorSpace(resultConfig.format.colorSpace()));
1166
1167 const bool rendererIsCorrect =
1168 compareOp.preferredRendererByUser() == KisOpenGL::RendererAuto ||
1169 compareOp.preferredRendererByUser() == resultConfig.rendererId();
1170
1171 if (!rendererIsCorrect && colorSpaceIsCorrect) {
1172 warningMessages << ki18n("Preferred renderer doesn't support requested surface format. Another renderer has been selected.");
1173 } else if (!colorSpaceIsCorrect) {
1174 warningMessages << ki18n("Preferred output format is not supported by available renderers");
1175 }
1176
1177 }
1178 } else {
1179 resultConfig.format = QSurfaceFormat();
1180 resultConfig.angleRenderer = AngleRendererDefault;
1181 }
1182
1183 overrideSupportedRenderers(supportedRenderers, preferredByQt);
1184 overrideOpenGLWarningString(warningMessages);
1185
1186 return resultConfig;
1187}
1188
1190{
1191 KIS_SAFE_ASSERT_RECOVER_NOOP(!g_sanityDefaultFormatIsSet);
1192
1193 g_sanityDefaultFormatIsSet = true;
1194 QSurfaceFormat::setDefaultFormat(config.format);
1195
1196 if (config.format.renderableType() == QSurfaceFormat::OpenGLES) {
1197 QCoreApplication::setAttribute(Qt::AA_UseOpenGLES, true);
1198#ifdef Q_OS_WIN
1199 if (!qEnvironmentVariableIsSet("QT_ANGLE_PLATFORM")) {
1200 // Force ANGLE to use Direct3D11. D3D9 doesn't support OpenGL ES 3 and WARP
1201 // might get weird crashes atm.
1202 qputenv("QT_ANGLE_PLATFORM", KisOpenGLModeProber::angleRendererToString(config.angleRenderer).toLatin1());
1203 }
1204#endif
1205 } else if (config.format.renderableType() == QSurfaceFormat::OpenGL) {
1206 QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL, true);
1207 }
1208}
1209
1211{
1212 return openGLCheckResult->isSupportedVersion();
1213}
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