Skip to content
Permalink
Browse files

experiments with point budget

  • Loading branch information
NEDJIMAbelgacem authored and wonder-sk committed Feb 10, 2021
1 parent eb1ff08 commit 81fe1c2103d99a80dc4d1230059065ad48bcdbe7
@@ -142,10 +142,25 @@ void QgsChunkedEntity::update( const SceneState &state )

int enabled = 0, disabled = 0, unloaded = 0;

Q_FOREACH ( QgsChunkNode *node, mActiveNodes )
QList<QgsChunkNode *> newActiveNodes;
int rendered = 0;
for ( QgsChunkNode *node : mActiveNodes )
{
QgsChunkLoader *nodeLoader = mChunkLoaderFactory->createChunkLoader( node );
if ( nodeLoader->primitiveCount() + rendered > nodeLoader->primitiveBudget() )
break;
rendered += nodeLoader->primitiveCount();
newActiveNodes.append( node );
}

mActiveNodes = newActiveNodes;

for ( QgsChunkNode *node : mActiveNodes )
{
if ( activeBefore.contains( node ) )
{
activeBefore.remove( node );
}
else
{
node->entity()->setEnabled( true );
@@ -154,7 +169,7 @@ void QgsChunkedEntity::update( const SceneState &state )
}

// disable those that were active but will not be anymore
Q_FOREACH ( QgsChunkNode *node, activeBefore )
for ( QgsChunkNode *node : activeBefore )
{
node->entity()->setEnabled( false );
++disabled;
@@ -241,69 +256,87 @@ int QgsChunkedEntity::pendingJobsCount() const
}


void QgsChunkedEntity::update( QgsChunkNode *node, const SceneState &state )
void QgsChunkedEntity::update( QgsChunkNode *root, const SceneState &state )
{
if ( Qgs3DUtils::isCullable( node->bbox(), state.viewProjectionMatrix ) )
int renderedCount = 0;
QVector<QgsChunkNode *> currentNodes;
currentNodes.append( root );
while ( !currentNodes.empty() )
{
++mFrustumCulled;
return;
}
QVector<QgsChunkNode *> nextNodes;
for ( QgsChunkNode * node : currentNodes )
{
QgsChunkLoader *nodeLoader = mChunkLoaderFactory->createChunkLoader( node );

// ensure we have child nodes (at least skeletons) available, if any
if ( node->childCount() == -1 )
{
node->populateChildren( mChunkLoaderFactory->createChildren( node ) );
}
if ( Qgs3DUtils::isCullable( node->bbox(), state.viewProjectionMatrix ) )
{
++mFrustumCulled;
continue;
}

// make sure all nodes leading to children are always loaded
// so that zooming out does not create issues
requestResidency( node );
// ensure we have child nodes (at least skeletons) available, if any
if ( node->childCount() == -1 )
{
node->populateChildren( mChunkLoaderFactory->createChildren( node ) );
}

if ( !node->entity() )
{
// this happens initially when root node is not ready yet
return;
}
// make sure all nodes leading to children are always loaded
// so that zooming out does not create issues
requestResidency( node );

//QgsDebugMsgLevel( QStringLiteral( "%1|%2|%3 %4 %5" ).arg( node->tileX() ).arg( node->tileY() ).arg( node->tileZ() ).arg( mTau ).arg( screenSpaceError( node, state ) ), 2 );
if ( node->childCount() == 0 )
{
// there's no children available for this node, so regardless of whether it has an acceptable error
// or not, it's the best we'll ever get...
mActiveNodes << node;
}
else if ( mTau > 0 && screenSpaceError( node, state ) <= mTau )
{
// acceptable error for the current chunk - let's render it
if ( !node->entity() )
{
// this happens initially when root node is not ready yet
continue;
}

mActiveNodes << node;
}
else if ( node->allChildChunksResident( mCurrentTime ) )
{
// error is not acceptable and children are ready to be used - recursive descent
//QgsDebugMsgLevel( QStringLiteral( "%1|%2|%3 %4 %5" ).arg( node->tileX() ).arg( node->tileY() ).arg( node->tileZ() ).arg( mTau ).arg( screenSpaceError( node, state ) ), 2 );
if ( node->childCount() == 0 )
{
if ( nodeLoader != nullptr )
renderedCount += nodeLoader->primitiveCount();
// there's no children available for this node, so regardless of whether it has an acceptable error
// or not, it's the best we'll ever get...
mActiveNodes << node;
}
else if ( mTau > 0 && screenSpaceError( node, state ) <= mTau )
{
// acceptable error for the current chunk - let's render it
mActiveNodes << node;
}
else if ( node->allChildChunksResident( mCurrentTime ) )
{
// error is not acceptable and children are ready to be used - recursive descent


if ( mAdditiveStrategy )
{
// With additive strategy enabled, also all parent nodes are added to active nodes.
// This is desired when child nodes add more detailed data rather than just replace
// coarser data in parents. We use this e.g. with point cloud data.
mActiveNodes << node;
}

QgsChunkNode *const *children = node->children();
for ( int i = 0; i < node->childCount(); ++i )
{
nextNodes.push_back( children[ i ] );
}
}
else
{
// error is not acceptable but children are not ready either - still use parent but request children
mActiveNodes << node;

if ( mAdditiveStrategy )
{
// With additive strategy enabled, also all parent nodes are added to active nodes.
// This is desired when child nodes add more detailed data rather than just replace
// coarser data in parents. We use this e.g. with point cloud data.
mActiveNodes << node;
QgsChunkNode *const *children = node->children();
for ( int i = 0; i < node->childCount(); ++i )
requestResidency( children[i] );
}
}

QgsChunkNode *const *children = node->children();
for ( int i = 0; i < node->childCount(); ++i )
update( children[i], state );
currentNodes = nextNodes;
}
else
{
// error is not acceptable but children are not ready either - still use parent but request children

mActiveNodes << node;

QgsChunkNode *const *children = node->children();
for ( int i = 0; i < node->childCount(); ++i )
requestResidency( children[i] );
}
// qDebug() << "Rendered points: " << renderedCount;
}


@@ -44,8 +44,9 @@ class QgsChunkLoader : public QgsChunkQueueJob
Q_OBJECT
public:
//! Construct chunk loader for a node
QgsChunkLoader( QgsChunkNode *node )
QgsChunkLoader( QgsChunkNode *node, int primitiveBudget = 1000000 )
: QgsChunkQueueJob( node )
, mPrimitiveBudget( primitiveBudget )
{
}

@@ -55,6 +56,10 @@ class QgsChunkLoader : public QgsChunkQueueJob
*/
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent ) = 0;

virtual int primitiveCount() { return 0; }
virtual int primitiveBudget() const { return mPrimitiveBudget; }
protected:
int mPrimitiveBudget = 1000000;
};


@@ -123,7 +123,7 @@ Qt3DCore::QEntity *QgsPointCloudLayer3DRenderer::createEntity( const Qgs3DMapSet

return new QgsPointCloudLayerChunkedEntity( pcl->dataProvider()->index(), map, dynamic_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() ), maximumScreenError(), showBoundingBoxes(),
static_cast< const QgsPointCloudLayerElevationProperties * >( pcl->elevationProperties() )->zScale(),
static_cast< const QgsPointCloudLayerElevationProperties * >( pcl->elevationProperties() )->zOffset() );
static_cast< const QgsPointCloudLayerElevationProperties * >( pcl->elevationProperties() )->zOffset(), mPointBudget );
}

void QgsPointCloudLayer3DRenderer::setSymbol( QgsPointCloud3DSymbol *symbol )
@@ -140,6 +140,7 @@ void QgsPointCloudLayer3DRenderer::writeXml( QDomElement &elem, const QgsReadWri
elem.setAttribute( QStringLiteral( "layer" ), mLayerRef.layerId );
elem.setAttribute( QStringLiteral( "max-screen-error" ), maximumScreenError() );
elem.setAttribute( QStringLiteral( "show-bounding-boxes" ), showBoundingBoxes() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
elem.setAttribute( QStringLiteral( "point-budget" ), mPointBudget );

QDomElement elemSymbol = doc.createElement( QStringLiteral( "symbol" ) );
if ( mSymbol )
@@ -159,6 +160,7 @@ void QgsPointCloudLayer3DRenderer::readXml( const QDomElement &elem, const QgsRe
const QString symbolType = elemSymbol.attribute( QStringLiteral( "type" ) );
mMaximumScreenError = elem.attribute( QStringLiteral( "max-screen-error" ), QStringLiteral( "1.0" ) ).toDouble();
mShowBoundingBoxes = elem.attribute( QStringLiteral( "show-bounding-boxes" ), QStringLiteral( "0" ) ).toInt();
mPointBudget = elem.attribute( QStringLiteral( "point-budget" ), QStringLiteral( "1000000" ) ).toInt();

if ( symbolType == QLatin1String( "single-color" ) )
mSymbol.reset( new QgsSingleColorPointCloud3DSymbol );
@@ -200,3 +202,8 @@ void QgsPointCloudLayer3DRenderer::setShowBoundingBoxes( bool showBoundingBoxes
mShowBoundingBoxes = showBoundingBoxes;
}

void QgsPointCloudLayer3DRenderer::setPointRenderingBudget( int budget )
{
qDebug() << budget;
mPointBudget = budget;
}
@@ -260,11 +260,15 @@ class _3D_EXPORT QgsPointCloudLayer3DRenderer : public QgsAbstract3DRenderer
*/
void setShowBoundingBoxes( bool showBoundingBoxes );

int pointRenderingBudget() const { return mPointBudget; };
void setPointRenderingBudget( int budget );

private:
QgsMapLayerRef mLayerRef; //!< Layer used to extract mesh data from
std::unique_ptr< QgsPointCloud3DSymbol > mSymbol;
double mMaximumScreenError = 1.0;
bool mShowBoundingBoxes = false;
int mPointBudget = 1000000;

private:
#ifdef SIP_RUN
@@ -47,10 +47,11 @@
///////////////

QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
double zValueScale, double zValueOffset )
: QgsChunkLoader( node )
double zValueScale, double zValueOffset, int pointBudget, int count )
: QgsChunkLoader( node, pointBudget )
, mFactory( factory )
, mContext( factory->mMap, std::move( symbol ), zValueScale, zValueOffset )
, mPointsCount( count )
{
mContext.setIsCanceledCallback( [this] { return mCanceled; } );

@@ -125,16 +126,21 @@ Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntit
return entity;
}

int QgsPointCloudLayerChunkLoader::primitiveCount()
{
return mPointsCount;
}

///////////////


QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
double zValueScale, double zValueOffset )
double zValueScale, double zValueOffset, int pointBudget )
: mMap( map )
, mPointCloudIndex( pc )
, mZValueScale( zValueScale )
, mZValueOffset( zValueOffset )
, mPointBudget( pointBudget )
{
mSymbol.reset( symbol );
}
@@ -143,7 +149,8 @@ QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChun
{
QgsChunkNodeId id = node->tileId();
Q_ASSERT( mPointCloudIndex->hasNode( IndexedPointCloudNode( id.d, id.x, id.y, id.z ) ) );
return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() ) ), mZValueScale, mZValueOffset );
QgsPointCloudBlock *block = mPointCloudIndex->nodeData( IndexedPointCloudNode( id.d, id.x, id.y, id.z ), QgsPointCloudRequest() );
return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() ) ), mZValueScale, mZValueOffset, mPointBudget, block->pointCount() );
}

QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, double zValueOffset );
@@ -202,9 +209,9 @@ QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset
}


QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, QgsPointCloud3DSymbol *symbol, float maxScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset )
QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, QgsPointCloud3DSymbol *symbol, float maxScreenError, bool showBoundingBoxes, double zValueScale, double zValueOffset, int pointBudget )
: QgsChunkedEntity( maxScreenError,
new QgsPointCloudLayerChunkLoaderFactory( map, pc, symbol, zValueScale, zValueOffset ), true )
new QgsPointCloudLayerChunkLoaderFactory( map, pc, symbol, zValueScale, zValueOffset, pointBudget ), true )
{
setUsingAdditiveStrategy( true );
setShowBoundingBoxes( showBoundingBoxes );
@@ -60,7 +60,7 @@ class QgsPointCloudLayerChunkLoaderFactory : public QgsChunkLoaderFactory
* The factory takes ownership over the passed \a symbol
*/
QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
double zValueScale, double zValueOffset );
double zValueScale, double zValueOffset, int pointBudget );

//! Creates loader for the given chunk node. Ownership of the returned is passed to the caller.
virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override;
@@ -71,6 +71,7 @@ class QgsPointCloudLayerChunkLoaderFactory : public QgsChunkLoaderFactory
std::unique_ptr< QgsPointCloud3DSymbol > mSymbol;
double mZValueScale = 1.0;
double mZValueOffset = 0;
int mPointBudget = 1000000;
};


@@ -90,9 +91,11 @@ class QgsPointCloudLayerChunkLoader : public QgsChunkLoader
* Constructs the loader
* QgsPointCloudLayerChunkLoader takes ownership over symbol
*/
QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol, double zValueScale, double zValueOffset );
QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol, double zValueScale, double zValueOffset, int pointBudget, int count );
~QgsPointCloudLayerChunkLoader() override;

int primitiveCount() override;

virtual void cancel() override;
virtual Qt3DCore::QEntity *createEntity( Qt3DCore::QEntity *parent ) override;

@@ -102,6 +105,7 @@ class QgsPointCloudLayerChunkLoader : public QgsChunkLoader
QgsPointCloud3DRenderContext mContext;
bool mCanceled = false;
QFutureWatcher<void> *mFutureWatcher = nullptr;
int mPointsCount;
};


@@ -120,7 +124,7 @@ class QgsPointCloudLayerChunkedEntity : public QgsChunkedEntity
Q_OBJECT
public:
explicit QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, QgsPointCloud3DSymbol *symbol, float maxScreenError, bool showBoundingBoxes,
double zValueScale, double zValueOffset );
double zValueScale, double zValueOffset, int pointBudget );

~QgsPointCloudLayerChunkedEntity();
};
@@ -650,9 +650,11 @@ void QgsClassificationPointCloud3DSymbolHandler::finalize( Qt3DCore::QEntity *pa
makeEntity( parent, context, outNormal, false );
}


Qt3DRender::QGeometry *QgsClassificationPointCloud3DSymbolHandler::makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride )
{
return new QgsColorRampPointCloud3DGeometry( parent, data, byteStride );
}


/// @endcond
@@ -77,6 +77,7 @@ class QgsSingleColorPointCloud3DSymbolHandler : public QgsPointCloud3DSymbolHand
bool prepare( const QgsPointCloud3DRenderContext &context ) override;
void processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context ) override;
void finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context ) override;

private:
Qt3DRender::QGeometry *makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride ) override;
};
@@ -89,6 +90,7 @@ class QgsColorRampPointCloud3DSymbolHandler : public QgsPointCloud3DSymbolHandle
bool prepare( const QgsPointCloud3DRenderContext &context ) override;
void processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context ) override;
void finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context ) override;

private:
Qt3DRender::QGeometry *makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride ) override;
};
@@ -101,6 +103,7 @@ class QgsRGBPointCloud3DSymbolHandler : public QgsPointCloud3DSymbolHandler
bool prepare( const QgsPointCloud3DRenderContext &context ) override;
void processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context ) override;
void finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context ) override;

private:
Qt3DRender::QGeometry *makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride ) override;
};
@@ -113,6 +116,7 @@ class QgsClassificationPointCloud3DSymbolHandler : public QgsPointCloud3DSymbolH
bool prepare( const QgsPointCloud3DRenderContext &context ) override;
void processNode( QgsPointCloudIndex *pc, const IndexedPointCloudNode &n, const QgsPointCloud3DRenderContext &context ) override;
void finalize( Qt3DCore::QEntity *parent, const QgsPointCloud3DRenderContext &context ) override;

private:
Qt3DRender::QGeometry *makeGeometry( Qt3DCore::QNode *parent, const QgsPointCloud3DSymbolHandler::PointData &data, unsigned int byteStride ) override;
};

0 comments on commit 81fe1c2

Please sign in to comment.