【问题标题】:GetMessage() while the thread is blocked in SwapBuffers()当线程在 SwapBuffers() 中被阻塞时 GetMessage()
【发布时间】:2014-05-19 20:27:03
【问题描述】:

Vsync 会阻塞 SwapBuffers(),这正是我想要的。我的问题是,由于输入消息进入拥有窗口的同一个线程,因此在 SwapBuffers() 被阻止时进入的任何消息都不会立即处理,但只有在 vsync 触发缓冲区交换并且 SwapBuffers() 返回之后.所以我的所有计算线程都处于空闲状态,而不是使用最新的输入处理场景以在下一帧中渲染。我特别关心具有非常低的延迟。我需要一些方法来从其他线程访问所有待处理的输入消息到窗口。

Windows API 提供了一种使用 MsgWaitForMultipleObjects() 来等待 Windows 事件或输入消息的方法,但没有类似的方法可以与其他东西一起等待缓冲区交换。太可惜了。

我考虑在另一个线程中调用 SwapBuffers(),但这需要在窗口的线程中调用 glFinish(),然后再向 SwapBuffers() 发出另一个线程的信号,并且 glFinish() 仍然是一个阻塞调用,所以它不是一个好的解决方案.

我考虑过挂钩,但这看起来也像死路一条。与 WH_GETMESSAGE 挂钩将不会异步调用 GetMsgProc(),但是当窗口的线程调用 GetMessage()/PeekMessage() 时,它没有帮助。由于需要使用特定的窗口句柄调用 RegisterTouchWindow() 来处理 WM_TOUCH,因此安装全局钩子对我也没有帮助——而且我的输入是触摸。而且,对于鼠标和键盘,您可以安装低级钩子,在消息发布到线程队列时捕获消息,而不是在线程调用 GetMessage()/PeekMessage() 时,似乎没有类似的触摸选项.

我还查看了 wglDelayBeforeSwapNV(),但我看不出是什么阻止了操作系统有时在调用该函数之后但在 SwapBuffers() 之前抢占线程,从而导致错过下一个 vsync 信号。

那么什么是好的解决方法?我可以制作第二个不可见的窗口,它会以某种方式始终处于活动状态,从而获取所有输入消息,而可见的窗口正在显示渲染?根据另一个讨论,仅消息窗口(带有 HWND_MESSAGE 的 CreateWindow)与 WM_TOUCH 不兼容。是否有一些 SwapBuffers() 在内部等待我可以访问并提供给 MsgWaitForMultipleObjects() 的未记录事件?我的目标是一个固定平台(Windows 8.1 64 位),所以我可以使用未记录的功能,如果它存在的话。不过,我确实想避免编写自己的触摸屏驱动程序。

【问题讨论】:

    标签: windows multithreading winapi opengl message-queue


    【解决方案1】:

    出于好奇,为什么不在另一个线程中实现整个绘图逻辑呢?您遇到的问题似乎是消息泵是由绘制的同一线程驱动的。由于 Windows 不允许您从与创建窗口的线程不同的线程驱动消息泵,因此最简单的解决方案是将所有 GL 内容推送到不同的线程。

    SwapBuffers (...) 也不一定会阻止。根据 VSYNC 的要求,实现只需要在 所有 后备缓冲区等待交换时阻止将修改后备缓冲区的 下一个命令。三重缓冲通过引入第二个后缓冲来稍微改变一下。

    三重缓冲的一种可能实现将在交换时丢弃最旧的后备缓冲区,因此SwapBuffers (...) 永远不会导致阻塞(这实际上是现代版本的 Windows 在windowed 启用 DWM 的模式)。其他实现最终将同时呈现两个后缓冲区,这减少(但不会消除)阻塞,但也会导致延迟帧的显示。

    不幸的是,WGL 不允许您请求交换链中的后缓冲数量(超出 0 单缓冲或 1 双缓冲);在 Windows 上获得三重缓冲的唯一方法是使用驱动程序设置。最低的消息延迟将来自于在不同的线程中驱动 GL,但三重缓冲可以提供一点帮助,同时无需您付出任何努力。

    【讨论】:

    • 如果我在没有窗口的情况下创建 OpenGL 上下文,在另一个线程上,我无法显示它(除非我在屏幕外渲染然后将其复制到窗口,这显然会挫败减少延迟的尝试)。 (另外,我已经通过驱动程序设置使用三重缓冲。)
    • 多个上下文何时进入讨论?我错过了什么吗?
    • 我的错误。第一句话不相关,所以我删除了它。
    • 您可以轻松地从与创建窗口的线程不同的线程中进行绘制。您所要做的就是通过调用wglMakeCurrent (NULL,NULL) 从创建窗口/上下文的线程中放弃上下文。然后,一旦您的 GL 工作线程启动,您就可以进行适当的 wglMakeCurrent (...) 调用并从该线程执行所有 GL 命令。 Win32 消息泵实际上是这里创建窗口的线程相关且不可变的唯一地方,这就是为什么我建议将 GL 移到单独的线程中。
    • 安东,这里可能有问题。从opengl.org/discussion_boards/showthread.php/… 我引用:“Nvidia 告诉我这是因为 DeviceContexts 实际上是线程仿射的,GDI 函数只能在创建它们的线程中调用它们。引用 Nvidia 'Calling GDI functions using an HDC from the non -hdc-affine 线程一直是错误的(OpenGL 是 GDI api,而 HDC 等 GDI 对象是线程仿射的),但故障情况通常很难重现。'"
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-25
    • 1970-01-01
    • 2013-11-16
    • 1970-01-01
    • 2021-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多