directx中消息循环的基本概念

时间:2013-05-06 14:46:49

标签: 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()。

我认为代码可以更改,如果〜,如果是〜,如果我点击时渲染。

你能解释一下吗?

3 个答案:

答案 0 :(得分:4)

你的理解很接近,但并不完全正确。循环不会每帧运行一次。相反,发生的是,对于循环的每次迭代,要么处理单个消息,要么调用Render。实际上,这会使渲染成为最低优先级,但会使应用程序保持响应。对于绘制的每个帧,循环可以运行多次或几次,具体取决于要做多少工作。

Render会直接调用Present吗?或者它是否使窗口无效?如果它使窗口无效,你不会想要改变为总是像你提到的那样调用Render,因为你可能不会在渲染之间重新绘制窗口。

答案 1 :(得分:2)

本质上,这个循环将处理窗口的任何待处理的Win32消息,如果没有,它将呈现一个帧。如果它看到WM_QUIT消息,则会退出循环以退出应用。

不需要踩油门'因为如果已经有3个待渲染的帧,则DirectX Present将阻塞该线程(即暂停它)。

此模型假设您正在执行一个框架'更新' per' 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次固定步骤更新,然后{ {1}}被调用一次。

答案 2 :(得分:1)

else中的Render()基本上优先处理队列中的消息而不是渲染。将鼠标移到directx渲染窗口上会将消息快速添加到消息队列中,但速度不足以导致渲染延迟到您可以看到的任何程度。每次迭代渲染都没有任何好处,因为迭代发生的速度很多比交换链中生成的每个帧都快,并且比新消息可以淹没队列要快得多。今天的大多数计算机每毫秒都会运行这个循环不止一次,甚至鼠标悬停事件的发生频率也低于此。每次迭代渲染都不会错,它只是不必要的。在运行示例的情况下,尽可能快地将鼠标移到directx窗口上将导致此循环的迭代次数少于10%来处理消息并延迟渲染。

此消息循环尽快执行,无法检测交换链何时准备呈现。 PeekMessage检查队列中是否有消息。如果有,它会处理它,如果没有它渲染。你担心的是一系列窗口事件会导致渲染延迟,但实际上这是不可能的。无论消息发送到队列的速度有多快,交换链的渲染速度都要比60fps快10倍。此循环是CPU利用率高的原因。它的原因可能是简化教程,但因为它本身就是一个复杂的环境。如果您担心延迟帧渲染的消息队列,您可以在单独的线程中修改交换链。

为了提高示例程序的CPU效率,只需添加一个Sleep(8);在Render()例程的底部。这将导致消息处理程序/呈现线程在处理消息的周期和每秒大约120次的呈现之间暂停。您可以通过在循环之间使用高分辨率计时器和基于模数的睡眠来改善这一点。

改善此示例的良好信息来源可以是found here