1
\$\begingroup\$

I have a problem with a simple game loop. As my rendering and iteration functions together take 10 ms, the time between update and swapBuffers measuers around 140 ms. What is causing this. Is there a straightforward way to fix this.

Could it be related to the fact that each rect() (in a for loop) calls glDrawElements() and it somehow ques the whole process.

void game::startup()
{
    startup();
    glfwSwapInterval(1);
}

void game::render(double currentTime)
{
    static const GLfloat green[] = { 0.0f, 0.25f, 0.0f, 1.0f };
    static const GLfloat one = 1.0f;

    glClearBufferfv(GL_COLOR, 0, green);
    glClearBufferfv(GL_DEPTH, 0, &one);

    auto t1 = std::chrono::high_resolution_clock::now();

    for (size_t y = 0; y < game_board.getWorldHeight(); ++y) {
        for (size_t x = 0; x < golf_board.getWorldWidth(); ++x) {
            rect(x * cell_size, y * cell_size, cell_size, cell_size, game_board.get_state(y * game_board.getWorldWidth() + x));
        }
    }

    auto t2 = std::chrono::high_resolution_clock::now();
    game_board.iterate(1);
    auto t3 = std::chrono::high_resolution_clock::now();

    float rendertime = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() / 1000.0f;
    float generatetime = std::chrono::duration_cast<std::chrono::microseconds>(t3 - t2).count() / 1000.0f;

    std::chrono::milliseconds ms_perframe(1000 / 60);  // About 60 fps
    auto cur_time = std::chrono::high_resolution_clock::now();
    float sleep_time = std::chrono::duration_cast<std::chrono::microseconds>(t1 + ms_perframe - t2).count() / 1000.0f;

    std::this_thread::sleep_for(t1 + ms_perframe - t3);

    cout << "render time (ms)" << rendertime <<"\n"
         << "generate time (ms)" <<generatetime << "\n"
         << "sleeping time (ms)" << sleep_time << "\n";
}

Output (board size: 256 x 128 = 32768 rectangles drawn per frame):

render time (ms) 8.1
generate time (ms) 1.3
sleeping time (ms) 6.6
SWAP TIME (ms) 146

application run function rendering part:

startup();

do
{
    render(glfwGetTime());

    auto t1 = std::chrono::high_resolution_clock::now();
    glfwSwapBuffers(window);
    auto t2 = std::chrono::high_resolution_clock::now();
    float  swap_time = 
    std::chrono::duration_cast<std::chrono::microseconds>(t2-t1).count() / 1000.0f;
    std::cout << "SWAP TIME (ms)" << swap_time << "\n";

    glfwPollEvents();

    running &= (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_RELEASE);
    running &= (glfwWindowShouldClose(window) != GL_TRUE);

} while (running);

shutdown();

glfwDestroyWindow(window);
glfwTerminate();

rectangle draw function:

rect(int x, int y, int w, int h, int state) {
// input is top left corner of rectangle, width and height of rectange 
and the state of unit

GLfloat fstate = (float)state * 0.5f;
GLfloat vertices[] = {
    x,  y,      fstate,         // Top-left
    x + w,  y,  fstate,         // Top-right
    x + w,  y + h,   fstate,    // Bottom-right
    x, y + h,    fstate          // Bottom-left
};


glBufferSubData(GL_ARRAY_BUFFER,0, sizeof(vertices),vertices);
glUseProgram(shaderProgram);

glUniformMatrix4fv(mvp_location, 1, GL_FALSE, mvp);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
\$\endgroup\$
  • \$\begingroup\$ Where is the swapBuffers call? I don't see that anywhere in your code. Show us the slow code, not the fast code, if that's what you need help with. \$\endgroup\$ – Dan Aug 23 '17 at 18:12
  • \$\begingroup\$ Also, out of curiosity, try changing glfwSwapInterval(1); to glfwSwapInterval(0); and let us know what effect, if any, that has. \$\endgroup\$ – Dan Aug 23 '17 at 18:15
  • \$\begingroup\$ i added extra code. Swap buffers is actually right after iterate() as described above. Changing swapinterval to 0 or -1 does not affect performance. \$\endgroup\$ – fredric Aug 24 '17 at 6:49
1
\$\begingroup\$

Currently you modify the buffer for each rectangle you draw. This is a very bad way to draw rectangles, because it's very slow.

Create instead a 1 unit by 1 unit square, and use a scale matrix to make it the appropariate size.

\$\endgroup\$
  • \$\begingroup\$ I only vaguely get the idea because i have little experience with OpenGl. So do you suggest the vertex buffer should stay unchanged for every draw call but the matrices for scaling and moving rectangle to right position to be updated before drawing each rectangle? How could i pass this matrix to a shader without altering any buffers. . And what about colors. each rectangle has individual color (black or white, now passed to shader as texcoords with each vertex) depending on state. Could you be a little more specific about the solution? Maybe some example code/pseudo code. \$\endgroup\$ – fredric Aug 24 '17 at 11:16
  • \$\begingroup\$ @fredric Uniforms can be loaded between drawcalls, so you first create the matrix, load it im the GPU, create a vector containing the rectangle's color, load it too, draw the rectangle with glDrawElements and repeat \$\endgroup\$ – Bálint Aug 24 '17 at 18:24
  • \$\begingroup\$ i used uniform matrices for translate and scale, and used uniform float for texcoord.. it went faster of about 2x. Now the whole render loop takes 90 - 100ms (swap takes 50 render 35-40) befor it took 200. But even with this improvement it is very slow. What are the fastest options for this rendering scenario? \$\endgroup\$ – fredric Aug 24 '17 at 21:47
  • \$\begingroup\$ @fredricThe swap shouldn't take 50 milliseconds \$\endgroup\$ – Bálint Aug 25 '17 at 0:32
  • \$\begingroup\$ i fixed a bug and performance went up by about 2x again. swap now takes 7ms mostly but every couple of frames it shows 25. Do not know how to read this but the overall movement is much faster. render-update loop takes about 40ms. So overall duration is around 50 ms. I guess this is the limit for drawing rectangles one by one? What approach would be appropriate for rendering larger boards fast. \$\endgroup\$ – fredric Aug 25 '17 at 7:30
0
\$\begingroup\$

Also remember when you have two loops. Always traverse in row major as it increases spatial locality and increases cache coherency. Edit: In your case you are doing it right I overlooked it.

You could also use instancing to send geometry of one rect and mutiple matrices and draw using instancing .

Also correct way to major time taken by rendering loop is as follows

Loop
{
  Call glFinish()
  Start timer

  Do rendering 
  Call swap buffers.
  Call glFinish()
  End timer.
  Get difference in start and end
}

This is important as opengl calls are not synchronised to your application.

\$\endgroup\$
  • \$\begingroup\$ But he is traversing it correctly \$\endgroup\$ – Sopel Oct 8 '17 at 10:16
  • \$\begingroup\$ I dint mean he is traversing it wrongly. Just suggested point that might help in performace.Sorry I my comment suggested that. \$\endgroup\$ – Paritosh Kulkarni Oct 8 '17 at 11:30
  • \$\begingroup\$ "You are traversing in coloumn major order which is bad for cache performance" you clearly say he's doing it wrong now \$\endgroup\$ – Sopel Oct 8 '17 at 13:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.