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 );
 
  418    else if ( job.picture )
 
  420      painter.begin( job.picture.get() );
 
  424      painter.begin( &self->mFinalImage );
 
  430      drawLabeling( job.context, self->mLabelingEngineV2.get(), &painter );
 
  437    catch ( std::exception &e )
 
  440      QgsDebugError( 
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
 
  444      QgsDebugError( QStringLiteral( 
"Caught unhandled unknown exception" ) );
 
  449    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.
 
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 a client to query the rendered image.
 
Contains configuration for rendering maps.
 
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)