【发布时间】:2019-11-04 15:48:59
【问题描述】:
我正在开发一个使用 Direct3D 和 WinAPI 的 3D 编辑器应用程序。我创建了一个主窗口,它有一个占据主窗口客户区一部分的子窗口。这被 D3D 用作渲染目标。然后,我还使用CreateWindow 创建了一个单独的窗口,它的构建方式与主窗口相同(即“主窗口”和用作渲染目标的内部子窗口),我将此窗口设为主窗口的子窗口应用程序窗口(以确保它们一起最小化/恢复/关闭)。
D3D 渲染由处理其WM_PAINT 消息的渲染目标子窗口执行。为了减少不必要的开销,我将窗口过程设置为仅在 GetForegroundWindow 和 GetFocus 匹配各自的窗口句柄时在 WM_PAINT 上呈现。换句话说,我只希望一个窗口的渲染在它位于顶部并获得焦点时被刷新。
这是我的主要消息循环:
HWND mainWnd;
HWND mainRenderWnd;
HWND childWnd;
HWND childRenderWnd;
// ...
MSG msg = {};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
if (!TranslateAccelerator(mainWnd, accel, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// Run non-UI code...
}
}
当主窗口获得WM_SETFOCUS 时,我让它将焦点设置到其渲染目标子窗口,因为我想在那里处理输入(例如相机控件):
// ...
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
//...
case WM_SETFOCUS:
{
SetFocus(mainRenderWnd);
return 0;
}
//...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
我在childWnd 的窗口过程中做同样的事情,将焦点设置为childRenderWnd。此窗口由用户打开和关闭,即在任何时候它可能存在也可能不存在,但是当它存在且未最小化时,它需要是具有焦点的前景窗口。另外,为了控制子窗口的帧率,我使用 Timer 来刷新它:
static constexpr UINT_PTR RENDER_TIMER_ID = (UINT_PTR)0x200;
void TimerCallback(HWND Arg1, UINT Arg2, UINT_PTR Arg3, DWORD Arg4)
{
if (IsIconic(childWnd) || !(GetFocus() == childRenderWnd))
return;
// Invalidate the render area to make sure it gets redrawn
InvalidateRect(childRenderWnd, nullptr, false);
}
// ...
SetTimer(childWnd, RENDER_TIMER_ID, 16, (TIMERPROC)TimerCallback);
完成所有这些设置后,mainWnd 和 mainRenderWnd 似乎工作得很好。然而,childRenderWnd 拒绝在它处于前景和焦点时对其进行渲染。在调试时,我发现虽然是这种情况,但定时器回调永远不会被执行,WM_TIMER 消息也不会被分派到子窗口。
另一方面,当我故意将焦点从子窗口移到主窗口上时(同时保持两者都打开),定时器消息被发送,回调被执行。另一个问题是,当我在两个窗口都打开时最小化应用程序,然后恢复它们,两个窗口的渲染目标都没有刷新。相反,似乎焦点被“翻转”了,因为我必须先点击我的子窗口,然后点击我的主窗口,这使它正确刷新(而孩子仍然拒绝渲染任何东西)。
我错过了什么?我搜索了其他有问题的人,例如不正确的消息泵设置阻止 WM_TIMER,但似乎没有什么可以解释这里发生了什么。
提前感谢您的帮助!
【问题讨论】:
-
(请注意,您的
TimerCallback调用约定不匹配;删除(TIMERPROC)演员以显示它。)计时器消息的优先级较低。如果队列中始终存在非定时器消息,则将处理该非定时器消息,并且定时器消息将永远不会出现。 -
@RaymondChen 那么如何解决呢?拥有这样一个功能并没有多大意义,只是在 UI 发生其他任何事情时它会被渲染为不可用。
-
我们需要看到minimal reproducible example。您发布的那部分代码没有表现出您描述的行为。
-
UI 计时器的优先级较低,因为该应用现在可能正忙于做更重要的事情。当应用程序最终准备好做其他事情时,计时器将运行。 UI 线程通常大部分时间都处于空闲状态,因此计时器延迟通常很短。如果您设计了一个通常持续忙碌的 UI 线程,那么您可以使用不同的计时器机制,例如线程池计时器。
-
我找出了问题所在,并使用相关细节编辑了我的问题。谢谢你们的cmets!
标签: c++ windows winapi timer rendering