当前位置: 首页 > 知识库问答 >
问题:

如何正确地与GLFW进行上下文共享?

谷梁淇
2023-03-14

我试图做到的是,如果我用一个新窗口替换正在渲染的窗口,这可能是因为用户切换屏幕,或从全屏切换到窗口,或出于其他任何原因。

到目前为止,我的代码如下所示:

“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. 当用户按键时,使用第一个窗口的上下文在第二个监视器上创建一个新的全屏窗口,以输入GLFW的窗口创建,并销毁原始窗口(按该顺序)
  4. 在第二个窗口上显示相同的矩形
  5. 继续定期转换背景
  6. 当用户再次按键时,破坏第二个窗口并退出程序

在这些步骤中,第4步根本不起作用,第3步部分起作用:窗口确实被创建,但默认情况下不显示,用户必须通过任务栏调用它。所有其他步骤都按预期工作,包括两个窗口上的过渡背景。

所以我的假设是,在上下文之间的对象共享方面出现了一些问题;具体来说,我正在创建的第二个上下文似乎没有接收第一个上下文创建的对象。我有没有明显的逻辑错误?我应该做些别的事情来确保上下文共享按预期工作吗?有没有可能GLFW中只有一个bug?

共有1个答案

卢锋
2023-03-14

所以我的假设是上下文之间的对象共享出了问题;具体来说,我正在创建的第二个上下文似乎没有接收第一个上下文创建的对象。我犯了明显的逻辑错误吗?

是的,你的前提是错的。共享的OpenGL上下文不会共享整个状态,只会共享实际包含用户特定数据的“大”对象(如VBO、纹理、着色器和程序、渲染缓冲区等),而不是仅引用它们的对象——VAO、FBO等状态容器永远不会共享。

我应该做些别的事情来确保上下文共享按预期工作吗?

好吧,如果你真的想走这条路,你必须重新构建所有这些状态容器,还必须恢复原始上下文的全局状态(所有那些glEnables,深度缓冲设置,混合状态,大量其他东西)。

然而,我觉得你的整个概念在这里值得怀疑。在同一GPU上从全屏切换到带窗口,或切换到不同的显示器时,不需要破坏窗口,GLFW通过glfwSetWindowMonitor()直接支持这一点。

即使您确实重新创建了一个窗口,这并不意味着您必须重新创建GL上下文。GLFWs API可能会在这方面施加一些限制,但基本概念是独立的。基本上,您可以在新窗口中使旧上下文成为当前上下文,然后就完成了。GLFW只是将窗口和上下文链接在一起,这是一种不幸的抽象。

然而,我能想象的唯一需要重新创建窗口的场景是,不同的屏幕由不同的GPU驱动——但GL上下文共享无法在不同的GL实现中工作,因此即使在该场景中,您也必须重建整个上下文状态。

 类似资料:
  • 我正在尝试使用测试驱动设计方法编写一个应用程序--我对单元测试很陌生,所以我只是想知道测试正确输入和异常的正确方法是什么。 我有一个用于加载配置文件的类: null 另外,这3个测试是否都有try{}catch(){}语句?在第一个测试中,正确性是隐含的,在第二个和第三个测试中,我无论如何都在检查异常,所以异常对测试没有影响。

  • 上下文:我正在使用java1.7.0_07和Jython构建一个JavaFX应用程序。我的应用程序有一个 ScrollPane,其中包含一个 GridPane,其中包含可变数量的 GridPanes。我的应用程序会在每次树视图选择发生更改时丢弃并重新生成最里面的 GridPanes。 问题:当我在树状视图中选择某个内容时,最里面的网格窗格非常大,除了第一个最里面的窗格外,其他所有的网格窗格都被推到

  • 问题内容: 我对使用Spring将对象注入到类中有疑问。我在项目中使用了以下代码: 然后通常在以下方法上使用它: 否则,我在Spring示例中发现了注入构造函数的情况: 所以两者都正确吗?还是每个都有其属性和用法? 问题答案: tl; dr-构造函数注入是执行DI的最佳方法 后者是正确的,并不是因为Spring或任何依赖注入容器,而是面向对象的类设计原则。 细节 应该设计一种类型,以便您只能根据其

  • 使用postgresql 9.5,查询显示错误,为什么? 我试着改变

  • @value(“${recaptcha.private.key}”)私有字符串recaptChaprivateKey; 和我的web.xml(更新)

  • 我有一个Spring Boot maven应用程序,我需要使用jib插件对其进行dockerize,并将注册表发送到azure容器注册表。注册表已设置并就绪,包括用户名、密码等。。。我的pom。xml如下所示: 当我运行命令“mvn compile jib: build”时,我得到以下输出: 基本映像在公共docker中心存储库中。当我运行docker拉...-我可以毫无问题地拉它。 我如何调整这