Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rcore] [SDL2] Add implementation for FLAG_WINDOW_ALWAYS_RUN #4598

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

asdqwe
Copy link
Contributor

@asdqwe asdqwe commented Dec 12, 2024

At the moment, on PLATFORM_DESKTOP_GLFW, when a window is iconified (minimized) it stops processing until the window is raised again. That happens because the FLAG_WINDOW_ALWAYS_RUN, when disabled (and the window iconified), calls glfwWaitEvents() (ref), which "puts the calling thread to sleep until at least one event is available" (doc).

Since FLAG_WINDOW_ALWAYS_RUN is disabled by default, every time the window is iconified, all processing stops, causing a series of problems for anyone expecting that the program will continue to run while iconified, unlike on the other platforms, which won't stop processing while iconified.

This PR fixes this by turning FLAG_WINDOW_ALWAYS_RUN on by default on PLATFORM_DESKTOP_GLFW's InitPlatform() so it will behave like the other platforms by default. The flag funcionality is still maintained and the user can still disable it if he wants to stop processing when iconifying. This change doesn't affect the other platforms since it's a PLATFORM_DESKTOP_GLFW-only flag.

The PR can be tested with:
#include "raylib.h"
int main(void) {
    InitWindow(800, 450, "test");
    SetTargetFPS(60);
    while (!WindowShouldClose()) {

        if (IsKeyPressed(KEY_ONE)) { MinimizeWindow(); TraceLog(LOG_INFO, "Minimized"); }
        if (IsKeyPressed(KEY_TWO)) { TraceLog(LOG_INFO, "FLAG_WINDOW_ALWAYS_RUN is %i", IsWindowState(FLAG_WINDOW_ALWAYS_RUN)); }
        if (IsKeyPressed(KEY_THREE)) { SetWindowState(FLAG_WINDOW_ALWAYS_RUN); TraceLog(LOG_INFO, "Set FLAG_WINDOW_ALWAYS_RUN"); }
        if (IsKeyPressed(KEY_FOUR)) { ClearWindowState(FLAG_WINDOW_ALWAYS_RUN); TraceLog(LOG_INFO, "Clear FLAG_WINDOW_ALWAYS_RUN"); }

        TraceLog(LOG_INFO, "GetTime():%f. GetFrameTime():%f.", GetTime(), GetFrameTime());

        BeginDrawing();
        ClearBackground(RAYWHITE);
        EndDrawing();
     }
    CloseWindow();
    return 0;
}

Also closes #4588.

@raysan5
Copy link
Owner

raysan5 commented Dec 15, 2024

@asdqwe Actually, stop running is my expected behaviour when minimizing the Window, I think it's the optimal option, so being default.

About the other platforms, the ones that have the minimizing option I think implement similar alternatives (afair, Android also pauses the Activity and Web browsers pauses tabs activity when minimized/unselected tab).

I prefer to let the user configure it for the sub-optimal use case that is ALWAYS_RUN, but maybe the time counter can be reseted/paused if the flag is active.

Also, about the othe Desktop backends (SDL, RGFW), probably behaviour should be the same, just forgot about it when added.

Just thinking... what is the default behaviour for games on Windows?

@asdqwe
Copy link
Contributor Author

asdqwe commented Dec 15, 2024

@raysan5 I'm not aware of any (big) game that fully stops processing like that when minimizing because that breaks timers, gameplay sincronization, animations, networking, etc. Most games just lower the framerate when minimized or unfocused (some even allow to set specific foreground fps and background fps values). Which was why I thought this was a bug when debugging #4588. Basically everything with timers break if this happens.

@asdqwe
Copy link
Contributor Author

asdqwe commented Dec 15, 2024

@raysan5 I don't know how Android specifically handles it. But on PLATFORM_WEB (where we have little control of the execution), when, for example, we change tabs, the browser continues to process the game/canvas just lowering the frequency of it.

This example can test/confirm that behavior:
#include "raylib.h"
int main(void) {
    InitWindow(800, 450, "test");

    SetWindowState(FLAG_WINDOW_ALWAYS_RUN);

    SetTargetFPS(60);
    while (!WindowShouldClose()) {

        TraceLog(LOG_INFO, "GetTime():%f", GetTime());

        BeginDrawing();
        ClearBackground(RAYWHITE);
        EndDrawing();
     }
    CloseWindow();
    return 0;
}

When running it, after 1 second I changed tabs. Waited 10 seconds then returned. Here's the screenshot of the console log where it shows the execution dropping to once a second:

img

Edit: I'm trying to find a case where iconifying the window halts processing by default, but I'm unable to find one so far.

GLFW continues execution by default (tested on Linux and Windows):
//     How to replicate:
// unzip glfw-master.zip
// cd glfw-master/
//
//     Pick any example from examples/ and replace its code with this source.
//
//     Then:
// mkdir build/
// cmake -S . -B build/
// cmake --build build/
// ./build/examples/example

#include <stdio.h>
#define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
int main(int argc, char** argv) {
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
    GLFWwindow* window;
    window = glfwCreateWindow(640, 480, "test", NULL, NULL);
    if (!window) { glfwTerminate(); exit(EXIT_FAILURE); }
    glfwMakeContextCurrent(window);
    gladLoadGL(glfwGetProcAddress);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glfwSwapInterval(1);
    while (!glfwWindowShouldClose(window)) {
        printf("%f\n", glfwGetTime());
        glClear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwDestroyWindow(window);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}
SDL2 (native) continues execution by default (tested on Linux and Windows):
// unzip SDL2-2.30.10.zip
// cd SDL2-2.30.10/
//
// vim.tiny test/testnative.c
//     Add this print to line 200:
// printf("%f\n", ((double)SDL_GetTicks()/1000));
//
//     Then:    
// mkdir build/
// cd build/
// cmake -DSDL_TESTS=ON -DSDL_SHARED=OFF -DSDL_STATIC=ON -DCMAKE_BUILD_TYPE=Release ..
// cmake --build . --config Release
// ./build/test/testnative

Nvidia has Background Application Max Frame Rate on it's Control Panel (ref).

@asdqwe asdqwe changed the title [rcore] Enable FLAG_WINDOW_ALWAYS_RUN by default on PLATFORM_DESKTOP_GLFW [rcore] [SDL2] Add implementation for FLAG_WINDOW_ALWAYS_RUN Dec 17, 2024
@asdqwe
Copy link
Contributor Author

asdqwe commented Dec 17, 2024

@raysan5 Although I don't think that should be the default behavior, I've reverted my previous commit on PLATFORM_DESKTOP_GLFW and, instead, added implemenation for FLAG_WINDOW_ALWAYS_RUN on PLATFORM_DESKTOP_SDL which will now behave, by default, like PLATFORM_DESKTOP_GLFW and stop processing until some event happen while minimized.

This PR was tested with SDL2.30.10 on Linux Mint 22.0. SDL3 wasn't tested but should work since SDL_WaitEvent() still appears to be compatible in this use case (doc, doc).

The PR can be tested with:
#include "raylib.h"
int main(void) {
    InitWindow(800, 450, "test");
    SetTargetFPS(60);
    while (!WindowShouldClose()) {

        if (IsKeyPressed(KEY_ONE)) { MinimizeWindow(); TraceLog(LOG_INFO, "Minimized"); }
        if (IsKeyPressed(KEY_TWO)) { SetWindowState(FLAG_WINDOW_ALWAYS_RUN); TraceLog(LOG_INFO, "Set FLAG_WINDOW_ALWAYS_RUN"); }
        if (IsKeyPressed(KEY_THREE)) { ClearWindowState(FLAG_WINDOW_ALWAYS_RUN); TraceLog(LOG_INFO, "Clear FLAG_WINDOW_ALWAYS_RUN"); }

        TraceLog(LOG_INFO, "GetTime():%f.", GetTime());

        BeginDrawing();
        ClearBackground(RAYWHITE);
        EndDrawing();
     }
    CloseWindow();
    return 0;
}

Regarding PLATFORM_WEB, I don't think it will be possible to implement FLAG_WINDOW_ALWAYS_RUN there since the web browser has control over the main loop execution (ref, ref), which is likely why glfwWaitEvents() doesn't work there (ref).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[rcore] GetFrameTime() counts time since application was minimized
2 participants