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