User comments
This is mainly just a rewording of this forum thread (year 2005) so go there for the full answer.
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).
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.
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