cancel
Showing results for 
Search instead for 
Did you mean: 

Archives Discussions

maxis11
Adept I

glMapBuffer and secondary thread problem

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 = ((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

0 Likes
1 Solution

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.

View solution in original post

0 Likes
6 Replies
maxis11
Adept I

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

0 Likes

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.

0 Likes


#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 = ((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).

0 Likes

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 = ((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.

0 Likes
chm
Staff
Staff

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

Chris

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.

0 Likes