酸奶过期3天还能喝吗:Ogre的多執行緒體驗

来源:百度文库 编辑:九乡新闻网 时间:2024/07/08 21:57:42

Ogre的多執行緒體驗

個人一直很欣賞multithread的程式寫作方式,可以在跑程式的時候,多出幾個人執行緒分別負責不同的事情。當然,多點人手自然就要多花精神去管理,天下沒有白吃的午餐。隨著CPU朝向多核心發展,多執行緒的程式架構在未來勢必是個重點。這篇體驗,主要說明如何讓Ogre能支援多執行緒,在進行rendering的時候同時進行模型的處理與計算(載入模型、改變幾何結構等等)。如果是關心利用多執行緒的方式描繪場景,或改善畫面更新率(frame rate),這篇多執行緒的OGRE render system有較專門的說明。

要進行模型的計算,幹麼不放在像是FrameListener裡,或是寫個自行render場景的步驟來處理,反而要利用多執行緒的方式來撰寫,不但架構複雜,甚至可能因為過度使用同步機制造成效能下降?Well, 如果只是放在FrameListener之中,不但白白浪費了多核心的資源,也不會有本篇了啦!事實上,Ogre已經有multithread的支援,允許使用者多開幾個執行緒來處理事情。不過,如果只是快樂的用下載的SDK,是沒辦法享受到這個優點的。要讓Ogre支援多執行緒,得從編譯函式庫開始,底下是大略的步驟:

  1. 下載 Ogre 1.6.1 source code
  2. 下載 Ogre precompiled dependencies
  3. 下載 boost library (Boost DateTime and Boost Thread)
  4. 定義 OGRE_THREAD_SUPPORT 並編譯 Ogre 函式庫

前兩個步驟可以在Ogre官網上下載(http://www.ogre3d.org/download/source)。Precompiled dependencies的部份,主要是Ogre編譯時需要的函式庫,包括CEGUI, FreeImage, OIS, zlib等。根據使用的編譯環境,須下載正確的對應版本,或是自己去找這些library的source code來編譯也可以。第三個步驟,從boostpro下載boost library。Ogre透過boost函式庫來支援多執行緒,編譯時至少需要Boost DateTime與Boost Thread兩個函式庫。以上三個步驟準備完成後,就可以打開Ogre的專案進行編譯的步驟。為了方便說明,Ogre source code所在的位置是 /ogre,而precompiled dependencies的部份則直接解壓縮在 /ogre 之中(要包含資料夾喔)。

從/ogre 開啟VC方案後,先打開OgreMain專案的OgreConfig.h,在127行之前加入

1 #define OGRE_THREAD_SUPPORT 1  (转载注:在OgreResourceBackgroundQueue.cpp的头文件包含中加入#include

儲存,並確定boost library的路徑設定後就可以開始編譯了。執行Ogre的函式庫,至少需要OgreMain與RenderSystem_Direct3D9 (or _GL) 才行,可以視需求編譯其他的Plugin。編譯完成後,DLL檔與implicit linking library會在 /ogre/lib 中。在使用支援多執行緒的Ogre函式庫時,別忘記定義OGRE_THREAD_SUPPORT,boost library也是需要的。

Ogre支援多執行緒的方式有兩種,分別是定義’OGRE_THREAD_SUPPORT 1′與’OGRE_THREAD_SUPPORT 2′。根據說明,OGRE_THREAD_SUPPORT=1 表示資源的載入(模型等等)與建構是在背景執行緒完成。若是2,表示只有資源載入是在背景執行緒,而資源的建構(包含仰賴RenderSystem的部份)則必須在主執行緒完成。

那,這樣就好了嗎?還沒耶~~還有一個很難在API參考文件中找到的mutex : sceneGraphMutex,定義在OgreSceneManager.h 中。這個sceneGraphMutex是SceneManager為了保護 scene graph 使用的mutex,舉凡動到 scene node 的動作,都會需要將mutex先上鎖,操作完後再解鎖,避免多執行緒的衝突。

1 { 2 ... 3 mSceneManager->sceneGraphMutex.lock(); 4 // creating, modifying or deleting a scene node, or attaching / detaching objects 5 mSceneManager->sceneGraphMutex.unlock(); 6 ... 7 }

不過,OGRE_THREAD_SUPPORT也僅支援了背景載入資源的能力(這…好像還少了點什麼)。如果想要用其他的執行緒來協同運作,並且有可能動到rendering API(或與RenderSystem相關的物件)的話,RenderSystem有提供幾個函式來支援:

  • RenderSystem::preExtraThreadsStarted()
  • RenderSystem::postExtraThreadsStarte()
  • RenderSystem::registerThread()
  • RenderSystem::unregisterThrea()

RenderSystem的說明解釋了上面四個函式的使用時機。假設要產生一個新的執行緒A,則主要執行緒P需要以下面特定的程序來執行:

  1. [執行緒 P] 呼叫 preExtraThreadsStarted()
  2. [執行緒 P] 產生新的執行緒 A 並等待
  3. [執行緒 A] 呼叫 registerThread(),接著通知執行緒 P 可以繼續
  4. [執行緒 P] 收到執行緒 A 的通知,繼續呼叫 postExtraThreadsStarted()
  5. [執行緒 A] 要結束時,呼叫unregisterThrea()

也就是說,registerThread() 是由新產生的執行緒來呼叫,而且要在主執行緒呼叫 preExtraThreadsStarted() 與 postExtraThreadsStarted() 之間。每產生一個新的執行緒,就必須按照上述的步驟初始化(如果該執行緒需要用到RenderSystem相關的物件的話)。

寫到這裡,Ogre對多執行緒的支援完整嗎?答案恐怕是否定的。到目前為止測試的結果發現,從其他執行緒讀取模型、操作Ogre中的SceneNode, Entity物件以及內部相關資料等等都沒問題。但是……在產生ManualObject以及加入scene的時候,仍然有碰到莫名的掛點狀況,這得花些時間來釐清才知道問題出在哪裡。此外,說明文件貧乏實在是最大的痛苦,以上的體驗還要感謝Ogre Forum上相關的討論串,才得以完成。如果有這方面相關的經驗,也歡迎指教。

p.s. 上述產生ManualObject然後加入scene時會掛點的情況,目前僅在RenderSystem為OpenGL時發生;改用D3D9就沒有這個問題。