c - 使用 OpenGL 渲染时的多线程对象加载
问题描述
我想在主程序显示加载屏幕时在单独的线程中加载一些纹理和网格,因为加载所有资源需要几秒钟。我正在使用 OpenGL 和 GLFW。我尝试使用以下代码来完成此操作:
void *status;
if(pthread_create(&loader, NULL, &loader_func, NULL))
{
fprintf(stderr, "Error creating loader thread\n");
return 1;
}
while(_flags & FLAG_LOADING)
{
vec3 color = { 0.1, 0.3, 1.0 };
if(glfwWindowShouldClose(window))
{
resource_destroy();
glfwTerminate();
return 0;
}
GL_CHECK(glClearColor(0.1, 0.1, 0.1, 1.0));
GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
font_renderer_activate();
render_string(&_font_menu, "Loading...", _width / 2, _height / 2, 64,
color, ALIGN_V_CENTER | ALIGN_H_CENTER);
glfwSwapBuffers(window);
glfwPollEvents();
}
if(pthread_join(loader, &status))
{
fprintf(stderr, "Error joining loader and main thread\n");
return 1;
}
if(*(int *)status)
{
fprintf(stderr, "Error loading resources\n");
return 1;
}
loader_func() 不会渲染到屏幕上,只使用 OpenGL 函数来创建 VAO、VBO 等并将数据加载到其中。
问题是在加载文本显示在屏幕上并且加载完成后,屏幕上什么都没有显示(编辑:除了文本 HUD)并且我的日志中收到了很多调试错误消息(我正在包装所有OpenGL 调用一个宏来检查 glGetError 的错误):
main.c:588
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_texture);
GL_Error 0x502: GL_INVALID_OPERATION
main.c:589
glDrawArrays(GL_TRIANGLES, OFFSET_SKYBOX, VERTICES_SKYBOX);
GL_Error 0x502: GL_INVALID_OPERATION
main.c:629
glDrawArrays(GL_TRIANGLES, OFFSET_SELECTOR, VERTICES_SELECTOR);
GL_Error 0x502: GL_INVALID_OPERATION
当我直接调用 loader_func 时,没有错误,主渲染循环正常工作。
我读到要在另一个需要调用的线程中使用 OpenGL 函数,glfwMakeContextCurrent
但这在我的情况下不起作用,因为这样就不会渲染加载屏幕。我唯一的想法是利用像 SDL 这样的第二个库在加载时创建一个窗口,然后销毁它并使用 GLFW 创建一个新窗口以与 OpenGL 一起使用。这就是我想用OpenGL实现的吗?
解决方案
处理这个问题的最简单方法是让主线程创建和管理所有 OpenGL 对象,而加载线程执行文件 IO(很容易成为加载中最慢的部分)。一旦加载线程完成加载特定资产,它可以通过 <在此处插入您最喜欢的线程安全机制> 将加载的数据传递到主线程,这可以完成最终的 OpenGL 上传部分。
毕竟,渲染加载屏幕并不是一个巨大的性能消耗或其他东西,因此在主线程上上传的成本将是最小的。这也允许您执行加载栏的操作,因为您的主线程将经常获取加载过程的结果,因此它随时知道完成了多少加载。
如果出于某种原因绝对必须有两个线程都进行 OpenGL 调用,那么您还应该有两个 OpenGL 上下文,每个上下文都在不同的线程中,并且这两个上下文彼此共享对象。如果您问得好,GLFW 非常乐意提供此功能。正常创建主窗口,然后将GLFW_VISIBLE
提示设置为GLFW_FALSE
,并创建第二个窗口(具有任意分辨率)。您应该将主窗口作为最后一个参数传递给glfwCreateWindow
,以便两个上下文可以共享对象。然后,您可以在不同的上下文中将每个窗口设置为当前窗口,就可以了。
一言以蔽之。在它们之间共享对象的上下文只共享某些对象。不能共享引用其他对象的对象(查询对象也由于某种原因不可共享)。VAO 引用缓冲区对象,因此它们不能被共享。因此,尝试在屏幕外上下文中创建它们是没有意义的。
推荐阅读
- jenkins - 将文件添加到现有 Openshift configMap
- java - 发送数据 Android Studio 到 PHP 崩溃(和号)
- javascript - 选择图像并在用户单击时滚动页面
- python - 如何设置 SQLAlchemy 方言?
- ios - 如何在 Swift 的单独类中使用函数时添加视图?
- java - 事务提交/回滚不在 Spring 集成通道适配器上
- java - JPA - 通过主键绑定实体 - 标识符无效 ORA 错误
- c# - 减小 PDF 文件大小
- sql - HiveQL 选择特定日期范围内的生日
- javascript - 在反应中添加不同表中的项目?