[英]glewInit fails when called from a background thread
在我的應用程序中,我有一個隱藏的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
修復錯誤
- 這個想法是否可行(從多個線程渲染到單個窗口)?
技術上正確的答案是肯定的。
實際上正確的答案是否定的。
OpenGL設計用於每個上下文的單個線程。 如果你試圖同時使用多個線程,那么它的API幾乎沒有任何表現,並且盡管通過從線程到線程傳遞上下文所有權完全可以使OpenGL表現自己,確保在多線程場景中,只有一個線程同時與上下文交互,我無法想象你實際上通過這樣做可以顯着提高性能的場景。
一般來說,我處理多線程渲染的方式是構建一個多線程可以寫入的Message Queue,但只有渲染線程可以讀取+執行。
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();
}
}
顯然我正在抽象出很多細節,但這主要是因為你的問題非常廣泛。 對於大多數應用程序而言,此模型應該適用並且具有可擴展性,這些應用程序至少在表面上需要多線程渲染的能力。
IMO你不需要glewInit
每個胎面。 AFAIK你只需要為每個應用程序調用一次。
我從來沒有嘗試過,但我認為你需要這樣的工作流程。
第一個線程:創建窗口, glewInit
,其他openGL代碼初始化渲染並渲染你的東西, wglMakeCurrent( NULL, NULL )
完成后。
其他線程: wglMakeCurrent( dc, glrc )
,其他用於渲染你的東西的openGL代碼, wglMakeCurrent( NULL, NULL )
完成后。
只需確保您不會同時在不同的線程上使用OpenGL上下文或Window'HDC。
這個想法有多好......我不太喜歡它。 OpenGL和GPU驅動程序的用戶模式部分非常龐大,L1-L2緩存是每個核心,當核心之間共享緩存行時,L3緩存變慢~2倍。
如您所見,您只能同時使用一個線程中的GL上下文+ Window的HDC。
如果我正在設計那種東西,我就創建了一個專用的屏幕外渲染線程。 並實現作業隊列以呈現作業。
如果您希望這些其他線程等待作業結果,一個簡單的方法是帶有自定義窗口消息的SendMessage
WinAPI,將指針傳遞給消息參數[s]中的作業。 無論如何,渲染線程需要Windows消息泵。
但是,如果您希望其他線程輪詢渲染結果,則可以使用PostMessage
提交作業,例如(每個線程的std::queue
由關鍵部分保護)用於輪詢結果。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.