Threading         Three quotes about threading in Ogre

User comments

This is mainly just a rewording of this forum thread (year 2005) so go there for the full answer.


Sinbad wrote:

You should thread things that naturally fall into the background, ie to be spread over multiple frames. That's AI, buffering of streaming data, but probably not physics (because you need to keep it in sync with the frame rate if it's not going to look weird). None of these things are Ogre itself. Due to the fact that the graphics are the most noticeable thing, and that the GPU is asynchronous anyway, rendering processes are rarely farmed out to the background, so there's generally no need for thread safety. Projects which use threading thread non-graphical things.

The one case where it might be useful is background loading using the resource system. In Azathoth the resource system is (experimentally) thread safe if you set OGRE_THREAD_SUPPORT to 1, allowing you to load things in a background thread if you want. But that's only the resource system, nothing else is thread safe, and in my opinion has no need to be. We use boost::threads for this, personally it seemed a much better solution than anything else I've seen (it reminds me of Java threads).


AlphaBeta wrote:

Keep in mind you don't need threads to run systems at varying rates (keep countdowns, if time is up step the system, if not move on).
If you're using ogre for a game threading isn't really necessary (like sinbad said, graphics are already asynchronous).

For things like physics, multi-steps are often done (instead of updating the physics simulation 30 milliseconds, you might have it do 15 milliseconds 2 times. Physics simulations get less accurate with bigger updates, which doesn't apply to most other systems).

You wouldn't want to simply run a physics update multiple times for each graphics one, you should work it out with timers and countdowns.


QSilver wrote:

I should point out that even though Ogre is not thread-safe, there is no reason why your application cannot use threads. It just means that you cannot have multiple threads which deal with Ogre.

As long as you make sure that only a single thread handles all your Ogre-related tasks, you are free to run your sound engine or AI scripts in other threads.

Example

This example is based on the Tutorial Framework and displays the ogre head. The orientation of the head is calculated in a new thread using boost. The result is used in main thread to rotate the head. This example is working since OGRE 1.9.
TutorialApplication.h

/*
-----------------------------------------------------------------------------
Filename:    TutorialApplication.h
-----------------------------------------------------------------------------

This source file is part of the
   ___                 __    __ _ _    _ 
  /___\__ _ _ __ ___  / / /\ \ (_) | _(_)
 //  // _` | '__/ _ \ \ \/  \/ / | |/ / |
/ \_// (_| | | |  __/  \  /\  /| |   <| |
\___/ \__, |_|  \___|   \/  \/ |_|_|\_\_|
	  |___/                              
	  Tutorial Framework
	  http://www.ogre3d.org/tikiwiki/
-----------------------------------------------------------------------------
*/
#ifndef __TutorialApplication_h_
#define __TutorialApplication_h_

#include "BaseApplication.h"

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/shared_ptr.hpp>
 
#include <boost/random/linear_congruential.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/generator_iterator.hpp>

class TutorialApplication : public BaseApplication
{
private:
	boost::shared_ptr<boost::thread> mThread;
	Ogre::SceneNode* mHeadNode;
	Ogre::Real mAngle;
	boost::mutex mMutex;

public:
	TutorialApplication(void);
	virtual ~TutorialApplication(void);

protected:
	virtual void createScene(void);
	virtual void destroyScene(void);

	virtual bool frameStarted(const Ogre::FrameEvent& evt);

private:
	void runThread();
};

#endif // #ifndef __TutorialApplication_h_

TutorialApplication.cpp

/*
-----------------------------------------------------------------------------
Filename:    TutorialApplication.cpp
-----------------------------------------------------------------------------

This source file is part of the
   ___                 __    __ _ _    _ 
  /___\__ _ _ __ ___  / / /\ \ (_) | _(_)
 //  // _` | '__/ _ \ \ \/  \/ / | |/ / |
/ \_// (_| | | |  __/  \  /\  /| |   <| |
\___/ \__, |_|  \___|   \/  \/ |_|_|\_\_|
	  |___/                              
	  Tutorial Framework
	  http://www.ogre3d.org/tikiwiki/
-----------------------------------------------------------------------------
*/
#include "TutorialApplication.h"

//-------------------------------------------------------------------------------------
TutorialApplication::TutorialApplication(void)
	: mHeadNode(NULL)
	, mAngle(0.0f)
{
}

//-------------------------------------------------------------------------------------
TutorialApplication::~TutorialApplication(void)
{
}

//-------------------------------------------------------------------------------------
void TutorialApplication::createScene(void)
{
	// Set the scene's ambient light
	mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5f, 0.5f, 0.5f));
 
	// Create an Entity
	Ogre::Entity* ogreHead = mSceneMgr->createEntity("ogrehead.mesh");
 
	// Create a SceneNode and attach the Entity to it
#if OGRE_VERSION >= ((2 << 16) | (0 << 8) | 0)
	mHeadNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
#else
	mHeadNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("HeadNode");
#endif
	mHeadNode->attachObject(ogreHead);
 
	// Create a Light and set its position
#if OGRE_VERSION >= ((2 << 16) | (0 << 8) | 0)
	Ogre::Light* light = mSceneMgr->createLight();
	Ogre::SceneNode* lightNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(Ogre::SCENE_DYNAMIC, Ogre::Vector3(20.0f, 80.0f, 50.0f));
	lightNode->attachObject(light);
#else
	Ogre::Light* light = mSceneMgr->createLight("MainLight");
	light->setPosition(20.0f, 80.0f, 50.0f);
#endif
 
	// Create the thread and start work
	assert(!mThread);
	mThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&TutorialApplication::runThread, this)));
}

//-------------------------------------------------------------------------------------
void TutorialApplication::destroyScene(void)
{
	// Stops the thread
	assert(mThread);
	mThread->join();
 
	// Destroy scene
	BaseApplication::destroyScene();
}

//-------------------------------------------------------------------------------------
bool TutorialApplication::frameStarted(const Ogre::FrameEvent& evt)
{
	// Lock mutex
	boost::unique_lock<boost::mutex> lock(mMutex);

	// Update Orientation
	mHeadNode->setOrientation(Ogre::Quaternion(Ogre::Radian(mAngle), Ogre::Vector3::UNIT_Y));

	return true;
} // Unlock mutex

//-------------------------------------------------------------------------------------
void TutorialApplication::runThread()
{
	while(!mShutDown)
	{
		// Lock mutex
		boost::unique_lock<boost::mutex>* lock = new boost::unique_lock<boost::mutex>(mMutex);

		// Update data
		mAngle += 0.005f;

		// Unlock mutex
		delete lock;

		// Wait
		boost::this_thread::sleep(boost::posix_time::milliseconds(25));
	}
}

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
	INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
	int main(int argc, char *argv[])
#endif
	{
		// Create application object
		TutorialApplication app;

		try
		{
			app.go();
		}
		catch( Ogre::Exception& e )
		{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
			MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
			std::cerr << "An exception has occured: " << e.getFullDescription().c_str() << std::endl;
#endif
		}

		return 0;
	}

#ifdef __cplusplus
}
#endif