diff --git a/krita/main.cc b/krita/main.cc index 94f025dd64..83da7ed187 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,281 +1,291 @@ /* * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * Copyright (c) 2015 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "data/splash/splash_screen.xpm" #include "data/splash/splash_holidays.xpm" #include "KisDocument.h" #include "kis_splash_screen.h" #include "KisPart.h" #include "KisApplicationArguments.h" #include #include "input/KisQtWidgetsTweaker.h" #if defined Q_OS_WIN #include #include #include #elif defined HAVE_X11 #include #include #endif #if defined HAVE_KCRASH #include #elif defined USE_DRMINGW namespace { void tryInitDrMingw() { wchar_t path[MAX_PATH]; QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll"); if (pathStr.size() > MAX_PATH - 1) { return; } int pathLen = pathStr.toWCharArray(path); path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator HMODULE hMod = LoadLibraryW(path); if (!hMod) { return; } // No need to call ExcHndlInit since the crash handler is installed on DllMain auto myExcHndlSetLogFileNameA = reinterpret_cast(GetProcAddress(hMod, "ExcHndlSetLogFileNameA")); if (!myExcHndlSetLogFileNameA) { return; } // Set the log file path to %LocalAppData%\kritacrash.log QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QStringLiteral("\\kritacrash.log"); myExcHndlSetLogFileNameA(logFile.toLocal8Bit()); } } // namespace #endif extern "C" int main(int argc, char **argv) { // The global initialization of the random generator qsrand(time(0)); bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty(); #if defined HAVE_X11 qputenv("QT_QPA_PLATFORM", "xcb"); #endif /** * Disable debug output by default. (krita.input enables tablet debugging.) * Debug logs can be controlled by an environment variable QT_LOGGING_RULES. * * As an example, to get full debug output, run the following: * export QT_LOGGING_RULES="krita*=true"; krita * * See: http://doc.qt.io/qt-5/qloggingcategory.html */ QLoggingCategory::setFilterRules("krita*.debug=false\n" "krita*.warning=true\n" "krita.tabletlog=true"); // A per-user unique string, without /, because QLocalServer cannot use names with a / in it QString key = "Krita3" + QDesktopServices::storageLocation(QDesktopServices::HomeLocation).replace("/", "_"); key = key.replace(":", "_").replace("\\","_"); QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); - KisOpenGL::setDefaultFormat(); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); bool singleApplication = true; + bool enableOpenGLDebug = false; + bool openGLDebugSynchronous = false; { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); singleApplication = kritarc.value("EnableSingleApplication", true).toBool(); #if QT_VERSION >= 0x050600 if (kritarc.value("EnableHiDPI", false).toBool()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } if (!qgetenv("KRITA_HIDPI").isEmpty()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); } #endif + if (!qgetenv("KRITA_OPENGL_DEBUG").isEmpty()) { + enableOpenGLDebug = true; + } else { + enableOpenGLDebug = kritarc.value("EnableOpenGLDebug", false).toBool(); + } + if (enableOpenGLDebug && (qgetenv("KRITA_OPENGL_DEBUG") == "sync" || kritarc.value("OpenGLDebugSynchronous", false).toBool())) { + openGLDebugSynchronous = true; + } } + KisOpenGL::setDefaultFormat(enableOpenGLDebug, openGLDebugSynchronous); KLocalizedString::setApplicationDomain("krita"); // first create the application so we can create a pixmap KisApplication app(key, argc, argv); #ifdef Q_OS_LINUX qputenv("XDG_DATA_DIRS", QFile::encodeName(KoResourcePaths::getApplicationRoot() + "share") + ":" + qgetenv("XDG_DATA_DIRS")); #else qputenv("XDG_DATA_DIRS", QFile::encodeName(KoResourcePaths::getApplicationRoot() + "share")); #endif qDebug() << "Setting XDG_DATA_DIRS" << qgetenv("XDG_DATA_DIRS"); qDebug() << "Available translations" << KLocalizedString::availableApplicationTranslations(); qDebug() << "Available domain translations" << KLocalizedString::availableDomainTranslations("krita"); // Now that the paths are set, set the language. First check the override from the langage // selection dialog. { QSettings languageoverride(configPath + QStringLiteral("/klanguageoverridesrc"), QSettings::IniFormat); languageoverride.beginGroup(QStringLiteral("Language")); QString language = languageoverride.value(qAppName(), "").toString(); qDebug() << "Override language:" << language; if (!language.isEmpty()) { KLocalizedString::setLanguages(language.split(":")); // And override Qt's locale, too qputenv("LANG", language.split(":").first().toUtf8()); QLocale locale(language.split(":").first()); QLocale::setDefault(locale); qDebug() << "Qt ui languages" << locale.uiLanguages(); } else { // And if there isn't one, check the one set by the system. // XXX: This doesn't work, for some !@#$% reason. QLocale locale = QLocale::system(); if (locale.bcp47Name() != QStringLiteral("en")) { qputenv("LANG", locale.bcp47Name().toLatin1()); KLocalizedString::setLanguages(QStringList() << locale.bcp47Name()); } } } #ifdef Q_OS_WIN QDir appdir(KoResourcePaths::getApplicationRoot()); QString path = qgetenv("PATH"); qputenv("PATH", QFile::encodeName(appdir.absolutePath() + "/bin" + ";" + appdir.absolutePath() + "/lib" + ";" + appdir.absolutePath() + "/Frameworks" + ";" + appdir.absolutePath() + ";" + path)); qDebug() << "PATH" << qgetenv("PATH"); #endif if (qApp->applicationDirPath().contains(KRITA_BUILD_DIR)) { qFatal("FATAL: You're trying to run krita from the build location. You can only run Krita from the installation location."); } #if defined HAVE_KCRASH KCrash::initialize(); #elif defined USE_DRMINGW tryInitDrMingw(); #endif // If we should clear the config, it has to be done as soon as possible after // KisApplication has been created. Otherwise the config file may have been read // and stored in a KConfig object we have no control over. app.askClearConfig(); KisApplicationArguments args(app); if (singleApplication && app.isRunning()) { // only pass arguments to main instance if they are not for batch processing // any batch processing would be done in this separate instance const bool batchRun = (args.print() || args.exportAs() || args.exportAsPdf()); if (!batchRun) { QByteArray ba = args.serialize(); if (app.sendMessage(ba)) { return 0; } } } if (!runningInKDE) { // Icons in menus are ugly and distracting app.setAttribute(Qt::AA_DontShowIconsInMenus); } #if defined HAVE_X11 app.installNativeEventFilter(KisXi2EventFilter::instance()); #endif app.installEventFilter(KisQtWidgetsTweaker::instance()); // then create the pixmap from an xpm: we cannot get the // location of our datadir before we've started our components, // so use an xpm. QDate currentDate = QDate::currentDate(); QWidget *splash = 0; if (currentDate > QDate(currentDate.year(), 12, 4) || currentDate < QDate(currentDate.year(), 1, 9)) { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_holidays_xpm)); } else { splash = new KisSplashScreen(app.applicationVersion(), QPixmap(splash_screen_xpm)); } app.setSplashScreen(splash); #if defined Q_OS_WIN KisTabletSupportWin::init(); // app.installNativeEventFilter(new KisTabletSupportWin()); #endif if (!app.start(args)) { return 1; } #if QT_VERSION >= 0x050700 app.setAttribute(Qt::AA_CompressHighFrequencyEvents, false); #endif // Set up remote arguments. QObject::connect(&app, SIGNAL(messageReceived(QByteArray,QObject*)), &app, SLOT(remoteArguments(QByteArray,QObject*))); QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), &app, SLOT(fileOpenRequested(QString))); int state = app.exec(); { QSettings kritarc(configPath + QStringLiteral("/kritadisplayrc"), QSettings::IniFormat); kritarc.setValue("canvasState", "OPENGL_SUCCESS"); } return state; } diff --git a/libs/ui/opengl/kis_opengl.cpp b/libs/ui/opengl/kis_opengl.cpp index e3ddf4dafd..f305e60979 100644 --- a/libs/ui/opengl/kis_opengl.cpp +++ b/libs/ui/opengl/kis_opengl.cpp @@ -1,233 +1,261 @@ /* * Copyright (c) 2007 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "opengl/kis_opengl.h" #include +#include #include #include #include #include #include #include #include #include #include #include #include #include namespace { bool defaultFormatIsSet = false; + bool isDebugEnabled = false; + bool isDebugSynchronous = false; bool initialized = false; bool NeedsFenceWorkaround = false; bool NeedsPixmapCacheWorkaround = false; int glMajorVersion = 0; int glMinorVersion = 0; bool supportsDeprecatedFunctions = false; QString Renderer; + + void openglOnMessageLogged(const QOpenGLDebugMessage& debugMessage) { + qDebug() << "OpenGL:" << debugMessage; + } } void KisOpenGL::initialize() { if (initialized) return; if (!defaultFormatIsSet) { qWarning() << "Default OpenGL format was not set before calling KisOpenGL::initialize. This might be a BUG!"; setDefaultFormat(); } // we need a QSurface active to get our GL functions from the context QWindow surface; surface.setSurfaceType( QSurface::OpenGLSurface ); surface.create(); QOpenGLContext context; context.create(); if (!context.isValid()) return; context.makeCurrent( &surface ); QOpenGLFunctions *funcs = context.functions(); funcs->initializeOpenGLFunctions(); #ifndef GL_RENDERER # define GL_RENDERER 0x1F01 #endif Renderer = QString((const char*)funcs->glGetString(GL_RENDERER)); /** * Warn about Intel's broken video drivers */ #if defined Q_OS_WIN KisConfig cfg; if (cfg.useOpenGL() && Renderer.startsWith("Intel") && !cfg.readEntry("WarnedAboutIntel", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You have an Intel(R) HD Graphics video adapter.\n" "If you experience problems like a crash, a black or blank screen," "please update your display driver to the latest version.\n\n" "If Krita crashes, it will disable OpenGL rendering. Please restart Krita in that case.\n After updating your drivers you can re-enable OpenGL in Krita's Settings.\n")); cfg.writeEntry("WarnedAboutIntel", true); } #endif qDebug() << "OpenGL Info"; qDebug() << " Vendor: " << reinterpret_cast(funcs->glGetString(GL_VENDOR)); qDebug() << " Renderer: " << Renderer; qDebug() << " Version: " << reinterpret_cast(funcs->glGetString(GL_VERSION)); qDebug() << " Shading language: " << reinterpret_cast(funcs->glGetString(GL_SHADING_LANGUAGE_VERSION)); qDebug() << " Requested format: " << QSurfaceFormat::defaultFormat(); qDebug() << " Current format: " << context.format(); glMajorVersion = context.format().majorVersion(); glMinorVersion = context.format().minorVersion(); supportsDeprecatedFunctions = (context.format().options() & QSurfaceFormat::DeprecatedFunctions); qDebug() << " Version:" << glMajorVersion << "." << glMinorVersion; qDebug() << " Supports deprecated functions" << supportsDeprecatedFunctions; initialized = true; } void KisOpenGL::initializeContext(QOpenGLContext *ctx) { KisConfig cfg; initialize(); dbgUI << "OpenGL: Opening new context"; + if (isDebugEnabled) { + // Passing ctx for ownership management only, not specifying context. + // QOpenGLDebugLogger only function on the current active context. + // FIXME: Do we need to make sure ctx is the active context? + QOpenGLDebugLogger* openglLogger = new QOpenGLDebugLogger(ctx); + if (openglLogger->initialize()) { + qDebug() << "QOpenGLDebugLogger is initialized. Check whether you get a message below."; + QObject::connect(openglLogger, &QOpenGLDebugLogger::messageLogged, &openglOnMessageLogged); + openglLogger->startLogging(isDebugSynchronous ? QOpenGLDebugLogger::SynchronousLogging : QOpenGLDebugLogger::AsynchronousLogging); + openglLogger->logMessage(QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("QOpenGLDebugLogger is logging."))); + } else { + qDebug() << "QOpenGLDebugLogger cannot be initialized."; + delete openglLogger; + } + } // Double check we were given the version we requested QSurfaceFormat format = ctx->format(); QOpenGLFunctions *f = ctx->functions(); f->initializeOpenGLFunctions(); QFile log(QDesktopServices::storageLocation(QDesktopServices::TempLocation) + "/krita-opengl.txt"); log.open(QFile::WriteOnly); QString vendor((const char*)f->glGetString(GL_VENDOR)); log.write(vendor.toLatin1()); log.write(", "); log.write(Renderer.toLatin1()); log.write(", "); QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); log.close(); // Check if we have a bugged driver that needs fence workaround bool isOnX11 = false; #ifdef HAVE_X11 isOnX11 = true; #endif if ((isOnX11 && Renderer.startsWith("AMD")) || cfg.forceOpenGLFenceWorkaround()) { NeedsFenceWorkaround = true; } /** * NVidia + Qt's openGL don't play well together and one cannot * draw a pixmap on a widget more than once in one rendering cycle. * * It can be workarounded by drawing strictly via QPixmapCache and * only when the pixmap size in bigger than doubled size of the * display framebuffer. That is for 8-bit HD display, you should have * a cache bigger than 16 MiB. Don't ask me why. (DK) * * See bug: https://bugs.kde.org/show_bug.cgi?id=361709 * * TODO: check if this workaround is still needed after merging * Qt5+openGL3 branch. */ if (vendor.toUpper().contains("NVIDIA")) { NeedsPixmapCacheWorkaround = true; const QRect screenSize = QApplication::desktop()->screenGeometry(); const int minCacheSize = 20 * 1024; const int cacheSize = 2048 + 2 * 4 * screenSize.width() * screenSize.height() / 1024; //KiB QPixmapCache::setCacheLimit(qMax(minCacheSize, cacheSize)); } } // XXX Temporary function to allow LoD on OpenGL3 without triggering // all of the other 3.2 functionality, can be removed once we move to Qt5.7 bool KisOpenGL::supportsLoD() { initialize(); return (glMajorVersion * 100 + glMinorVersion) >= 300; } bool KisOpenGL::hasOpenGL3() { initialize(); return (glMajorVersion * 100 + glMinorVersion) >= 302; } bool KisOpenGL::supportsFenceSync() { initialize(); return glMajorVersion >= 3; } bool KisOpenGL::needsFenceWorkaround() { initialize(); return NeedsFenceWorkaround; } bool KisOpenGL::needsPixmapCacheWorkaround() { initialize(); return NeedsPixmapCacheWorkaround; } -void KisOpenGL::setDefaultFormat() +void KisOpenGL::setDefaultFormat(bool enableDebug, bool debugSynchronous) { if (defaultFormatIsSet) { return; } defaultFormatIsSet = true; QSurfaceFormat format; #ifdef Q_OS_OSX format.setVersion(3, 2); format.setProfile(QSurfaceFormat::CoreProfile); #else // XXX This can be removed once we move to Qt5.7 format.setVersion(3, 0); format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setOptions(QSurfaceFormat::DeprecatedFunctions); #endif format.setDepthBufferSize(24); format.setStencilBufferSize(8); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setSwapInterval(0); // Disable vertical refresh syncing + isDebugEnabled = enableDebug; + if (enableDebug) { + format.setOption(QSurfaceFormat::DebugContext, true); + isDebugSynchronous = debugSynchronous; + qDebug() << "QOpenGLDebugLogger will be enabled, synchronous:" << debugSynchronous; + } QSurfaceFormat::setDefaultFormat(format); } bool KisOpenGL::hasOpenGL() { return ((glMajorVersion * 100 + glMinorVersion) >= 201); //return (glMajorVersion >= 3 && supportsDeprecatedFunctions); } diff --git a/libs/ui/opengl/kis_opengl.h b/libs/ui/opengl/kis_opengl.h index 4885026374..42a4641496 100644 --- a/libs/ui/opengl/kis_opengl.h +++ b/libs/ui/opengl/kis_opengl.h @@ -1,85 +1,85 @@ /* * Copyright (c) 2007 Adrian Page * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_OPENGL_H_ #define KIS_OPENGL_H_ /** @file */ #include #include class QOpenGLContext; #include "kritaui_export.h" /** * This class manages a shared OpenGL context and provides utility * functions for checking capabilities and error reporting. */ class KRITAUI_EXPORT KisOpenGL { public: enum FilterMode { NearestFilterMode, // nearest BilinearFilterMode, // linear, no mipmap TrilinearFilterMode, // LINEAR_MIPMAP_LINEAR HighQualityFiltering // Mipmaps + custom shader }; public: /// Request OpenGL version 3.2 static void initialize(); /// Initialize shared OpenGL context static void initializeContext(QOpenGLContext *ctx); static bool supportsLoD(); static bool hasOpenGL3(); /// Check for OpenGL static bool hasOpenGL(); /** * @brief supportsFilter * @return True if OpenGL provides fence sync methods. */ static bool supportsFenceSync(); /** * Returns true if we have a driver that has bugged support to sync objects (a fence) * and false otherwise. */ static bool needsFenceWorkaround(); /** * @see a comment in initializeContext() */ static bool needsPixmapCacheWorkaround(); - static void setDefaultFormat(); + static void setDefaultFormat(bool enableDebug = false, bool debugSynchronous = false); private: KisOpenGL(); }; #endif // KIS_OPENGL_H_