raspberrypi / bookworm-feedback

12 stars 1 forks source link

Can't get above 30FPS in full screen with GLFW #262

Open bill-connelly opened 2 weeks ago

bill-connelly commented 2 weeks ago

Describe the bug GLFW is a popular openGL library. However, it seems to misbehave when running at full screen on a raspberry pi 4 or 5. EVEN WHEN NOT RENDERING. Specifically it drops to 30FPS as soon as the window is full screen. Rendering in a windowed mode, a few pixels short of full screen allows performance in the 100s or 1000s of frames per second.

What makes it stranger is that for the first few 1000 frame updates or so, the inter-frame time is very low. equivalent to 3000 frames per second. But then it starts to chug at 30FPS.

GLFW developers don't think it can't be their fault. To quote them

"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."

To reproduce This code shows to issue:

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

    //Swapping between these two versions are what causes problems.
    GLFWwindow* window = glfwCreateWindow(1920, 1180, "My Title", NULL, NULL);
    //GLFWwindow* window = glfwCreateWindow(mode->width, mode->height, "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;
}

Compile with: gcc glfw5.c -o glfw5 -lglfw

Expected behaviour I expect 60FPS or better. Given that the first 400 frames/buffer swaps all happen in less than 1 millisecond, I would expect this to continue, but after around 400 buffer swaps, we get to 30 FPS

Actual behaviour A print out from the above looks like this: Interframe time is 0.377200 Interframe time is 0.335000 Interframe time is 0.336000 Interframe time is 1.431100 Interframe time is 2.262200 Interframe time is 0.314800 Interframe time is 0.292700 Interframe time is 0.305800 Interframe time is 0.294900 Interframe time is 0.299900 Interframe time is 0.952400 Interframe time is 3.718800 Interframe time is 0.668100 Interframe time is 3.280600 Interframe time is 0.359700 Interframe time is 0.383500 Interframe time is 0.375100 Interframe time is 1.101400 Interframe time is 4.069400 Interframe time is 4.337800 Interframe time is 0.282200 Interframe time is 0.290200 Interframe time is 0.659600 Interframe time is 2.090900 Interframe time is 0.230600 Interframe time is 0.223700 Interframe time is 0.213000 Interframe time is 0.539900 Interframe time is 2.131800 Interframe time is 0.206400 Interframe time is 0.276300 Interframe time is 0.196400 Interframe time is 0.716800 Interframe time is 0.546900 Interframe time is 0.445100 Interframe time is 2.523900 Interframe time is 2.230000 Interframe time is 0.207300 Interframe time is 10.192000 Interframe time is 33.365799 Interframe time is 33.335899 Interframe time is 33.357399

System Copy and paste the results of the raspinfo command in to this section. Alternatively, copy and paste a pastebin link, or add answers to the following questions:

System Information

Raspberry Pi 4 Model B Rev 1.1 PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" NAME="Debian GNU/Linux" VERSION_ID="12" VERSION="12 (bookworm)"

Raspberry Pi reference 2024-03-15 Generated using pi-gen, https://github.com/RPi-Distro/pi-gen, f19ee211ddafcae300827f953d143de92a5c6624, stage5

Linux raspberrypi 6.6.28+rpt-rpi-v8 raspberrypi/firmware#1 SMP PREEMPT Debian 1:6.6.28-1+rpt1 (2024-04-22) aarch64 GNU/Linux Revision : c03111 Serial : 10000000dc24ffb0 Model : Raspberry Pi 4 Model B Rev 1.1 Throttled flag : throttled=0x0 Camera : supported=0 detected=0, libcamera interfaces=0

Videocore information

Apr 17 2024 17:27:09 Copyright (c) 2012 Broadcom version 86ccc427f35fdc604edc511881cdf579df945fb4 (clean) (release) (start)

alloc failures: 0 compactions: 0 legacy block fails: 0

"

timg236 commented 2 weeks ago

Moved to bookworm-feedback because all OpenGL and display related code was moved out of the firmware on Raspberry Pi 4.

Most likely an interaction with Mesa or Compositor

timg236 commented 2 weeks ago

This is probably compositor related. It might be worth trying the labwc compositor which is better able to spot fast-paths and eliminate round trips to memory

sudo apt install labwc then run raspi-config to enable it https://forums.raspberrypi.com/viewtopic.php?p=2214879&hilit=labwc#p2214879

qrp73 commented 1 week ago

It looks like known and old issue which appears from first Bookworm beta version, I already reported it, but it still not fixed and it appears that the issue with my report disappears for some unknown reason. I don't see it in my github history. Probably project where I opened issue was removed from github.

I remember that @ghollingworth tested it, found what is wrong (as I remember he said it creates not enough buffer for fullscreen rendering) and he promised to fix it. But for some unknown reason it still not fixed, I still needs to set SwapInterval(0) and then SwapInterval(1) manually in my OpenGL engine console. I'm already used to it.

You can workaround it with set SwapInterval=0 and then SwapInterval=1 with call glXSwapIntervalEXT (GLX_EXT_swap_control extension).

If I remember correctly it even enough to just set SwapInterval=1, but it should be called when window and context is created and rendering is already in progress. If you call it before that it wont fix it.

When using GLFW you can call it with glfwSwapInterval(1);