Question

I'm trying to create sample app using glew and glfw. The main loop is straightforward and looks like:

 while (running) {
    someUsefullMathHere();
    renderer->render(timeDelta);

    glfwSwapBuffers(window);
    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}

The problem is that due to vsync current thread sleep for some tiin the glfwSwapBuffers execution (fps limited to 60 fps). I looking for a way to utilise this time for someUsefullMath method consecutive executions. Ideally code must looks something like:

while (running) {
    while (!timeToRenderAndSwap()) {
        someUsefullMathHere();
    }
    renderer->render(timeDelta);

    glfwSwapBuffers(window);
    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}

Is there a way for this?

Was it helpful?

Solution 2

The only feasible way to do this is to utilize a thread for your someUsefulMathHere() function. You can use regular thread synchronization methods to read back any results (e.g. a mutex, to name just one of may possibilities), or to queue up new jobs. This way you'll not only gain spare CPU cycles while the main thread truly sleeps, but you can also get additional performance on a multi-core CPU.

OTHER TIPS

Modern GL implementations will typically not block on the SwapBuffers, even if VSync is on. They will queue up GL commands for several frames ind advance and only block at the SwapBuffers after an implementation-specific limit on the number of queued frames is reached (nvidia's windows driver has an explicit setting for that, btw.). In the unmodified render loop you sketched, this leads to the situation that typically the first two to five iterations of the loop will not block, and every subsequent one will.

With reasonably modern GL, you can make use of sync objects, to improve on the situation. In pseudo code, this could look like this:

GLsync fence=NULL;
while (running) {
    if (fence) {
        while (glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT, 0)==GL_TIMEOUT_EXPIRED) {
            someUsefullMathHere();
        }
        glDeleteSync(fence);
        fence=NULL;
    }

    renderer->render(timeDelta);

    glfwSwapBuffers(window);
    fence=glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}

if (fence) {
    glDeleteSync(fence);
}

This makes use of the fact that the SwapBuffers will not immediately block, and will use all of the time until the buffer swap is actually carried out for the useful math you want to do.

This also has the side effect of limiting the latency to one frame, which might be a good or bad thing, depending on your scenario. But in principle, you also could interleave several fence sync objects, and wait for the second-last buffer swap instead for the last one, and so on.

First I want to note that even without vsync glfwSwapBuffers(window) could block for a while. Many functions of OpenGL simply put the command in a queue and return without waiting for completion. This mean the graphics card still may have a lot of work when you call glfwSwapBuffers(window). This function will wait until all work is finished before swapping the buffers and returning.

I'm not sure what type of work someUsefullMathHere() does so I have two possible answers:

If you need to do a specific amount of calculations every frame:
Often you have to update your scene every frame in addition to render it. To update your scene you will need to do some calculations every frame. You simply can put this code before glfwSwapBuffers(window) instead of putting it at the beginning or end of the loop. So you could achieve a higher frame rate without using threads. I think It should make no difference if the program is just waiting for vsync but it should make a difference if you have to wait for the graphics card:

while (running) {
    renderer->render(timeDelta);
    someUsefullMathHere();

    glfwSwapBuffers(window);
    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}

If you want to use the time for some work across frames:
Sometimes you have to do something which need too much time for a single frame. Something like loading a level or downloading data. To use the time until the buffers are swapped for things like this you can use a second thread. I think you could pause the thread while you are running the rest of the loop to avoid race conditions:

while (running) {
    renderer->render(timeDelta);

    unpauseThread();
    glfwSwapBuffers(window);
    pauseThreadWhenItIsSave();

    glfwPollEvents();

    running = running & (!glfwWindowShouldClose(window));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top