3

Currently, my Dear ImGui application (mainly the demo window with some custom OpenGL rendering) runs around 2000 fps if my window is small enough. How can I limit this to the monitor refresh rate (or even just 60fps)?

My current code looks like this:

static void glfw_error_callback(int error, const char * description)
{
    fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}

int main(int, char **)
{
    // Setup window
    glfwSetErrorCallback(glfw_error_callback);
    if (!glfwInit())
        return 1;
    const char * glsl_version = "#version 150";
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);  // 3.2+ only
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);            // Required on Mac

    glfwWindowHint(GLFW_REFRESH_RATE, 60);

    // Create window with graphics context
    GLFWwindow * window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", NULL, NULL);
    if (window == NULL)
        return 1;
    glfwMakeContextCurrent(window);
    glfwSwapInterval(1);  // Enable vsync

    bool err = gl3wInit() != 0;
    if (err) {
        fprintf(stderr, "Failed to initialize OpenGL loader!\n");
        return 1;
    }

    // Setup Dear ImGui binding
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO & io = ImGui::GetIO();
    (void)io;
    // io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Enable Keyboard Controls

    ImGui_ImplGlfw_InitForOpenGL(window, true);
    ImGui_ImplOpenGL3_Init(glsl_version);

    // Other stylistic setup
    ...

        while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();
        // Create ImGui Windows
        // Rendering
        ImGui::Render();
        int display_w, display_h;
        glfwMakeContextCurrent(window);
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
        glClear(GL_COLOR_BUFFER_BIT);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

        glfwMakeContextCurrent(window);
        glfwSwapBuffers(window);
    }

    // Cleanup
    return 0;
}

As you can see, it doesn't differ (nearly at all) from the original sample code for GLFW and OpenGL 3 given in the ImGui samples, apart from my unsuccessful attempt to limit the refresh rate using the glfwWindowHint(GLFW_REFRESH_RATE, 60), which I learned only affects the window in fullscreen mode. Also, I thought that the glfwSwapInterval(1) might also limit the refresh rate to the monitor's refresh rate, but it seems to not be doing that either. Any help would be much appreciated.

Edit: GLFW error function and loading.

iHowell
  • 2,263
  • 1
  • 25
  • 49
  • 2
    Have you tried e.g. `glfwSwapInterval(10);` for debug reasons? – Rabbid76 Jan 22 '19 at 19:20
  • That seems to not change anything. It still goes unbounded. I have an error function for glfw registered, but I'm not getting any errors. – iHowell Jan 22 '19 at 19:38
  • 1
    To all that see this (including @Rabbid76), I found that this is actually a recent issue with GFLW and Mac OSX 10.14 (Mojave). Looks like there is a pull request out, but until it is merged in and everything is running correct, I'm just going to leave the question open. https://github.com/glfw/glfw/issues/1337 – iHowell Jan 22 '19 at 19:48
  • As a last resource, you can store the last time the loop run, and skip it all if the lap for the current run is not big enough. – Ripi2 Jan 22 '19 at 20:02

3 Answers3

3

Setting glfwSwapInterval(0) removes the framecap. If you wish to have the framerate capped at 60fps, then pass 1 into the function.

cs1349459
  • 911
  • 9
  • 27
0

For people searching for a way to limit the framerate:

you can run a delay function written like this

void delay()
{
    auto start = std::chrono::high_resolution_clock::now();
    auto stop = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
    while (true)
    {
        stop = std::chrono::high_resolution_clock::now();
        duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start);
        if (duration.count() >= 20000)
        {
            return;
        }
        else
            continue;
    }
}

inside of the imgui main loop ^^ theres possibly another way to do it, but i found this to be very convenient.

Gisbert12843
  • 76
  • 1
  • 10
0

What I would use would be glfwSwapInterval(1). This will essentially enable vsync, which will run your program at the same fps as your monitor's refresh rate.

In my experimentation with glfwSwapInterval() whatever number you put in this function will result in

(monitor refresh rate, measured in hertz)/(number entered) = (application fps) #The application average ms/frame will increase

360hz/0 = 2400fps   #~.3 ms/frame (used ~70% of my gpu(3070ti) for the Demo Page)

360hz/1 = 360fps   #~2.7 ms/frame

360hz/2 = 180fps   #~5.5 ms/frame

360hz/4 = 90fps    #~11.12 ms/frame

360hz/6 = 60fpsf   #~16.6 ms/frame

putting in 0 will result in your computer running your program as fast as possible, wouldn't recommend

If you know what monitor your program is run on then this is an easy fix but if your distributing this then it can get a bit janky. My monitor runs at 360hz as its refresh rate, so a value of 6 will result in my program running at 60 fps. But then my program would run at only at 10 fps on a 60hz monitor which is still very common, especially in the work place. Refresh rates will also increase more as technology improves so it long term this isn't a great solution. I believe their are 500hz monitors for sale these days. That said it may not matter that much depending on what your use case is.

Another issue I found would be that if you have multiple monitors the your fps will change depending on what monitor your application is on. Mine can fluctuate from 360 to 220 to 180 depending on what monitor my GUI is on. Something to keep in mine

toyota Supra
  • 3,181
  • 4
  • 15
  • 19
waffleer
  • 1
  • 1