This commit is contained in:
goat 2021-09-11 20:04:33 +02:00
commit 936708861f
No known key found for this signature in database
GPG key ID: 7773BB5B43BA52E5
4 changed files with 176 additions and 0 deletions

View file

@ -0,0 +1,22 @@
namespace Dalamud.Interface.Internal.ManagedAsserts
{
/// <summary>
/// Offsets to various data in ImGui context.
/// </summary>
/// <remarks>
/// Last updated for ImGui 1.83.
/// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the unsage instead.")]
internal static class ImGuiContextOffsets
{
public const int CurrentWindowStackOffset = 0x73A;
public const int ColorStackOffset = 0x79C;
public const int StyleVarStackOffset = 0x7A0;
public const int FontStackOffset = 0x7A4;
public const int BeginPopupStackOffset = 0x7B8;
}
}

View file

@ -0,0 +1,136 @@
using System.Diagnostics;
using ImGuiNET;
using static Dalamud.NativeFunctions;
namespace Dalamud.Interface.Internal.ManagedAsserts
{
/// <summary>
/// Report ImGui problems with a MessageBox dialog.
/// </summary>
internal static class ImGuiManagedAsserts
{
/// <summary>
/// Gets a value indicating whether asserts are enabled for ImGui.
/// </summary>
public static bool AssertsEnabled { get; private set; }
/// <summary>
/// Create a snapshot of the current ImGui context.
/// Should be called before rendering an ImGui frame.
/// </summary>
/// <returns>A snapshot of the current context.</returns>
public static unsafe ImGuiContextSnapshot GetSnapshot()
{
var contextPtr = ImGui.GetCurrentContext();
var styleVarStack = *((int*)contextPtr + ImGuiContextOffsets.StyleVarStackOffset); // ImVector.Size
var colorStack = *((int*)contextPtr + ImGuiContextOffsets.ColorStackOffset); // ImVector.Size
var fontStack = *((int*)contextPtr + ImGuiContextOffsets.FontStackOffset); // ImVector.Size
var popupStack = *((int*)contextPtr + ImGuiContextOffsets.BeginPopupStackOffset); // ImVector.Size
var windowStack = *((int*)contextPtr + ImGuiContextOffsets.CurrentWindowStackOffset); // ImVector.Size
return new ImGuiContextSnapshot
{
StyleVarStackSize = styleVarStack,
ColorStackSize = colorStack,
FontStackSize = fontStack,
BeginPopupStackSize = popupStack,
WindowStackSize = windowStack,
};
}
/// <summary>
/// Compare a snapshot to the current post-draw state and report any errors in a MessageBox dialog.
/// </summary>
/// <param name="source">The source of any problems, something to blame.</param>
/// <param name="before">ImGui context snapshot.</param>
public static void ReportProblems(string source, ImGuiContextSnapshot before)
{
if (!AssertsEnabled)
{
return;
}
var cSnap = GetSnapshot();
if (before.StyleVarStackSize != cSnap.StyleVarStackSize)
{
ShowAssert(source, $"You forgot to pop a style var!\n\nBefore: {before.StyleVarStackSize}, after: {cSnap.StyleVarStackSize}");
return;
}
if (before.ColorStackSize != cSnap.ColorStackSize)
{
ShowAssert(source, $"You forgot to pop a color!\n\nBefore: {before.ColorStackSize}, after: {cSnap.ColorStackSize}");
return;
}
if (before.FontStackSize != cSnap.FontStackSize)
{
ShowAssert(source, $"You forgot to pop a font!\n\nBefore: {before.FontStackSize}, after: {cSnap.FontStackSize}");
return;
}
if (before.BeginPopupStackSize != cSnap.BeginPopupStackSize)
{
ShowAssert(source, $"You forgot to end a popup!\n\nBefore: {before.BeginPopupStackSize}, after: {cSnap.BeginPopupStackSize}");
return;
}
if (cSnap.WindowStackSize != 1)
{
if (cSnap.WindowStackSize > 1)
{
ShowAssert(source, $"Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?\n\ncSnap.WindowStackSize = {cSnap.WindowStackSize}");
}
else
{
ShowAssert(source, $"Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?\n\ncSnap.WindowStackSize = {cSnap.WindowStackSize}");
}
}
}
private static void ShowAssert(string source, string message)
{
var caption = $"You fucked up";
message = $"{message}\n\nSource: {source}\n\nAsserts are now disabled. You may re-enable them.";
var flags = MessageBoxType.Ok | MessageBoxType.IconError;
_ = MessageBoxW(Process.GetCurrentProcess().MainWindowHandle, message, caption, flags);
AssertsEnabled = false;
}
/// <summary>
/// A snapshot of various ImGui context properties.
/// </summary>
public class ImGuiContextSnapshot
{
/// <summary>
/// Gets the ImGui style var stack size.
/// </summary>
public int StyleVarStackSize { get; init; }
/// <summary>
/// Gets the ImGui color stack size.
/// </summary>
public int ColorStackSize { get; init; }
/// <summary>
/// Gets the ImGui font stack size.
/// </summary>
public int FontStackSize { get; init; }
/// <summary>
/// Gets the ImGui begin popup stack size.
/// </summary>
public int BeginPopupStackSize { get; init; }
/// <summary>
/// Gets the ImGui window stack size.
/// </summary>
public int WindowStackSize { get; init; }
}
}
}

View file

@ -6,6 +6,7 @@ using Dalamud.Configuration.Internal;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.Gui;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.ManagedAsserts;
using Dalamud.Interface.Internal.Notifications;
using ImGuiNET;
using ImGuiScene;
@ -275,6 +276,12 @@ namespace Dalamud.Interface
ImGui.End();
}
ImGuiManagedAsserts.ImGuiContextSnapshot? snapshot = null;
if (this.Draw != null)
{
snapshot = ImGuiManagedAsserts.GetSnapshot();
}
try
{
this.Draw?.Invoke();
@ -288,6 +295,12 @@ namespace Dalamud.Interface
this.hasErrorWindow = true;
}
// Only if Draw was successful
if (this.Draw != null && snapshot != null)
{
ImGuiManagedAsserts.ReportProblems(this.namespaceName, snapshot);
}
this.FrameCount++;
if (DoStats)

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Interface.Internal.ManagedAsserts;
using ImGuiNET;
using Serilog;
@ -98,8 +99,12 @@ namespace Dalamud.Interface.Windowing
#if DEBUG
// Log.Verbose($"[WS{(hasNamespace ? "/" + this.Namespace : string.Empty)}] Drawing {window.WindowName}");
#endif
var snapshot = ImGuiManagedAsserts.GetSnapshot();
window.DrawInternal();
var source = ($"{this.Namespace}::" ?? string.Empty) + window.WindowName;
ImGuiManagedAsserts.ReportProblems(source, snapshot);
}
var focusedWindow = this.windows.FirstOrDefault(x => x.IsFocused && x.RespectCloseHotkey);