2016-12-04 133 views
0

我想要做的是讓它如此,以便如果我用新窗口替換我正在渲染的窗口,這可能是因爲用戶切換屏幕,或從全屏切換到窗口或任何其他數目原因。如何正確使用GLFW進行上下文共享?

到目前爲止我的代碼看起來是這樣的:

「Context.h」

struct window_deleter { 
    void operator()(GLFWwindow * window) const; 
}; 

class context { 
    std::unique_ptr<GLFWwindow, window_deleter> window; 
public: 
    context(int width, int height, const char * s, GLFWmonitor * monitor, GLFWwindow * old_window, bool borderless); 
    GLFWwindow * get_window() const; 
    void make_current() const; 
}; 

「Context.cpp」

context::context(int width, int height, const char * s, GLFWmonitor * monitor, GLFWwindow * old_window, bool borderless) { 
    if (!glfwInit()) throw std::runtime_error("Unable to Initialize GLFW"); 
    if (borderless) glfwWindowHint(GLFW_DECORATED, 0); 
    else glfwWindowHint(GLFW_DECORATED, 1); 
    window.reset(glfwCreateWindow(width, height, s, monitor, old_window)); 
    if (!window) throw std::runtime_error("Unable to Create Window"); 
    make_current(); 
} 

GLFWwindow * context::get_window() const { 
    return window.get(); 
} 

void context::make_current() const { 
    glfwMakeContextCurrent(window.get()); 
} 

「WindowManager.h」

#include "Context.h" 
class window_style; 
/* window_style is basically a really fancy "enum class", and I don't 
* believe its implementation or interface are relevant to this project. 
* I'll add it if knowing how it works is super critical. 
*/ 

class window_manager { 
    context c_context; 
    uint32_t c_width, c_height; 
    std::string c_title; 
    window_style c_style; 
    std::function<bool()> close_test; 
    std::function<void()> poll_task; 
public: 
    static GLFWmonitor * get_monitor(window_style style); 
    window_manager(uint32_t width, uint32_t height, std::string const& title, window_style style); 
    context & get_context(); 
    const context & get_context() const; 
    bool resize(uint32_t width, uint32_t height, std::string const& title, window_style style); 
    std::function<bool()> get_default_close_test(); 
    void set_close_test(std::function<bool()> const& test); 
    std::function<void()> get_default_poll_task(); 
    void set_poll_task(std::function<void()> const& task); 

    void poll_loop(); 
}; 

「WindowManager.cpp」

GLFWmonitor * window_manager::get_monitor(window_style style) { 
    if (style.type != window_style::style_type::fullscreen) return nullptr; 
    if (!glfwInit()) throw std::runtime_error("Unable to initialize GLFW"); 
    int count; 
    GLFWmonitor ** monitors = glfwGetMonitors(&count); 
    if (style.monitor_number >= uint32_t(count)) throw invalid_monitor_exception{}; 
    return monitors[style.monitor_number]; 
} 

std::function<bool()> window_manager::get_default_close_test() { 
    return [&] {return glfwWindowShouldClose(c_context.get_window()) != 0; }; 
} 

window_manager::window_manager(uint32_t width, uint32_t height, std::string const& title, window_style style) : 
c_context(int(width), int(height), title.c_str(), get_monitor(style), nullptr, style.type == window_style::style_type::borderless), 
    c_width(width), c_height(height), c_title(title), c_style(style), close_test(get_default_close_test()), poll_task(get_default_poll_task()) { 
} 
context & window_manager::get_context() { 
    return c_context; 
} 
const context & window_manager::get_context() const { 
    return c_context; 
} 

bool window_manager::resize(uint32_t width, uint32_t height, std::string const& title, window_style style) { 
    if (width == c_width && height == c_height && title == c_title && style == c_style) return false; 
    c_width = width; 
    c_height = height; 
    c_title = title; 
    c_style = style; 
    c_context = context(int(width), int(height), title.c_str(), get_monitor(style), get_context().get_window(), style.type == window_style::style_type::borderless); 
    return true; 
} 

void window_manager::set_close_test(std::function<bool()> const& test) { 
    close_test = test; 
} 

std::function<void()> window_manager::get_default_poll_task() { 
    return [&] {glfwSwapBuffers(c_context.get_window()); }; 
} 

void window_manager::set_poll_task(std::function<void()> const& task) { 
    poll_task = task; 
} 

void window_manager::poll_loop() { 
    while (!close_test()) { 
     glfwPollEvents(); 
     poll_task(); 
    } 
} 

「Main.cpp的」

int main() { 
    try { 
     glfwInit(); 
     const GLFWvidmode * vid_mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); 
     gl_backend::window_manager window(vid_mode->width, vid_mode->height, "First test of the window manager", gl_backend::window_style::fullscreen(0)); 
     glfwSetKeyCallback(window.get_context().get_window(), [](GLFWwindow * window, int, int, int, int) {glfwSetWindowShouldClose(window, 1); }); 
     glbinding::Binding::initialize(); 
     //Anything with a "glresource" prefix is basically just a std::shared_ptr<GLuint> 
     //with some extra deletion code added. 
     glresource::vertex_array vao; 
     glresource::buffer square; 
     float data[] = { 
      -.5f, -.5f, 
      .5f, -.5f, 
      .5f, .5f, 
      -.5f, .5f 
     }; 
     gl::glBindVertexArray(*vao); 
     gl::glBindBuffer(gl::GL_ARRAY_BUFFER, *square); 
     gl::glBufferData(gl::GL_ARRAY_BUFFER, sizeof(data), data, gl::GL_STATIC_DRAW); 
     gl::glEnableVertexAttribArray(0); 
     gl::glVertexAttribPointer(0, 2, gl::GL_FLOAT, false, 2 * sizeof(float), nullptr); 

     std::string vert_src = 
      "#version 430\n" 
      "layout(location = 0) in vec2 vertices;" 
      "void main() {" 
      "gl_Position = vec4(vertices, 0, 1);" 
      "}"; 

     std::string frag_src = 
      "#version 430\n" 
      "uniform vec4 square_color;" 
      "out vec4 fragment_color;" 
      "void main() {" 
      "fragment_color = square_color;" 
      "}"; 
     glresource::shader vert(gl::GL_VERTEX_SHADER, vert_src); 
     glresource::shader frag(gl::GL_FRAGMENT_SHADER, frag_src); 
     glresource::program program({ vert, frag }); 
     window.set_poll_task([&] { 
      gl::glUseProgram(*program); 
      gl::glBindVertexArray(*vao); 
      glm::vec4 color{ (glm::sin(float(glfwGetTime())) + 1)/2, 0.f, 0.5f, 1.f }; 
      gl::glUniform4fv(gl::glGetUniformLocation(*program, "square_color"), 1, glm::value_ptr(color)); 
      gl::glDrawArrays(gl::GL_QUADS, 0, 4); 
      glfwSwapBuffers(window.get_context().get_window()); 
     }); 
     window.poll_loop(); 
     window.resize(vid_mode->width, vid_mode->height, "Second test of the window manager", gl_backend::window_style::fullscreen(1)); 
     glfwSetKeyCallback(window.get_context().get_window(), [](GLFWwindow * window, int, int, int, int) {glfwSetWindowShouldClose(window, 1); }); 
     window.poll_loop(); 
    } 
    catch (std::exception const& e) { 
     std::cerr << e.what() << std::endl; 
     std::ofstream error_log("error.log"); 
     error_log << e.what() << std::endl; 
     system("pause"); 
    } 
    return 0; 
} 

因此,代碼的當前版本是應該做到以下幾點

  1. 顯示在主顯示器
  2. 上全屏窗口
  3. 在這個顯示器上,顯示一個「方形」(長方形,真的是......),隨着時間在品紅色和藍色之間轉換,而t他是品紅色和綠色色彩之間的背景轉換。
  4. 當用戶按下一個鍵時,使用所述第一窗口的情況下,以進給到GLFW的窗口創建創建第二監視器上的新全屏窗口,並破壞原窗口(按該順序)
  5. 顯示在這同一矩形第二個窗口
  6. 繼續定期轉換背景
  7. 當用戶再次按下某個鍵時,銷燬第二個窗口並退出程序。

這些步驟中,第4步不能在所有的工作,和第3步部分作品:窗口不會生成,但它默認不顯示,並且用戶通過調用它任務欄。所有其他步驟按預期工作,包括兩個窗口上的過渡背景。

所以我的假設是關於上下文之間的對象共享出了問題;具體來說,我看不出創建的第二個上下文是接收由第一個上下文創建的對象。我有沒有明顯的邏輯錯誤?我是否應該做其他事情來確保上下文共享按預期工作?有沒有可能GLFW中存在一個錯誤?

回答

3

所以我的假設是關於上下文之間的對象共享出了問題;具體來說,我看不出創建的第二個上下文是接收由第一個上下文創建的對象。我有沒有明顯的邏輯錯誤?

是的,你的前提是錯的。共享OpenGL上下文不會共享整個狀態,只是實際存儲用戶特定數據(如VBO,紋理,着色器和程序,渲染緩衝區等)的「大」對象,而不是僅引用它們的狀態容器像VAO,FBO等都不會共享。

我應該做些什麼來確保上下文共享按預期工作嗎?

好吧,如果你真的想要走這條路,你必須重新構建所有這些狀態的容器,並恢復全局狀態(所有這些glEnable S,深度緩存設置,混合狀態,噸其他的東西)你的原始上下文。

但是,我發現你的整個概念在這裏有疑問。從全屏到窗口或同一GPU上的不同顯示器時,您不需要銷燬窗口,GLFW直接通過glfwSetWindowMonitor()支持該窗口。

即使你重新創建一個窗口,這並不意味着你必須重新創建GL上下文。 GLFWs API在這方面可能存在一些限制,但其基本概念是分開的。您基本上可以在新窗口中使舊的上下文保持當前狀態,並且完成它。 GLFW只是不可避免地將Window和Context聯繫在一起,這是一種不幸的抽象。

但是,我可以想象在哪裏重新創建窗口的唯一場景是將不同屏幕驅動爲不同GPU的東西 - 但GL上下文共享不適用於不同的GL實現,因此即使在該場景中,你將不得不重建整個上下文狀態。

+0

是否有一個全面的論壇或博客文章,討論到底什麼是共享和什麼不是?我嘗試閱讀[OpenGL規範](https://www.opengl.org/registry/doc/glspec45.core.pdf)(第5章)來弄清楚,但對我來說有點過於神祕。 我打算重寫我的代碼,以便它不必銷燬窗口,但問題將保持相關性,因爲我的下一步是要讓多個窗口同時呈現使用相同的對象,這意味着我無法擺脫上下文共享的煩惱。 – Xirema

+0

除了規範,我不知道有什麼好的描述。請注意,您不一定需要多個窗口的多個上下文,您也可以使用一個窗口在另一個窗口之後使用相同的上下文,並且有一些更新的擴展使這種上下文切換更不重要(儘管它仍然不清楚將是值得的)。 – derhass

+0

好的。我會花更多的時間盯着規範。同時,重寫這段代碼以使用'glfwSetWindowMonitor'和其他類似的函數解決了我的問題,所以我會接受這個答案。 – Xirema