From Paul Martz, "his pretty much wraps up the OcclusionQueryNode work. I might make some additional modifications if testing reveals any issues, otherwise it's ready for 2.4."
This commit is contained in:
@@ -41,6 +41,9 @@ public:
|
||||
|
||||
virtual osg::BoundingSphere computeBound() const;
|
||||
|
||||
virtual void releaseGLObjects( osg::State* state = 0 ) const;
|
||||
|
||||
|
||||
// When disabled, OQN doesn't perform occlusion queries, and simply
|
||||
// renders its children.
|
||||
void setQueriesEnabled( bool enable=true );
|
||||
@@ -79,6 +82,16 @@ public:
|
||||
void traverseQuery( const osg::Camera* camera, osg::NodeVisitor& nv );
|
||||
void traverseDebug( osg::NodeVisitor& nv );
|
||||
|
||||
|
||||
// Delete unused query IDs for this contextID.
|
||||
static void flushDeletedQueryObjects( unsigned int contextID, double currentTime, double& availableTime );
|
||||
|
||||
// discard all the cached query objects which need to be deleted
|
||||
// in the OpenGL context related to contextID.
|
||||
// Note, unlike flush no OpenGL calls are made, instead the handles are all removed.
|
||||
// this call is useful for when an OpenGL context has been destroyed.
|
||||
static void discardDeletedQueryObjects( unsigned int contextID );
|
||||
|
||||
protected:
|
||||
virtual ~OcclusionQueryNode();
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <osg/BufferObject>
|
||||
#include <osg/FrameBufferObject>
|
||||
#include <osg/Drawable>
|
||||
#include <osg/OcclusionQueryNode>
|
||||
|
||||
void osg::flushDeletedGLObjects(unsigned int contextID, double currentTime, double& availableTime)
|
||||
{
|
||||
@@ -32,6 +33,7 @@ void osg::flushDeletedGLObjects(unsigned int contextID, double currentTime, doub
|
||||
osg::Shader::flushDeletedGlShaders(contextID,currentTime,availableTime);
|
||||
osg::Texture::flushDeletedTextureObjects(contextID,currentTime,availableTime);
|
||||
osg::VertexProgram::flushDeletedVertexProgramObjects(contextID,currentTime,availableTime);
|
||||
osg::OcclusionQueryNode::flushDeletedQueryObjects(contextID,currentTime,availableTime);
|
||||
}
|
||||
|
||||
void osg::flushAllDeletedGLObjects(unsigned int contextID)
|
||||
@@ -48,6 +50,7 @@ void osg::flushAllDeletedGLObjects(unsigned int contextID)
|
||||
osg::Shader::flushDeletedGlShaders(contextID,currentTime,availableTime);
|
||||
osg::Texture::flushAllDeletedTextureObjects(contextID);
|
||||
osg::VertexProgram::flushDeletedVertexProgramObjects(contextID,currentTime,availableTime);
|
||||
osg::OcclusionQueryNode::flushDeletedQueryObjects(contextID,currentTime,availableTime);
|
||||
}
|
||||
|
||||
void osg::discardAllDeletedGLObjects(unsigned int contextID)
|
||||
@@ -62,4 +65,5 @@ void osg::discardAllDeletedGLObjects(unsigned int contextID)
|
||||
osg::Shader::discardDeletedGlShaders(contextID);
|
||||
osg::Texture::discardAllDeletedTextureObjects(contextID);
|
||||
osg::VertexProgram::discardDeletedVertexProgramObjects(contextID);
|
||||
osg::OcclusionQueryNode::discardDeletedQueryObjects(contextID);
|
||||
}
|
||||
|
||||
@@ -115,13 +115,15 @@ initOQDebugState()
|
||||
class TestResult : public osg::Referenced
|
||||
{
|
||||
public:
|
||||
TestResult() : _init( false ), _id( 0 ), _active( false ), _numPixels( 0 ) {}
|
||||
TestResult() : _init( false ), _id( 0 ), _contextID( 0 ), _active( false ), _numPixels( 0 ) {}
|
||||
~TestResult() {}
|
||||
|
||||
bool _init;
|
||||
|
||||
// Query ID for this context.
|
||||
GLuint _id;
|
||||
// Context ID owning this query ID.
|
||||
unsigned int _contextID;
|
||||
|
||||
// Set to true when a query gets issued and set to
|
||||
// false when the result is retrieved.
|
||||
@@ -137,7 +139,9 @@ class QueryGeometry : public osg::Geometry
|
||||
{
|
||||
public:
|
||||
QueryGeometry( const std::string& oqnName=std::string("") );
|
||||
~QueryGeometry() {}
|
||||
~QueryGeometry();
|
||||
|
||||
void reset();
|
||||
|
||||
// TBD implement copy constructor
|
||||
|
||||
@@ -145,6 +149,12 @@ public:
|
||||
|
||||
unsigned int getNumPixels( const osg::Camera* cam );
|
||||
|
||||
|
||||
void releaseGLObjects( osg::State* state = 0 );
|
||||
static void deleteQueryObject( unsigned int contextID, GLuint handle );
|
||||
static void flushDeletedQueryObjects( unsigned int contextID, double currentTime, double& availableTime );
|
||||
static void discardDeletedQueryObjects( unsigned int contextID );
|
||||
|
||||
protected:
|
||||
typedef std::map< const osg::Camera*, TestResult > ResultMap;
|
||||
mutable ResultMap _results;
|
||||
@@ -286,6 +296,15 @@ struct ClearQueriesCallback : public osg::Camera::DrawCallback
|
||||
};
|
||||
|
||||
|
||||
// static cache of deleted query objects which can only
|
||||
// be completely deleted once the appropriate OpenGL context
|
||||
// is set.
|
||||
typedef std::list< GLuint > QueryObjectList;
|
||||
typedef osg::buffered_object< QueryObjectList > DeletedQueryObjectCache;
|
||||
|
||||
static OpenThreads::Mutex s_mutex_deletedQueryObjectCache;
|
||||
static DeletedQueryObjectCache s_deletedQueryObjectCache;
|
||||
|
||||
QueryGeometry::QueryGeometry( const std::string& oqnName )
|
||||
: _oqnName( oqnName )
|
||||
{
|
||||
@@ -293,6 +312,28 @@ QueryGeometry::QueryGeometry( const std::string& oqnName )
|
||||
setUseDisplayList( false );
|
||||
}
|
||||
|
||||
QueryGeometry::~QueryGeometry()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
QueryGeometry::reset()
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _mapMutex );
|
||||
|
||||
ResultMap::iterator it = _results.begin();
|
||||
while (it != _results.end())
|
||||
{
|
||||
TestResult& tr = it->second;
|
||||
if (tr._init)
|
||||
QueryGeometry::deleteQueryObject( tr._contextID, tr._id );
|
||||
it++;
|
||||
}
|
||||
_results.clear();
|
||||
}
|
||||
|
||||
// After 1.2, param 1 changed from State to RenderInfo.
|
||||
// Warning: Version was still 1.2 on dev branch long after the 1.2 release,
|
||||
// and finally got bumped to 1.9 in April 2007.
|
||||
@@ -336,6 +377,7 @@ QueryGeometry::drawImplementation( osg::RenderInfo& renderInfo ) const
|
||||
if (!tr->_init)
|
||||
{
|
||||
ext->glGenQueries( 1, &(tr->_id) );
|
||||
tr->_contextID = contextID;
|
||||
tr->_init = true;
|
||||
}
|
||||
|
||||
@@ -376,6 +418,83 @@ QueryGeometry::getNumPixels( const osg::Camera* cam )
|
||||
return tr._numPixels;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
QueryGeometry::releaseGLObjects( osg::State* state )
|
||||
{
|
||||
if (!state)
|
||||
// delete all query IDs for all contexts.
|
||||
reset();
|
||||
|
||||
else
|
||||
{
|
||||
// Delete all query IDs for the specified context.
|
||||
unsigned int contextID = state->getContextID();
|
||||
ResultMap::iterator it = _results.begin();
|
||||
while (it != _results.end())
|
||||
{
|
||||
TestResult& tr = it->second;
|
||||
if (tr._contextID == contextID)
|
||||
{
|
||||
QueryGeometry::deleteQueryObject( contextID, tr._id );
|
||||
tr._init = false;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
QueryGeometry::deleteQueryObject( unsigned int contextID, GLuint handle )
|
||||
{
|
||||
if (handle!=0)
|
||||
{
|
||||
OpenThreads::ScopedLock< OpenThreads::Mutex > lock( s_mutex_deletedQueryObjectCache );
|
||||
|
||||
// insert the handle into the cache for the appropriate context.
|
||||
s_deletedQueryObjectCache[contextID].push_back( handle );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
QueryGeometry::flushDeletedQueryObjects( unsigned int contextID, double /*currentTime*/, double& availableTime )
|
||||
{
|
||||
// if no time available don't try to flush objects.
|
||||
if (availableTime<=0.0) return;
|
||||
|
||||
const osg::Timer& timer = *osg::Timer::instance();
|
||||
osg::Timer_t start_tick = timer.tick();
|
||||
double elapsedTime = 0.0;
|
||||
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_mutex_deletedQueryObjectCache);
|
||||
|
||||
const osg::Drawable::Extensions* extensions = getExtensions( contextID, true );
|
||||
|
||||
QueryObjectList& qol = s_deletedQueryObjectCache[contextID];
|
||||
|
||||
for(QueryObjectList::iterator titr=qol.begin();
|
||||
titr!=qol.end() && elapsedTime<availableTime;
|
||||
)
|
||||
{
|
||||
extensions->glDeleteQueries( 1L, &(*titr ) );
|
||||
titr = qol.erase(titr);
|
||||
elapsedTime = timer.delta_s(start_tick,timer.tick());
|
||||
}
|
||||
}
|
||||
|
||||
availableTime -= elapsedTime;
|
||||
}
|
||||
|
||||
void
|
||||
QueryGeometry::discardDeletedQueryObjects( unsigned int contextID )
|
||||
{
|
||||
OpenThreads::ScopedLock< OpenThreads::Mutex > lock( s_mutex_deletedQueryObjectCache );
|
||||
QueryObjectList& qol = s_deletedQueryObjectCache[ contextID ];
|
||||
qol.clear();
|
||||
}
|
||||
|
||||
// End support classes
|
||||
//
|
||||
|
||||
@@ -393,20 +512,6 @@ OcclusionQueryNode::OcclusionQueryNode()
|
||||
{
|
||||
setDataVariance( osg::Object::DYNAMIC );
|
||||
|
||||
#if 0
|
||||
// Before integration into core OSG, OcclusionQueryNode supported a config
|
||||
// file to override default values. Can't include this in core osg as it
|
||||
// would introduce a dependency onto osgDB for finding the config file.
|
||||
|
||||
// Override defaults if specified in the config file using the OptionLoader singleton.
|
||||
int clampMe;
|
||||
if ( OptionLoader::instance()->getOption( "VisibilityThreshold", clampMe ) )
|
||||
_visThreshold = (clampMe < 0 ) ? 0 : static_cast<unsigned int>( clampMe );
|
||||
if ( OptionLoader::instance()->getOption( "QueryFrameCount", clampMe ) )
|
||||
_queryFrameCount = (clampMe < 1) ? 1 : static_cast<unsigned int>( clampMe );
|
||||
OptionLoader::instance()->getOption( "DebugBoundingVolume", _debugBB );
|
||||
#endif
|
||||
|
||||
// OQN has two Geode member variables, one for doing the
|
||||
// query and one for rendering the debug geometry.
|
||||
// Create and initialize them.
|
||||
@@ -427,73 +532,6 @@ OcclusionQueryNode::OcclusionQueryNode( const OcclusionQueryNode& oqn, const osg
|
||||
createSupportNodes();
|
||||
}
|
||||
|
||||
//PORT-TBD
|
||||
#if 0
|
||||
// Currently, retrieves last frame's query results during current frame's cull traversal.
|
||||
// In the future, should retrive last frame's results immediately after last
|
||||
// frame's buffer swap.
|
||||
void
|
||||
OcclusionQueryNode::traverse( osg::NodeVisitor& nv )
|
||||
{
|
||||
if ( !_enabled || (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR) )
|
||||
{
|
||||
osg::Group::traverse( nv );
|
||||
return;
|
||||
}
|
||||
|
||||
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
|
||||
osg::Camera* camera = cv->getRenderStage()->getCamera();
|
||||
|
||||
|
||||
// In the future, we could hold a reference directly to the QueryDrawable
|
||||
// to avoid the dynamic_cast.
|
||||
QueryGeometry* qg = dynamic_cast< QueryGeometry* >( _queryGeode->getDrawable( 0 ) );
|
||||
if (qg == NULL)
|
||||
{
|
||||
osg::notify( osg::FATAL ) <<
|
||||
"osgOQ: OcclusionQueryNode: No QueryGeometry." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the distance to the bounding sphere shell is positive, retrieve
|
||||
// the results. Others (we're inside the BS shell) we are considered
|
||||
// to have passed and don't need to retrieve the query.
|
||||
const osg::BoundingSphere& bs = getBound();
|
||||
float distance = cv->getDistanceToEyePoint( bs._center, false ) - bs._radius;
|
||||
_passed = ( distance <= 0.f );
|
||||
if (!_passed)
|
||||
{
|
||||
int result = qg->getNumPixels( camera );
|
||||
_passed = ( (unsigned int)(result) > _visThreshold );
|
||||
}
|
||||
|
||||
if (_passed)
|
||||
// We're visible. Traverse our children
|
||||
osg::Group::traverse( nv );
|
||||
|
||||
// Submit a new query only if sufficient frames have elapsed.
|
||||
bool issueQuery;
|
||||
{
|
||||
const int curFrame = nv.getTraversalNumber();
|
||||
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock( _frameCountMutex );
|
||||
int& lastQueryFrame = _frameCountMap[ camera ];
|
||||
if ( issueQuery = (curFrame - lastQueryFrame >= _queryFrameCount) )
|
||||
lastQueryFrame = curFrame;
|
||||
}
|
||||
if (issueQuery)
|
||||
_queryGeode->accept( nv );
|
||||
|
||||
if (_debugBB)
|
||||
// If requested, display the debug geometry
|
||||
_debugGeode->accept( nv );
|
||||
|
||||
osg::notify( osg::DEBUG_INFO ) <<
|
||||
"oagOQ: OQN::traverse: OQN: " << getName() <<
|
||||
", Cam: " << camera <<
|
||||
", Passed: " << _passed << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
OcclusionQueryNode::getPassed( const osg::Camera* camera, float distanceToEyePoint )
|
||||
@@ -689,4 +727,28 @@ OcclusionQueryNode::createSupportNodes()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OcclusionQueryNode::releaseGLObjects( osg::State* state ) const
|
||||
{
|
||||
// Query object discard and deletion is handled by QueryGeometry support class.
|
||||
OcclusionQueryNode* nonConstThis = const_cast< OcclusionQueryNode* >( this );
|
||||
QueryGeometry* qg = dynamic_cast< QueryGeometry* >( nonConstThis->_queryGeode->getDrawable( 0 ) );
|
||||
qg->releaseGLObjects( state );
|
||||
}
|
||||
|
||||
void
|
||||
OcclusionQueryNode::flushDeletedQueryObjects( unsigned int contextID, double currentTime, double& availableTime )
|
||||
{
|
||||
// Query object discard and deletion is handled by QueryGeometry support class.
|
||||
QueryGeometry::flushDeletedQueryObjects( contextID, currentTime, availableTime );
|
||||
}
|
||||
|
||||
void
|
||||
OcclusionQueryNode::discardDeletedQueryObjects( unsigned int contextID )
|
||||
{
|
||||
// Query object discard and deletion is handled by QueryGeometry support class.
|
||||
QueryGeometry::discardDeletedQueryObjects( contextID );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user