2016-12-04 2 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. Когда пользователь нажимает клавишу, создайте новое полноэкранное окно на втором мониторе, используя контекст первого окна, чтобы подключиться к созданию окна GLFW и уничтожить исходное окно (в этом порядке).
  4. Отобразите тот же прямоугольник на этом второе окно
  5. Периодически переходить на задний план
  6. Когда пользователь снова нажимает клавишу, уничтожьте второе окно и выйдите из программы.

Из этих шагов шаг 4 вообще не работает, а шаг 3 частично работает: окно создается, но оно не отображается по умолчанию, и пользователь должен вызвать его через панель задач. Все остальные шаги работают так, как ожидалось, включая переходный фон в обоих окнах.

Таким образом, мое предположение заключается в том, что что-то не так происходит в отношении совместного использования объектов между контекстами; в частности, не кажется, что второй контекст, который я создаю, - это получение объектов, созданных в первом контексте. Есть ли очевидная логическая ошибка, которую я делаю? Должен ли я делать что-то еще, чтобы обеспечить совместное использование контекста по назначению? Возможно ли, что в GLFW есть ошибка?

ответ

3

Таким образом, мое предположение заключается в том, что что-то идет не так в отношении совместного использования объектов между контекстами; в частности, не кажется, что второй контекст, который я создаю, - это получение объектов, созданных в первом контексте. Есть ли очевидная логическая ошибка, которую я делаю?

Да, ваше предположение просто неправильно.Общий контекст OpenGL не будет разделять все состояние, просто «большие» объекты, которые фактически содержат данные, специфичные для пользователя (например, VBOs, текстуры, шейдеры и программы, рендерингбуферы и т. Д.), А не те, которые ссылаются только на них - контейнеры состояний такие как VAO, FBOs и т. д., никогда не используются.

Должен ли я делать что-то еще, чтобы обеспечить совместное использование контекста по назначению?

Ну, если вы действительно хотите, чтобы идти по этому пути, вы должны заново строить все эти состояния контейнеров, а также восстановить глобальное состояние (все эти glEnable сек, настройка буфера глубины, смешивая состояние, тонн другие вещи) вашего исходного контекста.

Однако я считаю, что вся ваша концепция здесь сомнительна. Вам не нужно уничтожать окно при переходе с полноэкранного режима на оконный или на другой монитор на том же графическом процессоре, и GLFW напрямую поддерживает это через glfwSetWindowMonitor().

И даже если вы do заново создадите окно, это не означает, что вам нужно воссоздать контекст GL. В этом отношении могут быть некоторые ограничения, налагаемые API GLFW, но основные концепции являются отдельными. В основном вы можете сделать старый контекст актуальным в новом окне и просто с ним справиться. GLFW просто взаимозависимо связывает Window и Context вместе, что является своего рода неудачной абстракцией.

Однако единственный сценарий, который я мог себе представить, когда нужно было создать окно, - это то, где разные экраны управляются разными графическими процессорами, но общий контекст GL не будет работать в разных реализациях GL, поэтому даже в этом сценарии , вам придется перестроить все контекстное состояние.

+0

Есть ли обширный форум или сообщение в блоге, в котором обсуждается, что именно делится, а что нет? Я попробовал прочитать [спецификацию OpenGL] (https://www.opengl.org/registry/doc/glspec45.core.pdf) (глава 5), чтобы понять это, но для меня это слишком загадочно. Я собираюсь работать над переписыванием моего кода, чтобы он не должен был разрушать окно, но вопрос будет оставаться актуальным, так как моим следующим шагом будет работа над получением нескольких окон для рендеринга в одно и то же время используя те же объекты, что означает, что я не смогу избежать ошибок в контексте совместного использования контекста. – Xirema

+0

Помимо спецификации, я не знаю никаких хороших описаний. Обратите внимание, что вам не обязательно нужно несколько контекстов для нескольких окон, вы также можете использовать одно окно за другим с одним и тем же контекстом, и есть несколько более новых расширений, которые делают такое переключение контекста менее тяжелым (хотя до сих пор неясно, будет стоить того). – derhass

+0

Хорошо. Я потрачу больше времени, глядя на спецификацию. Тем временем, переписывая этот код для использования 'glfwSetWindowMonitor' и других подобных функций, разрешили мои проблемы, поэтому я приму этот ответ. – Xirema

Смежные вопросы