26 #include <QtConcurrentMap>
27 #include <QtConcurrentRun>
35 QgsLogger::warning( QStringLiteral(
"Vector rendering in parallel job is not supported, so Qgis::MapSettingsFlag::ForceVectorOutput option will be ignored!" ) );
48 void QgsMapRendererParallelJob::startPrivate()
55 mStatus = RenderingLayers;
57 mLabelingEngineV2.reset();
62 mLabelingEngineV2->setMapSettings(
mSettings );
66 mLayerJobs =
prepareJobs(
nullptr, mLabelingEngineV2.get() );
70 QgsDebugMsgLevel( QStringLiteral(
"QThreadPool max thread count is %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ), 2 );
74 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
76 mFuture = QtConcurrent::map( mLayerJobs, renderLayerStatic );
77 mFutureWatcher.setFuture( mFuture );
85 QgsDebugMsgLevel( QStringLiteral(
"PARALLEL cancel at status %1" ).arg( mStatus ), 2 );
87 mLabelJob.context.setRenderingStopped(
true );
88 for ( LayerRenderJob &job : mLayerJobs )
90 job.context()->setRenderingStopped(
true );
91 if ( job.renderer && job.renderer->feedback() )
92 job.renderer->feedback()->cancel();
95 if ( mStatus == RenderingLayers )
97 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
99 mFutureWatcher.waitForFinished();
101 renderLayersFinished();
104 if ( mStatus == RenderingLabels )
106 disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
108 mLabelingFutureWatcher.waitForFinished();
113 if ( mStatus == RenderingSecondPass )
115 disconnect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
117 mSecondPassFutureWatcher.waitForFinished();
119 renderLayersSecondPassFinished();
122 Q_ASSERT( mStatus == Idle );
130 QgsDebugMsgLevel( QStringLiteral(
"PARALLEL cancel at status %1" ).arg( mStatus ), 2 );
132 mLabelJob.context.setRenderingStopped(
true );
133 for ( LayerRenderJob &job : mLayerJobs )
135 job.context()->setRenderingStopped(
true );
136 if ( job.renderer && job.renderer->feedback() )
137 job.renderer->feedback()->cancel();
140 if ( mStatus == RenderingLayers )
142 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
143 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
152 if ( mStatus == RenderingLayers )
154 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
159 mFutureWatcher.waitForFinished();
161 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ), 2 );
163 renderLayersFinished();
166 if ( mStatus == RenderingLabels )
168 disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
173 mLabelingFutureWatcher.waitForFinished();
175 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished (2): %1 ms" ).arg( t.elapsed() / 1000.0 ), 2 );
180 if ( mStatus == RenderingSecondPass )
182 disconnect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
187 mSecondPassFutureWatcher.waitForFinished();
189 QgsDebugMsg( QStringLiteral(
"waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ) );
191 renderLayersSecondPassFinished();
194 Q_ASSERT( mStatus == Idle );
199 return mStatus != Idle;
204 return mLabelJob.cached;
209 if ( mLabelingEngineV2 )
210 return mLabelingEngineV2->takeResults();
220 const bool jobIsComplete = mStatus == Idle && !mFinalImage.isNull();
222 if ( !jobIsComplete )
228 void QgsMapRendererParallelJob::renderLayersFinished()
230 Q_ASSERT( mStatus == RenderingLayers );
232 for (
const LayerRenderJob &job : mLayerJobs )
234 if ( !job.errors.isEmpty() )
236 mErrors.append( Error( job.layerId, job.errors.join(
',' ) ) );
241 if ( mSecondPassLayerJobs.empty() )
250 mStatus = RenderingLabels;
252 connect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
255 mLabelingFuture = QtConcurrent::run( renderLabelsStatic,
this );
256 mLabelingFutureWatcher.setFuture( mLabelingFuture );
265 #define DEBUG_RENDERING 0
267 void QgsMapRendererParallelJob::renderingFinished()
271 for ( LayerRenderJob &job : mLayerJobs )
275 job.img->save( QString(
"/tmp/first_pass_%1.png" ).arg( i ) );
277 if ( job.maskPass.image )
279 job.maskPass.image->save( QString(
"/tmp/first_pass_%1_mask.png" ).arg( i ) );
285 mLabelJob.img->save( QString(
"/tmp/labels.png" ) );
287 if ( mLabelJob.maskImage )
289 mLabelJob.maskImage->save( QString(
"/tmp/labels_mask.png" ) );
292 if ( ! mSecondPassLayerJobs.empty() )
296 mStatus = RenderingSecondPass;
298 connect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
299 mSecondPassFuture = QtConcurrent::map( mSecondPassLayerJobs, renderLayerStatic );
300 mSecondPassFutureWatcher.setFuture( mSecondPassFuture );
320 void QgsMapRendererParallelJob::renderLayersSecondPassFinished()
351 void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob &job )
353 if ( job.context()->renderingStopped() )
362 job.imageInitialized =
true;
367 QgsDebugMsgLevel( QStringLiteral(
"job %1 start (layer %2)" ).arg(
reinterpret_cast< quint64
>( &job ), 0, 16 ).arg( job.layerId ), 2 );
370 #ifdef SIMULATE_SLOW_RENDERER
373 job.completed = job.renderer->render();
380 catch ( std::exception &e )
383 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
387 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
390 job.errors = job.renderer->errors();
391 job.renderingTime += t.elapsed();
392 QgsDebugMsgLevel( QStringLiteral(
"job %1 end [%2 ms] (layer %3)" ).arg(
reinterpret_cast< quint64
>( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layerId ), 2 );
398 LabelRenderJob &job =
self->mLabelJob;
402 QElapsedTimer labelTime;
409 painter.begin( job.img );
413 painter.begin( &self->mFinalImage );
419 drawLabeling( job.context, self->mLabelingEngineV2.get(), &painter );
426 catch ( std::exception &e )
429 QgsDebugMsg(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
433 QgsDebugMsg( QStringLiteral(
"Caught unhandled unknown exception" ) );
438 job.renderingTime = labelTime.elapsed();
440 job.participatingLayers = _qgis_listRawToQPointer( self->mLabelingEngineV2->participatingLayers() );
443 self->mFinalImage =
composeImage( self->mSettings, self->mLayerJobs, self->mLabelJob );