0

我正在尝试使用 glew 和 glfw 创建示例应用程序。主循环很简单,看起来像:

 while (running) {
    someUsefullMathHere();
    renderer->render(timeDelta);

    glfwSwapBuffers(window);
    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}

问题是由于 vsync 当前线程在 glfwSwapBuffers 执行中休眠(fps 限制为 60 fps)。我正在寻找一种方法来利用这段时间来连续执行 someUsefullMath 方法。理想情况下,代码必须类似于:

while (running) {
    while (!timeToRenderAndSwap()) {
        someUsefullMathHere();
    }
    renderer->render(timeDelta);

    glfwSwapBuffers(window);
    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}

有办法吗?

4

3 回答 3

3

现代 GL 实现通常不会在 上阻塞SwapBuffers,即使 VSync 已打开。他们将提前为几帧排队 GL 命令,并且仅在达到SwapBuffers特定于实现的排队帧数限制后才阻塞(顺便说一句,nvidia 的 Windows 驱动程序对此有明确的设置)。在您绘制的未修改渲染循环中,这会导致循环的前两到五次迭代通常不会阻塞,而随后的每个迭代都会阻塞。

使用相当现代的 GL,您可以使用同步对象来改善这种情况。在伪代码中,这可能如下所示:

GLsync fence=NULL;
while (running) {
    if (fence)
        while (glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0)==GL_TIMEOUT_EXPIRED) {
            someUsefullMathHere();
        }
        glDeleteSync(fence);
        fence=NULL;
    }

    renderer->render(timeDelta);

    glfwSwapBuffers(window);
    fence=glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}

if (fence)
    glDeleteSync(fence);
}

这利用了SwapBuffers不会立即阻塞的事实,并且将一直使用直到实际执行缓冲区交换以进行您想要执行的有用数学运算。

这也具有将延迟限制为一帧的副作用,这可能是好事也可能是坏事,具体取决于您的场景。但原则上,您也可以交错几个栅栏同步对象,并等待倒数第二个缓冲区交换而不是最后一个,依此类推。

于 2015-10-05T21:52:35.907 回答
0

唯一可行的方法是为您的函数使用线程。someUsefulMathHere()您可以使用常规线程同步方法来读回任何结果(例如,互斥锁,仅列举一种可能性),或将新作业排队。这样,您不仅可以在主线程真正休眠时获得空闲的 CPU 周期,而且还可以在多核 CPU 上获得额外的性能。

于 2014-05-12T17:51:59.500 回答
0

首先我想指出,即使没有 vsyncglfwSwapBuffers(window)也可能会阻塞一段时间。OpenGL 的许多功能只是简单地将命令放入队列中,无需等待完成即可返回。这意味着当您调用时,显卡仍然可能有很多工作glfwSwapBuffers(window)。此函数将等待所有工作完成,然后再交换缓冲区并返回。

我不确定什么类型的工作someUsefullMathHere(),所以我有两个可能的答案:

如果您需要每帧进行特定数量的计算:
通常除了渲染场景之外,您还必须每帧更新场景。要更新您的场景,您需要每帧进行一些计算。您只需将此代码放在之前,glfwSwapBuffers(window)而不是将其放在循环的开头或结尾。因此,您可以在不使用线程的情况下实现更高的帧速率。我认为如果程序只是在等待 vsync 应该没有什么区别,但如果你必须等待显卡它应该会有所不同:

while (running) {
    renderer->render(timeDelta);
    someUsefullMathHere();

    glfwSwapBuffers(window);
    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}

如果您想将时间用于跨帧的某些工作:
有时您必须为单个帧做一些需要太多时间的事情。比如加载关卡或下载数据。要使用直到缓冲区被交换为这样的东西的时间,您可以使用第二个线程。我认为您可以在运行循环的其余部分时暂停线程以避免竞争条件:

while (running) {
    renderer->render(timeDelta);

    unpauseThread();
    glfwSwapBuffers(window);
    pauseThreadWhenItIsSave();

    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}
于 2015-10-05T21:23:32.450 回答