2013-05-06 58 views
1

嗯,我發現消息循環的奇怪點。directx中消息循環的基本概念

第一,鎖定下面

MSG msg = {0}; 
while(WM_QUIT != msg.message) 
{ 
    if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    else 
    { 
     Render(); // Do some rendering 
    } 
} 

這個代碼是的DirectX的教程,並且該部分是消息循環的一部分。

如果我點擊鼠標,它將作爲消息進入隊列。

所以這樣的輸入應該在win api的proc函數中進行處理。

現在,peekMessage返回true,render()將不會在框架中調用,當我點擊。

我覺得代碼被改變if〜else to if〜if for render when I click。

你能解釋一下嗎?

回答

4

你的理解很接近,但不完全正確。循環不會每幀運行一次。相反,會發生的是,對於循環的每次迭代,處理單個消息或調用Render。有效地,這使渲染成爲最低優先級,但保持應用程序響應。根據需要做多少工作,循環可以針對每個畫框運行多次或幾次。

渲染直接調用存在嗎?還是它使窗口無效?如果它使窗口無效,你不會想要像你所提到的那樣總是調用Render,因爲你不會在渲染之間重繪窗口。

+0

我主要使用Render()。 使用窗口消息系統和directx都不好。 就像你說的那樣,Render()的優先級低於消息。 這是DirectX教程,所以我認爲這個代碼可能無法有效的DirectX。 您對此有何看法? – Rikimaru 2013-05-07 00:59:03

+0

該代碼可以正常工作,並且對於教程而言非常簡單。如果你寫作遊戲結束了,並且你發現你無法保持幀速率,你可以做一些事情,比如將渲染切換到不同的線程,或者在用戶優先級上發佈消息以進行繪製,但爲了教程的目的,這將會沒事的。 – 2013-05-07 13:07:48

1

else中的Render()基本上優先考慮處理隊列中的消息而不是渲染。將鼠標移動到directx渲染窗口上會快速將消息添加到消息隊列中,但速度不夠快,從而導致渲染延遲到您所看到的程度。每次迭代都沒有優勢,因爲迭代發生的次數多於,這比在交換鏈中生成的每個幀快得多,並且比新消息可能使您的隊列更加快速。今天大多數電腦會每毫秒運行一次這個循環,甚至鼠標懸停事件的發生次數也比這少。每次迭代渲染都不會錯,只是沒有必要。在示例運行的情況下,儘可能快地將鼠標移動到directx窗口上將導致此循環的迭代次數少於10%,以處理消息並延遲呈現。

此消息循環儘可能快地執行,無法檢測交換鏈何時準備好呈現。 PeekMessage檢查隊列中是否有消息。如果有,它會處理它,如果沒有,則渲染。你擔心的是一系列窗口事件會導致渲染延遲,但這實際上是不可能的。無論郵件發送到隊列的速度有多快,交換鏈的渲染速度都比它需要的速度快10倍甚至60fps。此循環是CPU利用率高的原因。其原因可能是簡化教程,但因爲它是一個內在複雜的環境。如果您擔心消息隊列延遲幀渲染,則可以在單獨的線程中修改交換鏈。

爲了提高示例程序的CPU效率,只需添加一個Sleep(8);在Render()例程的底部。這將導致消息處理程序/渲染線程在處理消息的週期和每秒約120次渲染的週期之間暫停。您可以通過使用高分辨率定時器和週期之間的基於模數的睡眠來改善此問題。

改進此示例的一個很好的信息來源可以是found here

+0

請注意,這不會使用核心的100%。在Direct3D 11存在的情況下,如果有3個待處理幀,它將阻塞(即使線程進入睡眠狀態)。循環渲染儘可能快,但不會因此鎖定CPU。 – 2015-01-03 23:59:01

+0

奇怪。在Windows 8.1的i7-2600K上,我看到約30%的cpu。在渲染例程中添加一個Sleep(8)將其降至遠低於1%。我會着眼於此。謝謝 – wilsotc 2015-01-04 16:03:15

2

本質上,這個循環將處理任何待處理的窗口的Win32消息,如果沒有,它將渲染一個幀。如果看到WM_QUIT消息,它將退出循環以退出應用程序。

如果已經有3幀待渲染,DirectX Present就會阻塞線程(即掛起它),因此不需要'節流'。

這個模型假定你正在做一個'Render'調用的'更新',這對於一個遊戲來說並不現實,但對於本教程很簡單。與StepTimer擴展教程循環將類似於:

#include 「StepTimer.h 
DX::StepTimer g_timer; 

...

MSG msg = {0}; 
while(WM_QUIT != msg.message) 
{ 
    if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    else 
    { 
     g_timer.Tick([&]() 
     { 
      Update(g_timer); // Update world/game state 
     }); 
     Render(); // Do some rendering 
    } 
} 

...

void Render(); 
void Update(DX::StepTimer& timer); 

StepTimer默認使用變步更新,這意味着Update被稱爲每幀一次,不管時間增量,然後Render被調用一次。

您可以使用一個固定的步更新這樣的(比如說60次),:

g_timer.SetFixedTimeStep(true); 
g_timer.SetTargetElapsedSeconds(1.f/60.f); 

在這種模式下,你將擁有所有未決處理的Win32消息,然後Update被稱爲任時間,以保持平均每秒60個固定步驟更新,然後Render被調用一次。