From Tim Moore, "This patch fixes a race condition in Renderer::ThreadSafeQueue that was causing some notifications of available SceneView objects to be missed. I saw a very noticeable performance problem (60 fps -> 8 fps) in DrawThreadPerContext mode in an osgEarth application before this patch. I had high hopes that this change might fix the much-discussed multiple GPU problem; no such luck, but I think the root cause of that is probably a similar threading issue."

This commit is contained in:
Robert Osfield 2011-01-11 16:58:17 +00:00
parent 88987d194a
commit e4b1b6228d
2 changed files with 41 additions and 20 deletions

View File

@ -14,6 +14,7 @@
#ifndef OSGVIEWER_RENDERER
#define OSGVIEWER_RENDERER 1
#include <OpenThreads/Condition>
#include <osg/Timer>
#include <osgDB/DatabasePager>
#include <osgUtil/SceneView>
@ -89,20 +90,21 @@ class OSGVIEWER_EXPORT Renderer : public osg::GraphicsOperation
struct OSGVIEWER_EXPORT ThreadSafeQueue
{
OpenThreads::Mutex _mutex;
OpenThreads::Block _block;
OpenThreads::Condition _cond;
typedef std::list<osgUtil::SceneView*> SceneViewList;
SceneViewList _queue;
bool _isReleased;
ThreadSafeQueue();
~ThreadSafeQueue();
void release()
{
_block.release();
}
/** Release any thread waiting on the queue, even if the queue is empty. */
void release();
/** Take a SceneView from the queue. Can return 0 if release() is called when the queue is empty. */
osgUtil::SceneView* takeFront();
/** Add a SceneView object to the back of the queue. */
void add(osgUtil::SceneView* sv);
};

View File

@ -289,34 +289,53 @@ void ARBQuerySupport::checkQuery(osg::Stats* stats, osg::State* state,
// ThreadSafeQueue
Renderer::ThreadSafeQueue::ThreadSafeQueue()
: _isReleased(false)
{
_block.set(false);
}
Renderer::ThreadSafeQueue::~ThreadSafeQueue()
{
}
void Renderer::ThreadSafeQueue::release()
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
_isReleased = true;
_cond.broadcast();
}
osgUtil::SceneView* Renderer::ThreadSafeQueue::takeFront()
{
if (_queue.empty()) _block.block();
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
if (_queue.empty()) return 0;
osgUtil::SceneView* front = _queue.front();
_queue.pop_front();
if (_queue.empty()) _block.set(false);
return front;
// Loop in case there are spurious wakeups from the condition wait.
while (true)
{
// If the queue has been released but nothing is enqueued,
// just return. This prevents a deadlock when threading is
// restarted.
if (_isReleased)
{
if (!_queue.empty())
{
osgUtil::SceneView* front = _queue.front();
_queue.pop_front();
if (_queue.empty())
_isReleased = false;
return front;
}
return 0;
}
_cond.wait(&_mutex);
}
return 0; // Can't happen
}
void Renderer::ThreadSafeQueue::add(osgUtil::SceneView* sv)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
_queue.push_back(sv);
_block.set(true);
_isReleased = true;
_cond.broadcast();
}
static OpenThreads::Mutex s_drawSerializerMutex;