Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit fb43d4e5 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix openGL mipmaps syncing problem on OSX

The mipmaps problem on OSX happened because we used different
openGL context for uploading textures data and rendering. With
this patch, uploading will happen in the same context, so
explicit syncing with glFinish is not needed anymore.

WARNING: this patch changes the way how we render the frames.
I have streamlined rendering of new uploaded frames and added
a FPS-based compressor before them (to avoid too much context
switches). In my opinion, the rendering became slightly faster,
but the painters might not agree :) So it needs a bit of
testing.

CC:kimageshop@kde.org
parent 1a4eae55
......@@ -79,6 +79,7 @@ public:
// Called from KisCanvas2::updateCanvasProjection
virtual QRect updateCanvasProjection(KisUpdateInfoSP info) = 0;
virtual QVector<QRect> updateCanvasProjection(const QVector<KisUpdateInfoSP> &infoObjects) = 0;
/**
* Returns true if the asynchromous engine of the canvas
......
......@@ -21,6 +21,9 @@
#include "kis_canvas2.h"
#include <functional>
#include <numeric>
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
......@@ -117,7 +120,7 @@ public:
KisPrescaledProjectionSP prescaledProjection;
bool vastScrolling;
KisSignalCompressor updateSignalCompressor;
KisSignalCompressor canvasUpdateCompressor;
QRect savedUpdateRect;
QBitArray channelFlags;
......@@ -137,6 +140,8 @@ public:
QPointer<KoShapeManager> currentlyActiveShapeManager;
KisInputActionGroupsMask inputActionGroupsMask = AllActionGroup;
KisSignalCompressor frameRenderStartCompressor;
bool effectiveLodAllowedInCanvas() {
return lodAllowedInCanvas && !bootstrapLodBlocked;
}
......@@ -185,8 +190,11 @@ KisCanvas2::KisCanvas2(KisCoordinatesConverter *coordConverter, KoCanvasResource
KisImageConfig config;
m_d->updateSignalCompressor.setDelay(1000 / config.fpsLimit());
m_d->updateSignalCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
m_d->canvasUpdateCompressor.setDelay(1000 / config.fpsLimit());
m_d->canvasUpdateCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
m_d->frameRenderStartCompressor.setDelay(1000 / config.fpsLimit());
m_d->frameRenderStartCompressor.setMode(KisSignalCompressor::FIRST_ACTIVE);
}
void KisCanvas2::setup()
......@@ -226,7 +234,12 @@ void KisCanvas2::setup()
connect(kritaShapeController, SIGNAL(currentLayerChanged(const KoShapeLayer*)),
globalShapeManager()->selection(), SIGNAL(currentLayerChanged(const KoShapeLayer*)));
connect(&m_d->updateSignalCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate()));
connect(&m_d->canvasUpdateCompressor, SIGNAL(timeout()), SLOT(slotDoCanvasUpdate()));
connect(this, SIGNAL(sigCanvasCacheUpdated()), &m_d->frameRenderStartCompressor, SLOT(start()));
connect(&m_d->frameRenderStartCompressor, SIGNAL(timeout()), SLOT(updateCanvasProjection()));
connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), SLOT(finishResizingImage(qint32,qint32)));
initializeFpsDecoration();
}
......@@ -533,10 +546,8 @@ void KisCanvas2::initializeImage()
m_d->toolProxy.initializeImage(image);
connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection);
connect(this, SIGNAL(sigCanvasCacheUpdated()), SLOT(updateCanvasProjection()));
connect(image, SIGNAL(sigProofingConfigChanged()), SLOT(slotChangeProofingConfig()));
connect(image, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), SLOT(startResizingImage()), Qt::DirectConnection);
connect(this, SIGNAL(sigContinueResizeImage(qint32,qint32)), SLOT(finishResizingImage(qint32,qint32)));
connect(image->undoAdapter(), SIGNAL(selectionChanged()), SLOT(slotTrySwitchShapeManager()));
connectCurrentCanvas();
......@@ -727,21 +738,37 @@ void KisCanvas2::startUpdateCanvasProjection(const QRect & rc)
void KisCanvas2::updateCanvasProjection()
{
QVector<KisUpdateInfoSP> infoObjects;
while (KisUpdateInfoSP info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) {
QRect vRect = m_d->canvasWidget->updateCanvasProjection(info);
if (!vRect.isEmpty()) {
updateCanvasWidgetImpl(m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect());
}
infoObjects << info;
}
QVector<QRect> viewportRects = m_d->canvasWidget->updateCanvasProjection(infoObjects);
const QRect vRect = std::accumulate(viewportRects.constBegin(), viewportRects.constEnd(),
QRect(), std::bit_or<QRect>());
// TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas
if (m_d->currentCanvasIsOpenGL) {
updateCanvasWidgetImpl();
m_d->savedUpdateRect = QRect();
// we already had a compression in frameRenderStartCompressor, so force the update directly
slotDoCanvasUpdate();
} else if (/* !m_d->currentCanvasIsOpenGL && */ !vRect.isEmpty()) {
m_d->savedUpdateRect = m_d->coordinatesConverter->viewportToWidget(vRect).toAlignedRect();
// we already had a compression in frameRenderStartCompressor, so force the update directly
slotDoCanvasUpdate();
}
}
void KisCanvas2::slotDoCanvasUpdate()
{
/**
* WARNING: in isBusy() we access openGL functions without making the painting
* context current. We hope that currently active context will be Qt's one,
* which is shared with our own.
*/
if (m_d->canvasWidget->isBusy()) {
// just restarting the timer
updateCanvasWidgetImpl(m_d->savedUpdateRect);
......@@ -761,11 +788,11 @@ void KisCanvas2::slotDoCanvasUpdate()
void KisCanvas2::updateCanvasWidgetImpl(const QRect &rc)
{
if (!m_d->updateSignalCompressor.isActive() ||
if (!m_d->canvasUpdateCompressor.isActive() ||
!m_d->savedUpdateRect.isEmpty()) {
m_d->savedUpdateRect |= rc;
}
m_d->updateSignalCompressor.start();
m_d->canvasUpdateCompressor.start();
}
void KisCanvas2::updateCanvas()
......
......@@ -39,6 +39,8 @@
#include "KisViewManager.h"
#include "kis_selection_manager.h"
#include "KisDocument.h"
#include "kis_update_info.h"
struct KisCanvasWidgetBase::Private
{
......@@ -240,6 +242,17 @@ KisCoordinatesConverter* KisCanvasWidgetBase::coordinatesConverter() const
return m_d->coordinatesConverter;
}
QVector<QRect> KisCanvasWidgetBase::updateCanvasProjection(const QVector<KisUpdateInfoSP> &infoObjects)
{
QVector<QRect> dirtyViewRects;
Q_FOREACH (KisUpdateInfoSP info, infoObjects) {
dirtyViewRects << this->updateCanvasProjection(info);
}
return dirtyViewRects;
}
KoToolProxy *KisCanvasWidgetBase::toolProxy() const
{
return m_d->toolProxy;
......
......@@ -79,6 +79,9 @@ public: // KisAbstractCanvasWidget
KisCoordinatesConverter* coordinatesConverter() const;
QVector<QRect> updateCanvasProjection(const QVector<KisUpdateInfoSP> &infoObjects) override;
using KisAbstractCanvasWidget::updateCanvasProjection;
protected:
KisCanvas2 *canvas() const;
......
......@@ -68,6 +68,7 @@ public: // Implement kis_abstract_canvas_widget interface
void finishResizingImage(qint32 w, qint32 h) override;
KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags) override;
QRect updateCanvasProjection(KisUpdateInfoSP info) override;
using KisCanvasWidgetBase::updateCanvasProjection;
QWidget * widget() override {
return this;
......
......@@ -888,19 +888,36 @@ QRect KisOpenGLCanvas2::updateCanvasProjection(KisUpdateInfoSP info)
if (isOpenGLUpdateInfo) {
d->openGLImageTextures->recalculateCache(info);
}
return QRect(); // FIXME: Implement dirty rect for OpenGL
}
QVector<QRect> KisOpenGLCanvas2::updateCanvasProjection(const QVector<KisUpdateInfoSP> &infoObjects)
{
#ifdef Q_OS_OSX
/**
* There is a bug on OSX: if we issue frame redraw before the tiles finished
* uploading, the tiles will become corrupted. Depending on the GPU/driver
* version either the tile itself, or its mipmaps will become totally
* transparent.
* On OSX openGL defferent (shared) contexts have different execution queues.
* It means that the textures uploading and their painting can be easily reordered.
* To overcome the issue, we should ensure that the textures are uploaded in the
* same openGL context as the painting is done.
*/
glFinish();
QOpenGLContext *oldContext = QOpenGLContext::currentContext();
QSurface *oldSurface = oldContext ? oldContext->surface() : 0;
this->makeCurrent();
#endif
return QRect(); // FIXME: Implement dirty rect for OpenGL
QVector<QRect> result = KisCanvasWidgetBase::updateCanvasProjection(infoObjects);
#ifdef Q_OS_OSX
if (oldContext) {
oldContext->makeCurrent(oldSurface);
} else {
this->doneCurrent();
}
#endif
return result;
}
bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next)
......
......@@ -89,6 +89,7 @@ public: // Implement kis_abstract_canvas_widget interface
void finishResizingImage(qint32 w, qint32 h) override;
KisUpdateInfoSP startUpdateCanvasProjection(const QRect & rc, const QBitArray &channelFlags) override;
QRect updateCanvasProjection(KisUpdateInfoSP info) override;
QVector<QRect> updateCanvasProjection(const QVector<KisUpdateInfoSP> &infoObjects) override;
QWidget *widget() override {
return this;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment