Ogre線程渲染,渲染設備無法恢復窗口停止響應的案例及解決辦法

問題比較簡單:

我在主線程Main中創建了WIN32窗口,並初始化好了Ogre相關資源。獨立開啓一個線程Render進行Ogre的渲染以及場景掛接、移除等渲染循環操作。

正常情況下不會有任何問題,但是一旦我們改變了WIN32窗口的大小時,這時候會觸發Ogre重置渲染設備,但是重置失敗了,渲染循環也就崩潰了....

跟着Ogre源代碼到,bool D3D9Device::reset()裏面發現是調用hr = mDevice->Reset(mPresentationParams);時返回了一個錯誤的值,也就是說在非創建窗口消息循環的線程調用void WorkSearchContext::Reset(VirtualProcessor *pVirtualProcessor, Algorithm algorithm)時沒有正常執行(這裏有位是Release也就沒有深入跟蹤調試)

試過很多種改變窗口style和styleEx的方法,都沒用。這裏還要說明一下,使用FireBreath控件封裝好的WIN32窗口不存在之類的問題,這說明問題原因不是出自Ogre本身渲染部分,而是WIN32窗口默認不支持在其他線程修改渲染設備(個人猜測)。

至於具體解決方案,暫時還沒有找到,這裏記錄一下問題!


2013.11.21 15:37 臨時解決辦法

void RenderWindow::WindowMoveOrResize()
{
//暫停渲染
UGlobe::Base::MessageSender::SendRenderMessage(UGlobe::Base::PAUSE_RENDER);

//暫時用renderOneFrame來恢復渲染設備,以後需要進一步改進
//用於在異步線程渲染的時候重置渲染設備
RenderEngine::GetRenderEngine().GetRoot()->renderOneFrame(10);

//恢復渲染
UGlobe::Base::MessageSender::SendRenderMessage(UGlobe::Base::RECOVER_RENDER);
}

在創建窗口的Main線程裏面監聽窗口Resize消息,獲得消息後立馬暫停渲染線程的渲染。此時主線程進行一幀渲染,在這一幀裏面,D3D Reset被正常執行了,也就是設備正常恢復了,然後恢復渲染線程的渲染。

但是這樣造成了一個新的問題,就是主線程【暫停——渲染一幀——恢復】這個過程可能會產生線程互斥上的問題,而我的確遇到了(雖然概率很小)

雖然這樣不能作爲最終定論,但是這裏給了我一個解決辦法的思路,就是窗口改變大小的時候,暫停渲染線程,讓主線程手動調用恢復設備。。。然後繼續渲染

————接來下繼續探索完美的解決方案!


2013.11.22 10:00

經過半天的探究結果:結合MSDN對D3D Device的解釋【http://msdn.microsoft.com/en-us/library/microsoft.windowsmobile.directx.direct3d.device.reset(v=vs.85).aspx   “A call to Reset fails if made on a different thread than the one used to create the device being reset.”】,我們必須把創建渲染設備,也就是renderwindow放在渲染線程裏面。

解決方式:將創建renderwindow的過程封裝成一個即時任務發送到渲染線程,主線程必須等待任務做完再返回創建好的RenderWindow*指針,即同步

下面驗證結果!


2013.11.22  11:35

代碼:

第一步:

boost::condition_variable_any cond_callback_complete;//定義條件變量

boost::mutex muCreate; //定義互斥鎖


第二步:

boost::mutex::scoped_lock lock(muCreate);//鎖住互斥鎖
proxyPtr->PostTask(FROM_HERE,
boost::bind(&RenderWindow::InitRenderTask,this));//向渲染線程發送Create渲染設備任務
cond_callback_complete.wait(muCreate);//等待上訴任務執行完成的信號


第三步:

void RenderWindow::InitRenderTask()//向渲染線程發送的任務函數
{

......//創建渲染設備相關操作

......

cond_callback_complete.notify_one();//發出創建完成信號,讓主線程wait結束

}

通過以上三步,已經完美解決了上訴一系列問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章