【问题标题】:Multithreaded Rendering on OpenGLOpenGL 上的多线程渲染
【发布时间】:2012-06-21 06:26:15
【问题描述】:

我有一个多线程应用程序,我正在尝试使用不同的线程进行渲染。首先,我尝试在所有线程之间使用相同的渲染上下文,但我得到了其他线程的 NULL 当前上下文。我在互联网上读到,一个上下文一次只能在一个线程中处于当前状态。

所以我决定做一些不同的事情。我创建一个窗口,从中获取 HDC 并创建第一个 RC。之后,我在线程之间共享这个 HDC,并且在我创建的每个新线程中,我都从同一个 HDC 获得一个新的 RC,并使其成为该线程的最新版本。每次我这样做时,返回的 RC 总是不同的(通常是前一个值 + 1)。我做了一个断言来检查wglGetCurrentContext() 是否返回一个RC,看起来它返回了刚刚创建的那个。但是在进行渲染后,我没有得到任何渲染,如果我调用GetLastError(),我会得到错误 6(无效句柄??)

那么,这是否意味着,尽管wglCreateContext() 的每次新调用都会给我一个新值,但不知何故,这是否意味着所有这些不同的值对于 OpenGL 调用来说都是相同的“连接通道”?

这是否意味着我必须始终使线程上的先前渲染上下文无效,并在新线程上激活它?我真的必须一直进行这种同步,还是有其他方法可以解决这个问题?

【问题讨论】:

标签: c++ windows multithreading opengl rendering


【解决方案1】:

我有一个多线程应用程序,我正在尝试使用不同的线程进行渲染。

不要!!!

您将通过尝试对渲染器进行多线程处理一无所获。基本上你遇到了一个大的竞争条件,驱动程序将忙于同步线程以某种方式理解它。

为了获得最佳渲染性能,请将所有 OpenGL 操作保留在一个线程中。所有并行化都在 GPU 上免费进行。

【讨论】:

  • 好吧,我没有正确表达自己。并不意味着它们都会在屏幕上呈现。实际上他们不会。一个线程在后台缓冲区上绘制,并将其内容复制到纹理中。另一个只是“Blits”屏幕上的纹理。这意味着工作线程可以在后台缓冲区上渲染其他复杂的东西。
  • "一个线程在后台缓冲区上绘图,并将其内容复制到纹理。另一个只是简单地“Blits”屏幕上的纹理。"为什么?您不应该在想要演示时交换前后缓冲区吗?
  • @filipehd:即使那样使用多线程也没有意义。 “blitter”线程必须等待“renderer”线程完成。多线程的全部意义在于让事物同时工作。你没有。
  • @datenwolf 尽管你们是对的,但它并没有回答我为什么在创建 wglShareLists() 后会出现错误 6 的问题。尽管如此,这意味着为了做我想做的事,我需要像 FBO 或 PBuffer 这样的辅助缓冲区?
  • @filipehd:嗯,共享(即同时在多个线程中激活上下文)上下文是不可能的,但是在线程之间传递它是可能的。但是:您应该始终避免重新创建线程和上下文。只创建一次线程池并坚持下去。上下文也是如此,但我强烈建议不要在多个线程上使用多个上下文。拥有一个专用的 OpenGL 线程,事情会变得容易得多,而且性能也会更高。
【解决方案2】:

我建议阅读 OpenGL 联盟的 following wiki article

简单来说,这在很大程度上取决于您对 OpenGl 多线程的含义,如果您有一个线程负责渲染部分,而一个(或多个)线程负责其他工作(即 AI、物理、游戏逻辑等) ) 这是完全正确的。

如果您希望多个线程与 OpenGL 发生冲突,您不能,或者更好,但它确实会给您带来更多的麻烦而不是优势。

尝试阅读以下有关并行 OpenGL 使用的常见问题解答,以更好地了解此概念:

http://www.equalizergraphics.com/documentation/parallelOpenGLFAQ.html

【讨论】:

  • 但这正是我的应用程序的想法 - 不同的线程负责不同的逻辑 - 我无法理解的是为什么在调用 wglShareLists 后会出现错误 6。也许我调用这个函数太晚了?我会尝试使用你给我的第一个链接上写的想法来检查它是如何工作的。
  • @filipehd:不同的逻辑手段,不同的子系统。一个线程用于网络,一个用于用户输入,一个用于所有 OpenGL,等等。真的,不要试图将图形输出分布在多个线程上。这是一个巨大的混乱。还有“blitting”操作(绘制纹理四边形),切换到另一个线程的整个开销将导致更多时间消耗,即实际的“blitting”。此外,GPU 是专有资源,您不能让多个线程同时使用 GPU,因此无论如何,blitter 线程都必须等待 GPU 上的任何其他操作完成。
  • @filipehd:确实是这样。你为什么要尝试多线程渲染命令呢?并行化发生在 GPU 上,它采用大量几何图元,并行处理它们(顶点着色器),确定覆盖的片段并并行处理片段(片段着色器)。
  • @filipehd:现在想想如果你实际上将两批几何图元并行发送到 GPU 会发生什么:你最终会处于竞争状态,因为它们都会(很可能)接触相同的片段。这是一种竞争条件,只能通过同步两个批次来解决,这会停止处理管道,从而导致严重的性能损失。
  • @filipehd:现有的每一个主要游戏引擎都会尽其所能,将所有渲染器操作不仅保持在一个线程中,而且保持在同一个CPU内核上。只在一个线程中做所有事情是为了避免驱动程序错误(我知道根本没有 OpenGL 驱动程序,它没有某种多线程问题;只是最近 NVidia 有一些)。将事物保持在一个核心上是为了提高缓存局部性。
【解决方案3】:

在某些情况下,在不同线程中使用多个渲染上下文可能是有意义的。我已经使用这样的设计从文件系统加载图像数据并将这些数据推送到纹理中。

【讨论】:

  • 最好在另一个线程中加载纹理(所有非OpenGL的东西),然后在拥有上下文的线程中更新纹理。
  • 您认为上下文共享会减慢 GL 驱动程序的速度吗?更新纹理是大部分时间消耗的功能之一,因此恕我直言,在单独的线程中执行此操作可以在主线程中节省大量时间。当然,这只适用于不需要立即使用的数据。
  • 我也实现了一次纹理流。如果您同时使用来自多个线程的上下文,那么上下文共享真的很糟糕。但是,当您可以保证从不同时使用它时,我并不是说它有多快。我不认为它会很慢。但如果它有效,那么它就有效。顺便说一句:如果您需要异步纹理上传,请使用 PBO。
  • 我同意,在不同平台上尝试时,所有平台的性能都不是很好。我也同意它只适用于在后台预取的数据,而不是在活动渲染线程中使用的数据。
  • 我明白了...当我实现纹理流式传输(使用预先计算的 mip 贴图)时,它传输了数千个纹理。我为此使用了 PBO。表演相当不错。所有数据都在第二个线程中加载,但在渲染器线程中上传。我会选择类似的东西。
【解决方案4】:

Mac OS X 上的 OpenGL 是单线程安全的;启用它:

#include <OpenGL/OpenGL.h>

CGLError err = 0;
CGLContextObj ctx = CGLGetCurrentContext();

// Enable the multi-threading
err =  CGLEnable( ctx, kCGLCEMPEngine);

if (err != kCGLNoError ) {
     // Multi-threaded execution is possibly not available
     // Insert your code to take appropriate action
}

见: Concurrency and OpenGL - Apple Developer

并且: Technical Q&A QA1612: OpenGL ES multithreading and ...

【讨论】:

  • 问题是关于 Windows 的。标签中没有列出,但 HDC 和 GetLastError 很明显。
  • 如果您是 Windows 用户……我不是……另外,特定于操作系统的扩展通常会包含在非特定于操作系统的 GL 版本中。
【解决方案5】:

https://www.imaginationtech.com/blog/understanding-opengl-es-multi-thread-multi-window-rendering/

什么时候不应该使用多线程渲染?

当您不受 CPU 限制或加载时间不是问题时。

因此,如果您的 CPU 受限,请将其他线程分开来执行 CPU 受限的工作,例如编解码器、纹理上传、计算...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-04
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    相关资源
    最近更新 更多