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!" ) );
 
 
   48void 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    QgsDebugMsgLevel( QStringLiteral( 
"waitForFinished (1): %1 ms" ).arg( t.elapsed() / 1000.0 ), 2 );
 
  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 )
 
 
  228void 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 
  267void 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 );
 
  320void QgsMapRendererParallelJob::renderLayersSecondPassFinished()
 
  351void QgsMapRendererParallelJob::renderLayerStatic( LayerRenderJob &job )
 
  353  if ( job.context()->renderingStopped() )
 
  359  if ( job.previewRenderImage && !job.previewRenderImageInitialized )
 
  361    job.previewRenderImage->fill( 0 );
 
  362    job.previewRenderImageInitialized = 
true;
 
  368    job.imageInitialized = 
true;
 
  373  QgsDebugMsgLevel( QStringLiteral( 
"job %1 start (layer %2)" ).arg( 
reinterpret_cast< quint64 
>( &job ), 0, 16 ).arg( job.layerId ), 2 );
 
  376#ifdef SIMULATE_SLOW_RENDERER 
  379    job.completed = job.renderer->render();
 
  386  catch ( std::exception &e )
 
  389    QgsDebugError( 
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
 
  393    QgsDebugError( QStringLiteral( 
"Caught unhandled unknown exception" ) );
 
  396  job.errors = job.renderer->errors();
 
  397  job.renderingTime += t.elapsed();
 
  398  QgsDebugMsgLevel( QStringLiteral( 
"job %1 end [%2 ms] (layer %3)" ).arg( 
reinterpret_cast< quint64 
>( &job ), 0, 16 ).arg( job.renderingTime ).arg( job.layerId ), 2 );
 
  404  LabelRenderJob &job = self->mLabelJob;
 
  408    QElapsedTimer labelTime;
 
  415      painter.begin( job.img );
 
  419      painter.begin( &self->mFinalImage );
 
  425      drawLabeling( job.context, self->mLabelingEngineV2.get(), &painter );
 
  432    catch ( std::exception &e )
 
  435      QgsDebugError( 
"Caught unhandled std::exception: " + QString::fromLatin1( e.what() ) );
 
  439      QgsDebugError( QStringLiteral( 
"Caught unhandled unknown exception" ) );
 
  444    job.renderingTime = labelTime.elapsed();
 
  446    job.participatingLayers = _qgis_listRawToQPointer( self->mLabelingEngineV2->participatingLayers() );
 
@ 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)
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)