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

Reply via email to