Refactored the MergeGeometry::mergeGroup(..) method to avoid O(N^2) issue with using removeChildren() on groups with very large numbers of children.

This commit is contained in:
Robert Osfield
2017-11-27 15:44:11 +00:00
parent 06302a1082
commit bc4a9d9dd0

View File

@@ -1618,7 +1618,7 @@ void Optimizer::CombineLODsVisitor::combineLODs()
struct LessGeometry
{
bool operator() (const osg::Geometry* lhs,const osg::Geometry* rhs) const
bool operator() (const osg::ref_ptr<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& rhs) const
{
if (lhs->getStateSet()<rhs->getStateSet()) return true;
if (rhs->getStateSet()<lhs->getStateSet()) return false;
@@ -1714,7 +1714,7 @@ struct LessGeometry
struct LessGeometryPrimitiveType
{
bool operator() (const osg::Geometry* lhs,const osg::Geometry* rhs) const
bool operator() (const osg::ref_ptr<osg::Geometry>& lhs,const osg::ref_ptr<osg::Geometry>& rhs) const
{
for(unsigned int i=0;
i<lhs->getNumPrimitiveSets() && i<rhs->getNumPrimitiveSets();
@@ -1792,45 +1792,39 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group)
if (group.getNumChildren()>=2)
{
typedef std::vector<osg::Geometry*> DuplicateList;
typedef std::vector< osg::ref_ptr<osg::Drawable> > DrawableList;
typedef std::map<osg::Geometry*,DuplicateList,LessGeometry> GeometryDuplicateMap;
typedef std::vector< osg::ref_ptr<osg::Geometry> > DuplicateList;
typedef std::vector< osg::ref_ptr<osg::Node> > Nodes;
typedef std::map< osg::ref_ptr<osg::Geometry> ,DuplicateList,LessGeometry> GeometryDuplicateMap;
typedef std::vector<DuplicateList> MergeList;
GeometryDuplicateMap geometryDuplicateMap;
DrawableList standardDrawables;
Nodes standardChildren;
unsigned int i;
for(i=0;i<group.getNumChildren();++i)
{
osg::Drawable* drawable = group.getChild(i)->asDrawable();
if (drawable)
osg::Node* child = group.getChild(i);
osg::Geometry* geom = child->asGeometry();
if (geom)
{
osg::Geometry* geom = drawable->asGeometry();
if (geom)
if (!geometryContainsSharedArrays(*geom) &&
geom->getDataVariance()!=osg::Object::DYNAMIC &&
isOperationPermissibleForObject(geom))
{
//geom->computeCorrectBindingsAndArraySizes();
if (!geometryContainsSharedArrays(*geom) &&
geom->getDataVariance()!=osg::Object::DYNAMIC &&
isOperationPermissibleForObject(geom))
{
geometryDuplicateMap[geom].push_back(geom);
}
else
{
standardDrawables.push_back(drawable);
}
geometryDuplicateMap[geom].push_back(geom);
}
else
{
standardDrawables.push_back(drawable);
standardChildren.push_back(geom);
}
}
else
{
standardChildren.push_back(child);
}
}
#if 1
// first try to group geometries with the same properties
// (i.e. array types) to avoid loss of data during merging
MergeList mergeListChecked; // List of drawables just before merging, grouped by "compatibility" and vertex limit
@@ -1860,7 +1854,7 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group)
dupItr!=itr->second.end();
++dupItr)
{
osg::Geometry* geomToPush = *dupItr;
osg::Geometry* geomToPush = dupItr->get();
// try to group geomToPush with another geometry
MergeList::iterator eachMergeList=mergeListTmp.begin();
@@ -1946,6 +1940,16 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group)
if (needToDoMerge)
{
// to avoid performance issues associated with incrementally removing a large number children, we remove them all and add back the ones we need.
group.removeChildren(0, group.getNumChildren());
for(Nodes::iterator itr = standardChildren.begin();
itr != standardChildren.end();
++itr)
{
group.addChild(*itr);
}
// now do the merging of geometries
for(MergeList::iterator mitr = mergeList.begin();
mitr != mergeList.end();
@@ -1954,65 +1958,18 @@ bool Optimizer::MergeGeometryVisitor::mergeGroup(osg::Group& group)
DuplicateList& duplicateList = *mitr;
if (duplicateList.size()>1)
{
osg::Geometry* lhs = duplicateList.front();
osg::ref_ptr<osg::Geometry> lhs = duplicateList.front();
group.addChild(lhs.get());
for(DuplicateList::iterator ditr = duplicateList.begin()+1;
ditr != duplicateList.end();
++ditr)
{
mergeGeometry(*lhs,**ditr);
group.removeChild(*ditr);
mergeGeometry(*lhs, **ditr);
}
}
}
}
#else
// don't merge geometry if its above a maximum number of vertices.
for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin();
itr!=geometryDuplicateMap.end();
++itr)
{
if (itr->second.size()>1)
{
std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType());
osg::Geometry* lhs = itr->second[0];
for(DuplicateList::iterator dupItr=itr->second.begin()+1;
dupItr!=itr->second.end();
++dupItr)
{
osg::Geometry* rhs = *dupItr;
if (lhs->getVertexArray() && lhs->getVertexArray()->getNumElements()>=_targetMaximumNumberOfVertices)
{
lhs = rhs;
continue;
}
if (rhs->getVertexArray() && rhs->getVertexArray()->getNumElements()>=_targetMaximumNumberOfVertices)
{
continue;
}
if (lhs->getVertexArray() && rhs->getVertexArray() &&
(lhs->getVertexArray()->getNumElements()+rhs->getVertexArray()->getNumElements())>=_targetMaximumNumberOfVertices)
{
continue;
}
if (mergeGeometry(*lhs,*rhs))
{
geode.removeDrawable(rhs);
static int co = 0;
OSG_INFO<<"merged and removed Geometry "<<++co<<std::endl;
}
}
}
}
#endif
}