异步纹理加载的工具

时间:2013-03-30 17:38:19

标签: c++ windows visual-c++ opengl c++11

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

但我的主要问题是选择一个适当的多线程设施/框架来实际使用Windows和C ++实现这一点,我听说过很多关于C ++ 11的内容,包括其标准lbrary中的线程支持,但是有人可能只是基本步骤?

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

2 个答案:

答案 0 :(得分:5)

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

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

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

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

如果您专门使用Windows,则可以使用内置的线程函数,使用简单的回调模型(提供函数和初始参数,在本例中为纹理列表,并进行API调用) )。我个人并不熟悉C ++ 11的线程支持或它在Visual Studio中的工作方式,如果有的话,所以你必须检查它。

答案 1 :(得分:1)

@ssube的答案对于任务的复杂性分解是正确的,但假设“OpenGL需要你做一些相当大的工作才能有多个上下文或确保你正确地分享它们”,我不同意

一个简单的解决方案是在程序开始时创建一个主上下文(用于绘图)和一个辅助上下文(用于纹理加载),例如:

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

然后在m_hRCSecondarym_hRCDrawing上下文之间共享数据可以通过以下方式完成:

wglShareLists(m_hRCSecondary, m_hRCDrawing);

最后,当您要从没有GL上下文的“纹理加载”线程中读取纹理时,您只需调用:

wglMakeCurrent(m_hDC, m_hRCSecondary); 

之后,绘图线程的上下文共享此线程中加载的所有资源。

可以获得更详尽的解释here