Commit b5c33311 authored by Dmitry Kazakov's avatar Dmitry Kazakov

Fix delays in the end of Instant Preview rendering stroke

Needs your testing!

Test Plan:

1) Paint with complex brushes on Float32 images
2) Try painting many repetitive strokes, there should be no
   flockering and no delays.
3) Try cancel your srokes before they are finished with Esc key.
   Please note that the most dangerous moment is when Krita writes
   "Updating..." right before background stroke calculation is
   finished. Try pressing exactly at this moment! :)

Technical Details:

There was a short delay in the end of every Instant Preview
regeneration stroke. It was mostly seen on bigger canvases and
Float32-bitdepth mode.

The cause of this delay was the "update resume" stroke that
tried to upload the 100%-zoom image data to the canvas. Sometimes
such uploading could take up to 1.5 seconds, which could interfere
into the painter's workflow.

This patch does multiple things to mitigate this problem:

1) KisSuspendProjectionUpdatesStrokeStrategy is now suspendable
   (again). It means that Krita will suspend the uploading process
   if the user desides to paint further instead of waiting for the
   background rendering to complete. This is the main part of this
   patch.

2) On resuming Krita will not upload the entire image to the canvas,
   but only a changed part. This is achieved by collecting dirty
   requests in KisImage::enableUIUpdates(). This method itself doesn't
   solve the initial problem, but it makes uploading a bit more efficient.

3) While the resume stroke is suspended, KisOpenGLCanvas2 blocks all the
   synchronizations of tiles' mipmaps. It means that normal lodN strokes
   will run with the mipmaps blocked. It is a bit dangerous approach, but
   it works until KisSuspendProjectionUpdatesStrokeStrategy is the only
   user of sigRequestLodPlanesSyncBlocked() signal.

CC:kimageshop@kde.org
BUG:361448
Fixes T2145
parent 8564a3a4
......@@ -88,10 +88,10 @@ template <template <class> class Container, class T>
* support it. The main usage case is conversion of pointers in "descendant-
* to-parent" way.
*/
template <typename R, typename T>
inline QList<R> implicitCastList(const QList<T> &list)
template <typename R, typename T, template <typename U> class Container>
inline Container<R> implicitCastList(const Container<T> &list)
{
QList<R> result;
Container<R> result;
Q_FOREACH(const T &item, list) {
result.append(item);
......
......@@ -176,6 +176,7 @@ set(kritaimage_LIB_SRCS
kis_stroke_strategy_undo_command_based.cpp
kis_simple_stroke_strategy.cpp
KisRunnableBasedStrokeStrategy.cpp
KisRunnableStrokeJobDataBase.cpp
KisRunnableStrokeJobData.cpp
KisRunnableStrokeJobsInterface.cpp
KisFakeRunnableStrokeJobsExecutor.cpp
......
......@@ -23,9 +23,9 @@
#include <QVector>
void KisFakeRunnableStrokeJobsExecutor::addRunnableJobs(const QVector<KisRunnableStrokeJobData *> &list)
void KisFakeRunnableStrokeJobsExecutor::addRunnableJobs(const QVector<KisRunnableStrokeJobDataBase *> &list)
{
Q_FOREACH (KisRunnableStrokeJobData *data, list) {
Q_FOREACH (KisRunnableStrokeJobDataBase *data, list) {
KIS_SAFE_ASSERT_RECOVER_NOOP(data->sequentiality() != KisStrokeJobData::BARRIER && "barrier jobs are not supported on the fake executor");
KIS_SAFE_ASSERT_RECOVER_NOOP(data->exclusivity() != KisStrokeJobData::EXCLUSIVE && "exclusive jobs are not supported on the fake executor");
......
......@@ -25,7 +25,7 @@
class KRITAIMAGE_EXPORT KisFakeRunnableStrokeJobsExecutor : public KisRunnableStrokeJobsInterface
{
public:
void addRunnableJobs(const QVector<KisRunnableStrokeJobData*> &list);
void addRunnableJobs(const QVector<KisRunnableStrokeJobDataBase*> &list);
};
#endif // KISFAKERUNNABLESTROKEJOBSEXECUTOR_H
......@@ -32,10 +32,10 @@ struct KisRunnableBasedStrokeStrategy::JobsInterface : public KisRunnableStrokeJ
}
void addRunnableJobs(const QVector<KisRunnableStrokeJobData*> &list) {
void addRunnableJobs(const QVector<KisRunnableStrokeJobDataBase*> &list) {
QVector<KisStrokeJobData*> newList;
Q_FOREACH (KisRunnableStrokeJobData *item, list) {
Q_FOREACH (KisRunnableStrokeJobDataBase *item, list) {
newList.append(item);
}
......@@ -67,7 +67,7 @@ void KisRunnableBasedStrokeStrategy::doStrokeCallback(KisStrokeJobData *data)
{
if (!data) return;
KisRunnableStrokeJobData *runnable = dynamic_cast<KisRunnableStrokeJobData*>(data);
KisRunnableStrokeJobDataBase *runnable = dynamic_cast<KisRunnableStrokeJobDataBase*>(data);
if (!runnable) return;
runnable->run();
......
......@@ -29,7 +29,7 @@ private:
struct JobsInterface;
public:
KisRunnableBasedStrokeStrategy(QString id, const KUndo2MagicString &name);
KisRunnableBasedStrokeStrategy(QString id, const KUndo2MagicString &name = KUndo2MagicString());
KisRunnableBasedStrokeStrategy(const KisRunnableBasedStrokeStrategy &rhs);
~KisRunnableBasedStrokeStrategy();
......
......@@ -22,13 +22,13 @@
#include <kis_assert.h>
KisRunnableStrokeJobData::KisRunnableStrokeJobData(QRunnable *runnable, KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity)
: KisStrokeJobData(sequentiality, exclusivity),
: KisRunnableStrokeJobDataBase(sequentiality, exclusivity),
m_runnable(runnable)
{
}
KisRunnableStrokeJobData::KisRunnableStrokeJobData(std::function<void ()> func, KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity)
: KisStrokeJobData(sequentiality, exclusivity),
: KisRunnableStrokeJobDataBase(sequentiality, exclusivity),
m_func(func)
{
}
......
......@@ -20,12 +20,12 @@
#define KISRUNNABLESTROKEJOBDATA_H
#include "kritaimage_export.h"
#include "kis_stroke_job_strategy.h"
#include "KisRunnableStrokeJobDataBase.h"
#include <functional>
class QRunnable;
class KRITAIMAGE_EXPORT KisRunnableStrokeJobData : public KisStrokeJobData {
class KRITAIMAGE_EXPORT KisRunnableStrokeJobData : public KisRunnableStrokeJobDataBase {
public:
KisRunnableStrokeJobData(QRunnable *runnable, KisStrokeJobData::Sequentiality sequentiality = KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::Exclusivity exclusivity = KisStrokeJobData::NORMAL);
......@@ -35,7 +35,7 @@ public:
~KisRunnableStrokeJobData();
void run();
void run() override;
private:
QRunnable *m_runnable = 0;
......
/*
* Copyright (c) 2018 Dmitry Kazakov <dimula73@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
* 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 "KisRunnableStrokeJobDataBase.h"
KisRunnableStrokeJobDataBase::KisRunnableStrokeJobDataBase(KisStrokeJobData::Sequentiality sequentiality, KisStrokeJobData::Exclusivity exclusivity)
: KisStrokeJobData(sequentiality, exclusivity)
{
}
/*
* Copyright (c) 2018 Dmitry Kazakov <dimula73@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
* 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 KISRUNNABLESTROKEJOBDATABASE_H
#define KISRUNNABLESTROKEJOBDATABASE_H
#include "kritaimage_export.h"
#include "kis_stroke_job_strategy.h"
#include "kis_runnable.h"
class KRITAIMAGE_EXPORT KisRunnableStrokeJobDataBase : public KisStrokeJobData, public KisRunnable
{
public:
KisRunnableStrokeJobDataBase(KisStrokeJobData::Sequentiality sequentiality = KisStrokeJobData::SEQUENTIAL,
KisStrokeJobData::Exclusivity exclusivity = KisStrokeJobData::NORMAL);
};
#endif // KISRUNNABLESTROKEJOBDATABASE_H
......@@ -27,23 +27,23 @@
namespace KritaUtils
{
template <typename Func>
void addJobSequential(QVector<KisRunnableStrokeJobData*> &jobs, Func func) {
template <typename Func, typename Job>
void addJobSequential(QVector<Job*> &jobs, Func func) {
jobs.append(new KisRunnableStrokeJobData(func, KisStrokeJobData::SEQUENTIAL));
}
template <typename Func>
void addJobConcurrent(QVector<KisRunnableStrokeJobData*> &jobs, Func func) {
template <typename Func, typename Job>
void addJobConcurrent(QVector<Job*> &jobs, Func func) {
jobs.append(new KisRunnableStrokeJobData(func, KisStrokeJobData::CONCURRENT));
}
template <typename Func>
void addJobBarrier(QVector<KisRunnableStrokeJobData*> &jobs, Func func) {
template <typename Func, typename Job>
void addJobBarrier(QVector<Job*> &jobs, Func func) {
jobs.append(new KisRunnableStrokeJobData(func, KisStrokeJobData::BARRIER));
}
template <typename Func>
void addJobUniquelyCuncurrent(QVector<KisRunnableStrokeJobData*> &jobs, Func func) {
template <typename Func, typename Job>
void addJobUniquelyCuncurrent(QVector<Job*> &jobs, Func func) {
jobs.append(new KisRunnableStrokeJobData(func, KisStrokeJobData::UNIQUELY_CONCURRENT));
}
......
......@@ -25,7 +25,7 @@ KisRunnableStrokeJobsInterface::~KisRunnableStrokeJobsInterface()
}
void KisRunnableStrokeJobsInterface::addRunnableJob(KisRunnableStrokeJobData *data)
void KisRunnableStrokeJobsInterface::addRunnableJob(KisRunnableStrokeJobDataBase *data)
{
addRunnableJobs({data});
}
......@@ -21,8 +21,9 @@
#include "kritaimage_export.h"
#include <QtGlobal>
#include "kis_pointer_utils.h"
class KisRunnableStrokeJobData;
class KisRunnableStrokeJobDataBase;
class KRITAIMAGE_EXPORT KisRunnableStrokeJobsInterface
......@@ -30,8 +31,13 @@ class KRITAIMAGE_EXPORT KisRunnableStrokeJobsInterface
public:
virtual ~KisRunnableStrokeJobsInterface();
void addRunnableJob(KisRunnableStrokeJobData *data);
virtual void addRunnableJobs(const QVector<KisRunnableStrokeJobData*> &list) = 0;
void addRunnableJob(KisRunnableStrokeJobDataBase *data);
virtual void addRunnableJobs(const QVector<KisRunnableStrokeJobDataBase*> &list) = 0;
template <typename T>
void addRunnableJobs(const QVector<T*> &list) {
this->addRunnableJobs(implicitCastList<KisRunnableStrokeJobDataBase*>(list));
}
};
#endif // KISRUNNABLESTROKEJOBSINTERFACE_H
......@@ -96,6 +96,7 @@
#include "kis_layer_projection_plane.h"
#include "kis_update_time_monitor.h"
#include "tiles3/kis_lockless_stack.h"
#include <QtCore>
......@@ -219,6 +220,8 @@ public:
vKisAnnotationSP annotations;
QAtomicInt disableUIUpdateSignals;
KisLocklessStack<QRect> savedDisabledUIUpdates;
KisProjectionUpdatesFilterSP projectionUpdatesFilter;
KisImageSignalRouter signalRouter;
KisImageAnimationInterface *animationInterface;
......@@ -1631,9 +1634,18 @@ void KisImage::disableUIUpdates()
m_d->disableUIUpdateSignals.ref();
}
void KisImage::enableUIUpdates()
QVector<QRect> KisImage::enableUIUpdates()
{
m_d->disableUIUpdateSignals.deref();
QRect rect;
QVector<QRect> postponedUpdates;
while (m_d->savedDisabledUIUpdates.pop(rect)) {
postponedUpdates.append(rect);
}
return postponedUpdates;
}
void KisImage::notifyProjectionUpdated(const QRect &rc)
......@@ -1647,6 +1659,8 @@ void KisImage::notifyProjectionUpdated(const QRect &rc)
if (dirtyRect.isEmpty()) return;
emit sigImageUpdated(dirtyRect);
} else {
m_d->savedDisabledUIUpdates.push(rc);
}
}
......
......@@ -728,26 +728,6 @@ Q_SIGNALS:
*/
void sigImageUpdated(const QRect &);
/**
* Emitted whenever the image is going to reset load planes from lodN to
* lod0. All the signals emitted with sigImageUpdated() between the calls
* to sigBeginLodResetUpdatesBatch() and sigEndLodResetUpdatesBatch() will be
* considered as belonging to the same "batch". During processing a "batch"
* GUI is not permitted to change lod level of the tile textures. It should
* just update internal caches. The call to sigEndLodResetUpdatesBatch()
* will start rerendering of the canvas.
*
* NOTE: this feature is used to avoid flickering when switching
* back from lodN plane back to lod0. All the texture tiles should
* be loaded with new information before mipmaps can be regenerated.
*/
void sigBeginLodResetUpdatesBatch();
/**
* @see sigBeginLodResetUpdatesBatch()
*/
void sigEndLodResetUpdatesBatch();
/**
Emitted whenever the image has been modified, so that it
doesn't match with the version saved on disk.
......@@ -955,13 +935,16 @@ public Q_SLOTS:
* when we change the size of the image. In this case, the whole
* image will be reloaded into UI by sigSizeChanged(), so there is
* no need to inform the UI about individual dirty rects.
*
* The last call to enableUIUpdates() will return the list of udpates
* that were requested while they were blocked.
*/
void disableUIUpdates() override;
/**
* \see disableUIUpdates
*/
void enableUIUpdates() override;
QVector<QRect> enableUIUpdates() override;
/**
* Disables the processing of all the setDirty() requests that
......
......@@ -47,7 +47,7 @@ public:
virtual void unblockUpdates() = 0;
virtual void disableUIUpdates() = 0;
virtual void enableUIUpdates() = 0;
virtual QVector<QRect> enableUIUpdates() = 0;
virtual void disableDirtyRequests() = 0;
virtual void enableDirtyRequests() = 0;
......
......@@ -115,22 +115,20 @@ void KisImageSignalRouter::emitAboutToRemoveANode(KisNode *parent, int index)
emit sigRemoveNodeAsync(removedNode);
}
void KisImageSignalRouter::emitBeginLodResetUpdatesBatch()
void KisImageSignalRouter::emitRequestLodPlanesSyncBlocked(bool value)
{
KisImageSP image = m_image.toStrongRef();
KIS_SAFE_ASSERT_RECOVER_RETURN(image);
emit image->sigBeginLodResetUpdatesBatch();
emit sigRequestLodPlanesSyncBlocked(value);
}
void KisImageSignalRouter::emitEndLodResetUpdatesBatch()
void KisImageSignalRouter::emitNotifyBatchUpdateStarted()
{
KisImageSP image = m_image.toStrongRef();
KIS_SAFE_ASSERT_RECOVER_RETURN(image);
emit image->sigEndLodResetUpdatesBatch();
emit sigNotifyBatchUpdateStarted();
}
void KisImageSignalRouter::emitNotifyBatchUpdateEnded()
{
emit sigNotifyBatchUpdateEnded();
}
void KisImageSignalRouter::slotNotification(KisImageSignalType type)
{
......
......@@ -149,8 +149,9 @@ public:
void emitNodeHasBeenAdded(KisNode *parent, int index);
void emitAboutToRemoveANode(KisNode *parent, int index);
void emitBeginLodResetUpdatesBatch();
void emitEndLodResetUpdatesBatch();
void emitRequestLodPlanesSyncBlocked(bool value);
void emitNotifyBatchUpdateStarted();
void emitNotifyBatchUpdateEnded();
private Q_SLOTS:
void slotNotification(KisImageSignalType type);
......@@ -159,6 +160,37 @@ Q_SIGNALS:
void sigNotification(KisImageSignalType type);
/**
* Emitted whenever the image wants to update Lod0 plane of the canvas. Blocking
* synching effectively means that the canvas will not try to read from these planes
* until the **all** the data is loaded. Otherwise the user will see weird flickering
* because of partially loaded lod0 tiles.
*
* NOTE: while the sync is blockes, the canvas is considered to use LodN planes
* that are expected to contain valid data.
*/
void sigRequestLodPlanesSyncBlocked(bool value);
/**
* Emitted whenever the image is going to issue a lot of canvas update signals and
* it it a good idea to group then and rerender the canvas in one go. The canvas
* should initiate new rerenders while the batch is in progress.
*
* NOTE: even though the batched updates will not initiate a rerender, it does
* **not** guarantee that there will be processed during the batch. The
* updates may come from other sources, e.g. from mouse moves.
*
* NOTE: this feature is used to avoid flickering when switching
* back from lodN plane back to lod0. All the texture tiles should
* be loaded with new information before mipmaps can be regenerated.
*/
void sigNotifyBatchUpdateStarted();
/**
* \see sigNotifyBatchUpdateStarted()
*/
void sigNotifyBatchUpdateEnded();
// Notifications
void sigImageModified();
......
......@@ -193,15 +193,6 @@ bool KisStrokesQueue::Private::shouldWrapInSuspendUpdatesStroke() const
if (stroke->isCancelled()) continue;
if (stroke->type() == KisStroke::RESUME) {
/**
* Resuming process is long-running and consists of
* multiple actions, therefore, if it has already started,
* we cannot use it to guard our new stroke, so just skip it.
* see https://phabricator.kde.org/T2542
*/
if (stroke->isInitialized()) continue;
return false;
}
}
......@@ -218,9 +209,6 @@ StrokesQueueIterator KisStrokesQueue::Private::findNewLod0Pos()
if ((*it)->isCancelled()) continue;
if ((*it)->type() == KisStroke::RESUME) {
// \see a comment in shouldWrapInSuspendUpdatesStroke()
if ((*it)->isInitialized()) continue;
return it;
}
}
......@@ -236,14 +224,6 @@ StrokesQueueIterator KisStrokesQueue::Private::findNewLodNPos(KisStrokeSP lodN)
for (; it != end; ++it) {
if ((*it)->isCancelled()) continue;
if (((*it)->type() == KisStroke::SUSPEND ||
(*it)->type() == KisStroke::RESUME) &&
(*it)->isInitialized()) {
// \see a comment in shouldWrapInSuspendUpdatesStroke()
continue;
}
if ((*it)->type() == KisStroke::LOD0 ||
(*it)->type() == KisStroke::SUSPEND ||
(*it)->type() == KisStroke::RESUME) {
......
......@@ -19,11 +19,11 @@
#ifndef __KIS_SUSPEND_PROJECTION_UPDATES_STROKE_STRATEGY_H
#define __KIS_SUSPEND_PROJECTION_UPDATES_STROKE_STRATEGY_H
#include <kis_simple_stroke_strategy.h>
#include <KisRunnableBasedStrokeStrategy.h>
#include <QScopedPointer>
class KisSuspendProjectionUpdatesStrokeStrategy : public KisSimpleStrokeStrategy
class KisSuspendProjectionUpdatesStrokeStrategy : public KisRunnableBasedStrokeStrategy
{
public:
KisSuspendProjectionUpdatesStrokeStrategy(KisImageWSP image, bool suspend);
......@@ -33,9 +33,14 @@ public:
static QList<KisStrokeJobData*> createResumeJobsData(KisImageWSP image);
private:
void initStrokeCallback() override;
void doStrokeCallback(KisStrokeJobData *data) override;
void cancelStrokeCallback() override;
void suspendStrokeCallback() override;
void resumeStrokeCallback() override;
void resumeAndIssueUpdates(bool dropUpdates);
private:
......
......@@ -95,6 +95,8 @@
#include "opengl/kis_opengl_canvas_debugger.h"
#include "kis_algebra_2d.h"
#include "kis_image_signal_router.h"
class Q_DECL_HIDDEN KisCanvas2::KisCanvas2Private
{
......@@ -149,7 +151,7 @@ public:
QRect regionOfInterest;
QRect renderingLimit;
int lodResetBatchActive = 0;
int isBatchUpdateActive = 0;
bool effectiveLodAllowedInImage() {
return lodAllowedInImage && !bootstrapLodBlocked;
......@@ -562,8 +564,9 @@ void KisCanvas2::initializeImage()
m_d->toolProxy.initializeImage(image);
connect(image, SIGNAL(sigImageUpdated(QRect)), SLOT(startUpdateCanvasProjection(QRect)), Qt::DirectConnection);
connect(image, SIGNAL(sigBeginLodResetUpdatesBatch()), SLOT(slotBeginLodResetUpdatesBatch()), Qt::DirectConnection);
connect(image, SIGNAL(sigEndLodResetUpdatesBatch()), SLOT(slotEndLodResetUpdatesBatch()), Qt::DirectConnection);
connect(image->signalRouter(), SIGNAL(sigNotifyBatchUpdateStarted()), SLOT(slotBeginUpdatesBatch()), Qt::DirectConnection);
connect(image->signalRouter(), SIGNAL(sigNotifyBatchUpdateEnded()), SLOT(slotEndUpdatesBatch()), Qt::DirectConnection);
connect(image->signalRouter(), SIGNAL(sigRequestLodPlanesSyncBlocked(bool)), SLOT(slotSetLodUpdatesBlocked(bool)), Qt::DirectConnection);
connect(image, SIGNAL(sigProofingConfigChanged()), SLOT(slotChangeProofingConfig()));
connect(image, SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), SLOT(startResizingImage()), Qt::DirectConnection);
......@@ -758,7 +761,7 @@ void KisCanvas2::startUpdateCanvasProjection(const QRect & rc)
void KisCanvas2::updateCanvasProjection()
{
auto tryIssueCanvasUpdates = [this](const QRect &vRect) {
if (!m_d->lodResetBatchActive) {
if (!m_d->isBatchUpdateActive) {
// TODO: Implement info->dirtyViewportRect() for KisOpenGLCanvas2 to avoid updating whole canvas
if (m_d->currentCanvasIsOpenGL) {
m_d->savedUpdateRect = QRect();
......@@ -782,26 +785,30 @@ void KisCanvas2::updateCanvasProjection()
tryIssueCanvasUpdates(vRect);
};
bool shouldExplicitlyIssueUpdates = false;
QVector<KisUpdateInfoSP> infoObjects;
while (KisUpdateInfoSP info = m_d->projectionUpdatesCompressor.takeUpdateInfo()) {
const KisBatchControlUpdateInfo *batchInfo = dynamic_cast<const KisBatchControlUpdateInfo*>(info.data());
const KisMarkerUpdateInfo *batchInfo = dynamic_cast<const KisMarkerUpdateInfo*>(info.data());
if (batchInfo) {
if (!infoObjects.isEmpty()) {
uploadData(infoObjects);
infoObjects.clear();
}
if (batchInfo->type() == KisBatchControlUpdateInfo::StartBatch) {
m_d->lodResetBatchActive++;
if (batchInfo->type() == KisMarkerUpdateInfo::StartBatch) {
m_d->isBatchUpdateActive++;
} else if (batchInfo->type() == KisMarkerUpdateInfo::EndBatch) {
m_d->isBatchUpdateActive--;
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->isBatchUpdateActive >= 0);
if (m_d->isBatchUpdateActive == 0) {
shouldExplicitlyIssueUpdates = true;
}
} else if (batchInfo->type() == KisMarkerUpdateInfo::BlockLodUpdates) {
m_d->canvasWidget->setLodResetInProgress(true);
} else if (batchInfo->type() == KisBatchControlUpdateInfo::EndBatch) {
} else if (batchInfo->type() == KisMarkerUpdateInfo::UnblockLodUpdates) {
m_d->canvasWidget->setLodResetInProgress(false);
m_d->lodResetBatchActive--;
KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->lodResetBatchActive >= 0);
if (m_d->lodResetBatchActive == 0) {
tryIssueCanvasUpdates(m_d->coordinatesConverter->imageRectInImagePixels());
}
shouldExplicitlyIssueUpdates = true;
}
} else {
infoObjects << info;
......@@ -810,27 +817,40 @@ void KisCanvas2::updateCanvasProjection()
if (!infoObjects.isEmpty()) {
uploadData(infoObjects);
} else if (shouldExplicitlyIssueUpdates) {
tryIssueCanvasUpdates(m_d->coordinatesConverter->imageRectInImagePixels());
}
}
void KisCanvas2::slotBeginLodResetUpdatesBatch()
void KisCanvas2::slotBeginUpdatesBatch()
{
KisUpdateInfoSP info =
new KisBatchControlUpdateInfo(KisBatchControlUpdateInfo::StartBatch,
new KisMarkerUpdateInfo(KisMarkerUpdateInfo::StartBatch,
m_d->coordinatesConverter->imageRectInImagePixels());
m_d->projectionUpdatesCompressor.putUpdateInfo(info);
emit sigCanvasCacheUpdated();
}
void KisCanvas2::slotEndLodResetUpdatesBatch()
void KisCanvas2::slotEndUpdatesBatch()
{
KisUpdateInfoSP info =
new KisBatchControlUpdateInfo(KisBatchControlUpdateInfo::EndBatch,
new KisMarkerUpdateInfo(KisMarkerUpdateInfo::EndBatch,
m_d->coordinatesConverter->imageRectInImagePixels());
m_d->projectionUpdatesCompressor.putUpdateInfo(info);
emit sigCanvasCacheUpdated();
}
void KisCanvas2::slotSetLodUpdatesBlocked(bool value)
{
KisUpdateInfoSP info =
new KisMarkerUpdateInfo(value ?
KisMarkerUpdateInfo::BlockLodUpdates :
KisMarkerUpdateInfo::UnblockLodUpdates,
m_d->coordinatesConverter->imageRectInImagePixels());
m_d->projectionUpdatesCompressor.putUpdateInfo(info);
emit sigCanvasCacheUpdated();
}
void KisCanvas2::slotDoCanvasUpdate()
{
/**
......
......@@ -286,8 +286,9 @@ private Q_SLOTS:
void startUpdateCanvasProjection(const QRect & rc);
void updateCanvasProjection();
void slotBeginLodResetUpdatesBatch();
void slotEndLodResetUpdatesBatch();
void slotBeginUpdatesBatch();
void slotEndUpdatesBatch();
void slotSetLodUpdatesBlocked(bool value);
/**
* Called whenever the view widget needs to show a different part of
......
......@@ -97,25 +97,25 @@ bool KisOpenGLUpdateInfo::tryMergeWith(const KisOpenGLUpdateInfo &rhs)
return true;
}
KisBatchControlUpdateInfo::KisBatchControlUpdateInfo(KisBatchControlUpdateInfo::Type type, const QRect &dirtyImageRect)
KisMarkerUpdateInfo::KisMarkerUpdateInfo(KisMarkerUpdateInfo::Type type, const QRect &dirtyImageRect)
: m_type(type),
m_dirtyImageRect(dirtyImageRect)
{
}
KisBatchControlUpdateInfo::Type KisBatchControlUpdateInfo::type() const
KisMarkerUpdateInfo::Type KisMarkerUpdateInfo::type() const
{
return m_type;
}
QRect KisBatchControlUpdateInfo::dirtyImageRect() const
QRect KisMarkerUpdateInfo::dirtyImageRect() const
{
return m_dirtyImageRect;
}
int KisBatchControlUpdateInfo::levelOfDetail() const
int KisMarkerUpdateInfo::levelOfDetail() const
{
// return invalid level of detail to avoid merging the update info
// with other updates
return m_type == StartBatch ? -1 : -2;
return -1 - (int)m_type;
}
......@@ -142,16 +142,18 @@ public:
KisImagePatch patch;
};
class KisBatchControlUpdateInfo : public KisUpdateInfo
class KisMarkerUpdateInfo : public KisUpdateInfo
{
public:
enum Type {
StartBatch,
EndBatch
StartBatch = 0,
EndBatch,
BlockLodUpdates,
UnblockLodUpdates,
};
public:
KisBatchControlUpdateInfo(Type type, const QRect &dirtyImageRect);
KisMarkerUpdateInfo(Type type, const QRect &dirtyImageRect);
Type type() const;
......
......@@ -237,7 +237,7 @@ bool KisAnimationFrameCache::uploadFrame(int time)
// Previously we were trying to start cache regeneration in this point,
// but it caused even bigger slowdowns when scrubbing
} else {
m_d->textures->recalculateCache(info);
m_d->textures->recalculateCache(info, false);
}