Search code examples
cloopssdltimingframe-rate

gaffer fixed timestep dropping frames


I have a very simple application currently, it's basically the core loop that I'm trying to get sorted out properly before moving on.

I'm following gaffer's fix your timestep article but I must be doing something wrong because rendering a quad rotating around all three axis I am already dropping frames. Basically hitting the if(frame_time > 0.025).. line of code, which leads me to believe that I am doing something terribly wrong. What's worse I'm only rendering to a 320x480 viewport clearing only the background color.

Here's the core function. I have some variables

double delta_time = 1.0f/60.0f;
double frame_time;
double start_time;
double current_time;
double accum;
static int ce_run_game() {
while (game_running > 0) {
    current_time = SDL_GetTicks();
    frame_time = (current_time - start_time) * 0.001f; //convert from 250ms to 0.025

    printf("ft: %f\n",frame_time);

    if (frame_time >= 0.025){
        frame_time = 0.025;
        printf("slowdown\n");
    }

    start_time = current_time;


    while (SDL_PollEvent(&event)) {
        ce_handle_events(&event);
        }
        if (event.type == SDL_QUIT) {
            ce_quit();
        }
    }

    accum += frame_time;
    while (accum >= delta_time) {
        accum -= delta_time;
        ce_fixed_update();
        ce_fixed_render();
    }

    double alpha = accum / delta_time;
    // state = current_state * alpha + prev_state * (1.0 - alpha);
    ce_var_update(alpha);
    ce_var_render(alpha);
}

    return -1;
}

with this simple loop I seem to be dropping frames. The other part of the code that is relevant is the movement code. While it's nothing amazing, it shouldn't cause so much slowdown.

Typically the frame time print statement is hovering around 0.016 - 0.017

but sometimes it will just raise a lot even up to 0.85 or higher. Adding in the if(frame_time > 0.025) above things seem to stick right around 0.16 0.17 but I am unsure of why the frame time will spike and they just get so out of control, especially since I am only moving around one thing on screen.

movement code:

static void handle_event(SDL_Event* e) {
    switch (e->type) {
        case SDL_KEYUP:
            left = right = up = down = 0;
            break;
        case SDL_KEYDOWN:
            switch (e->key.keysym.sym) {
                case SDLK_AC_BACK:
                case SDLK_ESCAPE:
                    ce_quit();
                    break;
                case SDLK_LEFT:
                    left = 1;
                    break;
                case SDLK_RIGHT:
                    right = 1;
                    break;
                case SDLK_UP:
                    up = 1;
                    break;
                case SDLK_DOWN:
                    down = 1;
                    break;
            }
    }
}


static void variable_render(double alpha) {
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(ce_get_default_shader()->shader_program);
    glBindVertexArray(vao);

    vvv = vec3_new(1, 1, 1);
    angle += 0.0015f * alpha;
    if (angle >= 360.0) angle = 0.0f;
    mat4_rotate(model_mat, model_mat, angle, vvv);

    ce_get_view_matrices(&vview_mat, &pproj_mat, &mmvp_mat);

    mat4_multi(&mmvp_mat, &vview_mat, model_mat);
    mat4_multi(&mmvp_mat, &pproj_mat, &mmvp_mat);
    glUniformMatrix4fv(mvp_matrix_loc, 1, GL_FALSE, mat4_get_data(&mmvp_mat));

    glUniformMatrix4fv(model_mat_loc, 1, GL_FALSE, mat4_get_data(model_mat));
    glUniformMatrix4fv(view_mat_loc, 1, GL_FALSE, mat4_get_data(&vview_mat));
    glUniformMatrix4fv(proj_matrix_loc, 1, GL_FALSE, mat4_get_data(&pproj_mat));

    glDrawElements(GL_TRIANGLES, quad->vertex_count, GL_UNSIGNED_SHORT, 0);
    glBindVertexArray(0);

}

Solution

  • I have to answer my own question here but it seems that there was two issues at hand.

    One was that I got this slowdown because of vsync, turning off vsync totally got rid of the slowdown which wasn't actually a slowdown in my logic update but was a slowdown in the rendering. Since I am using a fixed step this shouldn't be a big issue.

    The other issue is the fact that SDL_GetTicks() isn't very accurate. I rewrote the timing functions with clock_gettime(realtimeclock...) and it had a lot more consistent numbers.

    For example SDL_GetTicks would return anywhere from 240ms to 267ms per update while clock_gettime was consistent on a 250ms update.

    I was using SDL_SetDelay(0.25 * 1000) and clock_gettime was on point, SDL was lacking a bit there.

    It's not as cross platform as SDL but there's a high performance counter for windows and mac that can easily be implemented.

    here is the code that I am using for timing btw.

    #define _POSIX_C_SOURCE 199309L
    #include "headers/timer_utils.h"
    
    void tu_set_current_time(timer* time) { clock_gettime(CLOCK_REALTIME, time); }
    
    void tu_copy_current_time(timer* to, timer* from) {
        to->tv_sec = from->tv_sec;
        to->tv_nsec = from->tv_nsec;
    }
    
    double tu_get_time_diff(timer* start, timer* finish) {
        return (finish->tv_sec - start->tv_sec) +
               (finish->tv_nsec - start->tv_nsec) * 0.000000001;// / 1E9;
    }