diff --git a/krita/main.cc b/krita/main.cc index 077983e42b9..3112558f45a 100644 --- a/krita/main.cc +++ b/krita/main.cc @@ -1,205 +1,210 @@ /* * main.cc - part of KImageShop * * Copyright (c) 1999 Matthias Elter * Copyright (c) 2002 Patrick Julien * * 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 */ #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 "ui/kis_aboutdata.h" #include "ui/kis_factory2.h" #include "ui/KisDocument.h" #include "kis_splash_screen.h" #include "KisPart.h" +#include "opengl/kis_opengl.h" #if defined Q_OS_WIN #include #include #include #ifdef USE_BREAKPAD #include "kis_crash_handler.h" #endif #elif defined HAVE_X11 #include #if QT_VERSION < 0x040800 // needed for XInitThreads() #include #endif #endif extern "C" int main(int argc, char **argv) { bool runningInKDE = !qgetenv("KDE_FULL_SESSION").isEmpty(); #ifdef HAVE_X11 if (runningInKDE) { qputenv("QT_NO_GLIB", "1"); } #endif #ifdef USE_BREAKPAD qputenv("KDE_DEBUG", "1"); KisCrashHandler crashHandler; Q_UNUSED(crashHandler); #endif #if defined Q_OS_WIN SetProcessDPIAware(); // The n-trig wintab driver needs this to report the correct dimensions #endif int state; K4AboutData *aboutData = KisFactory::aboutData(); KCmdLineArgs::init(argc, argv, aboutData); KCmdLineOptions options; options.add("print", ki18n("Only print and exit")); options.add("template", ki18n("Open a new document with a template")); options.add("dpi ", ki18n("Override display DPI")); options.add("export-pdf", ki18n("Only export to PDF and exit")); options.add("export", ki18n("Export to the given filename and exit")); options.add("export-filename ", ki18n("Filename for export/export-pdf")); options.add("profile-filename ", ki18n("Filename to write profiling information into.")); options.add("+[file(s)]", ki18n("File(s) or URL(s) to open")); KCmdLineArgs::addCmdLineOptions(options); // A per-user unique string, without /, because QLocalServer cannot use names with a / in it QString key = "Krita" + QDesktopServices::storageLocation(QDesktopServices::HomeLocation).replace("/", "_"); key = key.replace(":", "_").replace("\\","_"); #if defined HAVE_X11 #if QT_VERSION >= 0x040800 // we need to call XInitThreads() (which this does) because of gmic (and possibly others) // do their own X11 stuff in their own threads // this call must happen before the creation of the application (see AA_X11InitThreads docs) QCoreApplication::setAttribute(Qt::AA_X11InitThreads, true); #else XInitThreads(); #endif #endif #if defined HAVE_OPENGL QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true); QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); #endif // first create the application so we can create a pixmap KisApplication app(key); // 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(); // create factory only after application, the componentData it creates in the // constructor will need an existing QCoreApplication at least with Qt5/KF5, // to set name of application etc., as also needed to find resources KisFactory factory; Q_UNUSED(factory); // Not really, it'll self-destruct on exiting main if (app.isRunning()) { KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); // 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->isSet("print") || args->isSet("export") || args->isSet("export-pdf"); if (!batchRun) { QByteArray ba; QDataStream ds(&ba, QIODevice::WriteOnly); args->saveAppArgs(ds); ds.device()->close(); if (app.sendMessage(ba)) { return 0; } } } #if defined Q_OS_WIN KisTabletSupportWin tabletSupportWin; tabletSupportWin.init(); app.installNativeEventFilter(&tabletSupportWin); #elif defined HAVE_X11 KisTabletSupportX11 tabletSupportX11; tabletSupportX11.init(); app.installNativeEventFilter(&tabletSupportX11); #endif +#if defined HAVE_OPENGL + KisOpenGL::initialize(); +#endif + if (!runningInKDE) { // Icons in menus are ugly and distracting app.setAttribute(Qt::AA_DontShowIconsInMenus); } // 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(aboutData->version(), QPixmap(splash_holidays_xpm)); } else { splash = new KisSplashScreen(aboutData->version(), QPixmap(splash_screen_xpm)); } app.setSplashScreen(splash); if (!app.start()) { return 1; } // 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))); state = app.exec(); return state; } diff --git a/krita/ui/opengl/kis_opengl.cpp b/krita/ui/opengl/kis_opengl.cpp index 231c1596604..e59123b6952 100644 --- a/krita/ui/opengl/kis_opengl.cpp +++ b/krita/ui/opengl/kis_opengl.cpp @@ -1,127 +1,144 @@ /* * 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" #ifdef HAVE_OPENGL #include #include #include #endif #include #include #include #include #include #include #include #include #include namespace { #ifdef HAVE_OPENGL QOffscreenSurface *SharedSurface = 0; QOpenGLContext *SharedContext = 0; #endif bool NeedsFenceWorkaround = false; + int glVersion = 0; } void KisOpenGL::initialize() { #ifdef HAVE_OPENGL dbgUI << "OpenGL: initializing"; - KisConfig cfg;; + KisConfig cfg; QSurfaceFormat format; - format.setProfile(QSurfaceFormat::CoreProfile); + format.setProfile(QSurfaceFormat::CompatibilityProfile); format.setDepthBufferSize(24); format.setStencilBufferSize(8); format.setVersion(3, 2); - if (cfg.disableDoubleBuffering()) { + // if (cfg.disableDoubleBuffering()) { + if (false) { format.setSwapBehavior(QSurfaceFormat::SingleBuffer); } + else { + format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); + } format.setSwapInterval(0); // Disable vertical refresh syncing QSurfaceFormat::setDefaultFormat(format); +} + +int KisOpenGL::initializeContext(QOpenGLContext* s) { + KisConfig cfg; + dbgUI << "OpenGL: Opening new context"; + + // Double check we were given the version we requested + QSurfaceFormat format = s->format(); + glVersion = 100 * format.majorVersion() + format.minorVersion(); if (!SharedSurface) { SharedSurface = new QOffscreenSurface(); SharedSurface->setFormat(format); SharedSurface->create(); } if (!SharedContext) { - SharedContext = new QOpenGLContext(qApp); + SharedContext = new QOpenGLContext; SharedContext->setFormat(format); + SharedContext->setShareContext(s); SharedContext->create(); SharedContext->makeCurrent(SharedSurface); } QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); 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(", "); QString renderer((const char*)f->glGetString(GL_RENDERER)); log.write(renderer.toLatin1()); log.write(", "); QString version((const char*)f->glGetString(GL_VERSION)); log.write(version.toLatin1()); // 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; } #else NeedsFenceWorkaround = false; #endif + return glVersion; } bool KisOpenGL::supportsFenceSync() { // return glVersion > 302; return true; } bool KisOpenGL::supportsGLSL13() { + // return glVersion > 300; return true; // QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_0; -- we force 3.2 for now, since that has glFenceSync } bool KisOpenGL::needsFenceWorkaround() { return NeedsFenceWorkaround; } bool KisOpenGL::hasOpenGL() { // QT5TODO: figure out runtime whether we have opengl... return true; } diff --git a/krita/ui/opengl/kis_opengl.h b/krita/ui/opengl/kis_opengl.h index 8a6fda3c3d3..f1a3767772d 100644 --- a/krita/ui/opengl/kis_opengl.h +++ b/krita/ui/opengl/kis_opengl.h @@ -1,71 +1,75 @@ /* * 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 #if defined(_WIN32) || defined(_WIN64) #include #endif #include +class QOpenGLContext; #include "krita_export.h" /** * This class manages a shared OpenGL context and provides utility * functions for checking capabilities and error reporting. */ class KRITAUI_EXPORT KisOpenGL { public: + /// Request OpenGL version 3.2 static void initialize(); static bool hasOpenGL(); + /// Initialize shared OpenGL context + static int initializeContext(QOpenGLContext* s); /** * @brief supportsGLSL13 * @return true if we have a modern opengl capable of high-quality filtering */ static bool supportsGLSL13(); /** * @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(); private: KisOpenGL(); }; #endif // KIS_OPENGL_H_ diff --git a/krita/ui/opengl/kis_opengl_canvas2.cpp b/krita/ui/opengl/kis_opengl_canvas2.cpp index 2b264b7af66..c9c2f06c961 100644 --- a/krita/ui/opengl/kis_opengl_canvas2.cpp +++ b/krita/ui/opengl/kis_opengl_canvas2.cpp @@ -1,687 +1,700 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2006-2013 * * 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. */ #define GL_GLEXT_PROTOTYPES #include "opengl/kis_opengl_canvas2.h" #ifdef HAVE_OPENGL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include +#include +#include #include #include #include "KoToolProxy.h" #include "kis_config.h" #include "kis_types.h" #include #include "canvas/kis_canvas2.h" #include "kis_coordinates_converter.h" #include "kis_image.h" #include "opengl/kis_opengl_image_textures.h" #include "kis_canvas_resource_provider.h" #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_debug.h" #include "opengl/kis_opengl_canvas2_p.h" #include "kis_coordinates_converter.h" #include "canvas/kis_display_filter.h" #define NEAR_VAL -1000.0 #define FAR_VAL 1000.0 #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif #define PROGRAM_VERTEX_ATTRIBUTE 0 #define PROGRAM_TEXCOORD_ATTRIBUTE 1 static bool OPENGL_SUCCESS = false; namespace { const GLuint NO_PROGRAM = 0; } struct KisOpenGLCanvas2::Private { public: Private() : canvasInitialized(false) , displayShader(0) , checkerShader(0) , glSyncObject(0) , wrapAroundMode(false) { vertices = new QVector3D[6]; texCoords = new QVector2D[6]; } ~Private() { delete displayShader; delete checkerShader; delete[] vertices; delete[] texCoords; Sync::deleteSync(glSyncObject); } bool canvasInitialized; QVector3D *vertices; QVector2D *texCoords; KisOpenGLImageTexturesSP openGLImageTextures; QOpenGLShaderProgram *displayShader; int displayUniformLocationModelViewProjection; int displayUniformLocationTextureMatrix; int displayUniformLocationViewPortScale; int displayUniformLocationTexelSize; int displayUniformLocationTexture0; int displayUniformLocationTexture1; QOpenGLShaderProgram *checkerShader; int checkerUniformLocationModelViewProjection; int checkerUniformLocationTextureMatrix; KisDisplayFilter* displayFilter; KisTextureTile::FilterMode filterMode; GLsync glSyncObject; bool firstDrawImage; qreal scaleX, scaleY; bool wrapAroundMode; int xToColWithWrapCompensation(int x, const QRect &imageRect) { int firstImageColumn = openGLImageTextures->xToCol(imageRect.left()); int lastImageColumn = openGLImageTextures->xToCol(imageRect.right()); int colsPerImage = lastImageColumn - firstImageColumn + 1; int numWraps = floor(qreal(x) / imageRect.width()); int remainder = x - imageRect.width() * numWraps; return colsPerImage * numWraps + openGLImageTextures->xToCol(remainder); } int yToRowWithWrapCompensation(int y, const QRect &imageRect) { int firstImageRow = openGLImageTextures->yToRow(imageRect.top()); int lastImageRow = openGLImageTextures->yToRow(imageRect.bottom()); int rowsPerImage = lastImageRow - firstImageRow + 1; int numWraps = floor(qreal(y) / imageRect.height()); int remainder = y - imageRect.height() * numWraps; return rowsPerImage * numWraps + openGLImageTextures->yToRow(remainder); } }; KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisOpenGLImageTexturesSP imageTextures) : QOpenGLWidget(parent) , KisCanvasWidgetBase(canvas, coordinatesConverter) , d(new Private()) { - initializeOpenGLFunctions(); + QSurfaceFormat format; + format.setDepthBufferSize(24); + setFormat(format); KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_STARTED"); d->openGLImageTextures = imageTextures; setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_AcceptTouchEvents); setAutoFillBackground(false); setAttribute(Qt::WA_InputMethodEnabled, true); setAttribute(Qt::WA_DontCreateNativeAncestors, true); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); slotConfigChanged(); - - d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); - cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); } KisOpenGLCanvas2::~KisOpenGLCanvas2() { delete d; } void KisOpenGLCanvas2::setDisplayFilter(KisDisplayFilter* displayFilter) { d->displayFilter = displayFilter; - d->canvasInitialized = false; -// initializeDisplayShader(); - initializeCheckerShader(); - d->canvasInitialized = true; + if (d->canvasInitialized) { + d->canvasInitialized = false; + initializeDisplayShader(); + initializeCheckerShader(); + d->canvasInitialized = true; + } } void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value) { d->wrapAroundMode = value; update(); } void KisOpenGLCanvas2::initializeGL() { // KisConfig cfg; // if (cfg.disableVSync()) { // if (!VSyncWorkaround::tryDisableVSync(this)) { // qWarning(); // qWarning() << "WARNING: We didn't manage to switch off VSync on your graphics adapter."; // qWarning() << "WARNING: It means either your hardware or driver doesn't support it,"; // qWarning() << "WARNING: or we just don't know about this hardware. Please report us a bug"; // qWarning() << "WARNING: with the output of \'glxinfo\' for your card."; // qWarning(); // qWarning() << "WARNING: Trying to workaround it by disabling Double Buffering."; // qWarning() << "WARNING: You may see some flickering when painting with some tools. It doesn't"; // qWarning() << "WARNING: affect the quality of the final image, though."; // qWarning(); // if (cfg.disableDoubleBuffering() && QOpenGLContext::currentContext()->format().swapBehavior() == QSurfaceFormat::DoubleBuffer) { // qCritical() << "CRITICAL: Failed to disable Double Buffering. Lines may look \"bended\" on your image."; // qCritical() << "CRITICAL: Your graphics card or driver does not fully support Krita's OpenGL canvas."; // qCritical() << "CRITICAL: For an optimal experience, please disable OpenGL"; // qCritical(); // } // } // } + KisConfig cfg; + qDebug() << "OpenGL: Preparing to initialize OpenGL for KisCanvas"; + int glVersion = KisOpenGL::initializeContext(context()); + qDebug() << "OpenGL: Context gives version" << glVersion; + initializeOpenGLFunctions(); + VSyncWorkaround::tryDisableVSync(context()); + d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); initializeCheckerShader(); -// initializeDisplayShader(); + initializeDisplayShader(); -// Sync::init(); + Sync::init(); d->canvasInitialized = true; } void KisOpenGLCanvas2::resizeGL(int width, int height) { coordinatesConverter()->setCanvasWidgetSize(QSize(width, height)); paintGL(); } void KisOpenGLCanvas2::paintGL() { if (!OPENGL_SUCCESS) { KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_PAINT_STARTED"); } + QPainter gc(this); + gc.beginNativePainting(); renderCanvasGL(); - QPainter gc(this); + if (d->glSyncObject) { + Sync::deleteSync(d->glSyncObject); + } + d->glSyncObject = Sync::getSync(); + gc.endNativePainting(); + renderDecorations(&gc); gc.end(); -// if (d->glSyncObject) { -// Sync::deleteSync(d->glSyncObject); -// } - -// d->glSyncObject = Sync::getSync(); if (!OPENGL_SUCCESS) { KisConfig cfg; cfg.writeEntry("canvasState", "OPENGL_SUCCESS"); OPENGL_SUCCESS = true; } } bool KisOpenGLCanvas2::isBusy() const { -// return Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled; - return false; + return Sync::syncStatus(d->glSyncObject) == Sync::Unsignaled; } inline void rectToVertices(QVector3D* vertices, const QRectF &rc) { vertices[0] = QVector3D(rc.left(), rc.bottom(), 0.f); vertices[1] = QVector3D(rc.left(), rc.top(), 0.f); vertices[2] = QVector3D(rc.right(), rc.bottom(), 0.f); vertices[3] = QVector3D(rc.left(), rc.top(), 0.f); vertices[4] = QVector3D(rc.right(), rc.top(), 0.f); vertices[5] = QVector3D(rc.right(), rc.bottom(), 0.f); } inline void rectToTexCoords(QVector2D* texCoords, const QRectF &rc) { texCoords[0] = QVector2D(rc.left(), rc.bottom()); texCoords[1] = QVector2D(rc.left(), rc.top()); texCoords[2] = QVector2D(rc.right(), rc.bottom()); texCoords[3] = QVector2D(rc.left(), rc.top()); texCoords[4] = QVector2D(rc.right(), rc.top()); texCoords[5] = QVector2D(rc.right(), rc.bottom()); } void KisOpenGLCanvas2::drawCheckers() { if (!d->checkerShader) { return; } KisCoordinatesConverter *converter = coordinatesConverter(); QTransform textureTransform; QTransform modelTransform; QRectF textureRect; QRectF modelRect; QRectF viewportRect = !d->wrapAroundMode ? converter->imageRectInViewportPixels() : converter->widgetToViewport(this->rect()); converter->getOpenGLCheckersInfo(viewportRect, &textureTransform, &modelTransform, &textureRect, &modelRect); // XXX: getting a config object every time we draw the checkers is bad for performance! KisConfig cfg; GLfloat checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast(cfg.checkSize()); textureTransform *= QTransform::fromScale(checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE, checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE); d->checkerShader->bind(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(modelTransform); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->checkerShader->setUniformValue(d->checkerUniformLocationModelViewProjection, modelMatrix); QMatrix4x4 textureMatrix(textureTransform); d->checkerShader->setUniformValue(d->checkerUniformLocationTextureMatrix, textureMatrix); //Setup the geometry for rendering rectToVertices(d->vertices, modelRect); d->checkerShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->checkerShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); rectToTexCoords(d->texCoords, textureRect); d->checkerShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); d->checkerShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); // render checkers glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, d->openGLImageTextures->checkerTexture()); glDrawArrays(GL_TRIANGLES, 0, 6); glBindTexture(GL_TEXTURE_2D, 0); d->checkerShader->release(); } void KisOpenGLCanvas2::drawImage() { if (!d->displayShader) { return; } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); KisCoordinatesConverter *converter = coordinatesConverter(); d->displayShader->bind(); QMatrix4x4 projectionMatrix; projectionMatrix.setToIdentity(); projectionMatrix.ortho(0, width(), height(), 0, NEAR_VAL, FAR_VAL); // Set view/projection matrices QMatrix4x4 modelMatrix(coordinatesConverter()->imageToWidgetTransform()); modelMatrix.optimize(); modelMatrix = projectionMatrix * modelMatrix; d->displayShader->setUniformValue(d->displayUniformLocationModelViewProjection, modelMatrix); QMatrix4x4 textureMatrix; textureMatrix.setToIdentity(); d->displayShader->setUniformValue(d->displayUniformLocationTextureMatrix, textureMatrix); QRectF widgetRect(0,0, width(), height()); QRectF widgetRectInImagePixels = converter->documentToImage(converter->widgetToDocument(widgetRect)); qreal scaleX, scaleY; converter->imageScale(&scaleX, &scaleY); d->displayShader->setUniformValue(d->displayUniformLocationViewPortScale, (GLfloat) scaleX); d->displayShader->setUniformValue(d->displayUniformLocationTexelSize, (GLfloat) d->openGLImageTextures->texelSize()); QRect ir = d->openGLImageTextures->storedImageBounds(); QRect wr = widgetRectInImagePixels.toAlignedRect(); if (!d->wrapAroundMode) { // if we don't want to paint wrapping images, just limit the // processing area, and the code will handle all the rest wr &= ir; } int firstColumn = d->xToColWithWrapCompensation(wr.left(), ir); int lastColumn = d->xToColWithWrapCompensation(wr.right(), ir); int firstRow = d->yToRowWithWrapCompensation(wr.top(), ir); int lastRow = d->yToRowWithWrapCompensation(wr.bottom(), ir); int minColumn = d->openGLImageTextures->xToCol(ir.left()); int maxColumn = d->openGLImageTextures->xToCol(ir.right()); int minRow = d->openGLImageTextures->yToRow(ir.top()); int maxRow = d->openGLImageTextures->yToRow(ir.bottom()); int imageColumns = maxColumn - minColumn + 1; int imageRows = maxRow - minRow + 1; for (int col = firstColumn; col <= lastColumn; col++) { for (int row = firstRow; row <= lastRow; row++) { int effectiveCol = col; int effectiveRow = row; QPointF tileWrappingTranslation; if (effectiveCol > maxColumn || effectiveCol < minColumn) { int translationStep = floor(qreal(col) / imageColumns); int originCol = translationStep * imageColumns; effectiveCol = col - originCol; tileWrappingTranslation.rx() = translationStep * ir.width(); } if (effectiveRow > maxRow || effectiveRow < minRow) { int translationStep = floor(qreal(row) / imageRows); int originRow = translationStep * imageRows; effectiveRow = row - originRow; tileWrappingTranslation.ry() = translationStep * ir.height(); } KisTextureTile *tile = d->openGLImageTextures->getTextureTileCR(effectiveCol, effectiveRow); KIS_ASSERT_RECOVER_BREAK(tile); /* * We create a float rect here to workaround Qt's * "history reasons" in calculation of right() * and bottom() coordinates of integer rects. */ QRectF textureRect(tile->tileRectInTexturePixels()); QRectF modelRect(tile->tileRectInImagePixels().translated(tileWrappingTranslation.x(), tileWrappingTranslation.y())); //Setup the geometry for rendering rectToVertices(d->vertices, modelRect); d->displayShader->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE); d->displayShader->setAttributeArray(PROGRAM_VERTEX_ATTRIBUTE, d->vertices); rectToTexCoords(d->texCoords, textureRect); d->displayShader->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE); d->displayShader->setAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE, d->texCoords); if (d->displayFilter) { glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_3D, d->displayFilter->lutTexture()); d->displayShader->setUniformValue(d->displayUniformLocationTexture1, 1); } glActiveTexture(GL_TEXTURE0); tile->bindToActiveTexture(); if (SCALE_MORE_OR_EQUAL_TO(scaleX, scaleY, 2.0)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); switch(d->filterMode) { case KisTextureTile::NearestFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); break; case KisTextureTile::BilinearFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); break; case KisTextureTile::TrilinearFilterMode: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); break; case KisTextureTile::HighQualityFiltering: if (SCALE_LESS_THAN(scaleX, scaleY, 0.5)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } break; } } glDrawArrays(GL_TRIANGLES, 0, 6); } } glBindTexture(GL_TEXTURE_2D, 0); d->displayShader->release(); } void KisOpenGLCanvas2::reportShaderLinkFailedAndExit(bool result, const QString &context, const QString &log) { KisConfig cfg; if (cfg.useVerboseOpenGLDebugOutput()) { qDebug() << "GL-log:" << context << log; } if (result) return; QMessageBox::critical(this, i18nc("@title:window", "Krita"), QString(i18n("Krita could not initialize the OpenGL canvas:\n\n%1\n\n%2\n\n Krita will disable OpenGL and close now.")).arg(context).arg(log), QMessageBox::Close); cfg.setUseOpenGL(false); cfg.setCanvasState("OPENGL_FAILED"); } void KisOpenGLCanvas2::initializeCheckerShader() { if (d->canvasInitialized) return; delete d->checkerShader; d->checkerShader = new QOpenGLShaderProgram(); QString vertexShaderName; QString fragmentShaderName; if (KisOpenGL::supportsGLSL13()) { vertexShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform.vert"); fragmentShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/simple_texture.frag"); } else { vertexShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform_legacy.vert"); fragmentShaderName = KGlobal::dirs()->findResource("data", "krita/shaders/simple_texture_legacy.frag"); } bool result; result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Vertex, vertexShaderName); reportShaderLinkFailedAndExit(result, "Checker vertex shader", d->checkerShader->log()); result = d->checkerShader->addShaderFromSourceFile(QOpenGLShader::Fragment, fragmentShaderName); reportShaderLinkFailedAndExit(result, "Checker fragment shader", d->checkerShader->log()); d->checkerShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE); d->checkerShader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE); result = d->checkerShader->link(); reportShaderLinkFailedAndExit(result, "Checker shader (link)", d->checkerShader->log()); Q_ASSERT(d->checkerShader->isLinked()); d->checkerUniformLocationModelViewProjection = d->checkerShader->uniformLocation("modelViewProjection"); d->checkerUniformLocationTextureMatrix = d->checkerShader->uniformLocation("textureMatrix"); } QByteArray KisOpenGLCanvas2::buildFragmentShader() { QByteArray shaderText; bool haveDisplayFilter = d->displayFilter && !d->displayFilter->program().isEmpty(); bool useHiQualityFiltering = d->filterMode == KisTextureTile::HighQualityFiltering; bool haveGLSL13 = KisOpenGL::supportsGLSL13(); QString filename = haveGLSL13 && useHiQualityFiltering ? "highq_downscale" : "simple_texture"; QString legacyPostfix = !haveGLSL13 ? "_legacy" : ""; QString filterPostfix = haveDisplayFilter ? "_ocio" : ""; QString prefaceKey = QString("krita/shaders/%1%2_preface.frag.inc") .arg(filename) .arg(legacyPostfix); QString mainKey = QString("krita/shaders/%1%2_main%3.frag.inc") .arg(filename) .arg(legacyPostfix) .arg(filterPostfix); { QFile prefaceFile(KGlobal::dirs()->findResource("data", prefaceKey)); prefaceFile.open(QIODevice::ReadOnly); shaderText.append(prefaceFile.readAll()); } if (haveDisplayFilter) { shaderText.append(d->displayFilter->program().toLatin1()); } { QFile mainFile(KGlobal::dirs()->findResource("data", mainKey)); mainFile.open(QIODevice::ReadOnly); shaderText.append(mainFile.readAll()); } return shaderText; } void KisOpenGLCanvas2::initializeDisplayShader() { if (d->canvasInitialized) return; delete d->displayShader; d->displayShader = new QOpenGLShaderProgram(); bool result = d->displayShader->addShaderFromSourceCode(QOpenGLShader::Fragment, buildFragmentShader()); reportShaderLinkFailedAndExit(result, "Display fragment shader", d->displayShader->log()); if (KisOpenGL::supportsGLSL13()) { result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform.vert")); } else { result = d->displayShader->addShaderFromSourceFile(QOpenGLShader::Vertex, KGlobal::dirs()->findResource("data", "krita/shaders/matrix_transform_legacy.vert")); } reportShaderLinkFailedAndExit(result, "Display vertex shader", d->displayShader->log()); d->displayShader->bindAttributeLocation("a_vertexPosition", PROGRAM_VERTEX_ATTRIBUTE); d->displayShader->bindAttributeLocation("a_textureCoordinate", PROGRAM_TEXCOORD_ATTRIBUTE); result = d->displayShader->link(); reportShaderLinkFailedAndExit(result, "Display shader (link)", d->displayShader->log()); Q_ASSERT(d->displayShader->isLinked()); d->displayUniformLocationModelViewProjection = d->displayShader->uniformLocation("modelViewProjection"); d->displayUniformLocationTextureMatrix = d->displayShader->uniformLocation("textureMatrix"); d->displayUniformLocationViewPortScale = d->displayShader->uniformLocation("viewportScale"); d->displayUniformLocationTexelSize = d->displayShader->uniformLocation("texelSize"); d->displayUniformLocationTexture0 = d->displayShader->uniformLocation("texture0"); d->displayUniformLocationTexture1 = d->displayShader->uniformLocation("texture1"); } void KisOpenGLCanvas2::slotConfigChanged() { KisConfig cfg; d->openGLImageTextures->generateCheckerTexture(createCheckersImage(cfg.checkSize())); d->openGLImageTextures->updateConfig(cfg.useOpenGLTextureBuffer(), cfg.numMipmapLevels()); d->filterMode = (KisTextureTile::FilterMode) cfg.openGLFilteringMode(); notifyConfigChanged(); } QVariant KisOpenGLCanvas2::inputMethodQuery(Qt::InputMethodQuery query) const { return processInputMethodQuery(query); } void KisOpenGLCanvas2::inputMethodEvent(QInputMethodEvent *event) { processInputMethodEvent(event); } void KisOpenGLCanvas2::renderCanvasGL() { // Draw the border (that is, clear the whole widget to the border color) QColor widgetBackgroundColor = borderColor(); glClearColor(widgetBackgroundColor.redF(), widgetBackgroundColor.greenF(), widgetBackgroundColor.blueF(), 1.0); glClear(GL_COLOR_BUFFER_BIT); drawCheckers(); drawImage(); } void KisOpenGLCanvas2::renderDecorations(QPainter *painter) { QRect boundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect(); drawDecorations(*painter, boundingRect); } bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next) { return focusNextPrevChild(next); } //void KisOpenGLCanvas2::paintEvent(QPaintEvent* event) //{ // // Workaround for bug 322808, paint events with only a partial rect cause flickering // // Drop those event and trigger a new full update // if (event->rect().width() == width() && event->rect().height() == height()) { // QOpenGLWidget::paintEvent(event); // } else { // update(); // } //} #endif // HAVE_OPENGL diff --git a/krita/ui/opengl/kis_opengl_canvas2_p.h b/krita/ui/opengl/kis_opengl_canvas2_p.h index 449086b58c7..d30633d5dbb 100644 --- a/krita/ui/opengl/kis_opengl_canvas2_p.h +++ b/krita/ui/opengl/kis_opengl_canvas2_p.h @@ -1,254 +1,254 @@ /* * Copyright (C) Boudewijn Rempt , (C) 2006 * * 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_CANVAS_2_P_H #define KIS_OPENGL_CANVAS_2_P_H #include #ifdef HAVE_OPENGL /** * This is a workaround for a very slow updates in OpenGL canvas (~6ms). * The delay happens because of VSync in the swapBuffers() call. At first * we try to disable VSync. If it fails we just disable Double Buffer * completely. * * This file is effectively a bit of copy-paste from qgl_x11.cpp */ #if defined Q_OS_LINUX #include #include #include #include #include #include #ifndef GL_NUM_EXTENSIONS #define GL_NUM_EXTENSIONS 0x821D #endif QString gl_library_name() { #if defined (QT_OPENGL_ES_2) return QLatin1String("GLESv2"); #else return QLatin1String("GL"); #endif } namespace VSyncWorkaround { bool tryDisableVSync(QOpenGLContext* ctx) { bool result = false; bool triedDisable = false; Display *dpy = QX11Info::display(); qDebug() << "OpenGL architecture is" << gl_library_name(); if (ctx->hasExtension("GLX_EXT_swap_control")) { qDebug() << "Swap control extension found."; typedef WId (*k_glXGetCurrentDrawable)(void); typedef void (*kis_glXSwapIntervalEXT)(Display*, WId, int); - typedef int (*k_glXQueryDrawable)(Display *, WId, int, unsigned int *); k_glXGetCurrentDrawable kis_glXGetCurrentDrawable = (k_glXGetCurrentDrawable)ctx->getProcAddress("glXGetCurrentDrawable"); kis_glXSwapIntervalEXT glXSwapIntervalEXT = (kis_glXSwapIntervalEXT)ctx->getProcAddress("glXSwapIntervalEXT"); - k_glXQueryDrawable kis_glXQueryDrawable = (k_glXQueryDrawable)ctx->getProcAddress("glXQueryDrawable"); WId wid = kis_glXGetCurrentDrawable(); if (glXSwapIntervalEXT) { glXSwapIntervalEXT(dpy, wid, 0); triedDisable = true; unsigned int swap = 1; #ifdef GLX_SWAP_INTERVAL_EXT + typedef int (*k_glXQueryDrawable)(Display *, WId, int, unsigned int *); + k_glXQueryDrawable kis_glXQueryDrawable = (k_glXQueryDrawable)ctx->getProcAddress("glXQueryDrawable"); kis_glXQueryDrawable(dpy, wid, GLX_SWAP_INTERVAL_EXT, &swap); #endif result = !swap; } else { qDebug() << "Couldn't load glXSwapIntervalEXT extension function"; } } else if (ctx->hasExtension("GLX_MESA_swap_control")) { qDebug() << "MESA swap control extension found."; typedef int (*kis_glXSwapIntervalMESA)(unsigned int); typedef int (*kis_glXGetSwapIntervalMESA)(void); kis_glXSwapIntervalMESA glXSwapIntervalMESA = (kis_glXSwapIntervalMESA)ctx->getProcAddress("glXSwapIntervalMESA"); kis_glXGetSwapIntervalMESA glXGetSwapIntervalMESA = (kis_glXGetSwapIntervalMESA)ctx->getProcAddress("glXGetSwapIntervalMESA"); if (glXSwapIntervalMESA) { int retval = glXSwapIntervalMESA(0); triedDisable = true; int swap = 1; if (glXGetSwapIntervalMESA) { swap = glXGetSwapIntervalMESA(); } else { qDebug() << "Couldn't load glXGetSwapIntervalMESA extension function"; } result = !retval && !swap; } else { qDebug() << "Couldn't load glXSwapIntervalMESA extension function"; } } else { qDebug() << "There is neither GLX_EXT_swap_control or GLX_MESA_swap_control extension supported"; } if (triedDisable && !result) { qCritical(); qCritical() << "CRITICAL: Your video driver forbids disabling VSync!"; qCritical() << "CRITICAL: Try toggling some VSync- or VBlank-related options in your driver configuration dialog."; qCritical() << "CRITICAL: NVIDIA users can do:"; qCritical() << "CRITICAL: sudo nvidia-settings > (tab) OpenGL settings > Sync to VBlank ( unchecked )"; qCritical(); } return result; } } #elif defined Q_OS_WIN // QT5TODO: T360 //#include namespace VSyncWorkaround { bool tryDisableVSync(QWidget *) { bool retval = false; #ifdef WGL_EXT_swap_control if (WGLEW_EXT_swap_control) { wglSwapIntervalEXT(0); int interval = wglGetSwapIntervalEXT(); if (interval) { qWarning() << "Failed to disable VSync with WGLEW_EXT_swap_control"; } retval = !interval; } else { qWarning() << "WGL_EXT_swap_control extension is not available"; } #else qWarning() << "GLEW WGL_EXT_swap_control extension is not compiled in"; #endif return retval; } } #else // !defined Q_OS_LINUX && !defined Q_OS_WIN namespace VSyncWorkaround { bool tryDisableVSync(QWidget *) { return false; } } #endif // defined Q_OS_LINUX #include namespace Sync { //For checking sync status enum SyncStatus { Signaled, Unsignaled }; #ifndef GL_SYNC_GPU_COMMANDS_COMPLETE #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 #endif #ifndef GL_UNSIGNALED #define GL_UNSIGNALED 0x9118 #endif #ifndef GL_SIGNALED #define GL_SIGNALED 0x9119 #endif #ifndef GL_SYNC_STATUS #define GL_SYNC_STATUS 0x9114 #endif //Function pointers for glFenceSync and glGetSynciv typedef GLsync (*kis_glFenceSync)(GLenum, GLbitfield); static kis_glFenceSync k_glFenceSync = 0; typedef void (*kis_glGetSynciv)(GLsync, GLenum, GLsizei, GLsizei*, GLint*); static kis_glGetSynciv k_glGetSynciv = 0; typedef void (*kis_glDeleteSync)(GLsync); static kis_glDeleteSync k_glDeleteSync = 0; //Initialise the function pointers for glFenceSync and glGetSynciv //Note: Assumes a current OpenGL context. void init() { QOpenGLContext* ctx = QOpenGLContext::currentContext(); #if defined Q_OS_WIN if (KisOpenGL::supportsFenceSync()) { #ifdef ENV64BIT k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSync"); k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSynciv"); k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSync"); #else k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSyncARB"); k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSyncivARB"); k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSyncARB"); #endif } #elif defined Q_OS_LINUX if (KisOpenGL::supportsFenceSync()) { k_glFenceSync = (kis_glFenceSync)ctx->getProcAddress("glFenceSync"); k_glGetSynciv = (kis_glGetSynciv)ctx->getProcAddress("glGetSynciv"); k_glDeleteSync = (kis_glDeleteSync)ctx->getProcAddress("glDeleteSync"); } #endif if (k_glFenceSync == 0 || k_glGetSynciv == 0 || k_glDeleteSync == 0) { qWarning("Could not find sync functions, disabling sync notification."); } } //Get a fence sync object from OpenGL GLsync getSync() { if(k_glFenceSync) { GLsync sync = k_glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); if (KisOpenGL::needsFenceWorkaround()) { glClientWaitSync(sync, 0, 1); } return sync; } return 0; } //Check the status of a sync object SyncStatus syncStatus(GLsync syncObject) { if(syncObject && k_glGetSynciv) { GLint status = -1; k_glGetSynciv(syncObject, GL_SYNC_STATUS, 1, 0, &status); return status == GL_SIGNALED ? Sync::Signaled : Sync::Unsignaled; } return Sync::Signaled; } void deleteSync(GLsync syncObject) { if(syncObject && k_glDeleteSync) { k_glDeleteSync(syncObject); } } } #endif // HAVE_OPENGL #endif // KIS_OPENGL_CANVAS_2_P_H