Why calling glfwSwapInterval can cause screen tearing?

When I do not call glfwSwapInterval, there will be no tearing and the screen will display normally. But if I call glfwSwapInterval, it will cause tearing.

Calling glfwSwapInterval (0) will cause the entire screen to tear. When calling glfwSwapInterval (1), the tearing situation is not as severe and will only start tearing at the position where there is a line above the screen.

When I do not call glfwSwapInterval, the glfwSwapBuffers usage time is 3.52859e-05.

When I call glfwSwapInterval (1), glfwSwapBuffers take 0.0156.

When I call glfwSwapInterval (0), the glfwSwapBuffers take 1.52588e-05.

If I switch to window mode, there won’t be tearing, but I am developing in full screen mode. Although I don’t call glfwSwapInterval or it can work normally, I still hope to understand why this is happening.

Here is my code:

class MainWindow
{
public:
    GLFWwindow* window;

    unsigned int SCR_WIDTH;
    unsigned int SCR_HEIGHT;
    
    unsigned int MAX_WIDTH;
    unsigned int MAX_HEIGHT;

    bool isWindow = false;
    bool isStop = false;
    int simpCountMSAAA = 8;

    bool enableMSAA = false;
    bool enableKeyDown = false;
    bool enableMoveMuose = false;
    bool enableScrollMouse = false;
    bool enableVSYNC = false;
    bool enableShowMouse = true;
    bool enableDebug = false;

    std::map<GLchar, Character> Characters;
    GLuint fontsVAO, fontsVBO;

    int Init()
    {
        glfwInit();

#ifdef _DEBUG
        enableDebug = true;
#endif

        if (enableDebug)
        {
            AllocConsole();
            freopen("CONOUT$", "w", stdout);
            freopen("CONIN$", "r", stdin);
        }

        HDC hdc = GetDC(NULL);
        MAX_WIDTH = GetDeviceCaps(hdc, DESKTOPHORZRES);
        MAX_HEIGHT = GetDeviceCaps(hdc, DESKTOPVERTRES);

        if (isWindow)
        {
            SCR_WIDTH = 800;
            SCR_HEIGHT = 600;
        }
        else
        {
            SCR_WIDTH = MAX_WIDTH;
            SCR_HEIGHT = MAX_HEIGHT;
            glfwWindowHint(GLFW_REFRESH_RATE, GLFW_DONT_CARE);
        }
 
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

        if (isWindow)
        {
            glfwWindowHint(GLFW_DECORATED, GLFW_TRUE);          
            glfwWindowHint(GLFW_MAXIMIZED, GLFW_FALSE);          
        }
        else
        {
            glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);        
            glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);         
        }

        if (true)
        {
            glfwWindowHint(GLFW_SAMPLES, simpCountMSAAA);
        }
        else
        {
            glfwWindowHint(GLFW_SAMPLES, 0);
        }

        // 创建GLFW窗口
        window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "StrategicDefenseInitiative", NULL, NULL);
        if (window == NULL)
        {
            std::cout << "Failed to create GLFW window" << std::endl;
            glfwTerminate();
            return -1;
        }

        glfwMakeContextCurrent(window);

   
        if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
        {
            std::cout << "Failed to initialize GLAD" << std::endl;
            return -1;
        }


        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);


        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


        FT_Library ft;
        if (FT_Init_FreeType(&ft))
            std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;

        FT_Face face;
        if (FT_New_Face(ft, "./Fonts/simsun.ttc", 0, &face))
            std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;

        FT_Set_Pixel_Sizes(face, 48, 48);

        glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //禁用字节对齐限制
        for (GLubyte c = 0; c < 128; c++)
        {
  
            if (FT_Load_Char(face, c, FT_LOAD_RENDER))
            {
                std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
                continue;
            }

            GLuint texture;
            glGenTextures(1, &texture);
            glBindTexture(GL_TEXTURE_2D, texture);
            glTexImage2D(
                GL_TEXTURE_2D,
                0,
                GL_RED,
                face->glyph->bitmap.width,
                face->glyph->bitmap.rows,
                0,
                GL_RED,
                GL_UNSIGNED_BYTE,
                face->glyph->bitmap.buffer
            );

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

            Character character = {
                texture,
                glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
                glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
                face->glyph->advance.x
            };
            Characters.insert(std::pair<GLchar, Character>(c, character));
        }
        glBindTexture(GL_TEXTURE_2D, 0);

        FT_Done_Face(face);
        FT_Done_FreeType(ft);

        glGenVertexArrays(1, &fontsVAO);
        glGenBuffers(1, &fontsVBO);
        glBindVertexArray(fontsVAO);
        glBindBuffer(GL_ARRAY_BUFFER, fontsVBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }
}

I also found that if I call the Init function twice, I will create two windows, one of which will be blank, and no matter what I call, there will be no tearing. I really want to know why?

If you could provide suggestions, I would greatly appreciate it.

Hi @DG3215656604,

Welcome to the GLFW forum.

First off I would carefully read the documentation on glfwSwapInterval.

Normally if the OS and GPU driver respect the setting, setting the swap interval to 1 with glfwSwapInterval will prevent tearing. You mention a time of 0.0156s which is very close to 1/60s == 0.01666s, the refresh time of a 60Hz monitor.

What do you mean by at the position where there is a line above the screen?

It would be helpful to know what OS you are using, and you might want to check if you have GPU driver settings for vsync / swap interval which are overriding the application setting.