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 ( LayerRenderJob &job : mLayerJobs )
85 job.context()->setRenderingStopped(
true );
86 if ( job.renderer && job.renderer->feedback() )
87 job.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 ( LayerRenderJob &job : mLayerJobs )
130 job.context()->setRenderingStopped(
true );
131 if ( job.renderer && job.renderer->feedback() )
132 job.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 for (
const LayerRenderJob &job : mLayerJobs )
229 if ( !job.errors.isEmpty() )
231 mErrors.append( Error( job.layerId, job.errors.join(
',' ) ) );
236 if ( mSecondPassLayerJobs.empty() )
245 mStatus = RenderingLabels;
247 connect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
250 mLabelingFuture = QtConcurrent::run( renderLabelsStatic,
this );
251 mLabelingFutureWatcher.setFuture( mLabelingFuture );
260 #define DEBUG_RENDERING 0
262 void QgsMapRendererParallelJob::renderingFinished()
266 for ( LayerRenderJob &job : mLayerJobs )
270 job.img->save( QString(
"/tmp/first_pass_%1.png" ).arg( i ) );
272 if ( job.maskPass.image )
274 job.maskPass.image->save( QString(
"/tmp/first_pass_%1_mask.png" ).arg( i ) );
280 mLabelJob.img->save( QString(
"/tmp/labels.png" ) );
282 if ( mLabelJob.maskImage )
284 mLabelJob.maskImage->save( QString(
"/tmp/labels_mask.png" ) );
287 if ( ! mSecondPassLayerJobs.empty() )
289 mStatus = RenderingSecondPass;
291 connect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
292 mSecondPassFuture = QtConcurrent::map( mSecondPassLayerJobs, renderLayerStatic );
293 mSecondPassFutureWatcher.setFuture( mSecondPassFuture );
313 void QgsMapRendererParallelJob::renderLayersSecondPassFinished()
344 void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob &job )
346 if ( job.context()->renderingStopped() )
355 job.imageInitialized =
true;
360 QgsDebugMsgLevel( QStringLiteral(
"job %1 start (layer %2)" ).arg(
reinterpret_cast< quint64
>( &job ), 0, 16 ).arg( job.layerId ), 2 );
363 #ifdef SIMULATE_SLOW_RENDERER
366 job.completed = job.renderer->render();
373 catch ( std::exception &e )
376 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
380 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
383 job.errors = job.renderer->errors();
384 job.renderingTime += t.elapsed();
385 QgsDebugMsgLevel( QStringLiteral(
"job %1 end [%2 ms] (layer %3)" ).arg(
reinterpret_cast< quint64
>( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layerId ), 2 );
391 LabelRenderJob &job =
self->mLabelJob;
395 QElapsedTimer labelTime;
402 painter.begin( job.img );
406 painter.begin( &self->mFinalImage );
412 drawLabeling( job.context, self->mLabelingEngineV2.get(), &painter );
419 catch ( std::exception &e )
422 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
426 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
431 job.renderingTime = labelTime.elapsed();
433 job.participatingLayers = _qgis_listRawToQPointer( self->mLabelingEngineV2->participatingLayers() );
436 self->mFinalImage =
composeImage( self->mSettings, self->mLayerJobs, self->mLabelJob );
@ DrawLabeling
Enable drawing of labels on top of the map.
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 logRenderingTime(const std::vector< LayerRenderJob > &jobs, const std::vector< LayerRenderJob > &secondPassJobs, const LabelRenderJob &labelJob)
static void composeSecondPass(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob)
Compose second pass images into first pass images.
static QImage composeImage(const QgsMapSettings &settings, const std::vector< LayerRenderJob > &jobs, const LabelRenderJob &labelJob, const QgsMapRendererCache *cache=nullptr)
void cleanupSecondPassJobs(std::vector< LayerRenderJob > &jobs)
static Q_DECL_DEPRECATED void drawLabeling(const QgsMapSettings &settings, QgsRenderContext &renderContext, QgsLabelingEngine *labelingEngine2, QPainter *painter)
std::vector< LayerRenderJob > 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.
void cleanupJobs(std::vector< LayerRenderJob > &jobs)
QElapsedTimer mRenderingStart
QgsMapRendererCache * mCache
void finished()
emitted when asynchronous rendering is finished (or canceled).
std::vector< LayerRenderJob > prepareSecondPassJobs(std::vector< LayerRenderJob > &firstPassJobs, LabelRenderJob &labelJob)
Prepares jobs for a second pass, if selective masks exist (from labels or symbol layers).
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...
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(Qgis::MapSettingsFlag flag) const
Check whether a particular flag is enabled.
#define QgsDebugMsgLevel(str, level)