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

wait-event-timeout main loop modification #4133

Open
franko opened this issue May 12, 2021 · 2 comments
Open

wait-event-timeout main loop modification #4133

franko opened this issue May 12, 2021 · 2 comments

Comments

@franko
Copy link

franko commented May 12, 2021

This is not a real issue but something I would like to discuss and double-check with the imgui developers and users.

Proposition to discuss

Here the modification I made to the SDL/opengl3 example. The purpose is to let the demo application wait on event and stop using the CPU.

diff --git a/examples/example_sdl_opengl3/main.cpp b/examples/example_sdl_opengl3/main.cpp
index 804190ea..51e74b9c 100644
--- a/examples/example_sdl_opengl3/main.cpp
+++ b/examples/example_sdl_opengl3/main.cpp
@@ -40,6 +40,14 @@ using namespace gl;
 #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
 #endif
 
+static void process_event(SDL_Event *event, bool& done, Uint32 window_id) {
+    ImGui_ImplSDL2_ProcessEvent(event);
+    if (event->type == SDL_QUIT)
+        done = true;
+    if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_CLOSE && event->window.windowID == window_id)
+        done = true;
+}
+
 // Main code
 int main(int, char**)
 {
@@ -145,6 +153,8 @@ int main(int, char**)
 
     // Main loop
     bool done = false;
+    int idle_frames = 0;
+    const int config_fps = -1000; // could be 60 to run at 60 FPS. If negative wait indefinitely.
     while (!done)
     {
         // Poll and handle events (inputs, window resize, etc.)
@@ -153,14 +163,24 @@ int main(int, char**)
         // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
         // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
         SDL_Event event;
-        while (SDL_PollEvent(&event))
-        {
-            ImGui_ImplSDL2_ProcessEvent(&event);
-            if (event.type == SDL_QUIT)
-                done = true;
-            if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
-                done = true;
+        Uint32 window_id = SDL_GetWindowID(window);
+        if (idle_frames >= 5) {
+            if (SDL_WaitEventTimeout(&event, 1000 / config_fps)) {
+                process_event(&event, done, window_id);
+                while (SDL_PollEvent(&event))
+                {
+                    process_event(&event, done, window_id);
+                }
+                idle_frames = 0;
+            }
+        } else {
+            while (SDL_PollEvent(&event))
+            {
+                idle_frames = 0;
+                process_event(&event, done, window_id);
+            }
         }
+        idle_frames++;
 
         // Start the Dear ImGui frame
         ImGui_ImplOpenGL3_NewFrame();

Context / SDL2 issue

I am the author of a patch of the SDL2 library with the purpose to reduce CPU usage to zero when waiting for an incoming event. Here the PR (not accepted neither refused up to now):

libsdl-org/SDL#4180

The patch is meant for desktop applications that use SDL2. The SDL2 library internally continuously polls for events even when WaitEvent or WaitEventTimeout is used. This cause continuous CPU usage even when a desktop applications is idle waiting for incoming events. My patch fix this so that the functions WaitEvent/WaitEventTimeout actually wait for events instead of polling.

I am successfully using the modified SDL library for my applications lite-xl

Discussion

You may notice that I use a variable idle_frames to check for how many frames the applications was running idle without events. The idea is that when an event arrives we want to let the application process at least a few frames before blocking the application waiting for events.

I found empirically the imgui needs a few frames to make some transitions in the UI but I would appreciate to have a confirmation if I am correct or not.

If the variable config_fps is set to -1000 it waits indefinitely for events. To run at a fixed FPS the value should be set to the desired FPS value but no attempt is made to account for the elapsed time and correct the waiting time.

I am asking to @ocornut and other imgui users or contributors is they can validated the correctness of the modified loop I am using.

I don't mean the modification should be included into the imgui library.

@rokups
Copy link
Contributor

rokups commented May 13, 2021

Note that this is not enough. For example blinking cursor will not work if you do not interact with the window. Same is true for animating graphs like in the demo. Similar attempts exist: #4076, #3124, #2749.

@franko
Copy link
Author

franko commented May 13, 2021

Note that this is not enough. For example blinking cursor will not work if you do not interact with the window. Same is true for animating graphs like in the demo. Similar attempts exist: #4076, #3124, #2749.

Thank you for your comment, you are correctly pointing out a real problem with the modification I proposed.

On the other side my proposition was just meant to show an idea and it is not a complete solution to be used in a real applications as it is.

In my application, lite-xl I found the following approach to be optimal:

  1. when editor window is focused use WaitEventTimeout with timeout = 1000 / FPS.
  2. when editor window is not focused use WaitEventTimeout with a negative timeout.

With (1) the cursor keeps blinking and animations can run smoothly and with (2) we reduce CPU usage to zero as it should be when the window is unfocused or reduced to icon.

As for the animations, my application, lite-xl, has them and they run smoothly. The key is that the application has a flag and when the application needs to redraw, for example because there are some animations running, we set the redraw flag. In turns the main loop looks at the redraw flag and keep running the frames as long as the flag is on.

So, I mean that probably each application should come up with some application specific-logic to decide if it is ok to stop and wait for events or not. I am just wondering if this kind of logic can be backed directly in imgui in a generic way. For this we would need to have some flag that tells us if the application drawing is changing or not.

I think really ImGui can be used also for some desktop applications with these kind of adjustments, it doesn't need to be limited to game applications.

On the other side the game developers tends to completely disregards these kind of details because these are not really an issue for games, they just want to run at a fixed framerate anyway.

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

No branches or pull requests

2 participants