From b6f5e19bb497663e870627ea2a60a8dbca39cb31 Mon Sep 17 00:00:00 2001 From: Pascal Thomet Date: Mon, 30 Sep 2024 15:07:22 +0200 Subject: [PATCH] Added FpsIdlingMode --- src/hello_imgui/doc_api.md | 61 +++++++++++++++++++ src/hello_imgui/doc_api.src.md | 7 +++ src/hello_imgui/doc_params.md | 20 ++++++ src/hello_imgui/hello_imgui.h | 15 +++-- .../backend_impls/abstract_runner.cpp | 17 ++++-- src/hello_imgui/runner_params.h | 20 ++++++ 6 files changed, 130 insertions(+), 10 deletions(-) diff --git a/src/hello_imgui/doc_api.md b/src/hello_imgui/doc_api.md index d6eb8f4a..e997d3d7 100644 --- a/src/hello_imgui/doc_api.md +++ b/src/hello_imgui/doc_api.md @@ -24,6 +24,67 @@ the elements in the `RunnerParams` struct, or in the simpler `SimpleRunnerParam __HelloImGui::GetRunnerParams()__ will return the runnerParams of the current application. +# Run Application while handling the rendering loop +If you want to be in control of the rendering loop, you may use the class `HelloImGui::Renderer` (available since September 2024) + +```cpp + +// HelloImGui::Renderer is an alternative to HelloImGui::Run, allowing fine-grained control over the rendering process. +// - It is customizable like HelloImGui::Run: construct it with `RunnerParams` or `SimpleRunnerParams` +// - `Render()` will render the application for one frame: +// Ensure that `Render()` is triggered regularly (e.g., through a loop or other mechanism) to maintain responsiveness. +// This method must be called on the main thread. +// +// A typical use case is: +// ```cpp +// HelloImGui::RunnerParams runnerParams; +// runnerParams.callbacks.ShowGui = ...; // your GUI function +// // Optionally, choose between Sleep, EarlyReturn, or Auto for fps idling mode: +// // runnerParams.fpsIdling.fpsIdlingMode = HelloImGui::FpsIdlingMode::Sleep; // or EarlyReturn, Auto +// Renderer renderer(runnerParams); // note: a distinct copy of the `RunnerParams` will be stored inside the HelloImGui::GetRunnerParams() +// while (! HelloImGui::GetRunnerParams()->appShallExit) +// { +// renderer.Render(); +// } +// ``` +// +// **Notes:** +// 1. Depending on the configuration (`runnerParams.fpsIdling.fpsIdlingMode`), `HelloImGui` may enter an idle state to +// reduce CPU usage, if no events are received (e.g., no input or interaction). +// In this case, `Render()` will either sleep or return immediately. +// By default, +// - On Emscripten, `Render()` will return immediately to avoid blocking the main thread. +// - On other platforms, it will sleep +// 2. Only one instance of `Renderer` can exist at a time. +// 3. If constructed with `RunnerParams`, a copy of the `RunnerParams` will be made (which you can access with `GetRunnerParams())`. +class Renderer +{ +public: + // Initializes with the full customizable `RunnerParams` to set up the application. + // Nb: a distinct copy of the `RunnerParams` will be made, and you can access it with `GetRunnerParams()`. + Renderer(const RunnerParams& runnerParams); + + // Initializes with SimpleRunnerParams. + Renderer(const SimpleRunnerParams& simpleParams); + + // Initializes with a simple GUI function and additional parameters. + Renderer( + const VoidFunction &guiFunction, + const std::string &windowTitle = "", + bool windowSizeAuto = false, + bool windowRestorePreviousGeometry = false, + const ScreenSize &windowSize = DefaultWindowSize, + float fpsIdle = 10.f + ); + + // Render the current frame (or return immediately if in idle state). + void Render(); + + // Destructor (automatically tears down HelloImGui). + ~Renderer(); +}; +``` + ---- # Place widgets in a DPI-aware way diff --git a/src/hello_imgui/doc_api.src.md b/src/hello_imgui/doc_api.src.md index 08c5bec8..66ea7e5a 100644 --- a/src/hello_imgui/doc_api.src.md +++ b/src/hello_imgui/doc_api.src.md @@ -6,6 +6,13 @@ HelloImGui is extremely easy to use: there is **one** main function in the API, @import "hello_imgui.h" {md_id=HelloImGui::Run} +# Run Application while handling the rendering loop +If you want to be in control of the rendering loop, you may use the class `HelloImGui::Renderer` (available since September 2024) + +```cpp +@import "hello_imgui.h" {md_id=HelloImGui::Renderer} +``` + ---- # Place widgets in a DPI-aware way diff --git a/src/hello_imgui/doc_params.md b/src/hello_imgui/doc_params.md index 75e4a9dd..fd4dbc2f 100644 --- a/src/hello_imgui/doc_params.md +++ b/src/hello_imgui/doc_params.md @@ -830,6 +830,22 @@ See [runner_params.h](https://github.com/pthom/hello_imgui/blob/master/src/hello ```cpp +// FpsIdlingMode is an enum that describes the different modes of idling when rendering the GUI. +// - Sleep: the application will sleep when idling to reduce CPU usage. +// - EarlyReturn: rendering will return immediately when idling. +// This is specifically designed for event-driven, and real-time applications. +// Avoid using it in a tight loop without pauses, as it may cause excessive CPU consumption. +// - Auto: use platform-specific default behavior. +// On most platforms, it will sleep. On Emscripten, `Render()` will return immediately +// to avoid blocking the main thread. +// Note: you can override the default behavior by explicitly setting Sleep or EarlyReturn. +enum class FpsIdlingMode +{ + Sleep, + EarlyReturn, + Auto, +}; + // FpsIdling is a struct that contains Fps Idling parameters struct FpsIdling { @@ -861,6 +877,10 @@ struct FpsIdling // `rememberEnableIdling`: _bool, default=true_. // If true, the last value of enableIdling is restored from the settings at startup. bool rememberEnableIdling = false; + + // `fpsIdlingMode`: _FpsIdlingMode, default=FpsIdlingMode::Automatic_. + // Sets the mode of idling when rendering the GUI (Sleep, EarlyReturn, Automatic) + FpsIdlingMode fpsIdlingMode = FpsIdlingMode::Auto; }; ``` diff --git a/src/hello_imgui/hello_imgui.h b/src/hello_imgui/hello_imgui.h index 0ee7be43..ba1bff62 100644 --- a/src/hello_imgui/hello_imgui.h +++ b/src/hello_imgui/hello_imgui.h @@ -94,6 +94,10 @@ void Run( // // A typical use case is: // ```cpp +// HelloImGui::RunnerParams runnerParams; +// runnerParams.callbacks.ShowGui = ...; // your GUI function +// // Optionally, choose between Sleep, EarlyReturn, or Auto for fps idling mode: +// // runnerParams.fpsIdling.fpsIdlingMode = HelloImGui::FpsIdlingMode::Sleep; // or EarlyReturn, Auto // Renderer renderer(runnerParams); // note: a distinct copy of the `RunnerParams` will be stored inside the HelloImGui::GetRunnerParams() // while (! HelloImGui::GetRunnerParams()->appShallExit) // { @@ -102,11 +106,12 @@ void Run( // ``` // // **Notes:** -// 1. Depending on the configuration (such as `fpsIdle`), `HelloImGui` may enter an idle state to reduce CPU usage, -// if no events are received (e.g., no input or interaction). -// In this case, `Render()` will either sleep or return immediately: -// - On Emscripten, `Render()` will return immediately to avoid blocking the main thread. -// - On other platforms, it will sleep +// 1. Depending on the configuration (`runnerParams.fpsIdling.fpsIdlingMode`), `HelloImGui` may enter an idle state to +// reduce CPU usage, if no events are received (e.g., no input or interaction). +// In this case, `Render()` will either sleep or return immediately. +// By default, +// - On Emscripten, `Render()` will return immediately to avoid blocking the main thread. +// - On other platforms, it will sleep // 2. Only one instance of `Renderer` can exist at a time. // 3. If constructed with `RunnerParams`, a copy of the `RunnerParams` will be made (which you can access with `GetRunnerParams())`. class Renderer diff --git a/src/hello_imgui/internal/backend_impls/abstract_runner.cpp b/src/hello_imgui/internal/backend_impls/abstract_runner.cpp index b1fa7161..335769d7 100644 --- a/src/hello_imgui/internal/backend_impls/abstract_runner.cpp +++ b/src/hello_imgui/internal/backend_impls/abstract_runner.cpp @@ -1071,16 +1071,23 @@ void AbstractRunner::CreateFramesAndRender(bool insideReentrantCall) params.fpsIdling.isIdling = shallIdle; if (shallIdle) { - bool idleByEarlyReturn_Emscripten = false; - #ifdef __EMSCRIPTEN__ - idleByEarlyReturn_Emscripten = true; - #endif + bool idleByEarlyReturn = false; - if (idleByEarlyReturn_Emscripten) + if (params.fpsIdling.fpsIdlingMode == FpsIdlingMode::EarlyReturn) + idleByEarlyReturn = true; + + if (params.fpsIdling.fpsIdlingMode == FpsIdlingMode::Auto) { // Under emscripten, the idling implementation is different: // we cannot sleep (which would lead to a busy wait), so we skip rendering // if the last frame was rendered in time for the desired FPS + #ifdef __EMSCRIPTEN__ + idleByEarlyReturn = true; + #endif + } + + if (idleByEarlyReturn) + { if (fnWasLastFrameRenderedInTimeForDesiredFps()) return true; } diff --git a/src/hello_imgui/runner_params.h b/src/hello_imgui/runner_params.h index b1d01fa9..ed372b79 100644 --- a/src/hello_imgui/runner_params.h +++ b/src/hello_imgui/runner_params.h @@ -106,6 +106,22 @@ std::string IniFolderLocation(IniFolderType iniFolderType); // @@md#FpsIdling +// FpsIdlingMode is an enum that describes the different modes of idling when rendering the GUI. +// - Sleep: the application will sleep when idling to reduce CPU usage. +// - EarlyReturn: rendering will return immediately when idling. +// This is specifically designed for event-driven, and real-time applications. +// Avoid using it in a tight loop without pauses, as it may cause excessive CPU consumption. +// - Auto: use platform-specific default behavior. +// On most platforms, it will sleep. On Emscripten, `Render()` will return immediately +// to avoid blocking the main thread. +// Note: you can override the default behavior by explicitly setting Sleep or EarlyReturn. +enum class FpsIdlingMode +{ + Sleep, + EarlyReturn, + Auto, +}; + // FpsIdling is a struct that contains Fps Idling parameters struct FpsIdling { @@ -137,6 +153,10 @@ struct FpsIdling // `rememberEnableIdling`: _bool, default=true_. // If true, the last value of enableIdling is restored from the settings at startup. bool rememberEnableIdling = false; + + // `fpsIdlingMode`: _FpsIdlingMode, default=FpsIdlingMode::Automatic_. + // Sets the mode of idling when rendering the GUI (Sleep, EarlyReturn, Automatic) + FpsIdlingMode fpsIdlingMode = FpsIdlingMode::Auto; }; // @@md