异步纹理加载设施

5
如何在3D应用程序运行时异步加载纹理?我知道OpenGL上下文不支持多线程,因此应该将它们分别放在不同的线程中。
但我的主要问题是选择适当的多线程框架来实现Windows和C++。我听说C++11在其标准库中包含线程支持,但有人能否简要介绍一下基本步骤?
最安全的方法是什么?如何更新其他上下文的状态以使其能够感知其他线程所做出的更改?我猜测可以使用glFlushglBind*

实际上,OpenGL和OpenGL上下文是线程安全的。您可以在多个线程中同时激活单个上下文。 - datenwolf
2个回答

5
纹理加载中最耗时的部分通常是磁盘访问和任何格式转换,这两部分与OpenGL无关,因此可以安全地在另一个线程中进行。一旦纹理已被读入内存并以所需格式呈现,实际复制到OpenGL缓冲区的过程就非常快速。
深入了解线程编程的细节对于本回答来说过于复杂,但有很多文档可供参考,一旦理解,它就像指针和内存一样容易(易懂)。
这里的一般概念是创建一个纹理持有对象列表(包含文件/名称、初始为空缓冲区和加载完成标志),并在创建线程时将其传递给加载线程。加载线程然后遍历列表,打开每个文件,将其加载到内存中并将缓冲区附加到列表条目,然后设置已加载标志并可能增加计数器。主线程获取新加载的纹理,将其复制到OpenGL纹理中,并增加进度条或其他加载指示器。一旦列表中的所有纹理都具有缓冲区并标记为已加载,则其他线程的工作完成,可以停止它(或保留活动状态以加载未来的纹理)。
该模型的主要优点在于不需要共享实际图形上下文。在可以支持线程安全的API(DirectX)中,这样做会有性能损失,并且OpenGL需要您做出相当多的工作才能拥有多个上下文或确保正确共享它们。加载纹理时的重要工作通常是文件读取和参数检查,除非进行格式转换或旋转,否则可能超过磁盘访问。将数据实际复制到视频内存已经高度优化,不太可能成为瓶颈(如果您担心这一点,请尝试在能够识别GPU调用成本的工具中进行分析)。所有这些工作都不直接依赖于OpenGL,因此您可以将其推迟到另一个线程中,而几乎没有任何担忧。
如果您特别使用Windows,则可以使用内置的线程函数,基于简单的回调模型(提供一个函数和初始参数,在这种情况下是纹理列表,并进行API调用)。我个人不熟悉C++11的线程支持以及它在Visual Studio中的工作方式(如果有的话),因此您需要检查一下。

不错的回答。是的,C++11的std::thread可以在MSVC++上运行。它的使用方式与boost::thread非常相似。 - Drew Dormann

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); 

在此之后,任何在该线程中加载的资产都将由绘图线程的上下文共享。

更详细的解释请参见这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接