ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
61.47k stars 10.35k forks source link

SetNextWindowRefreshPolicy on docked window triggers EndFrame() assertion #8047

Open J8-8N opened 1 month ago

J8-8N commented 1 month ago

Version/Branch of Dear ImGui:

Version 1.91.3-docking

Back-ends:

imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp

Compiler, OS:

CLang 18.1.3 and GCC 13.2.0, Linux Mint 22, x64

Full config/build information:

Dear ImGui 1.91.3 (19130)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=201103
define: __linux__
define: __GNUC__=13
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_glfw
io.BackendRendererName: imgui_impl_opengl3
io.ConfigFlags: 0x00000483
 NavEnableKeyboard
 NavEnableGamepad
 DockingEnable
 ViewportsEnable
io.ConfigViewportsNoDecoration
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,64
io.DisplaySize: 1280.00,720.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

Hello, if I clone v1.91.3-docking and edit example_glfw_opengl3/main.cpp to add:

  ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags_RefreshOnHover | ImGuiWindowRefreshFlags_TryToAvoidRefresh);

before the "Hello, World!" window Begin() (line 163) then dock the "Hello, World!" window to the Demo window then move my mouse outside that window, the program fails with:

   ../imgui.cpp:5808: void ImGui::EndFrame(): Assertion `g.Windows.Size == g.WindowsTempSortBuffer.Size' failed.

And will fail the same on next launch as it tries to recreate these docked windows from imgui.ini

This is the easiest reproducible example I could think of. It happens with any two windows too.

Thank you very much for the help

main2.cpp.zip

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

        // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
        {
            static float f = 0.0f;
            static int counter = 0;

            ImGui::SetNextWindowRefreshPolicy(ImGuiWindowRefreshFlags_RefreshOnHover | ImGuiWindowRefreshFlags_TryToAvoidRefresh); // <-- ADD THIS then dock this window to the Demo window
            ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.

            ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
ocornut commented 1 month ago

Linking to #7797 FYI this function is imgui_internal.h and is experimental and for my own research, I'm not expecting people to be using this at this point.

J8-8N commented 1 month ago

Hello, yes I'm well aware this is experimental, I'm just raising this narrow issue. I found this in CPP, it has nothing to do with the CSharp bindings.

ocornut commented 1 month ago

I spent an hour toying with this and came to the conclusion that the feature is going to be too difficult to fully implement and maintain so I am tempted to remove it. I'm a bit worried that we will stumble on a whole new class of bugs which are going to be difficult to work with.

J8-8N commented 1 month ago

Yes it's quite user-specific. Could we have the means to save the results of a window rendering (after End()) and then decide ourselves to re-use that cached result after Begin() if we see fit? Then users can decide for themselves if they want to re-render on user interaction, drag, resize, a widget becoming blue, etc. Thank you

if(begin())
  if(last_render_was_less_than_one_second_ago and no_user_interaction_since)
     end(cached_drawings)
  else
    text("Hello");
    expensive_stuff();
    tabs();
    table();
    end();
    cached_drawings = ImGui::previousWindowDrawings();

The current power saving tools are nice but if one window needs a render, they suddenly all do.

I understand ImGui was made to be rendered in game engines and not to make usual CRUD GUIs. I also understand I could cache the expensive calls myself but this would be highly convenient and quickly applicable to any window. And would leave us the opportunity to have some windows at VSync frequency and some at 1Hz (example).

Thank you

ocornut commented 1 month ago

Yes it's quite user-specific. Could we have the means to save the results of a window rendering (after End()) and then decide ourselves to re-use that cached result after Begin() if we see fit? Then users can decide for themselves if they want to re-render on user interaction, drag, resize, a widget becoming blue, etc. Thank you

That's exactly equivalent to what we are aiming to do. The problem is figuring out the right rule for re-rendering, which include e.g. parents moving. User is able to give a correct "don't skip this frame" signal but won't be able to tell "definitively can skip this frame" better than the library would.

But there's a large hairy nest of inter-dependency and states which may be used by other subsystems and that are difficult to get right and are going to be incredibly tricky to get from 90% to 100%. I will another afternoon someday having a pass at this but I am not super hopeful.

I understand ImGui was made to be rendered in game engines and not to make usual CRUD GUIs.

Yes.

If you have expensive stuff, cache it or make it less expensive.