SDL/OpenGL: 实现“加载线程”

7
我目前正在尝试为一个非常基本的游戏引擎实现“加载线程”,该线程负责在主线程继续渲染适当的消息/屏幕,直到操作完成或即使在后台加载较小对象时也要渲染常规游戏场景,例如纹理或音频等内容。
然而,我并不是OpenGL专家,但是当我实现这样一个“加载”机制时,我很快发现OGL不太喜欢从除了创建它的线程之外的线程访问渲染上下文。我搜索了一下,解决方案似乎是:
“在线程上创建第二个渲染上下文,并与主线程的上下文共享”
问题在于,我使用SDL来管理窗口和上下文创建,据我所知,从检查API中无法告诉SDL在它们之间共享上下文。
我得出结论,对于我的情况,最好的解决方案是:
方法A)修改SDL库以支持平台特定函数(wglShareLists()和glXCreateContext())之间的上下文共享
方法B)让“加载线程”仅将数据加载到内存中并处理为OpenGL友好格式,然后传递给主线程,例如负责将纹理上传到图形适配器。当然,这仅适用于需要有效的OpenGL上下文才能完成的数据。
第一个解决方案可能效率最低。我真的不想和SDL搞在一起,除此之外我还读到上下文共享不是高性能操作。所以到目前为止,我的下一步尝试就是第二种方法。
关于“高性能操作”:我读错了文章,它实际上并不那么消耗性能。文章建议将CPU密集型操作移至第二个具有第二个上下文的线程中。对此我表示抱歉。
在这个介绍之后,如果有人能给我以下问题一些提示和评论,我会非常感激:
1)是否有任何方法可以与SDL共享上下文,而且即使这样做是否有好处?
2)是否有其他更“优雅”的方式来在后台加载我的数据,我可能忽略或没有考虑?
3)我选择采用B方法的意图是否被认为是一个好选择?仍然会有OpenGL操作在我的主线程上产生轻微的开销,从而阻塞渲染,或者这很小,可以忽略不计?

2
我会选择方案B。让后台线程加载所有必要的数据,主线程处理绘图。 - user500944
谢谢,我目前正在重构我的代码以按照这种方式加载数据。但是,如果有人遇到同样的问题,也许还有人可以发表评论,为这个问题提供更多信息 :) - PuerNoctis
请记住,这个解决方案说起来容易做起来难:根据您应用程序的细节,您的线程很可能需要得到适当的同步,而这并不总是一项直截了当的任务。我假设您已经调查了单线程的替代方案,并没有找到一个适合您的。 - user500944
不幸的是,我还没有找到一个合适的单线程解决方案 :( 但我仍在继续搜索。关于同步:我考虑每帧查询线程是否已完成,如果是,则知道我的数据已准备好由OpenGL处理。完成此步骤后,应用程序可以使用数据。如果我有一个正在运行的加载屏幕,我认为没有太多需要做的 - 对于在游戏场景运行时进行延迟加载,我还必须检查数据是否实际准备好。同样,稍微有点开销,但我认为没有其他解决方法。 - PuerNoctis
我看到过分享上下文不是高性能操作的说法。你在哪里看到的?因为这显然是错误的。 - Nicol Bolas
啊,好的,抱歉,我误解了文本。实际上它说:“[...]在第二个上下文中使用第二个线程执行CPU密集型操作”。我读到这种方法是CPU密集型的 - 我错了,对不起。 - PuerNoctis
1个回答

7

是否有办法与SDL共享上下文?

没有。

有!

您需要使用特定于平台的调用获取当前上下文。从那里,您可以创建一个新的上下文并使其共享,也可以使用特定于平台的调用。

是否有其他更“优雅”的方法可以在后台加载我的数据,可能我错过了或没有考虑到?

实际上没有。您已经很好地列举了选项:突破SDL以获得所需数据,或低效地加载数据。

但是,您可以将数据加载到映射缓冲区对象中,并将其传输到OpenGL中。您只能在OpenGL线程上进行映射/取消映射,但是映射时获得的指针可以在任何线程上使用。因此,映射缓冲区,将其传递给工作线程。它会将数据加载到映射内存中,并切换开关。GL线程取消映射指针(工作线程现在应该忘记指针),然后上传纹理数据。

我的B方案考虑得如何?

定义“好”?不知道您的问题域更多信息之前无法回答此问题。


“好的”实际上是指无论如何都让上下文共享是否通常更好或更专业。但感谢您的建议,我明天一定会研究一下。 - PuerNoctis
3
@Nicol Bolas: 实际上,如果你不介意编写平台相关的代码,你可以与SDL共享上下文:wglGetCurrent{DC,Context},glXGetCurrent{Display,Drawable,Context},你可以自己找出其余部分。我在我的PBuffer便利库中使用了这些函数。 - datenwolf
@datenworlf:我完全忘记了你可以直接获取当前上下文。 - Nicol Bolas
1
@datenwolf的建议非常好,现在一切都完美运行!我首先使用wglGetCurrent{DC,Context}获取了DC/RC,然后在线程中创建了一个新的上下文并将其设置为当前上下文,最后使用wglShareLists(mainContext, threadContext)共享显示列表空间。谢谢! - PuerNoctis

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