Search code examples
openglglfw

Noticable lag in a simple OpenGL program with mouse input through GLFW


Here's a simple program that draws a triangle following the mouse cursor's position.

What I (and hopefully you) can notice, is that the triangle lags behind the cursor, it's not as tight as when dragging around even a whole window.

So my question is: What am I doing wrong? What leads to this lag?

One thing I realize is that it would suffice to shift the actual pixel values of the triangle, and not rasterize it again and again. But is rasterizing this one triangle really that expensive? I also tried using glTranslate instead of drawing at varying coordinates, but no improvement on the lag resulted. So I hope you can enlighten me on how to draw this efficiently.

#include <GLFW/glfw3.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

float x = 0.0f;
float y = 0.0f;

static void error_callback(int error, const char* description)
{
    fputs(description, stderr);
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

static void cursor_callback(GLFWwindow *window, double xpos, double ypos)
{
    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    float ratio = width / (float) height;
    x = ratio*(2*xpos/(float)width - 1);
    y = 2*-ypos/(float)height + 1;
}

int main(void)
{
    GLFWwindow* window;
    glfwSetErrorCallback(error_callback);
    if (!glfwInit())
        exit(EXIT_FAILURE);
    window = glfwCreateWindow(640, 480, "Following Triangle", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }
    glfwMakeContextCurrent(window);
    // Callbacks
    glfwSetKeyCallback(window, key_callback);
    glfwSetCursorPosCallback(window, cursor_callback);
    // geometry for the equal sided triangle
    float r = 0.1f; // outer circle radius
    float u = r * sin(M_PI_2/3.0f);
    float l = 2.0f * r * cos(M_PI_2/3.0f);

    while (!glfwWindowShouldClose(window))
    {
        int width, height;
        glfwGetFramebufferSize(window, &width, &height);
        float ratio = width / (float) height;
        glViewport(0, 0, width, height);
        glClear(GL_COLOR_BUFFER_BIT);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(-ratio, ratio, -1.0f, 1.0f, 1.f, -1.f);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glBegin(GL_TRIANGLES);
            glColor3f(1.f, 0.f, 0.f);
            glVertex3f(x+0, y+r, 0.f);
            glColor3f(0.f, 1.f, 0.f);
            glVertex3f(x-l/2.0f, y-u, 0.f);
            glColor3f(0.f, 0.f, 1.f);
            glVertex3f(x+l/2.0f, y-u, 0.f);
        glEnd();

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwDestroyWindow(window);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

Solution

  • Your updates are purely event driven. Try replacing glfwPollEvents with glfwWaitEvents. I would then reimplement glfwSwapInterval(1). You gain nothing by updating more frequently than the monitor refresh rate - just tearing and burning through cycles.