在我的应用程序中,我有一个隐藏的 GLFW 窗口,用于离屏渲染。我想使用此窗口从多个后台线程进行渲染。确保一次只有一个线程正在使用该窗口。
在后台线程上的每次渲染操作之前,我都会执行以下操作:
glfwMakeContextCurrent((GLFWwindow *)window);
glewExperimental = withGlewExperimental ? GL_TRUE : GL_FALSE;
const auto glewInitStatus = glewInit();
if (glewInitStatus != GLEW_OK)
LOG(ERROR) << "Could not initialize glew, error: " << glewGetErrorString(glewInitStatus);
第一次,它正常执行,但是当第二个线程获取上下文时,glewInit
失败。
Could not initialize glew, error: Missing GL version
当我为每个线程创建一个新的隐藏窗口时,这似乎不会重现,但是 GLFW 禁止在主线程之外创建窗口,并且为每个线程维护窗口池变得复杂的实现并创建了很多不必要的窗口。这就是为什么我希望所有线程渲染到同一个窗口。
有一个叫做GLEW_MX
的东西,它支持多个上下文,但它只存在于GLEW
的旧版本中,2.0.0
之前,而我的 GLEW
版本没有此选项。
所以,我想知道以下问题的答案:
- 这个想法是否可行(从多个线程渲染到单个窗口)?
- 如果是这种情况,如何使用
GLEW
修复错误 - 如果情况并非如此,您建议采取什么解决方法?
最佳答案
- Is this idea viable at all (rendering to a single window from multiple threads)?
技术上正确的答案是肯定的。
实际上正确的答案是否定的。
OpenGL 设计用于每个上下文使用单个线程。如果您尝试同时使用多个线程,实际上其 API 的任何内容都不会正确运行,尽管完全有可能通过在线程之间传递上下文所有权来使 OpenGL 自行运行,从而确保在多线程场景中,一次只有一个线程与上下文交互,我无法想象通过这样做实际上可以显着提高性能的场景。
一般来说,我处理多线程渲染的方式是构建一个消息队列,多个线程可以写入,但只有渲染线程可以读取+执行。
class Renderer {
//Roll your own or find a good implementation somewhere online
concurrent::queue<std::function<void()>> rendering_queue;
std::thread rendering_thread;
GLFWwindow * window;
/*...*/
public:
Renderer(GLFWwindow * window) : window(window) {
rendering_thread = std::thread([this]{
glfwMakeContextCurrent(window);
glewInit(); //Check for error if necessary
while(!glfwWindowShouldClose(window)) {
draw();
glfwSwapBuffers(window);
}
});
}
Renderer(Renderer const&) = delete;
~Renderer() noexcept {
glfwSetWindowShouldClose(window, GLFW_TRUE);
rendering_thread.join();
}
void push_task(std::function<void()> func) {
rendering_queue.push(std::move(func));
}
void draw() {
std::function<void()> func;
while(rendering_queue.try_pop(func)) func();
/*Normal Rendering Tasks*/
}
};
int main() {
glfwInit();
GLFWwindow * window = glfwCreateWindow(800, 600, "Hello World!", nullptr, nullptr);
Renderer renderer(window);
std::thread circle_drawer{[&renderer]{
renderer.push_task([]{/*Draw a Circle*/});
renderer.push_task([]{/*Draw a Circle*/});
renderer.push_task([]{/*Draw a Circle*/});
renderer.push_task([]{/*Draw a Circle*/});
renderer.push_task([]{/*Draw a Circle*/});
renderer.push_task([]{/*Draw a Circle*/});
renderer.push_task([]{/*Draw a Circle*/});
renderer.push_task([]{/*Draw a Circle*/});
renderer.push_task([]{/*Draw a Circle*/});
}};
circle_drawer.detach();
std::thread square_drawer{[&renderer]{
renderer.push_task([]{/*Draw a Square*/});
renderer.push_task([]{/*Draw a Square*/});
renderer.push_task([]{/*Draw a Square*/});
renderer.push_task([]{/*Draw a Square*/});
}};
square_drawer.detach();
/*Etc...*/
while(!glfwWindowShouldClose(window)) {
glfwPollEvents();
}
}
显然我抽象了很多细节,但这主要是因为你的问题非常广泛。该模型应该适用于大多数需要(至少在表面上)多线程渲染能力的应用程序,并且具有可塑性。
关于c++ - 从后台线程调用时 glewInit 失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46350082/