Skip to content
Permalink
Browse files
Add QgsFeedback to handle canceling rendering
  • Loading branch information
NEDJIMAbelgacem authored and wonder-sk committed Apr 8, 2021
1 parent b01deeb commit 4a0230fc5c8f8c3c3aac3016ca9528fb361551e4
@@ -26,7 +26,7 @@ Encapsulates the render context for a 2D point cloud rendering operation.
public:

QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset,
double zValueScale, double zValueFixedOffset );
double zValueScale, double zValueFixedOffset, QgsFeedback *feedback );
%Docstring
Constructor for QgsPointCloudRenderContext.

@@ -135,6 +135,13 @@ Returns any constant offset which must be applied to z values taken from the poi
Scaling of z values via :py:func:`~QgsPointCloudRenderContext.zValueScale` should be applied before the :py:func:`~QgsPointCloudRenderContext.zValueFixedOffset`.
%End

QgsFeedback *feedback() const;
%Docstring
Returns the feedback object used to cancel rendering

.. versionadded:: 3.20
%End


private:
QgsPointCloudRenderContext( const QgsPointCloudRenderContext &rh );
@@ -126,9 +126,7 @@ bool QgsEptPointCloudIndex::loadSchema( const QByteArray &dataJson )
return false;

mSpan = result.value( QLatin1String( "span" ) ).toInt();
mPointCount = result.value( QLatin1String( "points" ) ).toInt();
if ( mPointCount == 0 )
mPointCount = result.value( QLatin1String( "points" ) ).toDouble();
mPointCount = std::min( ( double )std::numeric_limits<int>::max(), result.value( QLatin1String( "points" ) ).toDouble() );

// WKT
const QJsonObject srs = result.value( QLatin1String( "srs" ) ).toObject();
@@ -39,6 +39,7 @@ QgsPointCloudLayerRenderer::QgsPointCloudLayerRenderer( QgsPointCloudLayer *laye
: QgsMapLayerRenderer( layer->id(), &context )
, mLayer( layer )
, mLayerAttributes( layer->attributes() )
, mFeedback( new QgsFeedback )
{
// TODO: we must not keep pointer to mLayer (it's dangerous) - we must copy anything we need for rendering
// or use some locking to prevent read/write from multiple threads
@@ -68,7 +69,7 @@ QgsPointCloudLayerRenderer::QgsPointCloudLayerRenderer( QgsPointCloudLayer *laye

bool QgsPointCloudLayerRenderer::render()
{
QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset, mZScale, mZOffset );
QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset, mZScale, mZOffset, mFeedback.get() );

// Set up the render configuration options
QPainter *painter = context.renderContext().painter();
@@ -241,12 +242,14 @@ int QgsPointCloudLayerRenderer::renderNodesAsync( const QVector<IndexedPointClou
const int groupSize = 4;
for ( int groupIndex = 0; groupIndex < nodes.size(); groupIndex += groupSize )
{
if ( context.feedback()->isCanceled() )
break;
// Async loading of nodes
const int currentGroupSize = std::min( std::max( nodes.size() - groupIndex, 0 ), groupSize );
QVector<QgsPointCloudBlockRequest *> blockRequests( currentGroupSize, nullptr );
QVector<bool> finishedLoadingBlock( currentGroupSize, false );
QEventLoop loop;

QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, &QEventLoop::quit );
// Note: All capture by reference warnings here shouldn't be an issue since we have an event loop, so locals won't be deallocated
for ( int i = 0; i < blockRequests.size(); ++i )
{
@@ -269,31 +272,33 @@ int QgsPointCloudLayerRenderer::renderNodesAsync( const QVector<IndexedPointClou
loop.exec();

QgsDebugMsg( QStringLiteral( "Downloaded in : %1ms" ).arg( downloadTimer.elapsed() ) );

// Render all the point cloud blocks sequentially
for ( int i = 0; i < blockRequests.size(); ++i )
if ( !context.feedback()->isCanceled() )
{
if ( context.renderContext().renderingStopped() )
// Render all the point cloud blocks sequentially
for ( int i = 0; i < blockRequests.size(); ++i )
{
QgsDebugMsgLevel( "canceled", 2 );
canceled = true;
break;
}
if ( context.renderContext().renderingStopped() )
{
QgsDebugMsgLevel( "canceled", 2 );
canceled = true;
break;
}

if ( !blockRequests[ i ]->block() )
continue;
if ( !blockRequests[ i ]->block() )
continue;

context.setAttributes( blockRequests[ i ]->block()->attributes() );
context.setAttributes( blockRequests[ i ]->block()->attributes() );

mRenderer->renderBlock( blockRequests[ i ]->block(), context );
++nodesDrawn;
mRenderer->renderBlock( blockRequests[ i ]->block(), context );
++nodesDrawn;

// as soon as first block is rendered, we can start showing layer updates.
// but if we are blocking render updates (so that a previously cached image is being shown), we wait
// at most e.g. 3 seconds before we start forcing progressive updates.
if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
{
mReadyToCompose = true;
// as soon as first block is rendered, we can start showing layer updates.
// but if we are blocking render updates (so that a previously cached image is being shown), we wait
// at most e.g. 3 seconds before we start forcing progressive updates.
if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
{
mReadyToCompose = true;
}
}
}

@@ -30,6 +30,7 @@
#include "qgsidentifycontext.h"
#include "qgspointcloudrenderer.h"
#include "qgsmapclippingregion.h"
#include "qgsrasterinterface.h"

#include <QDomElement>
#include <QString>
@@ -43,6 +44,8 @@ class QgsPointCloudRenderContext;

#define SIP_NO_FILE

///@endcond

/**
* \ingroup core
*
@@ -65,6 +68,8 @@ class CORE_EXPORT QgsPointCloudLayerRenderer: public QgsMapLayerRenderer
bool forceRasterRender() const override;
void setLayerRenderingTimeHint( int time ) override;

QgsFeedback *feedback() const override { return mFeedback.get(); }

private:
QVector<IndexedPointCloudNode> traverseTree( const QgsPointCloudIndex *pc, const QgsRenderContext &context, IndexedPointCloudNode n, double maxErrorPixels, double nodeErrorPixels );

@@ -89,6 +94,7 @@ class CORE_EXPORT QgsPointCloudLayerRenderer: public QgsMapLayerRenderer
bool mBlockRenderUpdates = false;
QElapsedTimer mElapsedTimer;

std::unique_ptr<QgsFeedback> mFeedback = nullptr;
};

#endif // QGSPOINTCLOUDLAYERRENDERER_H
@@ -27,12 +27,13 @@
#include <QThread>
#include <QPointer>

QgsPointCloudRenderContext::QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset, double zValueScale, double zValueFixedOffset )
QgsPointCloudRenderContext::QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset, double zValueScale, double zValueFixedOffset, QgsFeedback *feedback )
: mRenderContext( context )
, mScale( scale )
, mOffset( offset )
, mZValueScale( zValueScale )
, mZValueFixedOffset( zValueFixedOffset )
, mFeedback( feedback )
{

}
@@ -55,7 +55,7 @@ class CORE_EXPORT QgsPointCloudRenderContext
* taken from the point cloud index.
*/
QgsPointCloudRenderContext( QgsRenderContext &context, const QgsVector3D &scale, const QgsVector3D &offset,
double zValueScale, double zValueFixedOffset );
double zValueScale, double zValueFixedOffset, QgsFeedback *feedback );

//! QgsPointCloudRenderContext cannot be copied.
QgsPointCloudRenderContext( const QgsPointCloudRenderContext &rh ) = delete;
@@ -154,6 +154,13 @@ class CORE_EXPORT QgsPointCloudRenderContext
*/
double zValueFixedOffset() const { return mZValueFixedOffset; }

/**
* Returns the feedback object used to cancel rendering
*
* \since QGIS 3.20
*/
QgsFeedback *feedback() const { return mFeedback; }

#ifndef SIP_RUN

/**
@@ -208,6 +215,8 @@ class CORE_EXPORT QgsPointCloudRenderContext
int mZOffset = 0;
double mZValueScale = 1.0;
double mZValueFixedOffset = 0;

QgsFeedback *mFeedback = nullptr;
};


@@ -96,7 +96,6 @@ void QgsRemoteEptPointCloudIndex::load( const QString &url )

QgsPointCloudBlock *QgsRemoteEptPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
{
qDebug() << __PRETTY_FUNCTION__ << " " << n.toString() << " start";
std::unique_ptr<QgsPointCloudBlockRequest> blockRequest( asyncNodeData( n, request ) );
if ( !blockRequest )
return nullptr;

0 comments on commit 4a0230f

Please sign in to comment.