/* * g++ -std=c++11 test.cpp -lGLEW -lGLU -lGL -lglfw -o test */ #include #include #include #include #include #include #include #include #include /**************************************************************************/ #define WINDOW_WIDTH 360 #define WINDOW_HEIGHT 180 enum : int { kCoo2d = 0 }; std::map g_attMap = { {kCoo2d, "coord2d"} }; const char *quadVert = R"glsl( #version 120 attribute vec2 coord2d; void main(void) { gl_Position = vec4(coord2d, 0., 1.); } )glsl"; const char *quadFrag = R"glsl( #version 120 vec4 color = vec4( 0., 1., 0., 1.); void main() { gl_FragColor = color; } )glsl"; class Program { public: Program(const char *vert, const char *frag) { programId_ = load(vert, frag, vertShId_, fragShId_); if (programId_ == 0) { abort(); } } ~Program() { glUseProgram(0); glDeleteProgram(programId_); glDeleteShader(vertShId_); glDeleteShader(fragShId_); } GLuint programId() const { return programId_; } private: GLuint programId_ = 0; GLuint vertShId_ = 0; GLuint fragShId_ = 0; static void checkStatus(GLuint obj) { GLint status = GL_FALSE; if (glIsShader(obj)) { glGetShaderiv(obj, GL_COMPILE_STATUS, &status); } if (glIsProgram(obj)) { glGetProgramiv(obj, GL_LINK_STATUS, &status); } if (status == GL_TRUE) { return; } GLchar log[1 << 15] = { 0 }; if (glIsShader(obj)) { glGetShaderInfoLog(obj, sizeof(log), NULL, log); } if (glIsProgram(obj)) { glGetProgramInfoLog(obj, sizeof(log), NULL, log); } fprintf(stderr, "%s\n", log); abort(); } static GLuint attachShader(GLuint program, GLenum type, const char* src) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &src, NULL); glCompileShader(shader); checkStatus(shader); glAttachShader(program, shader); glDeleteShader(shader); return shader; } static GLuint load(const char* vert, const char* frag, GLuint &vertShId, GLuint &fragShId) { GLuint prog = glCreateProgram(); if (vert) { vertShId = attachShader(prog, GL_VERTEX_SHADER, vert); } if (frag) { fragShId = attachShader(prog, GL_FRAGMENT_SHADER, frag); } glBindAttribLocation(prog, kCoo2d, g_attMap[kCoo2d].data()); glLinkProgram(prog); checkStatus(prog); GLint checkId = glGetAttribLocation(prog, g_attMap[kCoo2d].data()); if (checkId == -1 || checkId != kCoo2d) { fprintf(stderr, "Could not bind attribute %s\n", g_attMap[kCoo2d].data()); abort(); } return prog; } }; /**************************************************************************/ //#define ELEMENT_CNT (4095 * 2) // NO ERROR //#define ELEMENT_CNT (4096 * 2) // ERROR, >= 4096 #define ELEMENT_CNT (4096 * 2) class Drawer { public: Drawer() { fprintf(stderr, "Using GL_ARRAY_BUFFER of size %d elements, %d bytes\n", ELEMENT_CNT, ELEMENT_CNT * (int)sizeof(float)); // init verts_.fill(-1.); std::iota(data_.begin(), data_.end(), 0); glGenVertexArrays(1, &vao_); glBindVertexArray(vao_); glGenBuffers(1, &vbo_); glBindBuffer(GL_ARRAY_BUFFER, vbo_); glBufferData(GL_ARRAY_BUFFER, ELEMENT_CNT * sizeof(float), NULL, GL_STREAM_DRAW); glEnableVertexAttribArray(kCoo2d); glVertexAttribPointer(kCoo2d, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } ~Drawer() { glDeleteBuffers(1, &vbo_); glDeleteVertexArrays(1, &vao_); } void draw() const { glBindVertexArray(vao_); // do something with the buffer glDrawArrays(GL_TRIANGLE_STRIP, 0, ELEMENT_CNT / 2); glBindVertexArray(0); } void randomUpdate() { static int cnt = 0; cnt++; // error appears quickly when the updated range is important int a = 1 + (std::rand() % 100); int b = ELEMENT_CNT - 1; int offset = a * sizeof(float); int size = (b - a + 1) * sizeof(float); // suppress the error //glFinish(); glBindVertexArray(vao_); glBindBuffer(GL_ARRAY_BUFFER, vbo_); // update the buffer glBufferSubData(GL_ARRAY_BUFFER, offset, size, &data_[0]); // get it back glGetBufferSubData(GL_ARRAY_BUFFER, offset, size, &data_[0]); // check, data_ contains value (float)i at index i for (int i = 0; i < b - a + 1; i++) { if (data_[i] != (float)i) { fprintf(stderr, "(%f should be %f) at index %d after " "updating from index %d to %d and %d updates\n", data_[i], (float)i, a + i, a, b, cnt); abort(); } } glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); } private: std::array data_; // 0, 1, 2, 3 ... std::array verts_; GLuint vao_ = 0, vbo_ = 0; }; /**************************************************************************/ int main() { srand(1000); if (!glfwInit()) { exit(EXIT_FAILURE); } if (!glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, 0, 0, 16, 0, GLFW_WINDOW)) { glfwTerminate(); exit(EXIT_FAILURE); } glewExperimental = GL_TRUE; GLenum glew_status = glewInit(); if (glew_status != GLEW_OK) { exit(EXIT_FAILURE); } glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); glClearColor(1., 1., 1., 1.); Program prog(quadVert, quadFrag); glUseProgram(prog.programId()); Drawer drawer; while (glfwGetWindowParam(GLFW_OPENED)) { glClear(GL_COLOR_BUFFER_BIT); // use vblank_mode=0 or this to make the error appears everytime //for (int i = 0; i < 100; i++) { drawer.randomUpdate(); drawer.draw(); glfwSwapBuffers(); //} assert(glGetError() == GL_NO_ERROR); glfwPollEvents(); } glfwCloseWindow(); glfwTerminate(); return EXIT_SUCCESS; }