Krita Source Code Documentation
Loading...
Searching...
No Matches
KisAsyncAnimationRenderDialogBase Class Referenceabstract

KisAsyncAnimationRenderDialogBase is a special class for rendering multiple frames of the image and putting them somewhere (saving into a file or just pushing into an openGL cache) More...

#include <KisAsyncAnimationRenderDialogBase.h>

+ Inheritance diagram for KisAsyncAnimationRenderDialogBase:

Classes

struct  Private
 

Public Types

enum  Result { RenderComplete , RenderCancelled , RenderFailed , RenderTimedOut }
 

Public Member Functions

bool batchMode () const
 
 KisAsyncAnimationRenderDialogBase (const QString &actionTitle, KisImageSP image, int busyWait=200)
 construct and initialize the dialog
 
virtual Result regenerateRange (KisViewManager *viewManager)
 start generation of frames and (if not in batch mode) show the dialog
 
KisRegion regionOfInterest () const
 
void setBatchMode (bool value)
 setting batch mode to true will prevent any dialogs or message boxes from showing on screen. Please take it into account that using batch mode prevents some potentially dangerous recovery execution paths (e.g. delete the existing frames in the destination folder). In such case the rendering will be stopped with RenderFailed result.
 
void setRegionOfInterest (const KisRegion &roi)
 
virtual ~KisAsyncAnimationRenderDialogBase ()
 

Protected Member Functions

virtual QList< int > calcDirtyFrames () const =0
 returns a list of frames that should be regenerated by the dialog
 
virtual KisAsyncAnimationRendererBasecreateRenderer (KisImageSP image)=0
 create a renderer object linked to image
 
virtual void initializeRendererForFrame (KisAsyncAnimationRendererBase *renderer, KisImageSP image, int frame)=0
 

Private Slots

void slotCancelRegeneration ()
 
void slotFrameCancelled (int frame, KisAsyncAnimationRendererBase::CancelReason cancelReason)
 
void slotFrameCompleted (int frame)
 
void slotUpdateCompressedProgressData ()
 

Private Member Functions

void cancelProcessingImpl (KisAsyncAnimationRendererBase::CancelReason cancelReason)
 
void tryInitiateFrameRegeneration ()
 
void updateProgressLabel ()
 

Private Attributes

const QScopedPointer< Privatem_d
 

Detailed Description

KisAsyncAnimationRenderDialogBase is a special class for rendering multiple frames of the image and putting them somewhere (saving into a file or just pushing into an openGL cache)

The class handles a lot of boilerplate code for you and optimizes regeneration of the frames using multithreading, according to the user's settings. The responsibilities of the class are the following:

Rendering itself:

  • fetch the list of dirty frames using calcDirtyFrames()
  • create some clones of the image according to the user's settings to facilitate multithreaded rendering and processing of the frames
  • if the user doesn't have enough RAM, the clones will not be created (the memory overhead is calculated using "projections" metric of the statistics server).
  • feed the images/threads with dirty frames until the all the frames are done

Progress reporting:

  • if batchMode() is false, the user will see a progress dialog showing the current progress with estimate about total processing timer
  • the user can also cancel the regeneration by pressing Cancel button

Usage Details:

Definition at line 50 of file KisAsyncAnimationRenderDialogBase.h.

Member Enumeration Documentation

◆ Result

Constructor & Destructor Documentation

◆ KisAsyncAnimationRenderDialogBase()

KisAsyncAnimationRenderDialogBase::KisAsyncAnimationRenderDialogBase ( const QString & actionTitle,
KisImageSP image,
int busyWait = 200 )

construct and initialize the dialog

Parameters
actionTitlethe first line of the status reports that the user sees in the dialog
imagethe image that will be as a source of frames. Make sure the image is not locked by the time regenerateRange() is called
busyWaitthe dialog will not show for the specified time (in milliseconds)

Definition at line 112 of file KisAsyncAnimationRenderDialogBase.cpp.

113 : m_d(new Private(actionTitle, image, busyWait))
114{
115 connect(&m_d->progressDialogCompressor, SIGNAL(timeout()),
116 SLOT(slotUpdateCompressedProgressData()), Qt::QueuedConnection);
117}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))

References connect(), m_d, and slotUpdateCompressedProgressData().

◆ ~KisAsyncAnimationRenderDialogBase()

KisAsyncAnimationRenderDialogBase::~KisAsyncAnimationRenderDialogBase ( )
virtual

Definition at line 119 of file KisAsyncAnimationRenderDialogBase.cpp.

120{
121}

Member Function Documentation

◆ batchMode()

bool KisAsyncAnimationRenderDialogBase::batchMode ( ) const
See also
setBatchMode

Definition at line 404 of file KisAsyncAnimationRenderDialogBase.cpp.

405{
406 return m_d->isBatchMode;
407}

References m_d.

◆ calcDirtyFrames()

virtual QList< int > KisAsyncAnimationRenderDialogBase::calcDirtyFrames ( ) const
protectedpure virtual

returns a list of frames that should be regenerated by the dialog

Called by the dialog in the very beginning of regenerateRange()

Implemented in KisAsyncAnimationCacheRenderDialog, and KisAsyncAnimationFramesSaveDialog.

◆ cancelProcessingImpl()

void KisAsyncAnimationRenderDialogBase::cancelProcessingImpl ( KisAsyncAnimationRendererBase::CancelReason cancelReason)
private

Definition at line 286 of file KisAsyncAnimationRenderDialogBase.cpp.

287{
288 for (auto &pair : m_d->asyncRenderers) {
289 if (pair.renderer->isActive()) {
290 pair.renderer->cancelCurrentFrameRendering(cancelReason);
291 }
292 KIS_SAFE_ASSERT_RECOVER_NOOP(!pair.renderer->isActive());
293 }
294
295 m_d->stillDirtyFrames.clear();
296 m_d->framesInProgress.clear();
297 m_d->result =
302}
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130

References KIS_SAFE_ASSERT_RECOVER_NOOP, m_d, RenderCancelled, RenderFailed, KisAsyncAnimationRendererBase::RenderingFailed, RenderTimedOut, updateProgressLabel(), and KisAsyncAnimationRendererBase::UserCancelled.

◆ createRenderer()

virtual KisAsyncAnimationRendererBase * KisAsyncAnimationRenderDialogBase::createRenderer ( KisImageSP image)
protectedpure virtual

create a renderer object linked to image

Renderer are special objects that connect to the individual image signals, react on them and fetch the final frames data

See also
KisAsyncAnimationRendererBase

Implemented in KisAsyncAnimationCacheRenderDialog, and KisAsyncAnimationFramesSaveDialog.

◆ initializeRendererForFrame()

virtual void KisAsyncAnimationRenderDialogBase::initializeRendererForFrame ( KisAsyncAnimationRendererBase * renderer,
KisImageSP image,
int frame )
protectedpure virtual

◆ regenerateRange()

KisAsyncAnimationRenderDialogBase::Result KisAsyncAnimationRenderDialogBase::regenerateRange ( KisViewManager * viewManager)
virtual

start generation of frames and (if not in batch mode) show the dialog

The link to view manager is used to barrier lock with visual feedback in the end of the operation

Since this method can be called from the places where no view manager is available, we need this manually crafted ugly construction to "try-lock-cancel" the image.

Reimplemented in KisAsyncAnimationFramesSaveDialog.

Definition at line 124 of file KisAsyncAnimationRenderDialogBase.cpp.

125{
126 KisBlockBackgroundFrameGenerationLock populatorBlock(m_d->image->animationInterface());
127
128 {
135 bool imageIsIdle = true;
136
137 if (viewManager) {
138 imageIsIdle = viewManager->blockUntilOperationsFinished(m_d->image);
139 } else {
140 imageIsIdle = false;
141 if (m_d->image->tryBarrierLock(true)) {
142 m_d->image->unlock();
143 imageIsIdle = true;
144 }
145 }
146
147 if (!imageIsIdle) {
148 return RenderCancelled;
149 }
150 }
151
152
153 if (!m_d->isBatchMode) {
154 QWidget *parentWidget = viewManager ? viewManager->mainWindowAsQWidget() : 0;
155 KisLockFrameGenerationLockAdapter adapter(m_d->image->animationInterface());
156 KisAsyncActionFeedback feedback(i18n("Wait for existing frame generation process to complete..."), parentWidget);
157 feedback.waitForMutex(adapter);
158 }
159
160 m_d->stillDirtyFrames = calcDirtyFrames();
161 m_d->framesInProgress.clear();
162 m_d->result = RenderComplete;
163 m_d->dirtyFramesCount = m_d->stillDirtyFrames.size();
164
165 if (!m_d->isBatchMode) {
166 QWidget *parentWidget = viewManager ? viewManager->mainWindowAsQWidget() : 0;
167 m_d->progressDialog.reset(new QProgressDialog(m_d->actionTitle, i18n("Cancel"), 0, 0, parentWidget));
168 m_d->progressDialog->setWindowModality(Qt::ApplicationModal);
169 m_d->progressDialog->setMinimum(0);
170 m_d->progressDialog->setMaximum(m_d->dirtyFramesCount);
171 m_d->progressDialog->setMinimumDuration(m_d->busyWait);
172 connect(m_d->progressDialog.data(), SIGNAL(canceled()), SLOT(slotCancelRegeneration()));
173 }
174
175 if (m_d->dirtyFramesCount <= 0) return m_d->result;
176
177 m_d->processingTime.start();
178
179 KisImageConfig cfg(true);
180
181 const int maxThreads = cfg.maxNumberOfThreads();
182 const int numAllowedWorker = 1 + calculateNumberMemoryAllowedClones(m_d->image);
183 const int proposedNumWorkers = qMin(m_d->dirtyFramesCount, cfg.frameRenderingClones());
184 const int numWorkers = qMin(proposedNumWorkers, numAllowedWorker);
185 const int numThreadsPerWorker = qMax(1, qCeil(qreal(maxThreads) / numWorkers));
186
187 m_d->memoryLimitReached = numWorkers < proposedNumWorkers;
188
189 const int oldWorkingThreadsLimit = m_d->image->workingThreadsLimit();
190
191 for (int i = 0; i < numWorkers; i++) {
192 // reuse the image for one of the workers
193 const bool lastWorker = (i == numWorkers - 1);
194 KisImageSP image = m_d->image;
195
196 if (!lastWorker) {
197 //Only the last-most worker should try to use the source image pointer. Others need a copy.
198
199 if (m_d->asyncRenderers.size() == 0) {
200 // Copy source image when no renderers (aka no copies) exist, requires lock as image memory can be actively modified.
201 m_d->image->barrierLock(true);
202 image = m_d->image->clone(true);
203 m_d->image->unlock();
204 } else {
205 // Copy a previous copy, shouldn't require lock since image is "fresh" and untouchable by other krita systems.
206 image = m_d->asyncRenderers[m_d->asyncRenderers.size() - 1].image->clone(true);
207 }
208 }
209
210
211 image->setWorkingThreadsLimit(numThreadsPerWorker);
213
214 connect(renderer, SIGNAL(sigFrameCompleted(int)), SLOT(slotFrameCompleted(int)));
216
217 m_d->asyncRenderers.push_back(RendererPair(renderer, image));
218 }
219
220
223
224 if (m_d->numDirtyFramesLeft() > 0) {
225 m_d->waitLoop.exec();
226 }
227
228 for (auto &pair : m_d->asyncRenderers) {
229 KIS_SAFE_ASSERT_RECOVER_NOOP(!pair.renderer->isActive());
230 if (viewManager) {
231 viewManager->blockUntilOperationsFinishedForced(pair.image);
232 } else {
233 pair.image->barrierLock(true);
234 pair.image->unlock();
235 }
236
237 }
238 m_d->asyncRenderers.clear();
239
240 if (viewManager) {
241 viewManager->blockUntilOperationsFinishedForced(m_d->image);
242 } else {
243 m_d->image->barrierLock(true);
244 m_d->image->unlock();
245 }
246
247 m_d->image->setWorkingThreadsLimit(oldWorkingThreadsLimit);
248
249 m_d->progressDialog.reset();
250
251 return m_d->result;
252}
void slotFrameCancelled(int frame, KisAsyncAnimationRendererBase::CancelReason cancelReason)
virtual KisAsyncAnimationRendererBase * createRenderer(KisImageSP image)=0
create a renderer object linked to image
virtual QList< int > calcDirtyFrames() const =0
returns a list of frames that should be regenerated by the dialog
void setWorkingThreadsLimit(int value)
void unlock()
Definition kis_image.cc:805
void barrierLock(bool readOnly=false)
Wait until all the queued background jobs are completed and lock the image.
Definition kis_image.cc:756
KisImage * clone(bool exactCopy=false)
Definition kis_image.cc:405
bool blockUntilOperationsFinished(KisImageSP image)
blockUntilOperationsFinished blocks the GUI of the application until execution of actions on image is...
void blockUntilOperationsFinishedForced(KisImageSP image)
blockUntilOperationsFinished blocks the GUI of the application until execution of actions on image is...
QWidget * mainWindowAsQWidget() const

References KisImage::barrierLock(), KisViewManager::blockUntilOperationsFinished(), KisViewManager::blockUntilOperationsFinishedForced(), calcDirtyFrames(), KisImage::clone(), connect(), createRenderer(), KisImageConfig::frameRenderingClones(), KIS_SAFE_ASSERT_RECOVER_NOOP, m_d, KisViewManager::mainWindowAsQWidget(), KisImageConfig::maxNumberOfThreads(), RenderCancelled, RenderComplete, KisImage::setWorkingThreadsLimit(), slotCancelRegeneration(), slotFrameCancelled(), slotFrameCompleted(), tryInitiateFrameRegeneration(), KisImage::unlock(), updateProgressLabel(), and KisAsyncActionFeedback::waitForMutex().

◆ regionOfInterest()

KisRegion KisAsyncAnimationRenderDialogBase::regionOfInterest ( ) const
See also
setRegionOfInterest()

Definition at line 259 of file KisAsyncAnimationRenderDialogBase.cpp.

260{
261 return m_d->regionOfInterest;
262}

References m_d.

◆ setBatchMode()

void KisAsyncAnimationRenderDialogBase::setBatchMode ( bool value)

setting batch mode to true will prevent any dialogs or message boxes from showing on screen. Please take it into account that using batch mode prevents some potentially dangerous recovery execution paths (e.g. delete the existing frames in the destination folder). In such case the rendering will be stopped with RenderFailed result.

Definition at line 399 of file KisAsyncAnimationRenderDialogBase.cpp.

400{
401 m_d->isBatchMode = value;
402}
float value(const T *src, size_t ch)

References m_d, and value().

◆ setRegionOfInterest()

void KisAsyncAnimationRenderDialogBase::setRegionOfInterest ( const KisRegion & roi)

Set area of image that will be regenerated. If roi is empty, full area of the image is regenerated.

Definition at line 254 of file KisAsyncAnimationRenderDialogBase.cpp.

255{
256 m_d->regionOfInterest = roi;
257}

References m_d.

◆ slotCancelRegeneration

void KisAsyncAnimationRenderDialogBase::slotCancelRegeneration ( )
privateslot

◆ slotFrameCancelled

void KisAsyncAnimationRenderDialogBase::slotFrameCancelled ( int frame,
KisAsyncAnimationRendererBase::CancelReason cancelReason )
privateslot

Definition at line 274 of file KisAsyncAnimationRenderDialogBase.cpp.

275{
276 Q_UNUSED(frame);
277
278 cancelProcessingImpl(cancelReason);
279}

References cancelProcessingImpl().

◆ slotFrameCompleted

void KisAsyncAnimationRenderDialogBase::slotFrameCompleted ( int frame)
privateslot

Definition at line 264 of file KisAsyncAnimationRenderDialogBase.cpp.

265{
266 Q_UNUSED(frame);
267
268 m_d->framesInProgress.removeOne(frame);
269
272}

References m_d, tryInitiateFrameRegeneration(), and updateProgressLabel().

◆ slotUpdateCompressedProgressData

void KisAsyncAnimationRenderDialogBase::slotUpdateCompressedProgressData ( )
privateslot

Qt's implementation of QProgressDialog is a bit weird: it calls QApplication::processEvents() from inside setValue(), which means that our update method may reenter multiple times.

This code avoids reentering by using a compressor and an explicit entrance counter.

Definition at line 372 of file KisAsyncAnimationRenderDialogBase.cpp.

373{
383 if (m_d->progressDialogReentrancyCounter > 0) {
384 m_d->progressDialogCompressor.start();
385 return;
386 }
387
388 if (m_d->progressDialog && m_d->progressData) {
389 m_d->progressDialogReentrancyCounter++;
390
391 m_d->progressDialog->setLabelText(m_d->progressData->second);
392 m_d->progressDialog->setValue(m_d->progressData->first);
393 m_d->progressData = boost::none;
394
395 m_d->progressDialogReentrancyCounter--;
396 }
397}

References m_d.

◆ tryInitiateFrameRegeneration()

void KisAsyncAnimationRenderDialogBase::tryInitiateFrameRegeneration ( )
private

Definition at line 305 of file KisAsyncAnimationRenderDialogBase.cpp.

306{
307 bool hadWorkOnPreviousCycle = false;
308
309 while (!m_d->stillDirtyFrames.isEmpty()) {
310 for (auto &pair : m_d->asyncRenderers) {
311 if (!pair.renderer->isActive()) {
312 const int currentDirtyFrame = m_d->stillDirtyFrames.takeFirst();
313
314 KisLockFrameGenerationLock lock(pair.image->animationInterface());
315
316 initializeRendererForFrame(pair.renderer.get(), pair.image, currentDirtyFrame);
317 pair.renderer->startFrameRegeneration(pair.image, currentDirtyFrame, m_d->regionOfInterest,
318 KisAsyncAnimationRendererBase::None, std::move(lock));
319 hadWorkOnPreviousCycle = true;
320 m_d->framesInProgress.append(currentDirtyFrame);
321 break;
322 }
323 }
324
325 if (!hadWorkOnPreviousCycle) break;
326 hadWorkOnPreviousCycle = false;
327 }
328}
virtual void initializeRendererForFrame(KisAsyncAnimationRendererBase *renderer, KisImageSP image, int frame)=0

References initializeRendererForFrame(), m_d, and KisAsyncAnimationRendererBase::None.

◆ updateProgressLabel()

void KisAsyncAnimationRenderDialogBase::updateProgressLabel ( )
private

We should avoid reentrancy caused by explicit QApplication::processEvents() in QProgressDialog::setValue(), so use a compressor instead

Definition at line 330 of file KisAsyncAnimationRenderDialogBase.cpp.

331{
332 const int processedFramesCount = m_d->dirtyFramesCount - m_d->numDirtyFramesLeft();
333
334 const qint64 elapsedMSec = m_d->processingTime.elapsed();
335 const qint64 estimatedMSec =
336 !processedFramesCount ? 0 :
337 elapsedMSec * m_d->dirtyFramesCount / processedFramesCount;
338
339 const QTime elapsedTime = QTime::fromMSecsSinceStartOfDay(elapsedMSec);
340 const QTime estimatedTime = QTime::fromMSecsSinceStartOfDay(estimatedMSec);
341
342 const QString timeFormat = estimatedTime.hour() > 0 ? "HH:mm:ss" : "mm:ss";
343
344 const QString elapsedTimeString = elapsedTime.toString(timeFormat);
345 const QString estimatedTimeString = estimatedTime.toString(timeFormat);
346
347 const QString memoryLimitMessage(
348 i18n("\n\nThe memory limit has been reached.\nThe number of frames saved simultaneously is limited to %1\n\n",
349 m_d->asyncRenderers.size()));
350
351
352 const QString progressLabel(i18n("%1\n\nElapsed: %2\nEstimated: %3\n\n%4",
353 m_d->actionTitle,
354 elapsedTimeString,
355 estimatedTimeString,
356 m_d->memoryLimitReached ? memoryLimitMessage : QString()));
357 if (m_d->progressDialog) {
363 m_d->progressData = Private::ProgressData(processedFramesCount, progressLabel);
364 m_d->progressDialogCompressor.start();
365 }
366
367 if (!m_d->numDirtyFramesLeft()) {
368 m_d->waitLoop.quit();
369 }
370}

References m_d.

Member Data Documentation

◆ m_d

const QScopedPointer<Private> KisAsyncAnimationRenderDialogBase::m_d
private

Definition at line 140 of file KisAsyncAnimationRenderDialogBase.h.


The documentation for this class was generated from the following files: