Sharing textures between OpenGL contexts with GLFW3 is not working
Asked Answered
U

1

6

The program below is supposed to:

  1. Create an invisible GLFWwindow* masterWindow
  2. Load a texture from a file, while the active OpenGL context is the one associated with masterWindow
  3. create a GLFWwindow* childWindow, with sharing activated between this window's OpenGL context and masterWindow's context
  4. display a full-window quad with the texture in childWindow

However, this does not work, i.e. instead of the texture, I get random snippets of graphics memory displayed in childWindow. Displaying the texture does work if I make the context of childWindow current before loading it, which is why I think that the problem is neither with my shaders nor with my texture loading routine (which I took from here, https://github.com/DavidEGrayson/ahrs-visualizer/blob/master/png_texture.cpp). I understand that I should be able to share the texture (see answer to this SO question: OpenGL - Share existing textures with future contexts?), so what am I doing wrong? In case it matters, I use a late 2008 Macbook with a NVIDIA 9400m and Mavericks/OpenGL 3.3 installed.

#include <iostream>
#define GLEW_STATIC
#include <glew.h>
#include <glfw3.h>

const char* vShader =
  "#version 150 core\n"
  "in vec2 vertex;"
  "in vec2 vertexUV;"
  "out vec2 UV;"
  "void main() {gl_Position = vec4(vertex,0,1);UV=vertexUV;}";
const char* fShader =
 "#version 150 core\n"
 "uniform sampler2D sampler;"
 "in vec2 UV;"
 "out vec4 color;"
 "void main() {color = texture(sampler, UV);}";

GLuint png_texture_load(const char * file_name, unsigned int * width=NULL, unsigned int * height=NULL);
GLuint make_program(const char* vShader, const char* fShader);

GLFWwindow* masterWindow(NULL);

void create_window_with_texture(GLuint texture)
{
  GLFWwindow* childWindow(glfwCreateWindow(256,256, "", NULL, masterWindow));
  glfwMakeContextCurrent(childWindow);

  // texture appears if loaded from here
  // texture=png_texture_load("rose.png");                                                                                                               

  GLfloat vertices[]={-1,-1,1,-1,-1,1,1,1, // window corners in clip space                                                                               
                  0,0,1,0,0,1,1,1};    // corners in uv-space                                                                                        
  GLubyte indices[]={0,2,1,3};             // points for a two triangle strip                                                                            

  GLuint vArray,vBuffer,iBuffer;
  glGenVertexArrays(1,&vArray);glBindVertexArray(vArray);
  glGenBuffers(1,&vBuffer);glBindBuffer(GL_ARRAY_BUFFER,vBuffer);
  glGenBuffers(1,&iBuffer);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,iBuffer);
  glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_DYNAMIC_DRAW);
  glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_DYNAMIC_DRAW);

  GLuint program(make_program(vShader,fShader));
  glUseProgram(program);
  glUniform1i(glGetUniformLocation(program,"sampler"),0);
  GLuint vertexPtr(GLuint(glGetAttribLocation(program,"vertex")));
  glEnableVertexAttribArray(vertexPtr);
  GLuint vertexUVPtr(GLuint(glGetAttribLocation(program, "vertexUV")));
  glEnableVertexAttribArray(vertexUVPtr);
  glVertexAttribPointer(vertexPtr, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)0 );
  glVertexAttribPointer(vertexUVPtr, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)32);

  glActiveTexture(GL_TEXTURE0);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_BLEND);

  glBindTexture(GL_TEXTURE_2D,texture);
  glDrawElements(GL_TRIANGLE_STRIP,4, GL_UNSIGNED_BYTE, (GLvoid*)0);

  glfwSwapBuffers(childWindow);
}

int main()
{
  if (!glfwInit())
    return -1;
  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,2);
  glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT,GL_TRUE);
  glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
  glfwWindowHint(GLFW_VISIBLE,GL_FALSE);
  masterWindow=glfwCreateWindow(10,10, "", NULL,NULL);
  glfwWindowHint(GLFW_VISIBLE,GL_TRUE);
  glfwWindowHint(GLFW_SAMPLES, 4);
  glfwMakeContextCurrent(masterWindow);
  glewExperimental = GL_TRUE;
  GLenum err = glewInit();
  if(GLEW_OK!=err)
    return -1;
  glGetError(); // catch benign glewInit() invalid enum error                                                                                            

  GLuint texture(0);
  texture=png_texture_load("rose.png");

  create_window_with_texture(texture);

  std::cin.get();

  return 0;
}
Uncivilized answered 25/4, 2014 at 18:54 Comment(2)
Does it work if you add glFinish() directly after loading the texture?Cornhusk
My goodness, it does!! Thank you so much! Want to make it an answer so I can accept it?Uncivilized
C
7

You must be aware that OpenGL will work asynchronously. The client side commands get queued up to be later processed by the GL server/GPU. Typically, the GL will hide this fact from the user, by implicitely syncing if you access data which is not available. However, when working with multiple contexts, you must explicitely synchronize the operations manually. Calling glFinish() will flush the command queue and wait until the commands have actually been processed. Your case is a bit special in that typically such situations arise in multi-threaded programs, where one thread creates/updates GL objects and transfers the hande to another thread to use the object.

I'm not sure if making another context current would or should lead to implicit syncing of the old context, so my guess was that it might not (but I have not read the relevant API documentations), and adding the glFinish might be required here (or using some other means of synchronization), too.

Cornhusk answered 25/4, 2014 at 19:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.