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

View File

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