diff --git a/include/osg/GraphicsThread b/include/osg/GraphicsThread new file mode 100644 index 000000000..193da8e10 --- /dev/null +++ b/include/osg/GraphicsThread @@ -0,0 +1,136 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 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_GRAPHICSTHREAD +#define OSG_GRAPHICSTHREAD 1 + +#include +#include +#include +#include + +#include + +namespace osg { + +// forward declare GraphicsContext +class GraphicsContext; + +class Block: public osg::Referenced { + public: + Block():_released(false) {} + + inline void block() + { + OpenThreads::ScopedLock mutlock(_mut); + if( !_released ) + _cond.wait(&_mut); + } + + inline void release() + { + OpenThreads::ScopedLock mutlock(_mut); + if (!_released) + { + _released = true; + _cond.broadcast(); + } + } + + inline void reset() + { + OpenThreads::ScopedLock mutlock(_mut); + _released = false; + } + + inline void set(bool doRelease) + { + if (doRelease!=_released) + { + if (doRelease) release(); + else reset(); + } + } + + protected: + + ~Block() + { + release(); + } + + private: + OpenThreads::Mutex _mut; + OpenThreads::Condition _cond; + bool _released; +}; + +/** GraphicsThread is a helper class for running OpenGL GraphicsOperation within a single thread assigned to a specific GraphicsContext.*/ +class GraphicsThread : public Referenced, public OpenThreads::Thread +{ + public: + GraphicsThread(): _graphicsContext(0) {} + + + /** Base class for implementing GraphicsThread operations.*/ + struct OSG_EXPORT Operation : public Referenced + { + virtual void operator () (GraphicsContext*) {} + }; + + /** Add operation to end of OperationQueue, this will be + * executed by the graphics thread once this operation gets to the head of the queue.*/ + void add(Operation* operation, bool waitForCompletion=false); + + virtual void run(); + + protected: + + virtual ~GraphicsThread(); + + friend class GraphicsContext; + GraphicsContext* _graphicsContext; + + typedef std::list< ref_ptr > OperationQueue; + OpenThreads::Mutex _operationsMutex; + OperationQueue _operations; + +}; + + +/** SwapBufferOperation calls swap buffers on the GraphicsContext.*/ +struct OSG_EXPORT SwapBufferOperation : public GraphicsThread::Operation +{ + virtual void operator () (GraphicsContext* context); +}; + +/** BarrierOperation allows one syncronize multiple GraphicsThreads with each other.*/ +struct OSG_EXPORT BarrierOperation : public GraphicsThread::Operation, public OpenThreads::Barrier +{ + BarrierOperation(int numThreads=0): OpenThreads::Barrier(numThreads) {} + + virtual void operator () (GraphicsContext* context); +}; + +/** ReleaseContext_Block_MakeCurrentOperation releases the context for another thread to aquire, + * then blocks waiting for context to be released, once the block is release the context is re-aqquired.*/ +struct OSG_EXPORT ReleaseContext_Block_MakeCurrentOperation : public GraphicsThread::Operation, public Block +{ + ReleaseContext_Block_MakeCurrentOperation() {} + + virtual void operator () (GraphicsContext* context); +}; + +} + +#endif diff --git a/src/osg/GraphicsThread.cpp b/src/osg/GraphicsThread.cpp new file mode 100644 index 000000000..415b63172 --- /dev/null +++ b/src/osg/GraphicsThread.cpp @@ -0,0 +1,124 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 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 +#include + +using namespace osg; + + +GraphicsThread::~GraphicsThread() +{ +} + +void GraphicsThread::add(Operation* operation, bool waitForCompletion) +{ + osg::BarrierOperation* barrier = 0; + + { + // aquire the lock on the operations queue to prevent anyone else for modifying it at the same time + OpenThreads::ScopedLock lock(_operationsMutex); + + // add the operation to the end of the list + _operations.push_back(operation); + + if (waitForCompletion) + { + barrier = new BarrierOperation(2); + _operations.push_back(barrier); + } + } + + if (barrier) + { + // now we wait till the barrier is joined by the graphics thread. + barrier->block(); + } +} + +void GraphicsThread::run() +{ + // make the graphics context current. + if (_graphicsContext) _graphicsContext->makeCurrent(); + + bool firstTime = false; + + bool _done = false; + + do + { + ref_ptr operation; + + // get the front of the file request list. + { + OpenThreads::ScopedLock lock(_operationsMutex); + if (!_operations.empty()) + { + // get the front the queue + operation = _operations.front(); + + // remove it from the opeations queue + _operations.erase(_operations.begin()); + } + + } + + if (operation.valid()) + { + // call the graphics operation. + (*operation)(_graphicsContext); + } + + if (firstTime) + { + // do a yield to get round a peculiar thread hang when testCancel() is called + // in certain cirumstances - of which there is no particular pattern. + YieldCurrentThread(); + firstTime = false; + } + + } while (!testCancel() && !_done); + + // release the graphics context so that others can aquire it. + if (_graphicsContext) _graphicsContext->releaseContext(); + +} + +void SwapBufferOperation::operator () (GraphicsContext* context) +{ + if (context) context->swapBuffers(); +} + +void BarrierOperation::operator () (GraphicsContext*) +{ + block(); +} + +void ReleaseContext_Block_MakeCurrentOperation::operator () (GraphicsContext* context) +{ + if (!context) return; + + // release the graphics context. + context->releaseContext(); + + // reset the block so that it the next call to block() + reset(); + + // block this thread, untill the block is released externally. + block(); + + // re aquire the graphcis context. + context->makeCurrent(); +}