Commit 31529bc8 authored by Michael Abrahams's avatar Michael Abrahams

Hide all OpenGL internals from KisCanvas

Summary: This moves the troublesome KisOpenGLTextures entirely into KisOpenGLCanvas. This will make it easier to reason about potential OpenGL related crashes.

Reviewers: dkazakov, rempt

Reviewed By: rempt

Differential Revision: https://phabricator.kde.org/D321
parent 0bee0d19
/*
* Copyright (C) 2007 Boudewijn Rempt <boud@valdyas.org>, (C)
* Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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
......@@ -28,8 +29,11 @@ class KoToolProxy;
class KisCanvas2;
class KisCanvasDecoration;
class KisDisplayFilter;
class KisDisplayColorConverter;
class QBitArray;
#include "kis_types.h"
#include "kis_ui_types.h"
class KisAbstractCanvasWidget
......@@ -45,15 +49,15 @@ public:
virtual KoToolProxy * toolProxy() const = 0;
/**
* Draw the specified decorations on the view.
*/
/// Draw the specified decorations on the view.
virtual void drawDecorations(QPainter & gc, const QRect &updateWidgetRect) const = 0;
virtual void addDecoration(KisCanvasDecoration* deco) = 0;
virtual KisCanvasDecoration* decoration(const QString& id) const = 0;
virtual void setDecorations(const QList<KisCanvasDecoration*> &) = 0;
virtual QList<KisCanvasDecoration*> decorations() const = 0;
/// set the specified display filter on the canvas
......@@ -61,6 +65,24 @@ public:
virtual void setWrapAroundViewingMode(bool value) = 0;
// Called from KisCanvas2::channelSelectionChanged
virtual void channelSelectionChanged(QBitArray channelFlags) = 0;
// Called from KisCanvas2::slotSetDisplayProfile
virtual void setDisplayProfile(KisDisplayColorConverter *colorConverter) = 0;
// Called from KisCanvas2::disconnectCurrentCanvas
virtual void disconnectCurrentCanvas() = 0;
// Called from KisCanvas2::finishResizingImage
virtual void finishResizingImage(qint32 w, qint32 h) = 0;
// Called from KisCanvas2::startUpdateProjection
virtual KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags) = 0;
// Called from KisCanvas2::updateCanvasProjection
virtual QRect updateCanvasProjection(KisUpdateInfoSP info) = 0;
/**
* Returns true if the asynchromous engine of the canvas
* (e.g. openGL pipeline) is busy with processing of the previous
......
......@@ -66,7 +66,6 @@
#ifdef HAVE_OPENGL
#include "opengl/kis_opengl_canvas2.h"
#include "opengl/kis_opengl_image_textures.h"
#endif
#include "opengl/kis_opengl.h"
......@@ -112,9 +111,6 @@ public:
int openGLFilterMode;
#endif
KisToolProxy *toolProxy;
#ifdef HAVE_OPENGL
KisOpenGLImageTexturesSP openGLImageTextures;
#endif
KisPrescaledProjectionSP prescaledProjection;
bool vastScrolling;
......@@ -258,19 +254,7 @@ void KisCanvas2::channelSelectionChanged()
m_d->channelFlags = image->rootLayer()->channelFlags();
image->barrierLock();
if (m_d->currentCanvasIsOpenGL) {
#ifdef HAVE_OPENGL
Q_ASSERT(m_d->openGLImageTextures);
m_d->openGLImageTextures->setChannelFlags(m_d->channelFlags);
#else
Q_ASSERT_X(0, "KisCanvas2::setChannelFlags", "Bad use of setChannelFlags(). It shouldn't have happened =(");
#endif
} else {
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setChannelFlags(m_d->channelFlags);
}
m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags);
startUpdateInPatches(image->bounds());
image->unlock();
......@@ -383,24 +367,11 @@ void KisCanvas2::createQPainterCanvas()
void KisCanvas2::createOpenGLCanvas()
{
#ifdef HAVE_OPENGL
KisConfig cfg;
m_d->openGLFilterMode = cfg.openGLFilteringMode();
m_d->currentCanvasIsOpenGL = true;
m_d->openGLImageTextures = KisOpenGLImageTextures::getImageTextures(m_d->view->image(),
m_d->displayColorConverter->monitorProfile(),
m_d->displayColorConverter->renderingIntent(),
m_d->displayColorConverter->conversionFlags());
KisOpenGLCanvas2 *canvasWidget = new KisOpenGLCanvas2(this, m_d->coordinatesConverter, 0, m_d->openGLImageTextures);
canvasWidget->setDisplayFilter(m_d->displayColorConverter->displayFilter());
KisOpenGLCanvas2 *canvasWidget = new KisOpenGLCanvas2(this, m_d->coordinatesConverter, 0, m_d->view->image(), m_d->displayColorConverter);
setCanvasWidget(canvasWidget);
#else
qFatal("Bad use of createOpenGLCanvas(). It shouldn't have happened =(");
#endif
}
void KisCanvas2::createCanvas(bool useOpenGL)
......@@ -416,6 +387,7 @@ void KisCanvas2::createCanvas(bool useOpenGL)
createOpenGLCanvas();
if (cfg.canvasState() == "OPENGL_FAILED") {
// Creating the opengl canvas failed, fall back
warnKrita << "OpenGL Canvas initialization returned OPENGL_FAILED. Falling back to QPainter.";
createQPainterCanvas();
}
} else {
......@@ -423,14 +395,10 @@ void KisCanvas2::createCanvas(bool useOpenGL)
createQPainterCanvas();
}
#else
warnKrita << "OpenGL requested while it's not available, starting qpainter canvas";
warnKrita << "User requested an OpenGL canvas, but Krita was compiled without OpenGL support. Falling back to QPainter.";
createQPainterCanvas();
#endif
} else {
#ifdef HAVE_OPENGL
// Free shared pointer
m_d->openGLImageTextures = 0;
#endif
createQPainterCanvas();
}
if (m_d->popupPalette) {
......@@ -469,15 +437,7 @@ void KisCanvas2::connectCurrentCanvas()
void KisCanvas2::disconnectCurrentCanvas()
{
if (m_d->currentCanvasIsOpenGL) {
#ifdef HAVE_OPENGL
Q_ASSERT(m_d->openGLImageTextures);
m_d->openGLImageTextures->disconnect(this);
m_d->openGLImageTextures->disconnect(m_d->view->image());
#else
qFatal("Bad use of disconnectCurrentCanvas(). It shouldn't have happened =(");
#endif
}
m_d->canvasWidget->disconnectCurrentCanvas();
}
void KisCanvas2::resetCanvas(bool useOpenGL)
......@@ -527,40 +487,13 @@ void KisCanvas2::startUpdateInPatches(QRect imageRect)
void KisCanvas2::setDisplayFilter(KisDisplayFilter *displayFilter)
{
m_d->displayColorConverter->setDisplayFilter(displayFilter);
KisImageWSP image = this->image();
if (m_d->currentCanvasIsOpenGL) {
#ifdef HAVE_OPENGL
KisImageWSP image = this->image();
image->barrierLock();
bool needsInternalColorManagement =
!displayFilter || displayFilter->useInternalColorManagement();
bool needsFullRefresh =
m_d->openGLImageTextures->
setInternalColorManagementActive(needsInternalColorManagement);
m_d->canvasWidget->setDisplayFilter(displayFilter);
if (needsFullRefresh) {
startUpdateInPatches(image->bounds());
} else {
updateCanvas();
}
image->unlock();
#endif
}
else {
KisImageWSP image = this->image();
image->barrierLock();
image->barrierLock();
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setDisplayFilter(displayFilter);
startUpdateInPatches(image->bounds());
m_d->canvasWidget->setDisplayFilter(displayFilter);
image->unlock();
}
image->unlock();
}
KisDisplayFilter *KisCanvas2::displayFilter() const
......@@ -597,87 +530,29 @@ void KisCanvas2::startResizingImage()
void KisCanvas2::finishResizingImage(qint32 w, qint32 h)
{
if (m_d->currentCanvasIsOpenGL) {
#ifdef HAVE_OPENGL
Q_ASSERT(m_d->openGLImageTextures);
m_d->openGLImageTextures->slotImageSizeChanged(w, h);
#else
Q_ASSERT_X(0, "finishResizingImage()", "Bad use of finishResizingImage(). It shouldn't have happened =(");
#endif
} else {
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->slotImageSizeChanged(w, h);
}
m_d->canvasWidget->finishResizingImage(w, h);
}
void KisCanvas2::startUpdateCanvasProjection(const QRect & rc)
{
KisUpdateInfoSP info;
if (m_d->currentCanvasIsOpenGL) {
#ifdef HAVE_OPENGL
Q_ASSERT(m_d->openGLImageTextures);
m_d->openGLImageTextures->setChannelFlags(m_d->channelFlags);
info = m_d->openGLImageTextures->updateCache(rc);
#else
Q_ASSERT_X(0, "startUpdateCanvasProjection()", "Bad use of startUpdateCanvasProjection(). It shouldn't have happened =(");
#endif
} else {
Q_ASSERT(m_d->prescaledProjection);
info = m_d->prescaledProjection->updateCache(rc);
}
KisUpdateInfoSP info = m_d->canvasWidget->startUpdateCanvasProjection(rc, m_d->channelFlags);
if (m_d->projectionUpdatesCompressor.putUpdateInfo(info)) {
emit sigCanvasCacheUpdated();
}
}
void KisCanvas2::updateCanvasProjection()
{
KisUpdateInfoSP info;
while (info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) {
/**
* It might happen that the canvas type is switched while the
* update info is being stuck in the Qt's signals queue. Than a wrong
* type of the info may come. So just check it here.
*/
#ifdef HAVE_OPENGL
bool isOpenGLUpdateInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data());
if (isOpenGLUpdateInfo != m_d->currentCanvasIsOpenGL)
continue;
#endif
if (m_d->currentCanvasIsOpenGL) {
#ifdef HAVE_OPENGL
Q_ASSERT(m_d->openGLImageTextures);
m_d->openGLImageTextures->recalculateCache(info);
/**
* FIXME: Please not update entire canvas
* Implement info->dirtyViewportRect()
*/
updateCanvasWidgetImpl();
#else
Q_ASSERT_X(0, "updateCanvasProjection()", "Bad use of updateCanvasProjection(). It shouldn't have happened =(");
#endif
while (KisUpdateInfoSP info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) {
QRect vRect = m_d->canvasWidget->updateCanvasProjection(info);
if (!vRect.isEmpty()) {
updateCanvasWidgetImpl(m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect());
}
else {
// See comment in startUpdateCanvasProjection()
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->recalculateCache(info);
QRect vRect = m_d->coordinatesConverter->
viewportToWidget(info->dirtyViewportRect()).toAlignedRect();
}
if (!vRect.isEmpty()) {
updateCanvasWidgetImpl(vRect);
}
}
// TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas
if (m_d->currentCanvasIsOpenGL) {
updateCanvasWidgetImpl();
}
}
......@@ -819,21 +694,7 @@ void KisCanvas2::slotSetDisplayProfile(const KoColorProfile *monitorProfile)
image->barrierLock();
if (m_d->currentCanvasIsOpenGL) {
#ifdef HAVE_OPENGL
Q_ASSERT(m_d->openGLImageTextures);
m_d->openGLImageTextures->setMonitorProfile(m_d->displayColorConverter->monitorProfile(),
m_d->displayColorConverter->renderingIntent(),
m_d->displayColorConverter->conversionFlags());
#else
Q_ASSERT_X(0, "KisCanvas2::setMonitorProfile", "Bad use of setMonitorProfile(). It shouldn't have happened =(");
#endif
} else {
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setMonitorProfile(m_d->displayColorConverter->monitorProfile(),
m_d->displayColorConverter->renderingIntent(),
m_d->displayColorConverter->conversionFlags());
}
m_d->canvasWidget->setDisplayProfile(m_d->displayColorConverter);
startUpdateInPatches(image->bounds());
......@@ -941,5 +802,3 @@ KisPaintingAssistantsDecoration* KisCanvas2::paintingAssistantsDecoration()
KisCanvasDecoration* deco = decoration("paintingAssistantsDecoration");
return qobject_cast<KisPaintingAssistantsDecoration*>(deco);
}
......@@ -53,8 +53,10 @@
#include "kis_selection_manager.h"
#include "kis_selection.h"
#include "kis_perspective_grid_manager.h"
#include "kis_canvas_updates_compressor.h"
#include "kis_config_notifier.h"
#include "kis_group_layer.h"
#include "canvas/kis_display_color_converter.h"
//#define DEBUG_REPAINT
#include <KoCanvasController.h>
......@@ -164,6 +166,68 @@ void KisQPainterCanvas::inputMethodEvent(QInputMethodEvent *event)
processInputMethodEvent(event);
}
void KisQPainterCanvas::channelSelectionChanged(QBitArray channelFlags)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setChannelFlags(channelFlags);
}
void KisQPainterCanvas::setDisplayProfile(KisDisplayColorConverter *colorConverter)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setMonitorProfile(colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
}
void KisQPainterCanvas::setDisplayFilter(KisDisplayFilter* displayFilter)
{
Q_ASSERT(m_d->prescaledProjection);
m_d->prescaledProjection->setDisplayFilter(displayFilter);
startUpdateInPatches(image->bounds());
}
void KisQPainterCanvas::setWrapAroundViewingMode(bool value)
{
kDebug() << "Wrap around viewing mode not implemented in QPainter Canvas.";
return;
}
void KisQPainterCanvas::disconnectCurrentCanvas()
{ }
void KisQPainterCanvas::finishResizingImage(qint32 w, qint32 h)
{
m_d->prescaledProjection->slotImageSizeChanged(w, h);
}
KisUpdateInfoSP KisQPainterCanvas::startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags)
{
Q_UNUSED(channelFlags);
return m_d->prescaledProjection->updateCache(rc);
}
QRect KisQPainterCanvas::updateCanvasProjection(KisUpdateInfoSP info)
{
/**
* It might happen that the canvas type is switched while the
* update info is being stuck in the Qt's signals queue. Than a wrong
* type of the info may come. So just check it here.
*/
bool isPPUpdateInfo = dynamic_cast<KisPPUpdateInfo*>(info.data());
if (isPPUpdateInfo) {
m_d->prescaledProjection->recalculateCache(info);
return info->dirtyViewportRect();
} else {
return QRect();
}
}
void KisQPainterCanvas::resizeEvent(QResizeEvent *e)
{
QSize size(e->size());
......@@ -188,4 +252,3 @@ bool KisQPainterCanvas::callFocusNextPrevChild(bool next)
{
return focusNextPrevChild(next);
}
......@@ -23,11 +23,13 @@
#include "kis_canvas_widget_base.h"
#include "kis_prescaled_projection.h"
#include "kis_ui_types.h"
class QImage;
class QPaintEvent;
class QPainter;
class KisCanvas2;
class KisDisplayColorConverter;
/**
*
......@@ -51,21 +53,25 @@ public:
void setPrescaledProjection(KisPrescaledProjectionSP prescaledProjection);
public: // QWidget
public: // QWidget overrides
/// reimplemented method from superclass
void paintEvent(QPaintEvent * ev);
/// reimplemented method from superclass
void resizeEvent(QResizeEvent *e);
/// reimplemented method from superclass
virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
/// reimplemented method from superclass
virtual void inputMethodEvent(QInputMethodEvent *event);
public: // KisAbstractCanvasWidget
public: // Implement kis_abstract_canvas_widget interface
void setDisplayFilter(KisDisplayFilter* displayFilter);
void setWrapAroundViewingMode(bool value);
void channelSelectionChanged(QBitArray channelFlags);
void setDisplayProfile(KisDisplayColorConverter *colorConverter);
void disconnectCurrentCanvas();
void finishResizingImage(qint32 w, qint32 h);
KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags);
QRect updateCanvasProjection(KisUpdateInfoSP info);
QWidget * widget() {
return this;
......
/* This file is part of the KDE project
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2006-2013
* Copyright (C) 2015 Michael Abrahams <miabraha@gmail.com>
*
* 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
......@@ -61,6 +62,7 @@
#include "opengl/kis_opengl_canvas2_p.h"
#include "kis_coordinates_converter.h"
#include "canvas/kis_display_filter.h"
#include "canvas/kis_display_color_converter.h"
#define NEAR_VAL -1000.0
#define FAR_VAL 1000.0
......@@ -157,7 +159,7 @@ public:
};
KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisOpenGLImageTexturesSP imageTextures)
KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisImageWSP image, KisDisplayColorConverter *colorConverter)
: QOpenGLWidget(parent)
, KisCanvasWidgetBase(canvas, coordinatesConverter)
, d(new Private())
......@@ -170,8 +172,10 @@ KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *
KisConfig cfg;
cfg.writeEntry("canvasState", "OPENGL_STARTED");
d->openGLImageTextures = imageTextures;
d->openGLImageTextures = KisOpenGLImageTextures::getImageTextures(image,
colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
setAcceptDrops(true);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_NoSystemBackground);
......@@ -181,6 +185,8 @@ KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *
setAttribute(Qt::WA_InputMethodEnabled, true);
setAttribute(Qt::WA_DontCreateNativeAncestors, true);
setDisplayFilter(colorConverter->displayFilter());
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
slotConfigChanged();
cfg.writeEntry("canvasState", "OPENGL_SUCCESS");
......@@ -193,6 +199,14 @@ KisOpenGLCanvas2::~KisOpenGLCanvas2()
void KisOpenGLCanvas2::setDisplayFilter(KisDisplayFilter* displayFilter)
{
bool needsInternalColorManagement =
!displayFilter || displayFilter->useInternalColorManagement();
bool needsFullRefresh =
d->openGLImageTextures->
setInternalColorManagementActive(needsInternalColorManagement);
d->displayFilter = displayFilter;
if (d->canvasInitialized) {
d->canvasInitialized = false;
......@@ -200,8 +214,22 @@ void KisOpenGLCanvas2::setDisplayFilter(KisDisplayFilter* displayFilter)
initializeCheckerShader();
d->canvasInitialized = true;
}
if (needsFullRefresh) {
startUpdateInPatches(image->bounds());
} else {
updateCanvas();
}
}
void KisOpenGLCanvas2::disconnectCurrentCanvas()
{
Q_ASSERT(d->openGLImageTextures);
d->openGLImageTextures->disconnect(canvas());
d->openGLImageTextures->disconnect(canvas()->image());
}
void KisOpenGLCanvas2::setWrapAroundViewingMode(bool value)
{
d->wrapAroundMode = value;
......@@ -707,6 +735,42 @@ void KisOpenGLCanvas2::renderDecorations(QPainter *painter)
drawDecorations(*painter, boundingRect);
}
void KisOpenGLCanvas2::setDisplayProfile(KisDisplayColorConverter *colorConverter)
{
d->openGLImageTextures->setMonitorProfile(colorConverter->monitorProfile(),
colorConverter->renderingIntent(),
colorConverter->conversionFlags());
}
void KisOpenGLCanvas2::channelSelectionChanged(QBitArray channelFlags)
{
d->openGLImageTextures->setChannelFlags(channelFlags);
}
void KisOpenGLCanvas2::finishResizingImage(qint32 w, qint32 h)
{
d->openGLImageTextures->slotImageSizeChanged(w, h);
}
KisUpdateInfoSP KisOpenGLCanvas2::startUpdateCanvasProjection(const QRect & rc, QBitArray channelFlags)
{
d->openGLImageTextures->setChannelFlags(channelFlags);
return d->openGLImageTextures->updateCache(rc);
}
QRect KisOpenGLCanvas2::updateCanvasProjection(KisUpdateInfoSP info)
{
// See KisQPainterCanvas::updateCanvasProjection for more info
bool isOpenGLUpdateInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data());
if (isOpenGLUpdateInfo) {
d->openGLImageTextures->recalculateCache(info);
}
return QRect(); // FIXME: Implement dirty rect for OpenGL
}
bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next)
{
return focusNextPrevChild(next);
......
/*
/*
* Copyright (C) Boudewijn Rempt <boud@valdyas.org>, (C) 2006
* Copyright (C) Michael Abrahams <miabraha@gmail.com>, (C) 2015
*
* 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
......@@ -32,10 +33,12 @@
#include "opengl/kis_opengl_image_textures.h"
#include "kritaui_export.h"
#include "kis_ui_types.h"
class QWidget;
class QPaintEvent;
class KisCanvas2;
class KisDisplayColorConverter;
/**
......@@ -52,61 +55,59 @@ class KRITAUI_EXPORT KisOpenGLCanvas2 : public QOpenGLWidget, public QOpenGLFunc
public:
KisOpenGLCanvas2(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent, KisOpenGLImageTexturesSP imageTextures);
KisOpenGLCanvas2(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget *parent, KisImageWSP image, KisDisplayColorConverter *colorConverter);
virtual ~KisOpenGLCanvas2();
void setDisplayFilter(KisDisplayFilter* displayFilter);
void setWrapAroundViewingMode(bool value);
public: // QOpenGLWidget