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);

This system is absolutely capable for performing 60FPS. For references sake it can run doom 3 at 30FPS. Not exactly killer performance, but more than capable of doing a 60FPS blank window. Indeed, because I couldn’t get this to work, I’ve gone a much more complicated EGL approach. I’d really love to use GLFW. I’m about to raise this as a bug on the raspberry pi github, but I doubt we’ll see much headway.

The other thing I don’t understand, is when I call glfwSwapInterval(1); instead of getting 60 FPS, I get 30FPS. Any idea what that might mean?

It would be interesting to see the difference between the EGL approach and the GLFW one in terms of how the swapbuffers works. At the moment it’s tricky to know if this is GLFW specific or not.

The other thing I don’t understand, is when I call glfwSwapInterval(1); instead of getting 60 FPS, I get 30FPS. Any idea what that might mean?

Usually this is either because the monitor refresh rate is 30Hz or because the frame render takes longer than 16ms. Do you know what refresh rate is set when you are fullscreen (you can get this from glfwGetVideoMode)?

As noted above there are known issues with Wayland and glSwapBuffers (which will also affect X11 on Wayland).

There could also be an issue with GLFW on your system but the GLFW code for buffer swapping on EGL is trivial so I can’t see why there would be any impact of using GLFW vs EGL directly:

EDIT: did you try setting the mode hints as per Drastic performance decrease when full screen vs almost full screen - #5 by dougbinks?