From f92af9519a7bc58b7bb34d115df40ce952f9393f Mon Sep 17 00:00:00 2001 From: Pascal Thomet Date: Mon, 3 Jun 2024 12:44:04 +0200 Subject: [PATCH] Add preference repaintDuringResize_GotchaReentrantRepaint --- src/hello_imgui/app_window_params.h | 7 +++ src/hello_imgui/doc_params.md | 7 +++ .../backend_impls/abstract_runner.cpp | 48 +++++++++++++++---- .../internal/backend_impls/abstract_runner.h | 2 +- .../glfw_window_helper.cpp | 8 ++-- .../sdl_window_helper.cpp | 10 ++-- .../internal/backend_impls/runner_sdl2.cpp | 2 +- 7 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/hello_imgui/app_window_params.h b/src/hello_imgui/app_window_params.h index 4c467f83..b394029d 100644 --- a/src/hello_imgui/app_window_params.h +++ b/src/hello_imgui/app_window_params.h @@ -214,6 +214,13 @@ struct AppWindowParams // `handleEdgeInsets`: _bool, default = true_. iOS only. // If true, HelloImGui will handle the edgeInsets on iOS. bool handleEdgeInsets = true; + + // ----------------- repaint the window during resize ----------------- + // Very advanced and reserved for advanced C++ users. + // If you set this to true, the window will be repainted during resize. + // Do read https://github.com/pthom/hello_imgui/issues/112 for info about the possible gotchas + // (This API is not stable, as the name suggests, and this is not supported) + bool repaintDuringResize_GotchaReentrantRepaint = false; }; // @@md diff --git a/src/hello_imgui/doc_params.md b/src/hello_imgui/doc_params.md index 330437c2..68c88571 100644 --- a/src/hello_imgui/doc_params.md +++ b/src/hello_imgui/doc_params.md @@ -599,6 +599,13 @@ struct AppWindowParams // `handleEdgeInsets`: _bool, default = true_. iOS only. // If true, HelloImGui will handle the edgeInsets on iOS. bool handleEdgeInsets = true; + + // ----------------- repaint the window during resize ----------------- + // Very advanced and reserved for advanced C++ users. + // If you set this to true, the window will be repainted during resize. + // Do read https://github.com/pthom/hello_imgui/issues/112 for info about the possible gotchas + // (This API is not stable, as the name suggests, and this is not supported) + bool repaintDuringResize_GotchaReentrantRepaint = false; }; ``` diff --git a/src/hello_imgui/internal/backend_impls/abstract_runner.cpp b/src/hello_imgui/internal/backend_impls/abstract_runner.cpp index f9088cf8..330cc506 100644 --- a/src/hello_imgui/internal/backend_impls/abstract_runner.cpp +++ b/src/hello_imgui/internal/backend_impls/abstract_runner.cpp @@ -705,7 +705,14 @@ void AbstractRunner::Setup() auto fnRenderCallbackDuringResize = [this]() { if (! mWasWindowResizedByCodeDuringThisFrame) + { + //printf("Window resized by user\n"); CreateFramesAndRender(true); + } + else + { + //printf("Window resized by code\n"); + } }; Impl_CreateWindow(fnRenderCallbackDuringResize); @@ -863,8 +870,19 @@ void AbstractRunner::RenderGui() void _UpdateFrameRateStats(); // See hello_imgui.cpp -void AbstractRunner::CreateFramesAndRender(bool skipPollEvents) +void AbstractRunner::CreateFramesAndRender(bool insideReentrantCall) { + // Note: the reentrant call is non-existent for most cases: + // The preference + // repaintDuringResize_GotchaReentrantRepaint + // is false by default. As the name suggests, it is reserved for advanced users + // who are ready to deal with the possible consequences. + // + // This reentrant call is used to be able to repaint during window resize. + // It works on some combinations of OS / platform / rendering backends, but not all. + // See https://github.com/pthom/hello_imgui/issues/112 + if (insideReentrantCall && ! params.appWindowParams.repaintDuringResize_GotchaReentrantRepaint) + return; // ====================================================================================== // Introduction - Lambdas definitions @@ -924,7 +942,7 @@ void AbstractRunner::CreateFramesAndRender(bool skipPollEvents) // handle window size and position on first frames - auto fnHandleWindowSizeAndPositionOnFirstFrames = [this]() + auto fnHandleWindowSizeAndPositionOnFirstFrames_AndAfterResize = [this]() { // Note about the application window initial placement and sizing // i/ On the first frame (mIdxFrame==0), we create a window, and use the user provided size (if provided). The window is initially hidden. @@ -1193,6 +1211,7 @@ void AbstractRunner::CreateFramesAndRender(bool skipPollEvents) // // 2. Gotcha / possible re-entrance into this function when resizing the window // ----------------------------------------------------------------------------- + // See https://github.com/pthom/hello_imgui/issues/112 // There is a severe gotcha inside GLFW and SDL: PollEvent is supposed to // return immediately, but it doesn't when resizing the window! // If you do nothing, the window content is "stretching" during the resize @@ -1213,11 +1232,12 @@ void AbstractRunner::CreateFramesAndRender(bool skipPollEvents) fnHandleLayout(); } + fnRegisterTests_UserCallback(); { SCOPED_RELEASE_GIL_ON_MAIN_THREAD; - fnHandleWindowSizeAndPositionOnFirstFrames(); + fnHandleWindowSizeAndPositionOnFirstFrames_AndAfterResize(); } // Handle idling & poll events @@ -1226,14 +1246,20 @@ void AbstractRunner::CreateFramesAndRender(bool skipPollEvents) // return immediately, but it doesn't when resizing the window! // Instead, you have to subscribe to a kind of special "mid-resize" event, // and then call the render function yourself. - if (!skipPollEvents) - fnHandleIdlingAndPollEvents_MayReRenderDuringResize_GotchaReentrant(); + if (!insideReentrantCall) // Do not poll events again in a reentrant call! + { + // We cannot release the GIL here, since we may have a reentrant call! + if (!insideReentrantCall) + fnHandleIdlingAndPollEvents_MayReRenderDuringResize_GotchaReentrant(); + } - _UpdateFrameRateStats(); // not in a SCOPED_RELEASE_GIL_ON_MAIN_THREAD, because it is very fast + { + _UpdateFrameRateStats(); // not in a SCOPED_RELEASE_GIL_ON_MAIN_THREAD, because it is very fast + fnLoadAdditionalFontDuringExecution_UserCallback(); // User callback + } - fnLoadAdditionalFontDuringExecution_UserCallback(); - if (params.callbacks.PreNewFrame) + if ((params.callbacks.PreNewFrame) && !insideReentrantCall) params.callbacks.PreNewFrame(); { @@ -1245,9 +1271,11 @@ void AbstractRunner::CreateFramesAndRender(bool skipPollEvents) // so that it can *NOT* be called inside SCOPED_RELEASE_GIL_ON_MAIN_THREAD ImGui::NewFrame(); - fnCheckOpenGlErrorOnFirstFrame_WarnPotentialFontError(); // not in a SCOPED_RELEASE_GIL_ON_MAIN_THREAD, because it is very fast and rare + { + fnCheckOpenGlErrorOnFirstFrame_WarnPotentialFontError(); // not in a SCOPED_RELEASE_GIL_ON_MAIN_THREAD, because it is very fast and rare - fnDrawCustomBackgroundOrClearColor_UserCallback(); + fnDrawCustomBackgroundOrClearColor_UserCallback(); // User callback + } // iii/ At the end of the second frame, we measure the size of the widgets and use it as the application window size, // if the user required auto size diff --git a/src/hello_imgui/internal/backend_impls/abstract_runner.h b/src/hello_imgui/internal/backend_impls/abstract_runner.h index d8de4ce9..86edf184 100644 --- a/src/hello_imgui/internal/backend_impls/abstract_runner.h +++ b/src/hello_imgui/internal/backend_impls/abstract_runner.h @@ -25,7 +25,7 @@ class AbstractRunner virtual void Run(); // Only overriden in emscripten void Setup(); - void CreateFramesAndRender(bool skipPollEvents = false); + void CreateFramesAndRender(bool insideReentrantCall = false); void RenderGui(); void TearDown(bool gotException); diff --git a/src/hello_imgui/internal/backend_impls/backend_window_helper/glfw_window_helper.cpp b/src/hello_imgui/internal/backend_impls/backend_window_helper/glfw_window_helper.cpp index f391b6d5..1fe7caa5 100644 --- a/src/hello_imgui/internal/backend_impls/backend_window_helper/glfw_window_helper.cpp +++ b/src/hello_imgui/internal/backend_impls/backend_window_helper/glfw_window_helper.cpp @@ -14,9 +14,11 @@ namespace HelloImGui { namespace BackendApi } static void WindowSizeCallback(GLFWwindow* window, int width, int height) { - return; // Re-entrance into CreateFramesAndRender may break! - - //printf("WindowSizeCallback: %ix%i\n", width, height); + // See https://github.com/pthom/hello_imgui/issues/112 + // This may trigger a reentrant call to + // AbstractRunner::CreateFramesAndRender() + // By default, this is disabled. + // See pref AppWindowParams.repaintDuringResize_GotchaReentrantRepaint if (gRenderCallbackDuringResize_Glfw) gRenderCallbackDuringResize_Glfw(); } diff --git a/src/hello_imgui/internal/backend_impls/backend_window_helper/sdl_window_helper.cpp b/src/hello_imgui/internal/backend_impls/backend_window_helper/sdl_window_helper.cpp index d4772776..14b9f8b0 100644 --- a/src/hello_imgui/internal/backend_impls/backend_window_helper/sdl_window_helper.cpp +++ b/src/hello_imgui/internal/backend_impls/backend_window_helper/sdl_window_helper.cpp @@ -19,12 +19,16 @@ namespace HelloImGui { namespace BackendApi static int resizingEventWatcher(void* data, SDL_Event* event) { - return 0; // Re-entrance into CreateFramesAndRender may break! - + // See https://github.com/pthom/hello_imgui/issues/112 + // This may trigger a reentrant call to + // AbstractRunner::CreateFramesAndRender() + // By default, this is disabled. + // See pref AppWindowParams.repaintDuringResize_GotchaReentrantRepaint if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) { SDL_Window* win = SDL_GetWindowFromID(event->window.windowID); - if (win == (SDL_Window*)data) { + if (win == (SDL_Window*)data) + { if (gRenderCallbackDuringResize_Sdl) gRenderCallbackDuringResize_Sdl(); } diff --git a/src/hello_imgui/internal/backend_impls/runner_sdl2.cpp b/src/hello_imgui/internal/backend_impls/runner_sdl2.cpp index 708ed078..6138f2fc 100644 --- a/src/hello_imgui/internal/backend_impls/runner_sdl2.cpp +++ b/src/hello_imgui/internal/backend_impls/runner_sdl2.cpp @@ -84,7 +84,7 @@ namespace HelloImGui void RunnerSdl2::Impl_PollEvents() { SDL_Event event; - while (SDL_PollEvent(&event)) + while (SDL_PollEvent(&event)) { if (params.callbacks.AnyBackendEventCallback) {