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)