Drastic performance decrease when full screen vs almost full screen

GLFW 3.3.8-1
Raspberry Pi 5.
Raspberry Pi OS: Linux raspberrypi 6.6.28+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.6.28-1+rpt1 (2024-04-22) aarch64 GNU/Linux
Windowing: Wayland

My monitors native resolution is 1920 x 1200. If I make a window that is just not full screen (1920, 1180) and then repeatedly call glfwSwapBuffers() with no kind of rendering and time how long between buffer swaps, I get about 0.2 milliseconds, i.e. 5000 FPS

However, if I make the window full screen, and perform the same test I get a handful of frames with a 0.2 inter-frame time. Then it drops to 33.3 millisecond (which is very curiously, almost exactly 2 refreshes), i.e. 30 FPS

I appreciate I am on some atypical hardware, so I’m happy to try to look into anything.

My test code is as follows:

#include <stdio.h>
#include <GLFW/glfw3.h>
#include <sys/time.h>

struct timeval tv;
unsigned long
  frameCount = 0,
  time_in_micros,
  oldTime;

int main(void) {

    glfwInit();

    GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor();
    const GLFWvidmode* mode = glfwGetVideoMode(primaryMonitor);
 
    //glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); //This makes no difference
    
    //Swapping between these two versions are what causes problems.
    //GLFWwindow* window = glfwCreateWindow(1920, 1180, "My Title", NULL, NULL);
    GLFWwindow* window = glfwCreateWindow(1920, 1200, "My Title", primaryMonitor, NULL);

    glfwMakeContextCurrent(window);
    glfwSwapInterval(0);

    while (!glfwWindowShouldClose(window)) {
        frameCount++;
        glfwSwapBuffers(window);
        if (frameCount % 10 == 0) {
            oldTime = time_in_micros;
            gettimeofday(&tv,NULL);
            time_in_micros = 1000000 * tv.tv_sec + tv.tv_usec;
            printf("Interframe time is %f\n", (float)(time_in_micros-oldTime)/10000);
        }
    }

    glfwTerminate();
    return 0;
}

I’m not sure what’s causing this. I note you’re not polling events though, which I would recommend doing (i.e. call glfwPollEvents() after glfwSwapBuffers(window)).

GLFW itself isn’t doing anything special here, as the abstraction of glfwSwapBuffers is pretty thin in your case (pretty much just calling eglSwapBuffers), so the behaviour is almost certainly down to the OS + window manager + OpenGL + driver behaviour. There could be something which GLFW is doing to cause this, but it seems unlikely.

The first few long frames could be due to the switch from windowed mode to fullscreen, then the rest due to vsync being forced on when fullscreen, perhaps even forced to 2.

Note: for timing you can use the GLFW timer functions: GLFW: Time input

You might want to try using X11 instead of Wayland to see if that makes a difference. I know that there are outstanding problems with Wayland and glSwapBuffers (at the Wayland level, rather than GLFW - the linked issue is with SDL).

Thanks Doug. I believe you that it might not be glfw’s fault. But I wanted to check.

Polling doesn’t change anything. I had that before, but I was just trying to provide you with minimal possible code.

For what it is worth, when I use X11 the code works, but the frame rates jump around a lot. Sometimes 60, sometimes 30 FPS

I am a bit a bit puzzled, but I don’t know what to expect from this system + setup. Do you have any other fullscreen OpenGL non-GLFW apps you can test with?

It might also be worth checking that you’re setting the same mode (bits as well as refresh rate):

const GLFWvidmode* mode = glfwGetVideoMode(monitor);
 
glfwWindowHint(GLFW_RED_BITS, mode->redBits);
glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
 
GLFWwindow* window = glfwCreateWindow(mode->width, mode->height, "My Title", monitor, NULL);