26 #include <QtConcurrentMap>
27 #include <QtConcurrentRun>
43 void QgsMapRendererParallelJob::startPrivate()
50 mStatus = RenderingLayers;
52 mLabelingEngineV2.reset();
57 mLabelingEngineV2->setMapSettings(
mSettings );
61 mLayerJobs =
prepareJobs(
nullptr, mLabelingEngineV2.get() );
65 QgsDebugMsgLevel( QStringLiteral(
"QThreadPool max thread count is %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ), 2 );
69 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
71 mFuture = QtConcurrent::map( mLayerJobs, renderLayerStatic );
72 mFutureWatcher.setFuture( mFuture );
80 QgsDebugMsgLevel( QStringLiteral(
"PARALLEL cancel at status %1" ).arg( mStatus ), 2 );
82 mLabelJob.context.setRenderingStopped(
true );
83 for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
85 it->context.setRenderingStopped(
true );
86 if ( it->renderer && it->renderer->feedback() )
87 it->renderer->feedback()->cancel();
90 if ( mStatus == RenderingLayers )
92 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
94 mFutureWatcher.waitForFinished();
96 renderLayersFinished();
99 if ( mStatus == RenderingLabels )
101 disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
103 mLabelingFutureWatcher.waitForFinished();
108 if ( mStatus == RenderingSecondPass )
110 disconnect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
112 mSecondPassFutureWatcher.waitForFinished();
114 renderLayersSecondPassFinished();
117 Q_ASSERT( mStatus == Idle );
125 QgsDebugMsgLevel( QStringLiteral(
"PARALLEL cancel at status %1" ).arg( mStatus ), 2 );
127 mLabelJob.context.setRenderingStopped(
true );
128 for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
130 it->context.setRenderingStopped(
true );
131 if ( it->renderer && it->renderer->feedback() )
132 it->renderer->feedback()->cancel();
135 if ( mStatus == RenderingLayers )
137 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
138 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
147 if ( mStatus == RenderingLayers )
149 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
154 mFutureWatcher.waitForFinished();
156 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ), 2 );
158 renderLayersFinished();
161 if ( mStatus == RenderingLabels )
163 disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
168 mLabelingFutureWatcher.waitForFinished();
170 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished (2): %1 ms" ).arg( t.elapsed() / 1000.0 ), 2 );
175 if ( mStatus == RenderingSecondPass )
177 disconnect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
182 mSecondPassFutureWatcher.waitForFinished();
184 QgsDebugMsg( QStringLiteral(
"waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
186 renderLayersSecondPassFinished();
189 Q_ASSERT( mStatus == Idle );
194 return mStatus != Idle;
199 return mLabelJob.cached;
204 if ( mLabelingEngineV2 )
205 return mLabelingEngineV2->takeResults();
215 const bool jobIsComplete = mStatus == Idle && !mFinalImage.isNull();
217 if ( !jobIsComplete )
223 void QgsMapRendererParallelJob::renderLayersFinished()
225 Q_ASSERT( mStatus == RenderingLayers );
227 LayerRenderJobs::const_iterator it = mLayerJobs.constBegin();
228 for ( ; it != mLayerJobs.constEnd(); ++it )
230 if ( !it->errors.isEmpty() )
232 mErrors.append( Error( it->layer->id(), it->errors.join(
',' ) ) );
237 if ( mSecondPassLayerJobs.isEmpty() )
246 mStatus = RenderingLabels;
248 connect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
251 mLabelingFuture = QtConcurrent::run( renderLabelsStatic,
this );
252 mLabelingFutureWatcher.setFuture( mLabelingFuture );
261 #define DEBUG_RENDERING 0
263 void QgsMapRendererParallelJob::renderingFinished()
267 for ( LayerRenderJob &job : mLayerJobs )
271 job.img->save( QString(
"/tmp/first_pass_%1.png" ).arg( i ) );
273 if ( job.maskPass.image )
275 job.maskPass.image->save( QString(
"/tmp/first_pass_%1_mask.png" ).arg( i ) );
281 mLabelJob.img->save( QString(
"/tmp/labels.png" ) );
283 if ( mLabelJob.maskImage )
285 mLabelJob.maskImage->save( QString(
"/tmp/labels_mask.png" ) );
288 if ( ! mSecondPassLayerJobs.isEmpty() )
290 mStatus = RenderingSecondPass;
292 mSecondPassFuture = QtConcurrent::map( mSecondPassLayerJobs, renderLayerStatic );
293 mSecondPassFutureWatcher.setFuture( mSecondPassFuture );
294 connect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
314 void QgsMapRendererParallelJob::renderLayersSecondPassFinished()
345 void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob &job )
347 if ( job.context.renderingStopped() )
356 job.imageInitialized =
true;
361 QgsDebugMsgLevel( QStringLiteral(
"job %1 start (layer %2)" ).arg(
reinterpret_cast< quint64
>( &job ), 0, 16 ).arg( job.layerId ), 2 );
364 #ifdef SIMULATE_SLOW_RENDERER
367 job.completed = job.renderer->render();
374 catch ( std::exception &e )
377 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
381 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
384 job.errors = job.renderer->errors();
385 job.renderingTime += t.elapsed();
386 QgsDebugMsgLevel( QStringLiteral(
"job %1 end [%2 ms] (layer %3)" ).arg(
reinterpret_cast< quint64
>( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layerId ), 2 );
392 LabelRenderJob &job =
self->mLabelJob;
396 QElapsedTimer labelTime;
403 painter.begin( job.img );
407 painter.begin( &self->mFinalImage );
413 drawLabeling( job.context, self->mLabelingEngineV2.get(), &painter );
420 catch ( std::exception &e )
423 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
427 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
432 job.renderingTime = labelTime.elapsed();
434 job.participatingLayers = _qgis_listRawToQPointer( self->mLabelingEngineV2->participatingLayers() );
437 self->mFinalImage =
composeImage( self->mSettings, self->mLayerJobs, self->mLabelJob );
Default QgsLabelingEngine implementation, which completes the whole labeling operation (including lab...
Defines a QGIS exception class.
Class that stores computed placement from labeling engine.
void cleanupSecondPassJobs(LayerRenderJobs &jobs)
LayerRenderJobs prepareSecondPassJobs(LayerRenderJobs &firstPassJobs, LabelRenderJob &labelJob)
Prepares jobs for a second pass, if selective masks exist (from labels or symbol layers).
void logRenderingTime(const LayerRenderJobs &jobs, const LayerRenderJobs &secondPassJobs, const LabelRenderJob &labelJob)
static Q_DECL_DEPRECATED void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter)
LayerRenderJobs prepareJobs(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet=false)
Creates a list of layer rendering jobs and prepares them for later render.
void renderingLayersFinished()
Emitted when the layers are rendered.
static void composeSecondPass(LayerRenderJobs &secondPassJobs, LabelRenderJob &labelJob)
Compose second pass images into first pass images.
QElapsedTimer mRenderingStart
QgsMapRendererCache * mCache
void finished()
emitted when asynchronous rendering is finished (or canceled).
static QImage composeImage(const QgsMapSettings &settings, const LayerRenderJobs &jobs, const LabelRenderJob &labelJob, const QgsMapRendererCache *cache=nullptr)
LabelRenderJob prepareLabelingJob(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache=true)
Prepares a labeling job.
void cleanupLabelJob(LabelRenderJob &job)
Handles clean up tasks for a label job, including deletion of images and storing cached label results...
void cleanupJobs(LayerRenderJobs &jobs)
bool prepareLabelCache() const
Prepares the cache for storing the result of labeling.
Job implementation that renders all layers in parallel.
QgsLabelingResults * takeLabelingResults() override
Gets pointer to internal labeling engine (in order to get access to the results).
bool usedCachedLabels() const override
Returns true if the render job was able to use a cached labeling solution.
bool isActive() const override
Tell whether the rendering job is currently running in background.
QgsMapRendererParallelJob(const QgsMapSettings &settings)
void cancelWithoutBlocking() override
Triggers cancellation of the rendering job without blocking.
QImage renderedImage() override
Gets a preview/resulting image.
void cancel() override
Stop the rendering job - does not return until the job has terminated.
~QgsMapRendererParallelJob() override
void waitForFinished() override
Block until the job has finished.
Intermediate base class adding functionality that allows client to query the rendered image.
The QgsMapSettings class contains configuration for rendering of the map.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
@ DrawLabeling
Enable drawing of labels on top of the map.
#define QgsDebugMsgLevel(str, level)