Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Optimise handling of rendered item results
While testing with a layer containing millions of annotations there was
some significant bottlenecks on the main thread during transfering
and cleaning up of rendered annotation item details. Optimise this
by storing results in separate vectors per map layer, so that
we can clear an entire vector when invalidating the results for
one layer instead of having to iterate over one combined
vector and remove items element-by-element.
  • Loading branch information
nyalldawson committed Sep 14, 2021
1 parent 48b7c3d commit 75c7b28
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 29 deletions.
61 changes: 33 additions & 28 deletions src/core/maprenderer/qgsrendereditemresults.cpp
Expand Up @@ -109,12 +109,13 @@ QgsRenderedItemResults::~QgsRenderedItemResults() = default;
QList<QgsRenderedItemDetails *> QgsRenderedItemResults::renderedItems() const
{
QList< QgsRenderedItemDetails * > res;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
res.reserve( static_cast< int >( mDetails.size() ) );
#else
res.reserve( mDetails.size() );
#endif
std::transform( mDetails.begin(), mDetails.end(), std::back_inserter( res ), []( const auto & detail ) {return detail.get();} );
for ( const auto &it : mDetails )
{
std::transform( it.second.begin(), it.second.end(), std::back_inserter( res ), []( const auto & detail )
{
return detail.get();
} );
}
return res;
}

Expand Down Expand Up @@ -148,53 +149,57 @@ void QgsRenderedItemResults::appendResults( const QList<QgsRenderedItemDetails *
if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( details ) )
mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );

mDetails.emplace_back( std::unique_ptr< QgsRenderedItemDetails >( details ) );

mDetails[ details->layerId() ].emplace_back( std::unique_ptr< QgsRenderedItemDetails >( details ) );
}
}

void QgsRenderedItemResults::transferResults( QgsRenderedItemResults *other, const QStringList &layerIds )
{
for ( auto it = other->mDetails.begin(); it != other->mDetails.end(); )
for ( const QString &layerId : layerIds )
{
if ( layerIds.contains( ( *it )->layerId() ) )
auto otherLayerIt = other->mDetails.find( layerId );
if ( otherLayerIt == other->mDetails.end() )
continue;

std::vector< std::unique_ptr< QgsRenderedItemDetails > > &source = otherLayerIt->second;

for ( std::unique_ptr< QgsRenderedItemDetails > &details : source )
{
if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( ( *it ).get() ) )
if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( details.get() ) )
mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );

mDetails.emplace_back( std::move( *it ) );
other->mDetails.erase( it );
}
else
{
it++;
mDetails[layerId].emplace_back( std::move( details ) );
}

other->mDetails.erase( otherLayerIt );
}
}

void QgsRenderedItemResults::transferResults( QgsRenderedItemResults *other )
{
for ( auto it = other->mDetails.begin(); it != other->mDetails.end(); ++it )
for ( auto layerIt = other->mDetails.begin(); layerIt != other->mDetails.end(); ++layerIt )
{
if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( ( *it ).get() ) )
mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );
std::vector< std::unique_ptr< QgsRenderedItemDetails > > &dest = mDetails[layerIt->first];
dest.reserve( layerIt->second.size() );
for ( auto it = layerIt->second.begin(); it != layerIt->second.end(); ++it )
{
if ( QgsRenderedAnnotationItemDetails *annotationDetails = dynamic_cast< QgsRenderedAnnotationItemDetails * >( ( *it ).get() ) )
mAnnotationItemsIndex->insert( annotationDetails, annotationDetails->boundingBox() );

mDetails.emplace_back( std::move( *it ) );
dest.emplace_back( std::move( *it ) );
}
}
other->mDetails.clear();
}

void QgsRenderedItemResults::eraseResultsFromLayers( const QStringList &layerIds )
{
for ( auto it = mDetails.begin(); it != mDetails.end(); )
for ( const QString &layerId : layerIds )
{
if ( layerIds.contains( ( *it )->layerId() ) )
{
auto it = mDetails.find( layerId );
if ( it != mDetails.end() )
mDetails.erase( it );
}
else
{
it++;
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/core/maprenderer/qgsrendereditemresults.h
Expand Up @@ -24,6 +24,7 @@
#include <memory>
#include <QList>
#include <vector>
#include <unordered_map>

class QgsRenderedItemDetails;
class QgsRenderContext;
Expand Down Expand Up @@ -109,7 +110,7 @@ class CORE_EXPORT QgsRenderedItemResults

QgsRectangle mExtent;

std::vector< std::unique_ptr< QgsRenderedItemDetails > > mDetails;
std::unordered_map< QString, std::vector< std::unique_ptr< QgsRenderedItemDetails > > > mDetails;
std::unique_ptr< QgsRenderedItemResultsSpatialIndex > mAnnotationItemsIndex;

};
Expand Down

0 comments on commit 75c7b28

Please sign in to comment.