17#include "moc_qgsmaprendererparalleljob.cpp"
27#include <QtConcurrentMap>
28#include <QtConcurrentRun>
36 QgsLogger::warning( QStringLiteral(
"Vector rendering in parallel job is not supported, so Qgis::MapSettingsFlag::ForceVectorOutput option will be ignored!" ) );
49void QgsMapRendererParallelJob::startPrivate()
56 mStatus = RenderingLayers;
58 mLabelingEngineV2.reset();
63 mLabelingEngineV2->setMapSettings(
mSettings );
67 mLayerJobs =
prepareJobs(
nullptr, mLabelingEngineV2.get() );
71 QgsDebugMsgLevel( QStringLiteral(
"QThreadPool max thread count is %1" ).arg( QThreadPool::globalInstance()->maxThreadCount() ), 2 );
75 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
77 mFuture = QtConcurrent::map( mLayerJobs, renderLayerStatic );
78 mFutureWatcher.setFuture( mFuture );
86 QgsDebugMsgLevel( QStringLiteral(
"PARALLEL cancel at status %1" ).arg( mStatus ), 2 );
88 mLabelJob.context.setRenderingStopped(
true );
89 for ( LayerRenderJob &job : mLayerJobs )
91 job.context()->setRenderingStopped(
true );
92 if ( job.renderer && job.renderer->feedback() )
93 job.renderer->feedback()->cancel();
96 if ( mStatus == RenderingLayers )
98 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
100 mFutureWatcher.waitForFinished();
102 renderLayersFinished();
105 if ( mStatus == RenderingLabels )
107 disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
109 mLabelingFutureWatcher.waitForFinished();
114 if ( mStatus == RenderingSecondPass )
116 disconnect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
118 mSecondPassFutureWatcher.waitForFinished();
120 renderLayersSecondPassFinished();
123 Q_ASSERT( mStatus == Idle );
131 QgsDebugMsgLevel( QStringLiteral(
"PARALLEL cancel at status %1" ).arg( mStatus ), 2 );
133 mLabelJob.context.setRenderingStopped(
true );
134 for ( LayerRenderJob &job : mLayerJobs )
136 job.context()->setRenderingStopped(
true );
137 if ( job.renderer && job.renderer->feedback() )
138 job.renderer->feedback()->cancel();
141 if ( mStatus == RenderingLayers )
143 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
144 connect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
153 if ( mStatus == RenderingLayers )
155 disconnect( &mFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersFinished );
160 mFutureWatcher.waitForFinished();
162 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ), 2 );
164 renderLayersFinished();
167 if ( mStatus == RenderingLabels )
169 disconnect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
174 mLabelingFutureWatcher.waitForFinished();
176 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished (2): %1 ms" ).arg( t.elapsed() / 1000.0 ), 2 );
181 if ( mStatus == RenderingSecondPass )
183 disconnect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
188 mSecondPassFutureWatcher.waitForFinished();
190 QgsDebugMsgLevel( QStringLiteral(
"waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ), 2 );
192 renderLayersSecondPassFinished();
195 Q_ASSERT( mStatus == Idle );
200 return mStatus != Idle;
205 return mLabelJob.cached;
210 if ( mLabelingEngineV2 )
211 return mLabelingEngineV2->takeResults();
221 const bool jobIsComplete = mStatus == Idle && !mFinalImage.isNull();
223 if ( !jobIsComplete )
229void QgsMapRendererParallelJob::renderLayersFinished()
231 Q_ASSERT( mStatus == RenderingLayers );
233 for (
const LayerRenderJob &job : mLayerJobs )
235 if ( !job.errors.isEmpty() )
237 mErrors.append( Error( job.layerId, job.errors.join(
',' ) ) );
242 if ( mSecondPassLayerJobs.empty() )
251 mStatus = RenderingLabels;
253 connect( &mLabelingFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderingFinished );
256 mLabelingFuture = QtConcurrent::run( renderLabelsStatic,
this );
257 mLabelingFutureWatcher.setFuture( mLabelingFuture );
266#define DEBUG_RENDERING 0
268void QgsMapRendererParallelJob::renderingFinished()
272 for ( LayerRenderJob &job : mLayerJobs )
276 job.img->save( QString(
"/tmp/first_pass_%1.png" ).arg( i ) );
278 if ( job.maskPass.image )
280 job.maskPass.image->save( QString(
"/tmp/first_pass_%1_mask.png" ).arg( i ) );
286 mLabelJob.img->save( QString(
"/tmp/labels.png" ) );
288 if ( mLabelJob.maskImage )
290 mLabelJob.maskImage->save( QString(
"/tmp/labels_mask.png" ) );
293 if ( ! mSecondPassLayerJobs.empty() )
297 mStatus = RenderingSecondPass;
299 connect( &mSecondPassFutureWatcher, &QFutureWatcher<void>::finished,
this, &QgsMapRendererParallelJob::renderLayersSecondPassFinished );
300 mSecondPassFuture = QtConcurrent::map( mSecondPassLayerJobs, renderLayerStatic );
301 mSecondPassFutureWatcher.setFuture( mSecondPassFuture );
321void QgsMapRendererParallelJob::renderLayersSecondPassFinished()
352void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob &job )
354 if ( job.context()->renderingStopped() )
360 if ( job.previewRenderImage && !job.previewRenderImageInitialized )
362 job.previewRenderImage->fill( 0 );
363 job.previewRenderImageInitialized =
true;
369 job.imageInitialized =
true;
374 QgsDebugMsgLevel( QStringLiteral(
"job %1 start (layer %2)" ).arg(
reinterpret_cast< quint64
>( &job ), 0, 16 ).arg( job.layerId ), 2 );
377#ifdef SIMULATE_SLOW_RENDERER
380 job.completed = job.renderer->render();
387 catch ( std::exception &e )
390 QgsDebugError(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
394 QgsDebugError( QStringLiteral(
"Caught unhandled unknown exception" ) );
397 job.errors = job.renderer->errors();
398 job.renderingTime += t.elapsed();
399 QgsDebugMsgLevel( QStringLiteral(
"job %1 end [%2 ms] (layer %3)" ).arg(
reinterpret_cast< quint64
>( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layerId ), 2 );
405 LabelRenderJob &job = self->mLabelJob;
409 QElapsedTimer labelTime;
416 painter.begin( job.img );
420 painter.begin( &self->mFinalImage );
426 drawLabeling( job.context, self->mLabelingEngineV2.get(), &painter );
433 catch ( std::exception &e )
436 QgsDebugError(
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
440 QgsDebugError( QStringLiteral(
"Caught unhandled unknown exception" ) );
445 job.renderingTime = labelTime.elapsed();
@ ForceVectorOutput
Vector graphics should not be cached and drawn as raster images.
@ 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.
static void warning(const QString &msg)
Goes to qWarning.
void logRenderingTime(const std::vector< LayerRenderJob > &jobs, const std::vector< LayerRenderJob > &secondPassJobs, const LabelRenderJob &labelJob)
QList< QPointer< QgsMapLayer > > participatingLabelLayers(QgsLabelingEngine *engine)
Returns a list of the layers participating in the map labeling.
static QImage composeImage(const QgsMapSettings &settings, const std::vector< LayerRenderJob > &jobs, const LabelRenderJob &labelJob, const QgsMapRendererCache *cache=nullptr)
void cleanupSecondPassJobs(std::vector< LayerRenderJob > &jobs)
void initSecondPassJobs(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob) const
Initialize secondPassJobs according to what have been rendered (mask clipping path e....
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).
static void composeSecondPass(std::vector< LayerRenderJob > &secondPassJobs, LabelRenderJob &labelJob, bool forceVector=false)
Compose second pass images into first pass images.
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.
void setFlag(Qgis::MapSettingsFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)