Skip to content

Commit

Permalink
Internals: added experimental ErrorCheckEndFrameRecover() to unroll/e…
Browse files Browse the repository at this point in the history
…nd/pop. (#1651, #3600)
  • Loading branch information
ocornut committed Nov 19, 2020
1 parent c0ac4fb commit 9712bff
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 7 deletions.
89 changes: 82 additions & 7 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6842,8 +6842,8 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
// (IF YOU GET A WARNING OR COMPILE ERROR HERE: it means you assert macro is incorrectly defined!
// If your macro uses multiple statements, it NEEDS to be surrounded by a 'do { ... } while (0)' block.
// This is a common C/C++ idiom to allow multiple statements macros to be used in control flow blocks.)
// #define IM_ASSERT(EXPR) SomeCode(EXPR); SomeMoreCode(); // Wrong!
// #define IM_ASSERT(EXPR) do { SomeCode(EXPR); SomeMoreCode(); } while (0) // Correct!
// #define IM_ASSERT(EXPR) if (SomeCode(EXPR)) SomeMoreCode(); // Wrong!
// #define IM_ASSERT(EXPR) do { if (SomeCode(EXPR)) SomeMoreCode(); } while (0) // Correct!
if (true) IM_ASSERT(1); else IM_ASSERT(0);

// Check user data
Expand All @@ -6852,21 +6852,21 @@ static void ImGui::ErrorCheckNewFrameSanityChecks()
IM_ASSERT((g.IO.DeltaTime > 0.0f || g.FrameCount == 0) && "Need a positive DeltaTime!");
IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?");
IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value!");
IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?");
IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?");
IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()?");
IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting!");
IM_ASSERT(g.Style.CircleSegmentMaxError > 0.0f && "Invalid style setting!");
IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)!");
IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting!"); // Allows us to avoid a few clamps in color computations
IM_ASSERT(g.Style.WindowMinSize.x >= 1.0f && g.Style.WindowMinSize.y >= 1.0f && "Invalid style setting.");
IM_ASSERT(g.Style.WindowMenuButtonPosition == ImGuiDir_None || g.Style.WindowMenuButtonPosition == ImGuiDir_Left || g.Style.WindowMenuButtonPosition == ImGuiDir_Right);
for (int n = 0; n < ImGuiKey_COUNT; n++)
IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)");

// Perform simple check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP)
// Check: required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only added in 1.60 WIP)
if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard)
IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation.");

// Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
// Check: the io.ConfigWindowsResizeFromEdges option requires backend to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly.
if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors))
g.IO.ConfigWindowsResizeFromEdges = false;
}
Expand All @@ -6885,6 +6885,9 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
IM_ASSERT((key_mod_flags == 0 || g.IO.KeyMods == key_mod_flags) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods");
IM_UNUSED(key_mod_flags);

// Recover from errors
//ErrorCheckEndFrameRecover();

// Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you
// to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API).
if (g.CurrentWindowStack.Size != 1)
Expand All @@ -6904,6 +6907,78 @@ static void ImGui::ErrorCheckEndFrameSanityChecks()
IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!");
}

// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls.
// Must be called during or before EndFrame().
// This is generally flawed as we are not necessarily End/Popping things in the right order.
// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet.
// FIXME: Can't recover from interleaved BeginTabBar/Begin
void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data)
{
// PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations"
ImGuiContext& g = *GImGui;
while (g.CurrentWindowStack.Size > 0)
{
#ifdef IMGUI_HAS_TABLE
while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow))
{
if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name);
EndTable();
}
#endif
ImGuiWindow* window = g.CurrentWindow;
while (g.CurrentTabBar != NULL) //-V1044
{
if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name);
EndTabBar();
}
while (g.CurrentWindow->DC.TreeDepth > 0) //-V1044

This comment has been minimized.

Copy link
@elect86

elect86 Dec 1, 2020

Contributor

window->DC

And so on

{
if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name);
TreePop();
}
while (g.GroupStack.Size > g.CurrentWindow->DC.StackSizesOnBegin.SizeOfGroupStack) //-V1044
{
if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name);
EndGroup();
}
while (g.CurrentWindow->IDStack.Size > 1) //-V1044
{
if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name);
PopID();
}
while (g.ColorStack.Size > g.CurrentWindow->DC.StackSizesOnBegin.SizeOfColorStack) //-V1044
{
if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col));
PopStyleColor();
}
while (g.StyleVarStack.Size > g.CurrentWindow->DC.StackSizesOnBegin.SizeOfStyleVarStack) //-V1044
{
if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name);
PopStyleVar();
}
while (g.FocusScopeStack.Size > g.CurrentWindow->DC.StackSizesOnBegin.SizeOfFocusScopeStack) //-V1044
{
if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name);
PopFocusScope();
}
if (g.CurrentWindowStack.Size == 1)
{
IM_ASSERT(g.CurrentWindow->IsFallbackWindow);
break;
}
if (g.CurrentWindow->Flags & ImGuiWindowFlags_ChildWindow)
{
if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name);
EndChild();
}
else
{
if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name);
End();
}
}
}

// Save current stack sizes for later compare
void ImGuiStackSizes::SetToCurrentState()
{
Expand Down
3 changes: 3 additions & 0 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ typedef int ImGuiSeparatorFlags; // -> enum ImGuiSeparatorFlags_ // F
typedef int ImGuiTextFlags; // -> enum ImGuiTextFlags_ // Flags: for TextEx()
typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // Flags: for BeginTooltipEx()

typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...);

//-----------------------------------------------------------------------------
// [SECTION] Context pointer
// See implementation of this variable in imgui.cpp for comments and details.
Expand Down Expand Up @@ -2082,6 +2084,7 @@ namespace ImGui
IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window);

// Debug Tools
IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL);
inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(window->DC.LastItemRect.Min, window->DC.LastItemRect.Max, col); }
inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; }

Expand Down

0 comments on commit 9712bff

Please sign in to comment.