Hi Robert, Wojtek,
I have just spent about 2 weeks trying to fix a "memory leak" (not in
the classic sense, but in the ref counting sense). When loading a
scenario in our Crane simulator, and then loading the same scenario
again, memory usage would go up but not be freed completely until the
application was closed.
In the last few days I've tracked it down to the shadowing code, because
if I didn't enable shadows the "leak" didn't occur. By adding lots and
lots of instrumentation (dumping the call stack of where every
Referenced object was constructed, destroyed, reffed and unreffed) I was
able to find out that the ShadowTechnique
(LightSpacePerspectiveShadowMap*) was never destroyed.
Going up the chain from there, I found out that
MinimalDrawBoundsShadowMap holds a ref_ptr to the main camera
(_mainCamera). This in turn has a ref_ptr to the scene root, which will
be or will contain as a child the ShadowedScene, which contains a
ref_ptr to the shadow technique (which derives from
MinimalDrawBoundsShadowMap). So there's a cycle in ref_ptrs.
My fix is to remove the ref_ptr to _mainCamera in
MinimalDrawBoundsShadowMap, since the ref_ptr to the ShadowTechnique in
ShadowedScene has to be there for other shadow techniques too.
Attached are the fixed files. You'll see the only change is replacing
the ref_ptr by a raw C pointer. I've looked around the source to see if
this seems to be a problem elsewhere in the new osgShadow classes, but
it seems DebugShadowMap does the right thing (raw C pointer to
_viewCamera) and other classes don't seem to keep a camera ref_ptr at
all. I've also tested and there doesn't seem to be any problem of the
camera being deleted before the MinimalDrawBoundsShadowMap has finished
working on it.
Phew! Sorry for the length of the explanation, but I'm *really* glad I
finally found this...
J-S
--
______________________________________________________
Jean-Sebastien Guay [email protected]
http://www.cm-labs.com/
http://whitestar02.webhop.org/
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*
* ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski
* Thanks to to my company http://www.ai.com.pl for allowing me free this work.
*/
#ifndef OSGSHADOW_MINIMALDRAWBOUNDSSHADOWMAP
#define OSGSHADOW_MINIMALDRAWBOUNDSSHADOWMAP 1
#include <osgShadow/MinimalShadowMap>
namespace osgShadow {
class OSGSHADOW_EXPORT MinimalDrawBoundsShadowMap
: public MinimalShadowMap
{
public :
/** Convenient typedef used in definition of ViewData struct and
methods */
typedef MinimalDrawBoundsShadowMap ThisClass;
/** Convenient typedef used in definition of ViewData struct and
methods */
typedef MinimalShadowMap BaseClass;
/** Classic OSG constructor */
MinimalDrawBoundsShadowMap();
/** Classic OSG cloning constructor */
MinimalDrawBoundsShadowMap(
const MinimalDrawBoundsShadowMap& mdbsm,
const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
/** Declaration of standard OSG object methods */
META_Object( ViewDependentShadow, MinimalDrawBoundsShadowMap );
protected:
/** Classic protected OSG destructor */
virtual ~MinimalDrawBoundsShadowMap(void);
struct OSGSHADOW_EXPORT ViewData: public BaseClass::ViewData
{
osg::ref_ptr< osg::RefMatrix > _projection;
osg::Vec2s _boundAnalysisSize;
osg::ref_ptr< osg::Image > _boundAnalysisImage;
osg::ref_ptr< osg::Texture2D > _boundAnalysisTexture;
osg::ref_ptr< osg::Camera > _boundAnalysisCamera;
osg::Camera* _mainCamera;
void setShadowCameraProjectionMatrixPtr( osg::RefMatrix *
projection )
{ _projection = projection; }
osg::RefMatrix * getShadowCameraProjectionMatrixPtr( void )
{ return _projection.get(); }
virtual void init( ThisClass * st, osgUtil::CullVisitor * cv );
virtual void cullShadowReceivingScene( );
virtual void createDebugHUD( );
virtual void recordShadowMapParams( );
virtual void cullBoundAnalysisScene( );
static osg::BoundingBox scanImage( const osg::Image * image,
osg::Matrix m );
virtual void performBoundAnalysis( const osg::Camera& camera );
ViewData( void ): _boundAnalysisSize( 64, 64 ) {}
};
friend struct ViewData;
META_ViewDependentShadowTechniqueData( ThisClass, ThisClass::ViewData )
struct CameraPostDrawCallback : public osg::Camera::DrawCallback {
CameraPostDrawCallback( ViewData * vd ): _vd( vd )
{
}
virtual void operator ()( const osg::Camera& camera ) const
{
if( _vd.valid() )
_vd->performBoundAnalysis( camera );
}
osg::observer_ptr< ViewData > _vd;
};
struct CameraCullCallback: public osg::NodeCallback {
CameraCullCallback(ViewData * vd, osg::NodeCallback * nc): _vd(vd),
_nc(nc)
{
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor *cv = dynamic_cast< osgUtil::CullVisitor
*>( nv );
if( _nc.valid() )
_nc->operator()(node,nv);
else
traverse(node,nv);
if( cv )
_vd->recordShadowMapParams( );
}
protected:
osg::observer_ptr< ViewData > _vd;
osg::ref_ptr< osg::NodeCallback > _nc;
};
};
} // namespace osgShadow
#endif
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*
* ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski
* Thanks to to my company http://www.ai.com.pl for allowing me free this work.
*/
#include <osgShadow/MinimalDrawBoundsShadowMap>
#include <osgShadow/ConvexPolyhedron>
#include <osg/PolygonOffset>
#include <osgUtil/RenderLeaf>
#include <osgShadow/ShadowedScene>
#include <osg/FrameBufferObject>
#include <osg/BlendEquation>
#include <osg/Depth>
#include <osg/AlphaFunc>
#include <osg/Image>
#include <iostream>
#include <string.h>
#define ANALYSIS_DEPTH 1
#define USE_FLOAT_IMAGE 1
using namespace osgShadow;
MinimalDrawBoundsShadowMap::MinimalDrawBoundsShadowMap(): BaseClass()
{
}
MinimalDrawBoundsShadowMap::MinimalDrawBoundsShadowMap
(const MinimalDrawBoundsShadowMap& copy, const osg::CopyOp& copyop) :
BaseClass(copy,copyop)
{
}
MinimalDrawBoundsShadowMap::~MinimalDrawBoundsShadowMap()
{
}
void MinimalDrawBoundsShadowMap::ViewData::cullShadowReceivingScene( )
{
BaseClass::ViewData::cullShadowReceivingScene( );
ThisClass::ViewData::cullBoundAnalysisScene( );
}
void MinimalDrawBoundsShadowMap::ViewData::cullBoundAnalysisScene( )
{
_boundAnalysisCamera->setReferenceFrame( osg::Camera::ABSOLUTE_RF );
_boundAnalysisCamera->setViewMatrix( *_cv->getModelViewMatrix() );
_boundAnalysisCamera->setProjectionMatrix( _clampedProjection );
osg::Matrixd::value_type l,r,b,t,n,f;
_boundAnalysisCamera->getProjectionMatrixAsFrustum( l,r,b,t,n,f );
_mainCamera = _cv->getRenderStage()->getCamera();
extendProjection( _boundAnalysisCamera->getProjectionMatrix(),
_boundAnalysisCamera->getViewport(), osg::Vec2( 2,2 ) );
// record the traversal mask on entry so we can reapply it later.
unsigned int traversalMask = _cv->getTraversalMask();
_cv->setTraversalMask( traversalMask &
_st->getShadowedScene()->getReceivesShadowTraversalMask() );
// do RTT camera traversal
_boundAnalysisCamera->accept(*_cv);
// reapply the original traversal mask
_cv->setTraversalMask( traversalMask );
}
void MinimalDrawBoundsShadowMap::ViewData::createDebugHUD( )
{
// _hudSize[0] *= 2;
_viewportSize[0] *= 2;
_orthoSize[0] *= 2;
MinimalShadowMap::ViewData::createDebugHUD( );
osg::Camera * camera = _cameraDebugHUD.get();
osg::Geode* geode = new osg::Geode;
camera->addChild( geode );
osg::Geometry* geometry = osg::createTexturedQuadGeometry
( osg::Vec3(_hudOrigin[0]+_hudSize[0],_hudOrigin[1],0),
osg::Vec3(_hudSize[0],0,0),
osg::Vec3(0,_hudSize[1],0) );
geode->addDrawable(geometry);
osg::StateSet* stateset = geometry->getOrCreateStateSet();
stateset->setTextureAttributeAndModes
(0, _boundAnalysisTexture.get(),osg::StateAttribute::ON );
#if ANALYSIS_DEPTH
osg::Program* program = new osg::Program;
program->addShader( _depthColorFragmentShader.get() );
stateset->setAttribute( program );
stateset->addUniform( new osg::Uniform( "texture" , 0 ) );
#else
#endif
}
osg::BoundingBox MinimalDrawBoundsShadowMap::ViewData::scanImage
( const osg::Image * image, osg::Matrix m )
{
osg::BoundingBox bb, bbProj;
int components = osg::Image::computeNumComponents( image->getPixelFormat()
);
if( image->getDataType() == GL_FLOAT ) {
float scale = 255.f / 254.f;
float * pf = (float *)image->data();
for( int y = 0; y < image->t(); y++ ) {
float fY = ( 0.5f + y ) / image->t();
for( int x = 0; x < image->s(); x++ ) {
float fX = ( 0.5f + x ) / image->s();
if( pf[0] < 1.0 ) {
float fMinZ = pf[0] * scale;
bbProj.expandBy( osg::Vec3( fX, fY, fMinZ ) );
bb.expandBy( osg::Vec3( fX, fY, fMinZ ) * m );
if( components > 1 ) {
float fMaxZ = scale * ( 1.f - pf[1] );
bbProj.expandBy( osg::Vec3( fX, fY, fMaxZ ) );
bb.expandBy( osg::Vec3( fX, fY, fMaxZ ) * m );
}
}
pf += components;
}
}
} else if( image->getDataType() == GL_UNSIGNED_BYTE ) {
unsigned char * pb = (unsigned char *)image->data();
float scale = 1.f / 254;
for( int y = 0; y < image->t(); y++ ) {
float fY = ( 0.5f + y ) / image->t();
for( int x = 0; x < image->s(); x++ ) {
float fX = ( 0.5f + x ) / image->s();
if( pb[0] < 255 ) {
float fMinZ = scale * (pb[0] - 0.5f);
fMinZ = osg::clampTo( fMinZ, 0.f, 1.f );
bbProj.expandBy( osg::Vec3( fX, fY, fMinZ ) );
bb.expandBy( osg::Vec3( fX, fY, fMinZ ) * m );
if( components > 1 ) {
float fMaxZ = scale * (255 - pb[1] + 0.5f);
fMaxZ = osg::clampTo( fMaxZ, 0.f, 1.f );
bbProj.expandBy( osg::Vec3( fX, fY, fMaxZ ) );
bb.expandBy( osg::Vec3( fX, fY, fMaxZ ) * m );
}
}
pb += components;
}
}
}
return bb;
}
void MinimalDrawBoundsShadowMap::ViewData::performBoundAnalysis( const
osg::Camera& camera )
{
if( !_projection.valid() )
return;
osg::Camera::BufferAttachmentMap & bam
= const_cast<osg::Camera&>( camera ).getBufferAttachmentMap();
#if ANALYSIS_DEPTH
osg::Camera::Attachment & attachment = bam[ osg::Camera::DEPTH_BUFFER ];
#else
osg::Camera::Attachment & attachment = bam[ osg::Camera::COLOR_BUFFER ];
#endif
const osg::ref_ptr< osg::Image > image = attachment._image.get();
if( !image.valid() )
return;
osg::Matrix m;
m.invert( *_modellingSpaceToWorldPtr *
camera.getViewMatrix() *
camera.getProjectionMatrix() );
m.preMult( osg::Matrix::scale( osg::Vec3( 2.f, 2.f, 2.f ) ) *
osg::Matrix::translate( osg::Vec3( -1.f, -1.f, -1.f ) ) );
osg::BoundingBox bb = scanImage( image.get(), m );
if( getDebugDraw() ) {
ConvexPolyhedron p;
p.setToBoundingBox( bb );
p.transform( *_modellingSpaceToWorldPtr,
osg::Matrix::inverse( *_modellingSpaceToWorldPtr ) );
setDebugPolytope( "scan", p,
osg::Vec4( 0,0,0,1 ), osg::Vec4( 0,0,0,0.1 ) );
}
cutScenePolytope( *_modellingSpaceToWorldPtr,
osg::Matrix::inverse( *_modellingSpaceToWorldPtr ), bb );
frameShadowCastingCamera( _mainCamera, _camera.get() );
_projection->set( _camera->getProjectionMatrix( ) );
BaseClass::ViewData::_texgen->setPlanesFromMatrix(
_camera->getProjectionMatrix() *
osg::Matrix::translate(1.0,1.0,1.0) *
osg::Matrix::scale(0.5,0.5,0.5) );
updateDebugGeometry( _mainCamera, _camera.get() );
}
void MinimalDrawBoundsShadowMap::ViewData::recordShadowMapParams( )
{
const osgUtil::RenderStage * rs = _cv->getCurrentRenderBin()->getStage();
setShadowCameraProjectionMatrixPtr( _cv->getProjectionMatrix() );
if( !rs->getRenderBinList().empty() || rs->getBinNum() != 0 )
{
}
#if 0
MinimalShadowMap::RenderLeafList rll;
static unsigned pass = 0, c = 0;
pass++;
std::set< osg::ref_ptr< osg::RefMatrix > > projections;
MinimalShadowMap::GetRenderLeaves( , rll );
for( unsigned i =0; i < rll.size(); i++ ) {
if( rll[i]->_projection.get() != _projection.get() ) {
osg::RefMatrix * projection = rll[i]->_projection.get();
projections.insert( rll[i]->_projection );
c++;
}
}
if( projections.size() > 0 )
_projection = (*projections.begin()).get();
c = 0;
#endif
}
void MinimalDrawBoundsShadowMap::ViewData::init
( ThisClass *st, osgUtil::CullVisitor *cv )
{
BaseClass::ViewData::init( st, cv );
_camera->setCullCallback
( new CameraCullCallback( this, _camera->getCullCallback() ) );
_boundAnalysisTexture = new osg::Texture2D;
_boundAnalysisTexture->setTextureSize
( _boundAnalysisSize[0], _boundAnalysisSize[1] );
_boundAnalysisImage = new osg::Image;
#if ANALYSIS_DEPTH
_boundAnalysisImage->allocateImage( _boundAnalysisSize[0],
_boundAnalysisSize[1], 1,
GL_DEPTH_COMPONENT, GL_FLOAT );
_boundAnalysisTexture->setInternalFormat(GL_DEPTH_COMPONENT);
// _boundAnalysisTexture->setShadowComparison(true);
_boundAnalysisTexture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
_boundAnalysisImage->setInternalTextureFormat( GL_DEPTH_COMPONENT );
_boundAnalysisTexture->setInternalFormat( GL_DEPTH_COMPONENT );
#else
#if USE_FLOAT_IMAGE
_boundAnalysisImage->allocateImage( _boundAnalysisSize[0],
_boundAnalysisSize[1], 1,
GL_RGBA, GL_FLOAT );
_boundAnalysisImage->setInternalTextureFormat( GL_RGBA16F_ARB );
_boundAnalysisTexture->setInternalFormat(GL_RGBA16F_ARB);
#else
_boundAnalysisImage->allocateImage( _boundAnalysisSize[0],
_boundAnalysisSize[1], 1,
GL_RGBA, GL_UNSIGNED_BYTE );
_boundAnalysisImage->setInternalTextureFormat( GL_RGBA );
_boundAnalysisTexture->setInternalFormat( GL_RGBA );
#endif
#endif
memset( _boundAnalysisImage->data(), 0,
_boundAnalysisImage->getImageSizeInBytes() );
if( getDebugDraw() )
_boundAnalysisTexture->setImage(0, _boundAnalysisImage.get() );
_boundAnalysisTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);
_boundAnalysisTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
// the shadow comparison should fail if object is outside the texture
_boundAnalysisTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT);
_boundAnalysisTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT);
// set up the render to texture camera.
// create the camera
_boundAnalysisCamera = new osg::Camera;
_boundAnalysisCamera->setName( "AnalysisCamera" );
_boundAnalysisCamera->setCullCallback( new
BaseClass::CameraCullCallback(st) );
// _boundAnalysisCamera->setPreDrawCallback( _camera->getPreDrawCallback() );
_boundAnalysisCamera->setPostDrawCallback( new CameraPostDrawCallback(this)
);
_boundAnalysisCamera->setClearColor( osg::Vec4(1,1,1,1) );
_boundAnalysisCamera->setClearMask(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
_boundAnalysisCamera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
// set viewport
_boundAnalysisCamera->setViewport
( 0, 0, _boundAnalysisSize[0], _boundAnalysisSize[1] );
// set the camera to render before the main camera.
_boundAnalysisCamera->setRenderOrder(osg::Camera::PRE_RENDER);
// tell the camera to use OpenGL frame buffer object where supported.
_boundAnalysisCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
//_boundAnalysisCamera->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW);
const int OVERRIDE_ON = osg::StateAttribute::OVERRIDE |
osg::StateAttribute::ON;
const int OVERRIDE_OFF = osg::StateAttribute::OVERRIDE |
osg::StateAttribute::OFF;
osg::StateSet* stateset = _boundAnalysisCamera->getOrCreateStateSet();
stateset->setAttributeAndModes
( new osg::Depth( osg::Depth::LESS, 0.0, 254.f/255.f ), OVERRIDE_ON );
// stateset->setAttributeAndModes
// ( new osg::AlphaFunc( osg::AlphaFunc::EQUAL, 1.f ), OVERRIDE_ON );
stateset->setRenderBinDetails( 0, "RenderBin",
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS );
osg::Program* program = new osg::Program;
program->addShader( new osg::Shader( osg::Shader::FRAGMENT,
"uniform sampler2D texture; \n"
"void main(void) \n"
"{ \n"
#if ANALYSIS_DEPTH
" gl_FragColor = texture2D( texture, gl_TexCoord[0].xy ); \n"
#else
" gl_FragColor = vec4( gl_FragCoord.z, \n"
" 1.-gl_FragCoord.z, \n"
" 1., \n"
" texture2D( texture, gl_TexCoord[0].xy ).a );
\n"
#endif
"} \n"
) ); // program->addShader Fragment
program->addShader( new osg::Shader( osg::Shader::VERTEX,
"void main(void) \n"
"{ \n"
" gl_Position = ftransform(); \n"
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
"} \n"
) ); // program->addShader Vertex
stateset->setAttribute( program, OVERRIDE_ON );
// attach the texture and use it as the color buffer.
#if ANALYSIS_DEPTH
// _boundAnalysisCamera->attach(osg::Camera::DEPTH_BUFFER,
_boundAnalysisTexture.get());
_boundAnalysisCamera->attach(osg::Camera::DEPTH_BUFFER,
_boundAnalysisImage.get());
stateset->setMode( GL_BLEND, OVERRIDE_OFF );
#else
// _boundAnalysisCamera->attach(osg::Camera::COLOR_BUFFER,
_boundAnalysisTexture.get());
_boundAnalysisCamera->attach(osg::Camera::COLOR_BUFFER,
_boundAnalysisImage.get());
stateset->setAttributeAndModes
( new osg::BlendEquation( osg::BlendEquation::RGBA_MIN ), OVERRIDE_ON );
stateset->setMode( GL_DEPTH_TEST, OVERRIDE_OFF );
#endif
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org