5

如何在 3D 应用程序运行时异步加载纹理?我所理解的是 OpenGL 上下文不是线程安全的,我应该将它们分开在不同的线程上。

但我的主要问题是选择适当的多线程工具/框架来实际使用 Windows 和 C++ 实现这一点,我听说过很多关于 C++11 的信息,包括其标准库中的线程支持,但有人可以只列出基本步骤吗?

最安全的方法是什么?以及如何更新另一个上下文的状态,它注册了在另一个线程上完成的更改?我怀疑glFlushglBind*

4

2 回答 2

5

纹理加载最耗时的部分通常是磁盘访问和任何格式转换,这两者都独立于 OpenGL,因此可以安全地在另一个线程上进行。一旦纹理被读入内存并以所需的格式,实际复制到 OpenGL 缓冲区是相当快的。

进入线程编程的细节对于这个答案来说太复杂了,但是周围有很多文档,一旦它在你的脑海中点击,它就很容易(比如指针和内存)。

这里的一般概念是创建一个纹理持有者对象列表(例如,包含文件/名称、初始空缓冲区和加载完成标志),并在创建时将其传递给加载线程。加载线程然后遍历列表,打开每个文件,将其加载到内存中并将缓冲区附加到列表条目,然后设置加载标志并可能增加一个计数器。主线程获取新加载的纹理,将其复制到 OpenGL 纹理中,并增加进度条或任何加载指示器。一旦列表中的所有纹理都有缓冲区并被标记为已加载,另一个线程的工作就完成了,它可以停止(或保持活动状态以加载未来的纹理)。

该模型的主要优点是不必共享实际的图形上下文。在可以是线程安全的 API (DirectX) 中,这样做会降低性能,而 OpenGL 需要您做大量工作才能拥有多个上下文或确保正确共享它们。加载纹理时的繁重工作通常是文件读取和参数检查,除非您进行格式转换或旋转,这甚至可能使磁盘访问相形见绌。实际复制到视频内存是高度优化的,不太可能成为瓶颈(如果您担心这一点,请尝试使用能够识别 GPU 调用成本的工具进行分析,然后查看)。这些工作都没有直接依赖于 OpenGL,因此您可以将其推送到另一个线程而无需担心。

If you're on Windows specifically, there are built-in threading functions that can be used, working off of a simple callback model (provide a function and initial params, in this case your texture list, and make the API call). I'm not personally familiar with C++11's threading support or how it works in Visual Studio, if at all, so you'd have to check on that.

于 2013-03-30T17:57:12.383 回答
1

The answer by @ssube is correct about the complexity breakdown of the task, but assumes that "OpenGL requires a decent bit of work on your part to have multiple contexts or make sure you're sharing them properly", and I disagree.

An easy solution is to create a main context (for drawing) and a secondary context (for texture loading), at the start of the program, for example with:

m_hRCDrawing = wglCreateContext(m_hDC);
m_hRCSecondary = wglCreateContext(m_hDC);

And then sharing the data between the m_hRCSecondary and the m_hRCDrawing contexts can be done with:

wglShareLists(m_hRCSecondary, m_hRCDrawing);

Finally, when you are about to read a texture from a "texture loading" thread that has no GL context, you can simply call:

wglMakeCurrent(m_hDC, m_hRCSecondary); 

after which any assets loaded within this thread are shared by the drawing thread's context.

A slightly more thorough explanation is available here.

于 2017-05-23T15:48:42.273 回答