如何使用WinAPI每帧只重新绘制一次子窗口



我正在使用Direct3D和WinAPI在客户端区域创建窗口和渲染3D对象。我使用标准的Windows消息循环使渲染窗口的矩形无效,在渲染窗口的消息处理程序中,当处理WM_PAINT消息时,我在Direct3D中执行渲染调用:

BOOL bRet; 
HWND mainWnd; // Main window
HWND renderWnd; // Child of mainWnd, takes up a portion of the client area 
// to be used as a Direct3D render target
MSG msg = {};
while ((bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if(bRet != -1)
{
TranslateMessage(&msg); 
DispatchMessage(&msg);
InvalidateRect(renderWnd, nullptr, false);
}
} 
// ...
// Window procedure used by renderWnd
LRESULT renderWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// ...
case WM_PAINT:
{
// Perform Direct3D rendering
// ...
}
break;
// ...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

只要我的应用程序只有一个窗口,这个设置似乎就可以正常工作,因为每帧都会调用InvalidateRect,并确保客户端区域需要重新绘制,这反过来会导致Direct3D绘图调用。不过,这感觉不太优雅,尤其是当我试图创建多个窗口时,因为我必须在同一段代码中使它们的矩形无效,即使这些窗口所提供的功能彼此无关(有些窗口甚至可能在任何时候都不存在(。

也就是说,我的问题是:是否有可能让一个窗口使其客户端区域的一部分在每帧中恰好失效一次(假设它目前没有最小化,等等(?也许是通过使用消息队列?回到上面的代码段:我想让mainWnd(可能在它自己的窗口过程中(每帧在renderWnd上调用InvalidateRect一次。

提前感谢!

EDIT:代码示例中的小错误

请注意,处理此问题的"标准"方法更像这样:

// Main message loop
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Update your simulation/animation/etc. based on elapsed time
//   -or-
// multiple fixed time-steps.
//
// Then render one frame.
}
}
…
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;

该模型是一个"平面"渲染模型,在该模型中,您将以系统所能做到的最快速度进行渲染,但受Present细节的限制,如刷新率和在准备好3帧或更多帧时自动调节。

您应该查看GitHub以了解更多细节,如实现WM_ENTERSIZEMOVE/WM_EXITSIZEMOVE

实现WM_PAINT/InvalidateRect进行渲染的唯一时间是在执行编辑器或某些"混合"UI和Direct3D应用程序时。

因此,在原始帖子评论的帮助下,我发现最好的选择是使用WinAPI提供的Timers,以所需的帧速率重新绘制渲染窗口。将此标记为已解决。

最新更新