6 Replies Latest reply on Aug 20, 2014 2:13 PM by leonmaxx

    glMapBuffer and secondary thread problem

    maxis11

      glDrawArray doesn't draw anything when using secondary thread + glMapBuffer(thread receive pointer and use it as a buffer) to update data in VBO. Example code:

       

      #include <thread>
      #include <GL/glew.h>
      #include <GLFW/glfw3.h>
      
      
      const char vsource [] = "#version 330 core\nin vec3 position;\nvoid main(void){gl_Position = vec4(position,1.0f);}\0";
      const char fsource [] = "#version 330 core\nvoid main(void){gl_FragColor = vec4(1.0f,0.0f,0.0f,1.0f);}\0";
      
      
      bool ext = 0;
      
      
      void gen_function(float *data, unsigned count)
      {
          srand(time(NULL));
          while (!ext)
          {
              for (unsigned i = 0; i < (count/3)*3; i+=3)
              {
                  data[i] = ((rand() % 2001) * 0.001f) - 1.0f;
                  data[i+1] = ((rand() % 2001) * 0.001f) - 1.0f;
                  data[i+2] = 0.0f;
              }
          }
      }
      
      
      int main()
      {
          if (!glfwInit())
              return 1;
          glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
          glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
          glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
          glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
          GLFWwindow* window = glfwCreateWindow(640, 480, "Test", NULL, NULL);
          glfwMakeContextCurrent(window);
          glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
          glewExperimental = GL_TRUE;
          glewInit();
          GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
          char const* source_pointer = vsource;
          glShaderSource (vshader,1,&source_pointer,NULL);
          glCompileShader(vshader);
          GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
          source_pointer = fsource;
          glShaderSource (fshader,1,&source_pointer,NULL);
          glCompileShader(fshader);
          GLuint program = glCreateProgram();
          glAttachShader(program,vshader);
          glAttachShader(program,fshader);
          glLinkProgram(program);
          GLuint VAO;
          glGenVertexArrays(1,&VAO);
          glBindVertexArray(VAO);
          GLuint VBO;
          glGenBuffers(1,&VBO);
          glBindBuffer(GL_ARRAY_BUFFER,VBO);
          glBufferData(GL_ARRAY_BUFFER,300*sizeof(float),NULL,GL_DYNAMIC_DRAW);
          void* ptr = glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY | GL_MAP_UNSYNCHRONIZED_BIT);
          GLint pos = glGetAttribLocation(program,"position\0");
          glVertexAttribPointer(pos,3,GL_FLOAT,GL_FALSE,0,0);
          glEnableVertexAttribArray(pos);
          glUseProgram(program);
          std::thread thread1(gen_function,(float*)ptr,300);
          glPointSize(2.0f);
          do
          {
              glClear(GL_COLOR_BUFFER_BIT);
              glDrawArrays(GL_POINTS,0,100);
              glfwSwapBuffers(window);
              glfwPollEvents();
          } while (glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
                   glfwWindowShouldClose(window) == 0);
          ext = 1;
          thread1.join();
          glDisableVertexAttribArray(pos);
          glUnmapBuffer(GL_ARRAY_BUFFER);
          glDeleteBuffers(1,&VBO);
          glDeleteVertexArrays(1,&VAO);
          glDeleteProgram(program);
          glDeleteShader(vshader);
          glDeleteShader(fshader);
          glfwTerminate();
          return 0;
      }
      
      
      

       

      If I use open source driver(mesa(r600g)) then program draws without problems.
      Hardware: 6480G + 7470M
      OS: Kubuntu 14.10 daily
      Driver: AMD Catalyst 14.6 jul11

        • Re: glMapBuffer and secondary thread problem
          maxis11

          glMapBuffer in 14.6 jul11 totally broken. Using only one thread causes same result(black screen or single dot in the centre of window)

            • Re: glMapBuffer and secondary thread problem
              cgrant78@netzero.com

              looking at your code, the thread is joined with the main thread after the draw call. What synchronization do you have in place to make sure the buffer is not being modified during the draw call ?

              Using fences/sync I've been able to upload in one thread and use the data for drawing in the main up to and including the latest Catalyst drivers. Unless you can guarantee that the upload if completed before actually using then the result is undefined. Unless the buffer is mapped with the persistent bit set, then what you are doing should come up as an invalid operation.

                • Re: Re: glMapBuffer and secondary thread problem
                  maxis11
                  #include <thread>
                  #include <cstring>
                  #include <GL/glew.h>
                  #include <GLFW/glfw3.h>
                  
                  
                  const char vsource [] = "#version 330 core\nin vec3 position;\nvoid main(void){gl_Position = vec4(position,1.0f);}\0";
                  const char fsource [] = "#version 330 core\nvoid main(void){gl_FragColor = vec4(1.0f,0.0f,0.0f,1.0f);}\0";
                  
                  
                  int main()
                  {
                      if (!glfwInit())
                          return 1;
                      glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
                      glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
                      glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
                      glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
                      GLFWwindow* window = glfwCreateWindow(640, 480, "Test", NULL, NULL);
                      glfwMakeContextCurrent(window);
                      glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
                      glewExperimental = GL_TRUE;
                      glewInit();
                      GLuint vshader = glCreateShader(GL_VERTEX_SHADER);
                      char const* source_pointer = vsource;
                      glShaderSource (vshader,1,&source_pointer,NULL);
                      glCompileShader(vshader);
                      GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER);
                      source_pointer = fsource;
                      glShaderSource (fshader,1,&source_pointer,NULL);
                      glCompileShader(fshader);
                      GLuint program = glCreateProgram();
                      glAttachShader(program,vshader);
                      glAttachShader(program,fshader);
                      glLinkProgram(program);
                      GLuint VAO;
                      glGenVertexArrays(1,&VAO);
                      glBindVertexArray(VAO);
                      GLuint VBO;
                      glGenBuffers(1,&VBO);
                      glBindBuffer(GL_ARRAY_BUFFER,VBO);
                      glBufferData(GL_ARRAY_BUFFER,300*sizeof(float),NULL,GL_DYNAMIC_DRAW);
                      void* ptr = glMapBuffer(GL_ARRAY_BUFFER,GL_WRITE_ONLY | GL_MAP_UNSYNCHRONIZED_BIT);
                      GLint pos = glGetAttribLocation(program,"position\0");
                      glVertexAttribPointer(pos,3,GL_FLOAT,GL_FALSE,0,0);
                      glEnableVertexAttribArray(pos);
                      glUseProgram(program);
                      float* data = new float[300];
                      glPointSize(2.0f);
                      do
                      {
                          for (unsigned i = 0; i < 300; i+=3)
                          {
                              data[i] = ((rand() % 2001) * 0.001f) - 1.0f;
                              data[i+1] = ((rand() % 2001) * 0.001f) - 1.0f;
                              data[i+2] = 0.0f;
                          }
                          memcpy(ptr,data,300);
                          glClear(GL_COLOR_BUFFER_BIT);
                          glDrawArrays(GL_POINTS,0,100);
                          glfwSwapBuffers(window);
                          glfwPollEvents();
                      } while (glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
                               glfwWindowShouldClose(window) == 0);
                      delete [] data;
                      glDisableVertexAttribArray(pos);
                      glUnmapBuffer(GL_ARRAY_BUFFER);
                      glDeleteBuffers(1,&VBO);
                      glDeleteVertexArrays(1,&VAO);
                      glDeleteProgram(program);
                      glDeleteShader(vshader);
                      glDeleteShader(fshader);
                      glfwTerminate();
                      return 0;
                  }
                  
                  

                  There is same result if I use this example. Additionally I tried to add std::unique_lock, std::mutex, std::condition_variable (unique_lock lock main thread until loop fill data, then condition_variable notify to unique_lock but the result was same).

                    • Re: Re: Re: glMapBuffer and secondary thread problem
                      leonmaxx

                      You cannot use mapped buffer as source for glDraw* calls in OpenGL 3.3 (version "330" is specified in Your GLSL code). Change Your code to:

                       

                              // Map buffer before update

                              float* data = (float*) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY | GL_MAP_UNSYNCHRONIZED_BIT); 

                              for (unsigned i = 0; i < 300; i+=3{

                                  data[i] = ((rand() % 2001) * 0.001f) - 1.0f; 

                                  data[i+1] = ((rand() % 2001) * 0.001f) - 1.0f; 

                                  data[i+2] = 0.0f; 

                              } 

                              // Unmap buffer before use

                              glUnmapBuffer(GL_ARRAY_BUFFER);


                              glClear(GL_COLOR_BUFFER_BIT); 

                              glDrawArrays(GL_POINTS,0,100); 

                            ...

                       

                      Also notice that Your memcpy() call, just copies 300 bytes instead of copying 300*sizeof(float) which is also incorrect.

                      If You still want to update buffers and use them for glDraw* calls at the same time, You have to use ARB_buffer_storage extension of OpenGL 4.4 (e.g. create and map buffer with GL_MAP_PERSISTENT_BIT flag).

                       

                      Regards,

                      Leon.

                • Re: glMapBuffer and secondary thread problem
                  chm

                  The problem might be that you don't unmap your buffer before accessing its data with gl draw commands.

                   

                  Chris

                  1 of 1 people found this helpful
                    • Re: glMapBuffer and secondary thread problem
                      cgrant78@netzero.com

                      The updated code you posted still doesn't have any form of synchronization. CPU based synchronization ( locks, mutexes etc ) will not help in this case and here is why. Whenever you issue a command to the GPU, it may seem synchronous from an application perspective, but in reality the command may not be executed until several frames later ( drawing blank on link to resources, but there is plenty of documentation online describing this behavior ). With that said, CPU based sync cannot guarantee that the GPU have reached the command in its command stream,  you will need GPU sync for that ( GL_NV_fence, GL_ARB_sync, see the man pages ). The only way this may work without synchronization is to unmap the buffer like chm said, or if you are using a GL 4.4 context, mapping the buffer with the persistent bit set. The documentation for glDrawArrays states :

                      GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to an enabled array and the buffer object's data store is currently mapped.