【问题标题】:Win32 message queue gets flooded when rendering with OpenGL使用 OpenGL 渲染时,Win32 消息队列被淹没
【发布时间】:2017-10-30 03:58:55
【问题描述】:

我将 Windows API 的 CreateWindow 封装在我称为 GLWindow 的类周围,我使用 OpenGL 将图像渲染到该类。 我花了一些时间,我什至封装了消息队列,所以我的main函数基本上是这样的:

int main()
{
    GLWindow win;
    win.create("OpenGL Window", false, 1600, 800, -1, -1, true);
    win.hideCursor();
    win.moveMouseToCenter();

    init(); //initialize some opengl stuff

    bool quit = false;
    WindowEvent ev;

    while (!quit) {
        if (win.pollEvent(ev))
        {
            if (ev.type == ev.WindowClosed || (ev.type == ev.KeyboardKeyPressed && ev.keyboardKey.key == Keyboard::Key::Escape))
                quit = true;
            else
                handleEvent(ev);
        }

        updateCamera(&win);

        render(&win);
    }

    return 0;
}

render 函数显然占用了最多时间,它导致窗口过程函数被消息淹没,最终导致 WM_KEYDOWNWM_MOUSEMOVE 消息延迟,因此相机更新是也延迟了。 (内updateCamera()

知道我能做什么吗?

【问题讨论】:

  • 您可以将if (win.pollEvent(ev)) 更改为while (win.pollEvent(ev)),这样您就可以处理上一帧中生成的所有条消息。
  • 它会导致窗口过程函数被消息淹没” - 这听起来不对...
  • 为什么这被否决了?这似乎是一个完全合理的问题。 SO有什么问题?似乎很多人(显然?)害怕发布答案,而是使用评论部分提出解决方案或可能的解决方案?
  • @Manius:猜测一下,因为它不包含minimal reproducible example。尽管如此,核心问题还是很明显的,而且 - 无论如何对我来说 - 这个问题看起来比其他许多问题更有价值。
  • @Manius:我不同意这一点。生成minimal reproducible example 是一项艰苦的工作,但这并不意味着不应该这样做。这就是为什么 Stack Overflow 是一个如此宝贵的资源的部分原因:独立的问题,它讲述了整个故事,而不仅仅是 OP 认为相关的部分(这通常是一个错误的评估)。但是,对于任何进行过任何实时渲染的人以及寻求帮助的人来说,这个问题都有足够的信息来回答。尽管如此,它还是一个异常值,而不是常态。

标签: c++ windows winapi opengl


【解决方案1】:

问题是,您的代码在渲染帧之间处理单个消息。相反,它应该耗尽整个消息队列。这可以像在消息轮询表达式中将 if 替换为 while 一样简单。 break 立即退出循环可能也是一个好主意,以防您遇到终止条件。

while (!quit) {
    while (win.pollEvent(ev))
    {
        if (ev.type == ev.WindowClosed || (ev.type == ev.KeyboardKeyPressed && ev.keyboardKey.key == Keyboard::Key::Escape))
        {
            quit = true;
            break;
        }
        else
            handleEvent(ev);
    }

    updateCamera(&win);

    render(&win);
}

【讨论】:

    【解决方案2】:

    基本上,您的 OOP 抽象是垃圾。而且,正如 cmets 指出的那样,您还没有将其包括在内。

    win.pollEvent(ev) 强烈暗示您仅轮询单个窗口的事件。这是错误的——消息循环必须为线程上的所有窗口泵送消息。

    它也完全不清楚 handleEvent() 做了什么 - 我希望它调用 TranslateMessage / DispatchMessage 以确保消息到达您的 GLWindows 窗口过程。

    所以现在,我们看不到您的 WindowProc。它是否处理 WM_PAINT ?它处理得当吗?消息中包含updateCamerarender 意味着您可能没有正确处理您的绘画要求。虽然这是一种用于实现高帧率渲染的模式,但编写一个正确泵送消息的游戏循环很困难,而且您可能会更好(目前)让窗口在计时器上更新,并通过触发来进行绘画WM_PAINT 消息通过调用 InvalidateRect。无论如何,您必须正确处理窗口大小调整、最小化和被其他窗口覆盖。

    【讨论】:

    • “消息循环必须为线程上的所有窗口泵送消息” - 加上未发布到任何窗口的消息,即线程消息。
    • TranslateMessage / DispatchMessage 在 win.pollEvent() 中处理。 handleEvent() 只是 main.cpp 中的一个本地函数,我在其中进行测试。为什么我需要处理 WM_PAINT?不这样做我一直做得很好。
    • @Pilpel:当窗口的一部分被揭开时,系统将使其无效。这会触发WM_PAINT 消息的生成。如果您的WM_PAINT 处理程序未验证无效区域(例如,通过调用BeginPaintValidateRect),系统将继续触发WM_PAINT 消息的生成。不过,情况并没有听起来那么糟糕:WM_PAINT 消息实际上并没有发布到消息队列中,而是按需生成的,因此队列不会被 WM_PAINT 消息填满。
    • 它不会填满它们,但它会继续生成它们,而不是获取任何排队的消息。 @Pilpel - 改进抽象的第 1 步可能是添加一个 GLThread 类来处理诸如消息循环处理之类的线程事物。
    • @Pilpel:详细信息记录在案:The WM_PAINT Message。虽然这与本机窗口(OpenGL 渲染上下文附加到该窗口)有关,但它通常在连续渲染整个场景的代码中被忽略。当被遮挡的部分变得可见或调整窗口大小时,本机窗口仍然会失效,这会触发WM_PAINT 消息的生成。在您的设置中,不验证无效区域的唯一不利影响是每帧有一条多余的 WM_PAINT 消息,该消息将传递给消息处理程序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-28
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    • 2015-08-12
    • 1970-01-01
    • 2018-05-28
    相关资源
    最近更新 更多