Skip to content

Commit

Permalink
Added HelloImGui::Renderer
Browse files Browse the repository at this point in the history
// 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();
//    }
//   ```
  • Loading branch information
pthom committed Oct 5, 2024
1 parent 50bc587 commit a20d317
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 23 deletions.
6 changes: 6 additions & 0 deletions src/hello_imgui/doc_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,12 @@ ImVec2 ImageProportionalSize(const ImVec2& askedSize, const ImVec2& imageSize);
```cpp
// `GetRunnerParams()`: a convenience function that will return the runnerParams
// of the current application
RunnerParams* GetRunnerParams();
// `IsUsingHelloImGui()`: returns true if the application is using HelloImGui
bool IsUsingHelloImGui();
// `FrameRate(durationForMean = 0.5)`: Returns the current FrameRate.
// May differ from ImGui::GetIO().FrameRate, since one can choose the duration
Expand Down
64 changes: 59 additions & 5 deletions src/hello_imgui/hello_imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,72 @@ void Run(
float fpsIdle = 10.f
);

// `GetRunnerParams()`: a convenience function that will return the runnerParams
// of the current application
RunnerParams* GetRunnerParams();

// `IsUsingHelloImGui()`: returns true if the application is using HelloImGui
bool IsUsingHelloImGui();
// =========================== HelloImGui::Renderer ==================================
// @@md#HelloImGui::Renderer

// 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
// 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 (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
// 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();
};
// @@md


// ============================== Utility functions ===============================

// @@md#UtilityFunctions

// `GetRunnerParams()`: a convenience function that will return the runnerParams
// of the current application
RunnerParams* GetRunnerParams();

// `IsUsingHelloImGui()`: returns true if the application is using HelloImGui
bool IsUsingHelloImGui();

// `FrameRate(durationForMean = 0.5)`: Returns the current FrameRate.
// May differ from ImGui::GetIO().FrameRate, since one can choose the duration
Expand Down
134 changes: 120 additions & 14 deletions src/hello_imgui/impl/hello_imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@
#include <set>
#include <chrono>
#include <cstdio>
#include <optional>


namespace HelloImGui
{
RunnerParams* gLastRunnerParams = nullptr;
std::unique_ptr<AbstractRunner> gLastRunner;

std::string gMissingBackendErrorMessage = R"(
When running CMake, you should specify
Expand All @@ -33,7 +31,7 @@ When running CMake, you should specify
)";


bool _CheckAdditionLayoutNamesUniqueness(RunnerParams &runnerParams)
bool _CheckAdditionLayoutNamesUniqueness(const RunnerParams &runnerParams)
{
std::set<std::string> names_set;
names_set.insert(runnerParams.dockingParams.layoutName);
Expand All @@ -49,18 +47,123 @@ bool _CheckAdditionLayoutNamesUniqueness(RunnerParams &runnerParams)
return areNamesUnique;
}

void Run(RunnerParams& runnerParams)

// =========================== HelloImGui::Renderer ==================================

//
// Static instances to store the last runner and its parameters
// -------------------------------------------------------------
// gLastRunnerParamsOpt may contain a valid value if Renderer(const RunnerParams& ) was used
std::optional<RunnerParams> gLastRunnerParamsOpt;
// gLastRunnerParamsUserPointer may contain a valid value if Run(RunnerParams& ) was used
// (we may modify the user's runnerParams in this case)
static RunnerParams* gLastRunnerParamsUserPointer = nullptr;
// a pointer to the current AbstractRunner
static std::unique_ptr<AbstractRunner> gLastRunner;

static RunnerParams* Priv_CurrentRunnerParamsPtr()
{
if (gLastRunnerParamsOpt.has_value())
return &gLastRunnerParamsOpt.value();
if (gLastRunnerParamsUserPointer != nullptr)
return gLastRunnerParamsUserPointer;
return nullptr;
}


// Only one instance of `Renderer` can exist at a time
// ---------------------------------------------------
static int gRendererInstanceCount = 0;

enum class SetupMode
{
Renderer,
Run
};

static void Priv_SetupRunner(RunnerParams &passedUserParams, SetupMode setupMode)
{
bool isUserPointer = (setupMode == SetupMode::Run); // When using HelloImGui::Run, we may modify the user's runnerParams
bool shallSetupTearDown = (setupMode == SetupMode::Renderer); // When using HelloImGui::Renderer, we shall call Setup/TearDown()

if (gRendererInstanceCount > 0)
throw std::runtime_error("Only one instance of `HelloImGui::Renderer` can exist at a time.");
if (!isUserPointer)
gLastRunnerParamsOpt = passedUserParams; // Store a copy of the user's runnerParams
else
gLastRunnerParamsUserPointer = &passedUserParams; // Store a pointer to the user's runnerParams

IM_ASSERT(Priv_CurrentRunnerParamsPtr() != nullptr);
RunnerParams &runnerParams = *Priv_CurrentRunnerParamsPtr();
IM_ASSERT(_CheckAdditionLayoutNamesUniqueness(runnerParams));

gLastRunner = FactorRunner(runnerParams);
if (gLastRunner == nullptr)
{
fprintf(stderr, "HelloImGui::Run() failed to factor a runner!\n %s", gMissingBackendErrorMessage.c_str());
IM_ASSERT(false && "HelloImGui::Run() failed to factor a runner!");
fprintf(stderr, "HelloImGui::Renderer() failed to factor a runner!\n %s", gMissingBackendErrorMessage.c_str());
IM_ASSERT(false && "HelloImGui::Renderer() failed to factor a runner!");
}
gLastRunnerParams = &runnerParams;
if (shallSetupTearDown)
gLastRunner->Setup();
gRendererInstanceCount++;
}

static void Priv_TearDown(SetupMode setupMode)
{
IM_ASSERT(gLastRunner != nullptr && "HelloImGui::Renderer::~Renderer() called without a valid runner");
bool shallSetupTearDown = (setupMode == SetupMode::Renderer); // When using HelloImGui::Renderer, we shall call Setup/TearDown()
if (shallSetupTearDown)
gLastRunner->TearDown(false);
gLastRunner = nullptr;
gRendererInstanceCount = 0;
gLastRunnerParamsOpt.reset();
gLastRunnerParamsUserPointer = nullptr;
}

Renderer::Renderer(const HelloImGui::RunnerParams &runnerParams)
{
HelloImGui::RunnerParams runnerParamsCopy = runnerParams;
Priv_SetupRunner(runnerParamsCopy, SetupMode::Renderer);
}

Renderer::Renderer(const HelloImGui::SimpleRunnerParams &simpleParams)
{
RunnerParams fullParams = simpleParams.ToRunnerParams();
Priv_SetupRunner(fullParams, SetupMode::Renderer);
}

Renderer::Renderer(const HelloImGui::VoidFunction &guiFunction, const std::string &windowTitle, bool windowSizeAuto,
bool windowRestorePreviousGeometry, const HelloImGui::ScreenSize &windowSize, float fpsIdle)
{
SimpleRunnerParams params;
params.guiFunction = guiFunction;
params.windowTitle = windowTitle;
params.windowSizeAuto = windowSizeAuto;
params.windowRestorePreviousGeometry = windowRestorePreviousGeometry;
params.windowSize = windowSize;
params.fpsIdle = fpsIdle;
RunnerParams fullParams = params.ToRunnerParams();
Priv_SetupRunner(fullParams, SetupMode::Renderer);
}

void Renderer::Render()
{
IM_ASSERT(gLastRunner != nullptr && "HelloImGui::Renderer::Render() called without a valid runner");
gLastRunner->CreateFramesAndRender();
}

Renderer::~Renderer()
{
Priv_TearDown(SetupMode::Renderer);
}

// =========================== HelloImGui::Run ==================================

void Run(RunnerParams& runnerParams)
{
Priv_SetupRunner(runnerParams, SetupMode::Run);
gLastRunner->Run();
gLastRunnerParams = nullptr;
Priv_TearDown(SetupMode::Run);
}

void Run(const SimpleRunnerParams& simpleRunnerParams)
Expand Down Expand Up @@ -88,27 +191,30 @@ void Run(
Run(params);
}


// ============================== Utility functions ===============================

RunnerParams* GetRunnerParams()
{
if (gLastRunnerParams == nullptr)
auto ptr = Priv_CurrentRunnerParamsPtr();
if (ptr == nullptr)
throw std::runtime_error("HelloImGui::GetRunnerParams() would return null. Did you call HelloImGui::Run()?");
return gLastRunnerParams;
return ptr;
}

bool IsUsingHelloImGui()
{
return gLastRunnerParams != nullptr;
return Priv_CurrentRunnerParamsPtr() != nullptr;
}


void SwitchLayout(const std::string& layoutName)
{
gLastRunner->LayoutSettings_SwitchLayout(layoutName);
}

std::string CurrentLayoutName()
{
return gLastRunnerParams->dockingParams.layoutName;
return GetRunnerParams()->dockingParams.layoutName;
}


Expand Down
8 changes: 5 additions & 3 deletions src/hello_imgui/internal/backend_impls/abstract_runner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,10 @@ AbstractRunner::~AbstractRunner()
}


#ifndef USEHACK
void AbstractRunner::Run()
{
Setup();

mIdxFrame = 0;
#ifdef HELLOIMGUI_MOBILEDEVICE
while (true)
CreateFramesAndRender();
Expand All @@ -145,7 +143,7 @@ void AbstractRunner::Run()
}
#endif
}
#endif


#ifdef HELLO_IMGUI_IMGUI_SHARED
static void* MyMallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
Expand Down Expand Up @@ -783,6 +781,8 @@ void AbstractRunner::Setup()
// Create a remote display handler if needed
mRemoteDisplayHandler.Create();
mRemoteDisplayHandler.SendFonts();

mIdxFrame = 0;
}


Expand Down Expand Up @@ -1421,6 +1421,8 @@ void AbstractRunner::OnLowMemory()

void AbstractRunner::TearDown(bool gotException)
{
IM_ASSERT(!mWasTearedDown && "TearDown() called twice!");
mWasTearedDown = true;
if (! gotException)
{
// Store screenshot before exiting
Expand Down
5 changes: 4 additions & 1 deletion src/hello_imgui/internal/backend_impls/abstract_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ class AbstractRunner

void Setup();
void CreateFramesAndRender(bool insideReentrantCall = false);
void RenderGui();
void TearDown(bool gotException);

bool WasTearedDown() { return mWasTearedDown; }

// Events for mobile devices
void OnPause();
void OnResume();
Expand Down Expand Up @@ -82,6 +83,7 @@ class AbstractRunner
#endif

private:
void RenderGui();
void InitImGuiContext();
void SetImGuiPrefs();
void InitRenderBackendCallbacks();
Expand Down Expand Up @@ -110,6 +112,7 @@ class AbstractRunner
bool mPotentialFontLoadingError = false;
int mIdxFrame = 0;
bool mWasWindowAutoResizedOnPreviousFrame = false;
bool mWasTearedDown = false;

// Differentiate between cases where the window was resized by code
// and cases where the window was resized by the user
Expand Down

0 comments on commit a20d317

Please sign in to comment.