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