diff --git a/VisualStudio/osg/osg.dsp b/VisualStudio/osg/osg.dsp index 1a37637de..e28e650a2 100755 --- a/VisualStudio/osg/osg.dsp +++ b/VisualStudio/osg/osg.dsp @@ -524,6 +524,10 @@ SOURCE=..\..\src\osg\StateSet.cpp # End Source File # Begin Source File +SOURCE=..\..\src\osg\Stats.cpp +# End Source File +# Begin Source File + SOURCE=..\..\src\osg\Stencil.cpp # End Source File # Begin Source File @@ -1040,6 +1044,10 @@ SOURCE=..\..\Include\Osg\StateSet # End Source File # Begin Source File +SOURCE=..\..\Include\Osg\Stats +# End Source File +# Begin Source File + SOURCE=..\..\Include\Osg\Stencil # End Source File # Begin Source File diff --git a/applications/osgviewer/osgviewer.cpp b/applications/osgviewer/osgviewer.cpp index ab2cbb0b1..754caa081 100644 --- a/applications/osgviewer/osgviewer.cpp +++ b/applications/osgviewer/osgviewer.cpp @@ -200,6 +200,39 @@ public: bool _done; }; +class StatsHandler : public osgGA::GUIEventHandler +{ +public: + + StatsHandler() {} + + bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) + { + osgViewer::Viewer* viewer = dynamic_cast(&aa); + if (!viewer) return false; + + switch(ea.getEventType()) + { + case(osgGA::GUIEventAdapter::KEYUP): + { + if (ea.getKey()=='s') + { + if (viewer->getStats()) + { + viewer->getStats()->report(osg::notify(osg::NOTICE)); + } + return true; + } + } + default: break; + } + + return false; + } + + bool _done; +}; + int main_osgViewer(osg::ArgumentParser& arguments) { arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); @@ -273,9 +306,10 @@ int main_osgViewer(osg::ArgumentParser& arguments) } // add the thread model handler - { - viewer.addEventHandler(new ThreadingHandler); - } + viewer.addEventHandler(new ThreadingHandler); + + // add the stats handler + viewer.addEventHandler(new StatsHandler); // load the data osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); diff --git a/include/osg/Camera b/include/osg/Camera index 8beab8149..dcf1e2779 100644 --- a/include/osg/Camera +++ b/include/osg/Camera @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -53,6 +54,16 @@ class OSG_EXPORT Camera : public Transform, public CullSettings const View* getView() const { return _view; } + /** Set the Stats object used for collect various frame related timing and scene graph stats.*/ + void setStats(osg::Stats* stats) { _stats = stats; } + + /** Get the Stats object.*/ + osg::Stats* getStats() { return _stats.get(); } + + /** Get the const Stats object.*/ + const osg::Stats* getStats() const { return _stats.get(); } + + /** Sets the clear color. */ inline void setClearColor(const Vec4& color) { _clearColor = color; } @@ -80,8 +91,6 @@ class OSG_EXPORT Camera : public Transform, public CullSettings ColorMask* getColorMask() { return _colorMask.get(); } - - /** Set the viewport of the camera to use specified osg::Viewport. */ void setViewport(osg::Viewport* viewport); @@ -376,6 +385,8 @@ class OSG_EXPORT Camera : public Transform, public CullSettings View* _view; + + osg::ref_ptr _stats; Vec4 _clearColor; GLbitfield _clearMask; diff --git a/include/osg/Stats b/include/osg/Stats new file mode 100644 index 000000000..871f4f8c2 --- /dev/null +++ b/include/osg/Stats @@ -0,0 +1,80 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2007 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. +*/ + +#ifndef OSG_STATS +#define OSG_STATS 1 + +#include + +#include +#include +#include +#include + +namespace osg { + +class Stats : public osg::Referenced +{ + public: + + Stats(const std::string& name, unsigned int numberOfFrames=100); + + void setName(const std::string& name) { _name = name; } + const std::string& getName() const { return _name; } + + void allocate(unsigned int numberOfFrames); + + int getEarliestFrameNumber() const { return _latestFrameNumber < static_cast(_attributeMapList.size()) ? 0 : _latestFrameNumber - _attributeMapList.size() + 1; } + int getLatestFrameNumber() const { return _latestFrameNumber; } + + typedef std::map AttributeMap; + typedef std::vector AttributeMapList; + + bool setAttribute(int frameNumber, const std::string& attributeName, double value); + bool getAttribute(int frameNumber, const std::string& attributeName, double& value) const; + + AttributeMap& getAttributeMap(int frameNumber); + const AttributeMap& getAttributeMap(int frameNumber) const; + + void report(std::ostream& out); + + protected: + + virtual ~Stats() {} + + int getIndex(int frameNumber) const + { + // reject frame that are in the future + if (frameNumber > _latestFrameNumber) return -1; + + // reject frames that are too early + if (frameNumber < getEarliestFrameNumber()) return -1; + + if (frameNumber >= _baseFrameNumber) return frameNumber - _baseFrameNumber; + else return _attributeMapList.size() - (_baseFrameNumber-frameNumber); + } + + std::string _name; + + int _baseFrameNumber; + int _latestFrameNumber; + + AttributeMapList _attributeMapList; + AttributeMap _invalidAttributeMap; + +}; + + +} + +#endif diff --git a/include/osg/View b/include/osg/View index 214654935..24ecd5111 100644 --- a/include/osg/View +++ b/include/osg/View @@ -31,6 +31,17 @@ class OSG_EXPORT View : public virtual osg::Referenced View(); + + /** Set the Stats object used for collect various frame related timing and scene graph stats.*/ + void setStats(osg::Stats* stats) { _stats = stats; } + + /** Get the Stats object.*/ + osg::Stats* getStats() { return _stats.get(); } + + /** Get the const Stats object.*/ + const osg::Stats* getStats() const { return _stats.get(); } + + /** Set the master camera of the view. */ void setCamera(osg::Camera* camera) { _camera = camera; } @@ -84,6 +95,8 @@ class OSG_EXPORT View : public virtual osg::Referenced virtual ~View(); + osg::ref_ptr _stats; + osg::ref_ptr _camera; typedef std::vector Slaves; diff --git a/src/osg/GNUmakefile b/src/osg/GNUmakefile index b53452a71..8dbf16dfa 100644 --- a/src/osg/GNUmakefile +++ b/src/osg/GNUmakefile @@ -92,6 +92,7 @@ CXXFILES =\ Shape.cpp\ ShapeDrawable.cpp\ State.cpp\ + Stats.cpp\ StateAttribute.cpp\ StateSet.cpp\ Stencil.cpp\ diff --git a/src/osg/Stats.cpp b/src/osg/Stats.cpp new file mode 100644 index 000000000..b16d0b0c0 --- /dev/null +++ b/src/osg/Stats.cpp @@ -0,0 +1,114 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2007 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. +*/ + +#include +#include + +using namespace osg; + +Stats::Stats(const std::string& name, unsigned int numberOfFrames): + _name(name) +{ + allocate(numberOfFrames); +} + +void Stats::allocate(unsigned int numberOfFrames) +{ + _baseFrameNumber = 0; + _latestFrameNumber = 0; + _attributeMapList.clear(); + _attributeMapList.resize(numberOfFrames); +} + + +bool Stats::setAttribute(int frameNumber, const std::string& attributeName, double value) +{ + if (frameNumber_latestFrameNumber) + { + // need to advance + + // first clear the entries up to and including the new frameNumber + for(int i = _latestFrameNumber+1; i<= frameNumber; ++i) + { + int index = (i - _baseFrameNumber) % _attributeMapList.size(); + _attributeMapList[index].clear(); + } + + if ( (frameNumber-_baseFrameNumber) >= static_cast(_attributeMapList.size())) + { + _baseFrameNumber = (frameNumber/_attributeMapList.size())*_attributeMapList.size(); + } + + _latestFrameNumber = frameNumber; + + } + + int index = getIndex(frameNumber); + if (index<0) + { + osg::notify(osg::NOTICE)<<"Failed to assing valid index for Stats::setAttribute("<second; + return true; +} + +Stats::AttributeMap& Stats::getAttributeMap(int frameNumber) +{ + int index = getIndex(frameNumber); + if (index<0) return _invalidAttributeMap; + + return _attributeMapList[index]; +} + +const Stats::AttributeMap& Stats::getAttributeMap(int frameNumber) const +{ + int index = getIndex(frameNumber); + if (index<0) return _invalidAttributeMap; + + return _attributeMapList[index]; +} + +void Stats::report(std::ostream& out) +{ + out<<"Stats "<<_name<first<<"\t"<second<setActionAdapter(this); + + setStats(new osg::Stats("Viewer")); } Viewer::~Viewer() @@ -459,16 +458,73 @@ struct ViewerRenderingOperation : public osg::GraphicsOperation // osg::notify(osg::NOTICE)<<"RenderingOperation"<cull(); - _sceneView->draw(); - double availableTime = 0.004; // 4 ms - if (_databasePager.valid()) + _sceneView->cull(); + + +#if 0 + osg::State* state = _sceneView->getState(); + osg::Stats* stats = _sceneView->getCamera()->getStats(); + const osg::Drawable::Extensions* extensions = stats ? osg::Drawable::getExtensions(state->getContextID(),true) : 0; + bool aquireGPUStats = extensions && extensions->isTimerQuerySupported(); + + + if (aquireGPUStats) { - _databasePager->compileGLObjects(*(_sceneView->getState()), availableTime); + const osg::Drawable::Extensions* extensions = osg::Drawable::getExtensions(state->getContextID(),true); + bool aquireGPUStats = extensions->isTimerQuerySupported() && stats; + + GLuint queries[2]; + GLint available = 0; + + // Create a query object. + extensions->glGenQueries(2, queries); + + extensions->glBeginQuery(GL_TIME_ELAPSED, queries[0]); + + _sceneView->draw(); + + extensions->glEndQuery(GL_TIME_ELAPSED); + extensions->glBeginQuery(GL_TIME_ELAPSED, queries[1]); + + double availableTime = 0.004; // 4 ms + if (_databasePager.valid()) + { + _databasePager->compileGLObjects(*(_sceneView->getState()), availableTime); + } + _sceneView->flushDeletedGLObjects(availableTime); + + extensions->glEndQuery(GL_TIME_ELAPSED); + + // Wait for all results to become available + while (!available) { + extensions->glGetQueryObjectiv(queries[1], GL_QUERY_RESULT_AVAILABLE, &available); + } + + GLuint64EXT timeElapsed = 0; + extensions->glGetQueryObjectui64v(queries[0], GL_QUERY_RESULT, &timeElapsed); + stats->setAttribute(state->getFrameStamp()->getFrameNumber(), "GPU draw elapsed time", double(timeElapsed)*1e-9); + + extensions->glGetQueryObjectui64v(queries[1], GL_QUERY_RESULT, &timeElapsed); + stats->setAttribute(state->getFrameStamp()->getFrameNumber(), "GPU compile & flush elapsed time", double(timeElapsed)*1e-9); + } - _sceneView->flushDeletedGLObjects(availableTime); + else +#endif + + { + _sceneView->draw(); + + double availableTime = 0.004; // 4 ms + if (_databasePager.valid()) + { + _databasePager->compileGLObjects(*(_sceneView->getState()), availableTime); + } + _sceneView->flushDeletedGLObjects(availableTime); + } + + + } osg::observer_ptr _sceneView; @@ -498,6 +554,8 @@ void Viewer::setUpRenderingSupport() if (_camera.valid() && _camera->getGraphicsContext()) { + _camera->setStats(new osg::Stats("Viewer")); + osgUtil::SceneView* sceneView = new osgUtil::SceneView; _cameraSceneViewMap[_camera] = sceneView; @@ -519,6 +577,8 @@ void Viewer::setUpRenderingSupport() Slave& slave = getSlave(i); if (slave._camera.valid() && slave._camera->getGraphicsContext()) { + _camera->setStats(new osg::Stats("Slave Camera")); + osgUtil::SceneView* sceneView = new osgUtil::SceneView; _cameraSceneViewMap[slave._camera] = sceneView; @@ -626,8 +686,22 @@ void Viewer::advance() { if (_done) return; + double prevousReferenceTime = _frameStamp->getReferenceTime(); + int previousFrameNumber = _frameStamp->getFrameNumber(); + _frameStamp->setReferenceTime( osg::Timer::instance()->delta_s(_startTick, osg::Timer::instance()->tick()) ); _frameStamp->setFrameNumber(_frameStamp->getFrameNumber()+1); + + if (getStats()) + { + // update previous frame stats + double deltaFrameTime = _frameStamp->getReferenceTime() - prevousReferenceTime; + getStats()->setAttribute(previousFrameNumber, "Frame duration", deltaFrameTime); + getStats()->setAttribute(previousFrameNumber, "Frame rate", 1.0/deltaFrameTime); + + // update current frames stats + getStats()->setAttribute(_frameStamp->getFrameNumber(), "Reference time", _frameStamp->getReferenceTime()); + } } void Viewer::eventTraversal()