26 #include <QtConcurrentMap> 27 #include <QtConcurrentRun> 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();
212 if ( mStatus == RenderingLayers )
218 void QgsMapRendererParallelJob::renderLayersFinished()
220 Q_ASSERT( mStatus == RenderingLayers );
222 LayerRenderJobs::const_iterator it = mLayerJobs.constBegin();
223 for ( ; it != mLayerJobs.constEnd(); ++it )
225 if ( !it->errors.isEmpty() )
227 mErrors.append(
Error( it->layer->id(), it->errors.join(
',' ) ) );
232 if ( mSecondPassLayerJobs.isEmpty() )
241 mStatus = RenderingLabels;
243 connect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
246 mLabelingFuture = QtConcurrent::run( renderLabelsStatic,
this );
247 mLabelingFutureWatcher.setFuture( mLabelingFuture );
256 #define DEBUG_RENDERING 0 258 void QgsMapRendererParallelJob::renderingFinished()
262 for ( LayerRenderJob &job : mLayerJobs )
266 job.img->save( QString(
"/tmp/first_pass_%1.png" ).arg( i ) );
268 if ( job.maskPass.image )
270 job.maskPass.image->save( QString(
"/tmp/first_pass_%1_mask.png" ).arg( i ) );
276 mLabelJob.img->save( QString(
"/tmp/labels.png" ) );
278 if ( mLabelJob.maskImage )
280 mLabelJob.maskImage->save( QString(
"/tmp/labels_mask.png" ) );
283 if ( ! mSecondPassLayerJobs.isEmpty() )
285 mStatus = RenderingSecondPass;
287 mSecondPassFuture = QtConcurrent::map( mSecondPassLayerJobs, renderLayerStatic );
288 mSecondPassFutureWatcher.setFuture( mSecondPassFuture );
289 connect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
309 void QgsMapRendererParallelJob::renderLayersSecondPassFinished()
334 void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob &job )
336 if ( job.context.renderingStopped() )
345 job.imageInitialized =
true;
350 QgsDebugMsgLevel( QStringLiteral(
"job %1 start (layer %2)" ).arg( reinterpret_cast< quint64 >( &job ), 0, 16 ).arg( job.layerId ), 2 );
353 job.renderer->render();
360 catch ( std::exception &e )
363 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
367 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
370 job.errors = job.renderer->errors();
371 job.renderingTime += t.elapsed();
372 QgsDebugMsgLevel( QStringLiteral(
"job %1 end [%2 ms] (layer %3)" ).arg( reinterpret_cast< quint64 >( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layerId ), 2 );
378 LabelRenderJob &job =
self->mLabelJob;
382 QElapsedTimer labelTime;
389 painter.begin( job.img );
393 painter.begin( &self->mFinalImage );
399 drawLabeling( job.context, self->mLabelingEngineV2.get(), &painter );
406 catch ( std::exception &e )
409 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
413 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
418 job.renderingTime = labelTime.elapsed();
420 job.participatingLayers = _qgis_listRawToQPointer( self->mLabelingEngineV2->participatingLayers() );
423 self->mFinalImage =
composeImage( self->mSettings, self->mLayerJobs, self->mLabelJob );
void finished()
emitted when asynchronous rendering is finished (or canceled).
QElapsedTimer mRenderingStart
void waitForFinished() override
Block until the job has finished.
void cleanupJobs(LayerRenderJobs &jobs)
static Q_DECL_DEPRECATED void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter)
void cleanupSecondPassJobs(LayerRenderJobs &jobs)
~QgsMapRendererParallelJob() override
void logRenderingTime(const LayerRenderJobs &jobs, const LayerRenderJobs &secondPassJobs, const LabelRenderJob &labelJob)
Default QgsLabelingEngine implementation, which completes the whole labeling operation (including lab...
void renderingLayersFinished()
Emitted when the layers are rendered.
void cancel() override
Stop the rendering job - does not return until the job has terminated.
Enable drawing of labels on top of the map.
The QgsMapSettings class contains configuration for rendering of the map.
QgsMapRendererParallelJob(const QgsMapSettings &settings)
static QImage composeImage(const QgsMapSettings &settings, const LayerRenderJobs &jobs, const LabelRenderJob &labelJob)
LabelRenderJob prepareLabelingJob(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool canUseLabelCache=true)
Prepares a labeling job.
bool usedCachedLabels() const override
Returns true if the render job was able to use a cached labeling solution.
bool prepareLabelCache() const
Prepares the cache for storing the result of labeling.
LayerRenderJobs prepareJobs(QPainter *painter, QgsLabelingEngine *labelingEngine2, bool deferredPainterSet=false)
Creates a list of layer rendering jobs and prepares them for later render.
#define QgsDebugMsgLevel(str, level)
Job implementation that renders all layers in parallel.
void cleanupLabelJob(LabelRenderJob &job)
Handles clean up tasks for a label job, including deletion of images and storing cached label results...
LayerRenderJobs prepareSecondPassJobs(LayerRenderJobs &firstPassJobs, LabelRenderJob &labelJob)
Prepares jobs for a second pass, if selective masks exist (from labels or symbol layers).
void cancelWithoutBlocking() override
Triggers cancellation of the rendering job without blocking.
Intermediate base class adding functionality that allows client to query the rendered image...
void start() override
Start the rendering job and immediately return.
static void composeSecondPass(LayerRenderJobs &secondPassJobs, LabelRenderJob &labelJob)
Compose second pass images into first pass images.
Class that stores computed placement from labeling engine.
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
Defines a QGIS exception class.
bool isActive() const override
Tell whether the rendering job is currently running in background.
QImage renderedImage() override
Gets a preview/resulting image.
QgsLabelingResults * takeLabelingResults() override
Gets pointer to internal labeling engine (in order to get access to the results). ...