Hi Robert -- Based on your feedback (thanks), I've reworked this submission.
John tested the attached change and verifies it works for him.
-Paul
On 1/21/2012 10:59 AM, Robert Osfield wrote:
Hi Paul,
I haven't looked at the submission yet but as quick reply it rather
sounded to me like you've missed the capability of the
osg::BarrierOperation to optionally use a glFlush or glFinish.
Changing the barrier operation to use glFinish and selecting it so
that it's before swap would achieve what you are suggesting. The only
essential change to make this possible would be to add a
getEndBarrierOperation() method into ViewerBase.
I do wonder if a hint to osgViewer as to the type of sync to use when
setting up threading would be nice - so the user can select that they
want to ensure swaps are synced.
Robert.
On 20 January 2012 21:28, Paul Martz<[email protected]> wrote:
Hi Robert --
This is a proposed (strawman, really) fix that fell our of the thread "Frame
syncing over multiple contexts" over in osg-users.
It seems like what John really needs is for all draw threads to have
completely executed all of their respective OpenGL commands before they
enter the end barrier. That way, when the final thread enters the end
barrier and all threads are released, all their associated OpenGL contexts
will be idle, thus maximizing the probability of simultaneous execution of
the swap commands.
It seems like the best way to achieve that is to have each thread perform
the following operations:
1. Do all their drawing (aka "runOperations")
2. Issue a glFinish() command.
3. Enter the end barrier
4. Swap.
The attached modified ViewerBase.cpp works for John, but is probably not an
acceptable change. Would you provide us with feedback on how we can make
this change acceptable? (Or, if you like the change as-is, excellent! :-) )
The change is twofold:
1. It causes the existing SwapReadyBarrier to issue a glFinish() command.
2. It also modifies the thread Operation order to ensure that the threads
are ready to swap *before* entering the end barrier.
I'm fairly confident that #2 is the right thing to do, but I fear I've
broken something with #1.
Guidance and advice appreciated, thanks.
--
-Paul Martz Skew Matrix Software
http://www.skew-matrix.com/
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
--
-Paul Martz Skew Matrix Software
http://www.skew-matrix.com/
/* -*-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.
*/
#include <stdlib.h>
#include <string.h>
#include <osgViewer/ViewerBase>
#include <osgViewer/View>
#include <osgViewer/Renderer>
#include <osg/io_utils>
#include <osg/TextureCubeMap>
#include <osg/TextureRectangle>
#include <osg/TexMat>
#include <osg/DeleteHandler>
#include <osgUtil/Optimizer>
#include <osgUtil/IntersectionVisitor>
#include <osgUtil/Statistics>
static osg::ApplicationUsageProxy
ViewerBase_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_CONFIG_FILE
<filename>","Specify a viewer configuration file to load by default.");
static osg::ApplicationUsageProxy
ViewerBase_e1(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_THREADING
<value>","Set the threading model using by Viewer, <value> can be
SingleThreaded, CullDrawThreadPerContext, DrawThreadPerContext or
CullThreadPerCameraDrawThreadPerContext.");
static osg::ApplicationUsageProxy
ViewerBase_e2(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_SCREEN
<value>","Set the default screen that windows should open up on.");
static osg::ApplicationUsageProxy
ViewerBase_e3(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_WINDOW x y
width height","Set the default window dimensions that windows should open up
on.");
static osg::ApplicationUsageProxy
ViewerBase_e4(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_RUN_FRAME_SCHEME","Frame
rate manage scheme that viewer run should use, ON_DEMAND or CONTINUOUS
(default).");
static osg::ApplicationUsageProxy
ViewerBase_e5(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_RUN_MAX_FRAME_RATE","Set
the maximum number of frame as second that viewer run. 0.0 is default and
disables an frame rate capping.");
using namespace osgViewer;
ViewerBase::ViewerBase():
osg::Object(true)
{
viewerBaseInit();
}
ViewerBase::ViewerBase(const ViewerBase&):
osg::Object(true)
{
viewerBaseInit();
}
void ViewerBase::viewerBaseInit()
{
_firstFrame = true;
_done = false;
_keyEventSetsDone = osgGA::GUIEventAdapter::KEY_Escape;
_quitEventSetsDone = true;
_releaseContextAtEndOfFrameHint = true;
_threadingModel = AutomaticSelection;
_threadsRunning = false;
_endBarrierPosition = AfterSwapBuffers;
_endBarrierOperation = osg::BarrierOperation::NO_OPERATION;
_requestRedraw = true;
_requestContinousUpdate = false;
_runFrameScheme = CONTINUOUS;
_runMaxFrameRate = 0.0f;
const char* str = getenv("OSG_RUN_FRAME_SCHEME");
if (str)
{
if (strcmp(str, "ON_DEMAND")==0) _runFrameScheme = ON_DEMAND;
else if (strcmp(str, "CONTINUOUS")==0) _runFrameScheme = CONTINUOUS;
}
str = getenv("OSG_RUN_MAX_FRAME_RATE");
if (str)
{
_runMaxFrameRate = osg::asciiToDouble(str);
}
}
void ViewerBase::setThreadingModel(ThreadingModel threadingModel)
{
if (_threadingModel == threadingModel) return;
if (_threadsRunning) stopThreading();
_threadingModel = threadingModel;
if (isRealized() && _threadingModel!=SingleThreaded) startThreading();
}
ViewerBase::ThreadingModel ViewerBase::suggestBestThreadingModel()
{
const char* str = getenv("OSG_THREADING");
if (str)
{
if (strcmp(str,"SingleThreaded")==0) return SingleThreaded;
else if (strcmp(str,"CullDrawThreadPerContext")==0) return
CullDrawThreadPerContext;
else if (strcmp(str,"DrawThreadPerContext")==0) return
DrawThreadPerContext;
else if (strcmp(str,"CullThreadPerCameraDrawThreadPerContext")==0)
return CullThreadPerCameraDrawThreadPerContext;
}
Contexts contexts;
getContexts(contexts);
if (contexts.empty()) return SingleThreaded;
#if 0
// temporary hack to disable multi-threading under Windows till we find
good solutions for
// crashes that users are seeing.
return SingleThreaded;
#endif
Cameras cameras;
getCameras(cameras);
if (cameras.empty()) return SingleThreaded;
int numProcessors = OpenThreads::GetNumberOfProcessors();
if (contexts.size()==1)
{
if (numProcessors==1) return SingleThreaded;
else return DrawThreadPerContext;
}
#if 1
if (numProcessors >= static_cast<int>(cameras.size()+contexts.size()))
{
return CullThreadPerCameraDrawThreadPerContext;
}
#endif
return DrawThreadPerContext;
}
void ViewerBase::setUpThreading()
{
Contexts contexts;
getContexts(contexts);
if (_threadingModel==SingleThreaded)
{
if (_threadsRunning) stopThreading();
else
{
// we'll set processor affinity here to help single threaded apps
// with multiple processor cores, and using the database pager.
int numProcessors = OpenThreads::GetNumberOfProcessors();
bool affinity = numProcessors>1;
if (affinity)
{
OpenThreads::SetProcessorAffinityOfCurrentThread(0);
Scenes scenes;
getScenes(scenes);
}
}
}
else
{
if (!_threadsRunning) startThreading();
}
}
void ViewerBase::setEndBarrierPosition(BarrierPosition bp)
{
if (_endBarrierPosition == bp) return;
if (_threadsRunning) stopThreading();
_endBarrierPosition = bp;
if (_threadingModel!=SingleThreaded) startThreading();
}
void ViewerBase::stopThreading()
{
if (!_threadsRunning) return;
OSG_INFO<<"ViewerBase::stopThreading() - stopping threading"<<std::endl;
Contexts contexts;
getContexts(contexts);
Cameras cameras;
getCameras(cameras);
Contexts::iterator gcitr;
Cameras::iterator citr;
for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer) renderer->release();
}
// delete all the graphics threads.
for(gcitr = contexts.begin();
gcitr != contexts.end();
++gcitr)
{
(*gcitr)->setGraphicsThread(0);
}
// delete all the camera threads.
for(citr = cameras.begin();
citr != cameras.end();
++citr)
{
(*citr)->setCameraThread(0);
}
for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer)
{
renderer->setGraphicsThreadDoesCull( true );
renderer->setDone(false);
}
}
_threadsRunning = false;
_startRenderingBarrier = 0;
_endRenderingDispatchBarrier = 0;
_endDynamicDrawBlock = 0;
OSG_INFO<<"Viewer::stopThreading() - stopped threading."<<std::endl;
}
void ViewerBase::startThreading()
{
if (_threadsRunning) return;
OSG_INFO<<"Viewer::startThreading() - starting threading"<<std::endl;
// release any context held by the main thread.
releaseContext();
_threadingModel = _threadingModel==AutomaticSelection ?
suggestBestThreadingModel() : _threadingModel;
Contexts contexts;
getContexts(contexts);
OSG_INFO<<"Viewer::startThreading() -
contexts.size()="<<contexts.size()<<std::endl;
Cameras cameras;
getCameras(cameras);
unsigned int numThreadsOnStartBarrier = 0;
unsigned int numThreadsOnEndBarrier = 0;
switch(_threadingModel)
{
case(SingleThreaded):
numThreadsOnStartBarrier = 1;
numThreadsOnEndBarrier = 1;
return;
case(CullDrawThreadPerContext):
numThreadsOnStartBarrier = contexts.size()+1;
numThreadsOnEndBarrier = contexts.size()+1;
break;
case(DrawThreadPerContext):
numThreadsOnStartBarrier = 1;
numThreadsOnEndBarrier = 1;
break;
case(CullThreadPerCameraDrawThreadPerContext):
numThreadsOnStartBarrier = cameras.size()+1;
numThreadsOnEndBarrier = 1;
break;
default:
OSG_NOTICE<<"Error: Threading model not selected"<<std::endl;
return;
}
// using multi-threading so make sure that new objects are allocated with
thread safe ref/unref
osg::Referenced::setThreadSafeReferenceCounting(true);
Scenes scenes;
getScenes(scenes);
for(Scenes::iterator scitr = scenes.begin();
scitr != scenes.end();
++scitr)
{
if ((*scitr)->getSceneData())
{
OSG_INFO<<"Making scene thread safe"<<std::endl;
// make sure that existing scene graph objects are allocated with
thread safe ref/unref
(*scitr)->getSceneData()->setThreadSafeRefUnref(true);
// update the scene graph so that it has enough GL object buffer
memory for the graphics contexts that will be using it.
(*scitr)->getSceneData()->resizeGLObjectBuffers(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts());
}
}
int numProcessors = OpenThreads::GetNumberOfProcessors();
bool affinity = numProcessors>1;
Contexts::iterator citr;
unsigned int numViewerDoubleBufferedRenderingOperation = 0;
bool graphicsThreadsDoesCull = _threadingModel == CullDrawThreadPerContext
|| _threadingModel==SingleThreaded;
for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer)
{
renderer->setGraphicsThreadDoesCull(graphicsThreadsDoesCull);
renderer->setDone(false);
++numViewerDoubleBufferedRenderingOperation;
}
}
if (_threadingModel==CullDrawThreadPerContext)
{
_startRenderingBarrier = 0;
_endRenderingDispatchBarrier = 0;
_endDynamicDrawBlock = 0;
}
else if (_threadingModel==DrawThreadPerContext ||
_threadingModel==CullThreadPerCameraDrawThreadPerContext)
{
_startRenderingBarrier = 0;
_endRenderingDispatchBarrier = 0;
_endDynamicDrawBlock = new
osg::EndOfDynamicDrawBlock(numViewerDoubleBufferedRenderingOperation);
#ifndef OSGUTIL_RENDERBACKEND_USE_REF_PTR
if (!osg::Referenced::getDeleteHandler())
osg::Referenced::setDeleteHandler(new osg::DeleteHandler(2));
else
osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(2);
#endif
}
if (numThreadsOnStartBarrier>1)
{
_startRenderingBarrier = new
osg::BarrierOperation(numThreadsOnStartBarrier,
osg::BarrierOperation::NO_OPERATION);
}
if (numThreadsOnEndBarrier>1)
{
_endRenderingDispatchBarrier = new
osg::BarrierOperation(numThreadsOnEndBarrier, _endBarrierOperation);
}
osg::ref_ptr<osg::BarrierOperation> swapReadyBarrier = contexts.empty() ? 0
: new osg::BarrierOperation(contexts.size(),
osg::BarrierOperation::NO_OPERATION);
osg::ref_ptr<osg::SwapBuffersOperation> swapOp = new
osg::SwapBuffersOperation();
typedef std::map<OpenThreads::Thread*, int> ThreadAffinityMap;
ThreadAffinityMap threadAffinityMap;
unsigned int processNum = 1;
for(citr = contexts.begin();
citr != contexts.end();
++citr, ++processNum)
{
osg::GraphicsContext* gc = (*citr);
if (!gc->isRealized())
{
OSG_INFO<<"ViewerBase::startThreading() : Realizng window
"<<gc<<std::endl;
gc->realize();
}
gc->getState()->setDynamicObjectRenderingCompletedCallback(_endDynamicDrawBlock.get());
// create the a graphics thread for this context
gc->createGraphicsThread();
if (affinity) gc->getGraphicsThread()->setProcessorAffinity(processNum
% numProcessors);
threadAffinityMap[gc->getGraphicsThread()] = processNum % numProcessors;
// add the startRenderingBarrier
if (_threadingModel==CullDrawThreadPerContext &&
_startRenderingBarrier.valid())
gc->getGraphicsThread()->add(_startRenderingBarrier.get());
// add the rendering operation itself.
gc->getGraphicsThread()->add(new osg::RunOperations());
if (_threadingModel==CullDrawThreadPerContext &&
_endBarrierPosition==BeforeSwapBuffers && _endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
}
if (swapReadyBarrier.valid())
gc->getGraphicsThread()->add(swapReadyBarrier.get());
// add the swap buffers
gc->getGraphicsThread()->add(swapOp.get());
if (_threadingModel==CullDrawThreadPerContext &&
_endBarrierPosition==AfterSwapBuffers && _endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
}
}
if (_threadingModel==CullThreadPerCameraDrawThreadPerContext &&
numThreadsOnStartBarrier>1)
{
Cameras::iterator camItr;
for(camItr = cameras.begin();
camItr != cameras.end();
++camItr, ++processNum)
{
osg::Camera* camera = *camItr;
camera->createCameraThread();
if (affinity)
camera->getCameraThread()->setProcessorAffinity(processNum % numProcessors);
threadAffinityMap[camera->getCameraThread()] = processNum %
numProcessors;
osg::GraphicsContext* gc = camera->getGraphicsContext();
// add the startRenderingBarrier
if (_startRenderingBarrier.valid())
camera->getCameraThread()->add(_startRenderingBarrier.get());
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
renderer->setGraphicsThreadDoesCull(false);
camera->getCameraThread()->add(renderer);
if (_endRenderingDispatchBarrier.valid())
{
// add the endRenderingDispatchBarrier
gc->getGraphicsThread()->add(_endRenderingDispatchBarrier.get());
}
}
for(camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
if (camera->getCameraThread() &&
!camera->getCameraThread()->isRunning())
{
OSG_INFO<<" camera->getCameraThread()->
"<<camera->getCameraThread()<<std::endl;
camera->getCameraThread()->startThread();
}
}
}
#if 0
if (affinity)
{
OpenThreads::SetProcessorAffinityOfCurrentThread(0);
if (_scene.valid() && _scene->getDatabasePager())
{
#if 0
_scene->getDatabasePager()->setProcessorAffinity(1);
#else
_scene->getDatabasePager()->setProcessorAffinity(0);
#endif
}
}
#endif
#if 0
if (affinity)
{
for(ThreadAffinityMap::iterator titr = threadAffinityMap.begin();
titr != threadAffinityMap.end();
++titr)
{
titr->first->setProcessorAffinity(titr->second);
}
}
#endif
for(citr = contexts.begin();
citr != contexts.end();
++citr)
{
osg::GraphicsContext* gc = (*citr);
if (gc->getGraphicsThread() && !gc->getGraphicsThread()->isRunning())
{
OSG_INFO<<" gc->getGraphicsThread()->startThread()
"<<gc->getGraphicsThread()<<std::endl;
gc->getGraphicsThread()->startThread();
// OpenThreads::Thread::YieldCurrentThread();
}
}
_threadsRunning = true;
OSG_INFO<<"Set up threading"<<std::endl;
}
void ViewerBase::getWindows(Windows& windows, bool onlyValid)
{
windows.clear();
Contexts contexts;
getContexts(contexts, onlyValid);
for(Contexts::iterator itr = contexts.begin();
itr != contexts.end();
++itr)
{
osgViewer::GraphicsWindow* gw =
dynamic_cast<osgViewer::GraphicsWindow*>(*itr);
if (gw) windows.push_back(gw);
}
}
void ViewerBase::checkWindowStatus()
{
Contexts contexts;
getContexts(contexts);
checkWindowStatus(contexts);
}
void ViewerBase::checkWindowStatus(const Contexts& contexts)
{
if (contexts.size()==0)
{
_done = true;
if (areThreadsRunning()) stopThreading();
}
}
void ViewerBase::addUpdateOperation(osg::Operation* operation)
{
if (!operation) return;
if (!_updateOperations) _updateOperations = new osg::OperationQueue;
_updateOperations->add(operation);
}
void ViewerBase::removeUpdateOperation(osg::Operation* operation)
{
if (!operation) return;
if (_updateOperations.valid())
{
_updateOperations->remove(operation);
}
}
void
ViewerBase::setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation*
ico)
{
if (_incrementalCompileOperation == ico) return;
Contexts contexts;
getContexts(contexts, false);
if (_incrementalCompileOperation.valid())
_incrementalCompileOperation->removeContexts(contexts);
// assign new operation
_incrementalCompileOperation = ico;
Scenes scenes;
getScenes(scenes,false);
for(Scenes::iterator itr = scenes.begin();
itr != scenes.end();
++itr)
{
osgDB::DatabasePager* dp = (*itr)->getDatabasePager();
dp->setIncrementalCompileOperation(ico);
}
if (_incrementalCompileOperation)
_incrementalCompileOperation->assignContexts(contexts);
}
int ViewerBase::run()
{
if (!isRealized())
{
realize();
}
const char* run_frame_count_str = getenv("OSG_RUN_FRAME_COUNT");
unsigned int runTillFrameNumber = run_frame_count_str==0 ?
osg::UNINITIALIZED_FRAME_NUMBER : atoi(run_frame_count_str);
while(!done() && (run_frame_count_str==0 ||
getViewerFrameStamp()->getFrameNumber()<runTillFrameNumber))
{
double minFrameTime = _runMaxFrameRate>0.0 ? 1.0/_runMaxFrameRate : 0.0;
osg::Timer_t startFrameTick = osg::Timer::instance()->tick();
if (_runFrameScheme==ON_DEMAND)
{
if (checkNeedToDoFrame())
{
frame();
}
else
{
// we don't need to render a frame but we don't want to spin
the run loop so make sure the minimum
// loop time is 1/100th of second, if not otherwise set, so
enabling the frame microSleep below to
// avoid consume excessive CPU resources.
if (minFrameTime==0.0) minFrameTime=0.01;
}
}
else
{
frame();
}
// work out if we need to force a sleep to hold back the frame rate
osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
double frameTime = osg::Timer::instance()->delta_s(startFrameTick,
endFrameTick);
if (frameTime < minFrameTime)
OpenThreads::Thread::microSleep(static_cast<unsigned
int>(1000000.0*(minFrameTime-frameTime)));
}
return 0;
}
void ViewerBase::frame(double simulationTime)
{
if (_done) return;
// OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl;
if (_firstFrame)
{
viewerInit();
if (!isRealized())
{
realize();
}
_firstFrame = false;
}
advance(simulationTime);
eventTraversal();
updateTraversal();
renderingTraversals();
}
void ViewerBase::renderingTraversals()
{
bool _outputMasterCameraLocation = false;
if (_outputMasterCameraLocation)
{
Views views;
getViews(views);
for(Views::iterator itr = views.begin();
itr != views.end();
++itr)
{
osgViewer::View* view = *itr;
if (view)
{
const osg::Matrixd& m =
view->getCamera()->getInverseViewMatrix();
OSG_NOTICE<<"View "<<view<<", Master Camera
position("<<m.getTrans()<<"), rotation("<<m.getRotate()<<")"<<std::endl;
}
}
}
Contexts contexts;
getContexts(contexts);
// check to see if windows are still valid
checkWindowStatus(contexts);
if (_done) return;
double beginRenderingTraversals = elapsedTime();
osg::FrameStamp* frameStamp = getViewerFrameStamp();
if (getViewerStats() && getViewerStats()->collectStats("scene"))
{
unsigned int frameNumber = frameStamp ? frameStamp->getFrameNumber() :
0;
Views views;
getViews(views);
for(Views::iterator vitr = views.begin();
vitr != views.end();
++vitr)
{
View* view = *vitr;
osg::Stats* stats = view->getStats();
osg::Node* sceneRoot = view->getSceneData();
if (sceneRoot && stats)
{
osgUtil::StatsVisitor statsVisitor;
sceneRoot->accept(statsVisitor);
statsVisitor.totalUpStats();
unsigned int unique_primitives = 0;
osgUtil::Statistics::PrimitiveCountMap::iterator pcmitr;
for(pcmitr = statsVisitor._uniqueStats.GetPrimitivesBegin();
pcmitr != statsVisitor._uniqueStats.GetPrimitivesEnd();
++pcmitr)
{
unique_primitives += pcmitr->second;
}
stats->setAttribute(frameNumber, "Number of unique StateSet",
static_cast<double>(statsVisitor._statesetSet.size()));
stats->setAttribute(frameNumber, "Number of unique Group",
static_cast<double>(statsVisitor._groupSet.size()));
stats->setAttribute(frameNumber, "Number of unique Transform",
static_cast<double>(statsVisitor._transformSet.size()));
stats->setAttribute(frameNumber, "Number of unique LOD",
static_cast<double>(statsVisitor._lodSet.size()));
stats->setAttribute(frameNumber, "Number of unique Switch",
static_cast<double>(statsVisitor._switchSet.size()));
stats->setAttribute(frameNumber, "Number of unique Geode",
static_cast<double>(statsVisitor._geodeSet.size()));
stats->setAttribute(frameNumber, "Number of unique Drawable",
static_cast<double>(statsVisitor._drawableSet.size()));
stats->setAttribute(frameNumber, "Number of unique Geometry",
static_cast<double>(statsVisitor._geometrySet.size()));
stats->setAttribute(frameNumber, "Number of unique Vertices",
static_cast<double>(statsVisitor._uniqueStats._vertexCount));
stats->setAttribute(frameNumber, "Number of unique Primitives",
static_cast<double>(unique_primitives));
unsigned int instanced_primitives = 0;
for(pcmitr = statsVisitor._instancedStats.GetPrimitivesBegin();
pcmitr != statsVisitor._instancedStats.GetPrimitivesEnd();
++pcmitr)
{
instanced_primitives += pcmitr->second;
}
stats->setAttribute(frameNumber, "Number of instanced
Stateset", static_cast<double>(statsVisitor._numInstancedStateSet));
stats->setAttribute(frameNumber, "Number of instanced Group",
static_cast<double>(statsVisitor._numInstancedGroup));
stats->setAttribute(frameNumber, "Number of instanced
Transform", static_cast<double>(statsVisitor._numInstancedTransform));
stats->setAttribute(frameNumber, "Number of instanced LOD",
static_cast<double>(statsVisitor._numInstancedLOD));
stats->setAttribute(frameNumber, "Number of instanced Switch",
static_cast<double>(statsVisitor._numInstancedSwitch));
stats->setAttribute(frameNumber, "Number of instanced Geode",
static_cast<double>(statsVisitor._numInstancedGeode));
stats->setAttribute(frameNumber, "Number of instanced
Drawable", static_cast<double>(statsVisitor._numInstancedDrawable));
stats->setAttribute(frameNumber, "Number of instanced
Geometry", static_cast<double>(statsVisitor._numInstancedGeometry));
stats->setAttribute(frameNumber, "Number of instanced
Vertices", static_cast<double>(statsVisitor._instancedStats._vertexCount));
stats->setAttribute(frameNumber, "Number of instanced
Primitives", static_cast<double>(instanced_primitives));
}
}
}
Scenes scenes;
getScenes(scenes);
for(Scenes::iterator sitr = scenes.begin();
sitr != scenes.end();
++sitr)
{
Scene* scene = *sitr;
osgDB::DatabasePager* dp = scene ? scene->getDatabasePager() : 0;
if (dp)
{
dp->signalBeginFrame(frameStamp);
}
if (scene->getSceneData())
{
// fire off a build of the bounding volumes while we
// are still running single threaded.
scene->getSceneData()->getBound();
}
}
// OSG_NOTICE<<std::endl<<"Start frame"<<std::endl;
Cameras cameras;
getCameras(cameras);
Contexts::iterator itr;
bool doneMakeCurrentInThisThread = false;
if (_endDynamicDrawBlock.valid())
{
_endDynamicDrawBlock->reset();
}
// dispatch the rendering threads
if (_startRenderingBarrier.valid()) _startRenderingBarrier->block();
// reset any double buffer graphics objects
for(Cameras::iterator camItr = cameras.begin();
camItr != cameras.end();
++camItr)
{
osg::Camera* camera = *camItr;
Renderer* renderer = dynamic_cast<Renderer*>(camera->getRenderer());
if (renderer)
{
if (!renderer->getGraphicsThreadDoesCull() &&
!(camera->getCameraThread()))
{
renderer->cull();
}
}
}
for(itr = contexts.begin();
itr != contexts.end();
++itr)
{
if (_done) return;
if (!((*itr)->getGraphicsThread()) && (*itr)->valid())
{
doneMakeCurrentInThisThread = true;
makeCurrent(*itr);
(*itr)->runOperations();
}
}
// OSG_NOTICE<<"Joing _endRenderingDispatchBarrier block
"<<_endRenderingDispatchBarrier.get()<<std::endl;
// wait till the rendering dispatch is done.
if (_endRenderingDispatchBarrier.valid())
_endRenderingDispatchBarrier->block();
for(itr = contexts.begin();
itr != contexts.end();
++itr)
{
if (_done) return;
if (!((*itr)->getGraphicsThread()) && (*itr)->valid())
{
doneMakeCurrentInThisThread = true;
makeCurrent(*itr);
(*itr)->swapBuffers();
}
}
for(Scenes::iterator sitr = scenes.begin();
sitr != scenes.end();
++sitr)
{
Scene* scene = *sitr;
osgDB::DatabasePager* dp = scene ? scene->getDatabasePager() : 0;
if (dp)
{
dp->signalEndFrame();
}
}
// wait till the dynamic draw is complete.
if (_endDynamicDrawBlock.valid())
{
// osg::Timer_t startTick = osg::Timer::instance()->tick();
_endDynamicDrawBlock->block();
// OSG_NOTICE<<"Time waiting
"<<osg::Timer::instance()->delta_m(startTick,
osg::Timer::instance()->tick())<<std::endl;;
}
if (_releaseContextAtEndOfFrameHint && doneMakeCurrentInThisThread)
{
//OSG_NOTICE<<"Doing release context"<<std::endl;
releaseContext();
}
if (getViewerStats() && getViewerStats()->collectStats("update"))
{
double endRenderingTraversals = elapsedTime();
// update current frames stats
getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering
traversals begin time ", beginRenderingTraversals);
getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering
traversals end time ", endRenderingTraversals);
getViewerStats()->setAttribute(frameStamp->getFrameNumber(), "Rendering
traversals time taken", endRenderingTraversals-beginRenderingTraversals);
}
_requestRedraw = false;
}
/* -*-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.
*/
#ifndef OSGVIEWER_VIEWERBASE
#define OSGVIEWER_VIEWERBASE 1
#include <osg/Stats>
#include <osgUtil/UpdateVisitor>
#include <osgUtil/IncrementalCompileOperation>
#include <osgGA/EventVisitor>
#include <osgGA/EventQueue>
#include <osgViewer/Scene>
#include <osgViewer/GraphicsWindow>
namespace osgViewer {
#define USE_REFERENCE_TIME DBL_MAX
class View;
/** ViewerBase is the view base class that is inherited by both Viewer and
CompositeViewer.*/
class OSGVIEWER_EXPORT ViewerBase : public virtual osg::Object
{
public:
ViewerBase();
ViewerBase(const ViewerBase& vb);
/** Set the Stats object used for collect various frame related timing
and scene graph stats.*/
virtual void setViewerStats(osg::Stats* stats) = 0;
/** Get the Viewers Stats object.*/
virtual osg::Stats* getViewerStats() = 0;
/** Get the Viewers Stats object.*/
virtual const osg::Stats* getViewerStats() const = 0;
/** read the viewer configuration from a configuration file.*/
virtual bool readConfiguration(const std::string& filename) = 0;
/** Get whether at least of one of this viewers windows are realized.*/
virtual bool isRealized() const = 0;
/** set up windows and associated threads.*/
virtual void realize() = 0;
enum ThreadingModel
{
SingleThreaded,
CullDrawThreadPerContext,
ThreadPerContext = CullDrawThreadPerContext,
DrawThreadPerContext,
CullThreadPerCameraDrawThreadPerContext,
ThreadPerCamera = CullThreadPerCameraDrawThreadPerContext,
AutomaticSelection
};
/** Set the threading model the rendering traversals will use.*/
virtual void setThreadingModel(ThreadingModel threadingModel);
/** Get the threading model the rendering traversals will use.*/
ThreadingModel getThreadingModel() const { return _threadingModel; }
/** Let the viewer suggest the best threading model for the viewers
camera/window setup and the hardware available.*/
virtual ThreadingModel suggestBestThreadingModel();
/** Set up the threading and processor affinity as per the viewers
threading model.*/
virtual void setUpThreading();
/** Return true if viewer threads are running. */
bool areThreadsRunning() const { return _threadsRunning; }
/** Stop any threads begin run by viewer.*/
virtual void stopThreading();
/** Start any threads required by the viewer.*/
virtual void startThreading();
enum BarrierPosition
{
BeforeSwapBuffers,
AfterSwapBuffers
};
/** Set the position of the end barrier.
* AfterSwapBuffers may result in slightly higher framerates, but may
* lead to inconsistent swapping between different windows.
* BeforeSwapBuffers may lead to slightly lower framerate, but improve
consistency in timing of swap buffers,
* especially important if you are likely to consistently break
frame.*/
void setEndBarrierPosition(BarrierPosition bp);
/** Get the end barrier position.*/
BarrierPosition getEndBarrierPosition() const { return
_endBarrierPosition; }
/** Set the end barrier operation. \c op may be one of GL_FLUSH,
GL_FINISH,
* or NO_OPERATION. NO_OPERATION is the default. Per
BarrierOperation::operator()(),
* a glFlush() command, glFinish() command, or no additional OpenGL
command will be
* issued before entering the end barrier. */
void setEndBarrierOperation(const osg::BarrierOperation::PreBlockOp&
op) { _endBarrierOperation = op; }
/** Get the end barrier operation. */
osg::BarrierOperation::PreBlockOp getEndBarrierOperation() const {
return _endBarrierOperation; }
/** Set the done flag to signal the viewer's work is done and should
exit the frame loop.*/
void setDone(bool done) { _done = done; }
/** Return true if viewer's work is done and should exit the frame
loop.*/
bool done() const { return _done; }
/** Set the EventVisitor. */
void setEventVisitor(osgGA::EventVisitor* eventVisitor) { _eventVisitor
= eventVisitor; }
/** Get the EventVisitor. */
osgGA::EventVisitor* getEventVisitor() { return _eventVisitor.get(); }
/** Get the const EventVisitor. */
const osgGA::EventVisitor* getEventVisitor() const { return
_eventVisitor.get(); }
/** Set the key event that the viewer checks on each frame to see if
the viewer's done flag should be set to
* signal end of viewers main loop.
* Default value is Escape (osgGA::GUIEVentAdapter::KEY_Escape).
* Setting to 0 switches off the feature.*/
void setKeyEventSetsDone(int key) { _keyEventSetsDone = key; }
/** get the key event that the viewer checks on each frame to see if
the viewer's done flag.*/
int getKeyEventSetsDone() const { return _keyEventSetsDone; }
/** if the flag is true, the viewer set its done flag when a
QUIT_APPLICATION is received, false disables this feature */
void setQuitEventSetsDone(bool flag) { _quitEventSetsDone = flag; }
/** @return true if the viewer respond to the QUIT_APPLICATION-event */
bool getQuitEventSetsDone() const { return _quitEventSetsDone; }
/** Hint to tell the renderingTraversals() method whether to call
relaseContext() on the last
* context that was made current by the thread calling
renderingTraverals(). Note, when
* running multi-threaded viewer no threads will be made current or
release current.
* Setting this hint to false can enable the frame loop to be lazy
about calling makeCurrent
* and releaseContext on each new frame, helping performance.
However, if you frame loop
* is managing multiple graphics context all from the main frame
thread then this hint must
* be left on, otherwise the wrong context could be left active,
introducing errors in rendering.*/
void setReleaseContextAtEndOfFrameHint(bool hint) {
_releaseContextAtEndOfFrameHint = hint; }
/** Hint to tell the renderingTraversals() method whether to call
relaseContext().*/
bool getReleaseContextAtEndOfFrameHint() const { return
_releaseContextAtEndOfFrameHint; }
/** Set the UpdateVisitor. */
void setUpdateVisitor(osgUtil::UpdateVisitor* updateVisitor) {
_updateVisitor = updateVisitor; }
/** Get the UpdateVisitor. */
osgUtil::UpdateVisitor* getUpdateVisitor() { return
_updateVisitor.get(); }
/** Get the const UpdateVisitor. */
const osgUtil::UpdateVisitor* getUpdateVisitor() const { return
_updateVisitor.get(); }
/** Set the Update OperationQueue. */
void setUpdateOperations(osg::OperationQueue* operations) {
_updateOperations = operations; }
/** Get the Update OperationQueue. */
osg::OperationQueue* getUpdateOperations() { return
_updateOperations.get(); }
/** Get the const Update OperationQueue. */
const osg::OperationQueue* getUpdateOperations() const { return
_updateOperations.get(); }
/** Add an update operation.*/
void addUpdateOperation(osg::Operation* operation);
/** Remove an update operation.*/
void removeUpdateOperation(osg::Operation* operation);
/** Set the graphics operation to call on realization of the viewers
graphics windows.*/
void setRealizeOperation(osg::Operation* op) { _realizeOperation = op; }
/** Get the graphics operation to call on realization of the viewers
graphics windows.*/
osg::Operation* getRealizeOperation() { return _realizeOperation.get();
}
/** Set the incremental compile operation.
* Used to manage the OpenGL object compilation and merging of
subgraphs in a way that avoids overloading
* the rendering of frame with too many new objects in one frame. */
void
setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico);
/** Get the incremental compile operation. */
osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation()
{ return _incrementalCompileOperation.get(); }
enum FrameScheme
{
ON_DEMAND,
CONTINUOUS
};
void setRunFrameScheme(FrameScheme fs) { _runFrameScheme = fs; }
FrameScheme getRunFrameScheme() const { return _runFrameScheme; }
void setRunMaxFrameRate(double frameRate) { _runMaxFrameRate =
frameRate; }
double getRunMaxFrameRate() const { return _runMaxFrameRate; }
/** Execute a main frame loop.
* Equivalent to while (!viewer.done()) viewer.frame();
* Also calls realize() if the viewer is not already realized,
* and installs trackball manipulator if one is not already assigned.
*/
virtual int run();
/** check to see if the new frame is required, called by run(..) when
FrameScheme is set to ON_DEMAND.*/
virtual bool checkNeedToDoFrame() = 0;
/** Render a complete new frame.
* Calls advance(), eventTraversal(), updateTraversal(),
renderingTraversals(). */
virtual void frame(double simulationTime=USE_REFERENCE_TIME);
virtual void advance(double simulationTime=USE_REFERENCE_TIME) = 0;
virtual void eventTraversal() = 0;
virtual void updateTraversal() = 0;
virtual void renderingTraversals();
typedef std::vector<osg::Camera*> Cameras;
virtual void getCameras(Cameras& cameras, bool onlyActive=true) = 0;
typedef std::vector<osg::GraphicsContext*> Contexts;
virtual void getContexts(Contexts& contexts, bool onlyValid=true) = 0;
typedef std::vector<osgViewer::GraphicsWindow*> Windows;
virtual void getWindows(Windows& windows, bool onlyValid=true);
typedef std::vector<OpenThreads::Thread*> Threads;
virtual void getAllThreads(Threads& threads, bool onlyActive=true) = 0;
typedef std::vector<osg::OperationThread*> OperationThreads;
virtual void getOperationThreads(OperationThreads& threads, bool
onlyActive=true) = 0;
typedef std::vector<osgViewer::Scene*> Scenes;
virtual void getScenes(Scenes& scenes, bool onlyValid=true) = 0;
typedef std::vector<osgViewer::View*> Views;
virtual void getViews(Views& views, bool onlyValid=true) = 0;
/** Check to see if any windows are still open. If not, set viewer done
to true. */
void checkWindowStatus();
/** Check to see if windows are still open using the list of contexts
given as a parameter.
* If no windows are open, stop rendering threads and set viewer done
to true.
* This function is more effective than checkWindowStatus() as it does
not query
* the context list and should be used whenever context list is
already available in your code.*/
void checkWindowStatus(const Contexts& contexts);
virtual double elapsedTime() = 0;
virtual osg::FrameStamp* getViewerFrameStamp() = 0;
/** Get the keyboard and mouse usage of this viewer.*/
virtual void getUsage(osg::ApplicationUsage& usage) const = 0;
protected:
void viewerBaseInit();
friend class osgViewer::View;
inline void makeCurrent(osg::GraphicsContext* gc)
{
if (_currentContext==gc) return;
releaseContext();
if (gc && gc->valid() && gc->makeCurrent()) _currentContext = gc;
}
inline void releaseContext()
{
if (_currentContext.valid() && _currentContext->valid())
{
_currentContext->releaseContext();
}
_currentContext = 0;
}
virtual void viewerInit() = 0;
bool _firstFrame;
bool _done;
int _keyEventSetsDone;
bool _quitEventSetsDone;
bool
_releaseContextAtEndOfFrameHint;
ThreadingModel _threadingModel;
bool _threadsRunning;
bool _requestRedraw;
bool
_requestContinousUpdate;
FrameScheme _runFrameScheme;
double _runMaxFrameRate;
BarrierPosition _endBarrierPosition;
osg::BarrierOperation::PreBlockOp
_endBarrierOperation;
osg::ref_ptr<osg::BarrierOperation>
_startRenderingBarrier;
osg::ref_ptr<osg::BarrierOperation>
_endRenderingDispatchBarrier;
osg::ref_ptr<osg::EndOfDynamicDrawBlock>
_endDynamicDrawBlock;
osg::ref_ptr<osgGA::EventVisitor> _eventVisitor;
osg::ref_ptr<osg::OperationQueue> _updateOperations;
osg::ref_ptr<osgUtil::UpdateVisitor> _updateVisitor;
osg::ref_ptr<osg::Operation> _realizeOperation;
osg::ref_ptr<osgUtil::IncrementalCompileOperation>
_incrementalCompileOperation;
osg::observer_ptr<osg::GraphicsContext> _currentContext;
};
}
#endif
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org