【问题标题】:Avoid waiting on SwapBuffers避免等待 SwapBuffers
【发布时间】:2011-04-29 08:39:05
【问题描述】:

我发现 OpenGL 中的 SwapBuffers 将忙于等待,只要显卡未完成渲染或正在等待 V-Sync。

这对我来说是个问题,因为我不想在等待卡完成时浪费 100% 的 CPU 内核。我不是在写游戏,所以我不能将 CPU 周期用于更高效的工作,我只想将它们交给操作系统中的其他进程。

我发现 glutTimerFunc 和 glutIdleFunc 等回调函数对我有用,但我不想使用 glut。不过,glut 必须以某种方式使用正常的 gl 函数来执行此操作,对吗?

有没有“glReadyToSwap”之类的功能?在这种情况下,我可以每毫秒左右检查一次,并确定我是否应该再等一段时间或进行交换。我也可以想象也许跳过 SwapBuffers 并编写我自己的类似函数,如果有人能指出我正确的方向,它不会忙着等待。

【问题讨论】:

    标签: c++ winapi opengl


    【解决方案1】:

    SwapBuffers 不是忙于等待,它只是在驱动程序上下文中阻塞你的线程,这使得 Windows 错误地计算 CPU 使用率:Windows 通过确定空闲进程获得多少 CPU 时间来计算 CPU 使用率+ 程序不花多少时间在驱动程序上下文中。 SwapBuffers 将在驱动程序上下文中阻塞,您的程序显然会从空闲进程中占用 CPU 时间。但是您的 CPU 在这段时间内实际上什么也没做,调度程序很高兴地等待将时间传递给其他进程。空闲进程 OTOH 除了立即将其时间让给系统的其余部分外,什么也不做,因此调度程序会直接跳回您的进程,这会在驱动程序中阻塞 Windows 视为“正在阻塞 CPU”。如果您要测量实际功耗或热量输出,对于一个简单的 OpenGL 程序,这将保持相当低的水平。

    这种恼人的行为实际上是一个 OpenGL 常见问题解答!

    只需为并行数据处理创建额外的线程。将 OpenGL 保留在一个线程中,数据处理在另一个线程中。如果您想降低报告的 CPU 使用率,添加 Sleep(0) 或 Sleep(1) after SwapBuffers 即可。 Sleep(1) 将使您的进程在用户上下文中花费一点时间,因此空闲进程获得更多时间,这将使数字均匀。如果你不想睡觉,你可以这样做:

    const float time_margin = ... // some margin
    float display_refresh_period; // something like 1./60. or so.
    
    void render(){
    
        float rendertime_start = get_time();
    
        render_scene();
        glFinish();
    
        float rendertime_finish = get_time();
        float time_to_finish = rendertime_finish - rendertime_start;
    
        float time_rest = fmod(render_finish - time_margin, display_refresh_period);
        sleep(time_rest);
        SwapBuffers();
     }
    

    在我的程序中,我使用这种计时,但还有一个原因:我让 SwapBuffers 在没有任何帮助程序睡眠的情况下阻塞,但是我让其他一些工作线程通过共享上下文在 GPU 上做一些事情(比如更新纹理)我让垃圾收集器运行。不一定要准确计时,但是在 SwapBuffers 返回之前完成的工作线程允许几乎立即开始渲染下一帧,因为那时大多数互斥锁已经解锁。

    【讨论】:

    • 好吧,等待如何发生的细节对我来说并不重要。我能说的是计算机中的风扇加速了,我假设它们对热量有反应,这意味着 CPU 确实在 100% 运行时确实会变热。即使它没有并且只是 Windows 认为它​​以 100% 运行,这仍然意味着 Windows 不会给任何其他进程提供任何 CPU 时间,因为它认为它已经使用了 100%!不管你怎么看,你都在浪费 CPU 时间,这些时间可以通过烧掉或闲置来被其他东西使用。
    • 您是否尝试过运行另一个消耗大量 CPU 时间的程序?
    • ^^ 连同我的意思是你的 OpenGL 程序。
    • 我认为我有足够的计算机知识可以知道 CPU 何时耗尽。我认为在您的程序中发生的是 1)渲染,2)卡在 glFinish 上,直到卡完成 3)在剩下的 1/60 秒内睡觉 4)SwapBuffers。但是,在#2中,您将忙于等待,因为据我所知, glFinish 与平台无关,除了忙于等待暂停执行之外,不能使用任何其他方法。在游戏或类似游戏中,这并不重要,您可以在 #1 和 #2 之间做一些有用的事情,但在普通的 Windows 程序中,您不想为了显示一些 3D 图像而烧掉一个内核上 100% 的 CPU。
    • 繁忙的等待循环是否应该耗电? ALU、FPU 等均不涉及。忙着等待是浪费,但应该不会特别热。
    【解决方案2】:

    虽然eglSwapBuffers 不忙于等待非阻塞eglSwapBuffers 的合法用途是拥有一个响应更快的GUI 线程,它可以监听用户输入或退出信号,而不是等待OpenGL 完成交换缓冲区。我有办法解决这个问题的一半。首先在主循环中缓冲 OpenGL 命令以在换出的缓冲区上执行。然后轮询同步对象以查看您的命令是否已在换出的缓冲区上完成执行。然后,如果命令已完成执行,您可以交换缓冲区。不幸的是,此解决方案仅异步等待命令在您换出的缓冲区上完成执行,而不异步等待 vsync。代码如下:

     void process_gpu_stuff(struct gpu_context *gpu_context)
     {
         int errnum = 0;
    
         switch (gpu_context->state) {
         case BUFFER_COMMANDS:
             glDeleteSync(gpu_context->sync_object);
             gpu_context->sync_object = 0;
    
             real_draw(gpu_context);
             glFlush();
    
             gpu_context->sync_object = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
             if (0 == gpu_context->sync_object) {
                 errnum = get_gl_error();
                 break;
             }
             gpu_context->state = SWAP_BUFFERS;
             break;
    
         case SWAP_BUFFERS:
             /* Poll to see if the buffer is ready for swapping, if
              * it is not in ready we can listen for updates in the
              * meanwhile. */
             switch (glClientWaitSync(gpu_context->sync_object, 0, 1000U)) {
             case GL_ALREADY_SIGNALED:
             case GL_CONDITION_SATISFIED:
                 if (EGL_FALSE == eglSwapBuffers(display, surface)) {
                     errnum = get_egl_error();
                     break;
                 }
                 gpu_context->state = BUFFER_COMMANDS;
                 break;
    
             case GL_TIMEOUT_EXPIRED:
                 /* Do nothing. */
                 break;
    
             case GL_WAIT_FAILED:
                 errnum = get_gl_error();
                 break;
             }
             break;
         }
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-07
      • 1970-01-01
      • 2022-01-21
      • 2014-12-04
      • 2021-06-21
      • 2021-12-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多