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