2016-12-04 12 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のウィンドウ作成にフィードする新しいフルスクリーンウィンドウを2番目のモニターに作成し、元のウィンドウを破棄します(この順序で)
  5. これに同じ矩形を表示します第2のウィンドウ
  6. 定期的にバックグラウンドの遷移を続ける
  7. ユーザーがキーをもう一度押すと、2番目のウィンドウを破棄してプログラムを終了します。

これらの手順のうち、手順4はまったく機能しません。手順3は部分的に機能します。ウィンドウは作成されますが、デフォルトでは表示されず、ユーザーはタスクバー。両方のウィンドウの背景を遷移させるなど、他のすべてのステップは期待通りに機能します。

私の前提は、コンテキスト間のオブジェクトの共有に関して何かが間違っていることです。具体的には、私が作成している2番目のコンテキストが最初のコンテキストによって作成されたオブジェクトを受け取っているようには見えません。私が作っている明らかな論理エラーはありますか?コンテキストの共有が意図どおりに機能するようにするために他の何かをしなければならないでしょうか? GLFWにバグがある可能性はありますか?

答えて

3

私の前提は、コンテキスト間のオブジェクト共有に関して何かが間違っていることです。具体的には、私が作成している2番目のコンテキストが最初のコンテキストによって作成されたオブジェクトを受け取っているようには見えません。私が作っている明らかな論理エラーはありますか?

はい、あなたの前提は間違っています。共有OpenGLコンテキストは状態全体を共有するのではなく、実際にユーザ固有のデータ(VBO、テクスチャ、シェーダ、プログラム、レンダバッファなど)を保持する「ビッグ」オブジェクトだけでなく、それらを参照するものVAOやFBOのように共有されることはありません。

コンテキスト共有が意図したとおりに機能するようにするために何か他のことを行う必要がありますか?

さて、あなたは本当にそのルートを移動したい場合、あなたはすべてのそれらの状態のコンテナを再構築し、また、グローバル状態(これらすべてのglEnable S、デプスバッファ設定、ブレンド状態、のトンを復元する必要が他のもの)をあなたの元の文脈の中に置きます。

しかし、あなたの全体のコンセプトはここでは疑問です。フルスクリーンからウインドウ、または同じGPU上の別のモニタに行くときにウィンドウを破壊する必要はなく、GLFWはglfwSetWindowMonitor()で直接サポートします。

を実行してもにウィンドウを再作成しても、GLコンテキストを再作成する必要はありません。その点でGLFW APIにはいくつかの制限が課せられるかもしれませんが、根底にある概念は別です。基本的には、新しいウィンドウで古いコンテキストを現在の状態にすることができます。 GLFWはWindowとContextを一体不可分にリンクしています。これは残念なことに抽象的なものです。

しかし、ウィンドウを再作成する必要があると私が想像できる唯一のシナリオは、異なるスクリーンが異なるGPUになるようなものですが、GLのコンテキストの共有は異なるシナリオでも機能しませんコンテキスト全体の状態を再構築する必要があります。

+0

何が共有されるのか、何が共有されないのかを正確に議論する包括的なフォーラムまたはブログの投稿はありますか?私はそれを理解するために[OpenGL spec](https://www.opengl.org/registry/doc/glspec45.core.pdf)(第5章)を読んでみましたが、私にとってはあまりにも難解です。 私は、ウィンドウを破壊する必要がないようにコードを書き直すつもりですが、次のステップは複数のウィンドウを同時にレンダリングする作業です同じオブジェクトを使用しているため、コンテキスト共有の不具合から逃れることはできません。 – Xirema

+0

スペックは別として、私は良い説明は分かりません。複数のウィンドウに対して複数のコンテキストが必要なわけではなく、同じコンテキストを持つウィンドウの後ろに1つのウィンドウを使用することもできます。また、このようなコンテキスト切り替えを少なくするような新しい拡張があります(ただし、それに値するだろう)。 – derhass

+0

よろしくお願いします。私は仕様を凝視してより多くの時間を費やすでしょう。その間、 'glfwSetWindowMonitor'と他の同様の関数を使うためにこのコードを書き直すことは私の問題を解決したので、私はこの答えを受け入れます。 – Xirema

関連する問題