Merge pull request #5789 from nyalldawson/render_jobs

[Rendering] Only render in preview jobs layers that are fast enough
This commit is contained in:
Nyall Dawson 2017-12-05 11:17:44 +11:00 committed by GitHub
commit b0a36e9bb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 128 additions and 12 deletions

View File

@ -241,6 +241,8 @@ const double DEFAULT_LINE_WIDTH;
const double DEFAULT_SEGMENT_EPSILON;
typedef unsigned long long qgssize;

View File

@ -380,6 +380,7 @@ Current time stamp of data source
.. versionadded:: 3.0
%End
signals:
void fullExtentCalculated();

View File

@ -137,10 +137,12 @@ List of errors that happened during the rendering job - available when the rende
int renderingTime() const;
%Docstring
Find out how long it took to finish the job (in milliseconds)
Returns the total time it took to finish the job (in milliseconds).
.. seealso:: perLayerRenderingTime()
:rtype: int
%End
const QgsMapSettings &mapSettings() const;
%Docstring
Return map settings with which this job was started.
@ -179,6 +181,7 @@ emitted when asynchronous rendering is finished (or canceled).
};

View File

@ -440,6 +440,18 @@ const double DEFAULT_LINE_WIDTH = 0.26;
//! Default snapping tolerance for segments
const double DEFAULT_SEGMENT_EPSILON = 1e-8;
///@cond PRIVATE
#ifndef SIP_RUN
//! Delay between the scheduling of 2 preview jobs
const int PREVIEW_JOB_DELAY_MS = 250;
//! Maximum rendering time for a layer of a preview job
const int MAXIMUM_LAYER_PREVIEW_TIME_MS = 250;
#endif
///@endcond
typedef QMap<QString, QString> QgsStringMap SIP_SKIP;
/**

View File

@ -42,3 +42,7 @@ void QgsDataProvider::setListening( bool isListening )
Q_UNUSED( isListening );
}
bool QgsDataProvider::renderInPreview( const PreviewContext &context )
{
return context.lastRenderingTimeMs <= context.maxRenderingTimeMs;
}

View File

@ -461,6 +461,37 @@ class CORE_EXPORT QgsDataProvider : public QObject
*/
virtual void setListening( bool isListening );
#ifndef SIP_RUN
/**
* Stores settings related to the context in which a preview job runs.
* \note Not available in Python bindings
* \since QGIS 3.0
*/
struct PreviewContext
{
//! Previous rendering time for the layer, in ms
double lastRenderingTimeMs = -1;
//! Default maximum allowable render time, in ms
double maxRenderingTimeMs = MAXIMUM_LAYER_PREVIEW_TIME_MS;
};
#endif
/**
* Returns whether the layer must be rendered in preview jobs.
*
* The \a context argument gives useful information which can be used
* to determine whether the layer should be rendered or not.
*
* The base implementation returns true if lastRenderingTimeMs <= maxRenderingTimeMs.
*
* \since QGIS 3.0
*
* \note not available in Python bindings
*/
virtual bool renderInPreview( const QgsDataProvider::PreviewContext &context ); // SIP_SKIP
signals:
/**

View File

@ -64,6 +64,17 @@ void QgsMapRendererJob::setCache( QgsMapRendererCache *cache )
mCache = cache;
}
QHash<QgsMapLayer *, int> QgsMapRendererJob::perLayerRenderingTime() const
{
QHash<QgsMapLayer *, int> result;
for ( auto it = mPerLayerRenderingTime.constBegin(); it != mPerLayerRenderingTime.constEnd(); ++it )
{
if ( it.key() )
result.insert( it.key(), it.value() );
}
return result;
}
const QgsMapSettings &QgsMapRendererJob::mapSettings() const
{
return mSettings;
@ -412,8 +423,10 @@ void QgsMapRendererJob::cleanupJobs( LayerRenderJobs &jobs )
delete job.renderer;
job.renderer = nullptr;
}
}
if ( job.layer )
mPerLayerRenderingTime.insert( job.layer, job.renderingTime );
}
jobs.clear();
}

View File

@ -199,9 +199,19 @@ class CORE_EXPORT QgsMapRendererJob : public QObject
*/
void setCache( QgsMapRendererCache *cache );
//! Find out how long it took to finish the job (in milliseconds)
/**
* Returns the total time it took to finish the job (in milliseconds).
* \see perLayerRenderingTime()
*/
int renderingTime() const { return mRenderingTime; }
/**
* Returns the render time (in ms) per layer.
* \note Not available in Python bindings.
* \since QGIS 3.0
*/
QHash< QgsMapLayer *, int > perLayerRenderingTime() const SIP_SKIP;
/**
* Return map settings with which this job was started.
* \returns A QgsMapSettings instance with render settings
@ -239,6 +249,9 @@ class CORE_EXPORT QgsMapRendererJob : public QObject
int mRenderingTime = 0;
//! Render time (in ms) per layer, by layer ID
QHash< QgsWeakMapLayerPointer, int > mPerLayerRenderingTime;
/**
* Prepares the cache for storing the result of labeling. Returns false if
* the render cannot use cached labels and should not cache the result.

View File

@ -66,7 +66,6 @@ email : sherman at mrcc.com
#include "qgsmapthemecollection.h"
#include <cmath>
/**
* \ingroup gui
* Deprecated to be deleted, stuff from here should be moved elsewhere.
@ -612,6 +611,13 @@ void QgsMapCanvas::rendererJobFinished()
p.end();
mMap->setContent( img, imageRect( img, mSettings ) );
mLastLayerRenderTime.clear();
const auto times = mJob->perLayerRenderingTime();
for ( auto it = times.constBegin(); it != times.constEnd(); ++it )
{
mLastLayerRenderTime.insert( it.key()->id(), it.value() );
}
if ( mUsePreviewJobs )
startPreviewJobs();
}
@ -633,6 +639,13 @@ void QgsMapCanvas::previewJobFinished()
{
mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
mPreviewJobs.removeAll( job );
int number = job->property( "number" ).toInt();
if ( number < 8 )
{
startPreviewJob( number + 1 );
}
delete job;
}
}
@ -2269,15 +2282,29 @@ void QgsMapCanvas::startPreviewJob( int number )
jobSettings.setFlag( QgsMapSettings::DrawLabeling, false );
jobSettings.setFlag( QgsMapSettings::RenderPreviewJob, true );
// truncate preview layers to fast layers
const QList<QgsMapLayer *> layers = jobSettings.layers();
QList< QgsMapLayer * > previewLayers;
QgsDataProvider::PreviewContext context;
context.maxRenderingTimeMs = MAXIMUM_LAYER_PREVIEW_TIME_MS;
for ( QgsMapLayer *layer : layers )
{
context.lastRenderingTimeMs = mLastLayerRenderTime.value( layer->id(), 0 );
if ( !layer->dataProvider()->renderInPreview( context ) )
{
QgsDebugMsgLevel( QString( "Layer %1 not rendered because it does not match the renderInPreview criterion %2" ).arg( layer->id() ).arg( mLastLayerRenderTime.value( layer->id() ) ), 3 );
continue;
}
previewLayers << layer;
}
jobSettings.setLayers( previewLayers );
QgsMapRendererQImageJob *job = new QgsMapRendererSequentialJob( jobSettings );
job->setProperty( "number", number );
mPreviewJobs.append( job );
connect( job, &QgsMapRendererJob::finished, this, &QgsMapCanvas::previewJobFinished );
job->start();
if ( number < 8 )
{
schedulePreviewJob( number + 1 );
}
}
void QgsMapCanvas::stopPreviewJobs()
@ -2299,7 +2326,7 @@ void QgsMapCanvas::stopPreviewJobs()
void QgsMapCanvas::schedulePreviewJob( int number )
{
mPreviewTimer.setSingleShot( true );
mPreviewTimer.setInterval( 250 );
mPreviewTimer.setInterval( PREVIEW_JOB_DELAY_MS );
disconnect( mPreviewTimerConnection );
mPreviewTimerConnection = connect( &mPreviewTimer, &QTimer::timeout, this, [ = ]()
{

View File

@ -1000,6 +1000,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
bool mUsePreviewJobs = false;
QHash< QString, int > mLastLayerRenderTime;
/**
* Force a resize of the map canvas item
* \since QGIS 2.16

View File

@ -3199,18 +3199,25 @@ QString QgsWmsProvider::lastErrorFormat()
QString QgsWmsProvider::name() const
{
return WMS_KEY;
} // QgsWmsProvider::name()
}
QString QgsWmsProvider::description() const
{
return WMS_DESCRIPTION;
} // QgsWmsProvider::description()
}
void QgsWmsProvider::reloadData()
{
}
bool QgsWmsProvider::renderInPreview( const QgsDataProvider::PreviewContext &context )
{
if ( mSettings.mTiled || mSettings.mXyz )
return true;
return QgsRasterDataProvider::renderInPreview( context );
}
QVector<QgsWmsSupportedFormat> QgsWmsProvider::supportedFormats()
{

View File

@ -208,6 +208,7 @@ class QgsWmsProvider : public QgsRasterDataProvider
QString name() const override;
QString description() const override;
virtual void reloadData() override;
bool renderInPreview( const QgsDataProvider::PreviewContext &context ) override;
static QVector<QgsWmsSupportedFormat> supportedFormats();