diff --git a/Wobble.Tests/Screens/Tests/Imgui/TestImGuiMenu.cs b/Wobble.Tests/Screens/Tests/Imgui/TestImGuiMenu.cs
index 21827ab4..b5b813b2 100644
--- a/Wobble.Tests/Screens/Tests/Imgui/TestImGuiMenu.cs
+++ b/Wobble.Tests/Screens/Tests/Imgui/TestImGuiMenu.cs
@@ -1,5 +1,5 @@
using System;
-using ImGuiNET;
+using Hexa.NET.ImGui;
using Microsoft.Xna.Framework;
using Wobble.Graphics.ImGUI;
using Wobble.Graphics.UI.Buttons;
@@ -45,7 +45,7 @@ protected override void RenderImguiLayout()
{
if (ImGui.BeginMenu("File"))
{
- ImGui.MenuItem("Open", null, ref Opened);
+ ImGui.MenuItem("Open", (string)null, ref Opened);
ImGui.Separator();
ImGui.MenuItem("Save");
ImGui.MenuItem("Exit");
@@ -54,12 +54,12 @@ protected override void RenderImguiLayout()
if (ImGui.BeginMenu("Effects"))
{
- ImGui.MenuItem("Rotation", null, ref Rotation);
- ImGui.MenuItem("Lightshow", null, ref Lightshow);
+ ImGui.MenuItem("Rotation", (string)null, ref Rotation);
+ ImGui.MenuItem("Lightshow", (string)null, ref Lightshow);
ImGui.EndMenu();
}
- ImGui.EndMenuBar();
+ ImGui.EndMainMenuBar();
Button.IsGloballyClickable = !ImGui.IsAnyItemHovered();
}
}
@@ -70,4 +70,4 @@ public override void Destroy()
Button.IsGloballyClickable = true;
}
}
-}
\ No newline at end of file
+}
diff --git a/Wobble.Tests/Screens/Tests/Imgui/TestImGuiScreenView.cs b/Wobble.Tests/Screens/Tests/Imgui/TestImGuiScreenView.cs
index dc71c425..d5ee3c9c 100644
--- a/Wobble.Tests/Screens/Tests/Imgui/TestImGuiScreenView.cs
+++ b/Wobble.Tests/Screens/Tests/Imgui/TestImGuiScreenView.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using ImGuiNET;
+using Hexa.NET.ImGui;
using Microsoft.Xna.Framework;
using Wobble.Assets;
using Wobble.Graphics;
diff --git a/Wobble/Graphics/ImGUI/DrawVertDeclaration.cs b/Wobble/Graphics/ImGUI/DrawVertDeclaration.cs
index 148db544..8c49a76e 100644
--- a/Wobble/Graphics/ImGUI/DrawVertDeclaration.cs
+++ b/Wobble/Graphics/ImGUI/DrawVertDeclaration.cs
@@ -1,4 +1,4 @@
-using ImGuiNET;
+using Hexa.NET.ImGui;
using Microsoft.Xna.Framework.Graphics;
namespace Wobble.Graphics.ImGUI
diff --git a/Wobble/Graphics/ImGUI/ImGuiOptions.cs b/Wobble/Graphics/ImGUI/ImGuiOptions.cs
index 9c798d0b..658140e6 100644
--- a/Wobble/Graphics/ImGUI/ImGuiOptions.cs
+++ b/Wobble/Graphics/ImGUI/ImGuiOptions.cs
@@ -1,5 +1,5 @@
using System.Collections.Generic;
-using ImGuiNET;
+using Hexa.NET.ImGui;
namespace Wobble.Graphics.ImGUI
{
diff --git a/Wobble/Graphics/ImGUI/ImGuiRenderer.cs b/Wobble/Graphics/ImGUI/ImGuiRenderer.cs
index 4ba640df..6178ccfc 100644
--- a/Wobble/Graphics/ImGUI/ImGuiRenderer.cs
+++ b/Wobble/Graphics/ImGUI/ImGuiRenderer.cs
@@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Net.Mime;
using System.Runtime.InteropServices;
-using ImGuiNET;
+using System.Threading;
+using Hexa.NET.ImGui;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
@@ -11,7 +11,7 @@
namespace Wobble.Graphics.ImGUI
{
- public sealed class ImGuiRenderer : IDisposable
+ public sealed unsafe class ImGuiRenderer : IDisposable
{
///
/// ImGui renderers use separate contexts, so ImGui cannot arbitrate input between overlapping windows itself.
@@ -31,7 +31,7 @@ public sealed class ImGuiRenderer : IDisposable
///
///
- public IntPtr Context { get; }
+ public ImGuiContextPtr Context { get; }
///
///
@@ -79,11 +79,14 @@ public sealed class ImGuiRenderer : IDisposable
///
///
- private int TextureId { get; set; }
+ private static long NextTextureId;
- ///
- ///
- private IntPtr? FontTextureId { get; set; }
+ private static readonly ImGuiErrorCallback RecoveredPluginErrorCallback = IgnoreRecoveredPluginError;
+
+ private static unsafe void IgnoreRecoveredPluginError(ImGuiContext* context, void* userData, byte* message)
+ {
+ // Recovery is expected for legacy Lua plugins that scoped style pushes to End().
+ }
///
///
@@ -153,10 +156,9 @@ public ImGuiRenderer(bool destroyContext = true, ImGuiOptions options = null, fl
DestroyContext = destroyContext;
Options = options;
Scale = scale;
- FontAtlas = SharedFontAtlasCache.Retain(options, scale);
-
- Context = ImGui.CreateContext(FontAtlas.Atlas);
+ Context = ImGui.CreateContext();
ImGui.SetCurrentContext(Context);
+ FontAtlas = new SharedFontAtlas(options, scale, ImGui.GetIO().Fonts);
LoadedTextures = new Dictionary();
@@ -189,56 +191,25 @@ public unsafe void RebuildFontAtlas()
if (previousContext != Context)
ImGui.SetCurrentContext(Context);
- var io = ImGui.GetIO();
FontAtlas.AssignFontPointers(Options, out var defaultFontPtr);
DefaultFontPtr = defaultFontPtr;
- FontAtlas.EnsureTexture(GraphicsDevice);
-
- if (FontTextureId.HasValue && FontTextureId.Value != FontAtlas.TextureId)
- UnbindTexture(FontTextureId.Value);
-
- FontTextureId = FontAtlas.TextureId;
- LoadedTextures[FontTextureId.Value] = new TextureBinding(FontAtlas.Texture, false);
+ var io = ImGui.GetIO();
+ io.FontDefault = defaultFontPtr;
- // Let ImGui know where to find the texture
- io.Fonts.SetTexID(FontTextureId.Value);
+ if (Options?.Fonts.Count > 0)
+ ImGui.GetStyle().FontSizeBase = Options.Fonts[0].Size * Scale;
if (previousContext != Context)
ImGui.SetCurrentContext(previousContext);
}
- private static IntPtr GetGlyphRanges(ImFontAtlasPtr fonts, ImGuiGlyphRanges ranges)
- {
- switch (ranges)
- {
- case ImGuiGlyphRanges.ChineseFull:
- return fonts.GetGlyphRangesChineseFull();
- case ImGuiGlyphRanges.ChineseSimplifiedCommon:
- return fonts.GetGlyphRangesChineseSimplifiedCommon();
- case ImGuiGlyphRanges.Japanese:
- return fonts.GetGlyphRangesJapanese();
- case ImGuiGlyphRanges.Korean:
- return fonts.GetGlyphRangesKorean();
- case ImGuiGlyphRanges.Cyrillic:
- return fonts.GetGlyphRangesCyrillic();
- case ImGuiGlyphRanges.Greek:
- return fonts.GetGlyphRangesGreek();
- case ImGuiGlyphRanges.Thai:
- return fonts.GetGlyphRangesThai();
- case ImGuiGlyphRanges.Vietnamese:
- return fonts.GetGlyphRangesVietnamese();
- default:
- return fonts.GetGlyphRangesDefault();
- }
- }
-
///
/// Creates a pointer to a texture, which can be passed through ImGui calls such as .
/// That pointer is then used by ImGui to let us know what texture to draw
///
public IntPtr BindTexture(Texture2D texture)
{
- var id = new IntPtr(TextureId++);
+ var id = new IntPtr(Interlocked.Increment(ref NextTextureId));
LoadedTextures.Add(id, new TextureBinding(texture, true));
@@ -308,9 +279,23 @@ private void OnWindowOnTextInput(object s, TextInputEventArgs a)
///
/// Maps ImGui keys to XNA keys. We use this later on to tell ImGui what keys were pressed
///
- private void SetupInput()
+ private unsafe void SetupInput()
{
var io = ImGui.GetIO();
+ io.BackendFlags |= ImGuiBackendFlags.RendererHasTextures | ImGuiBackendFlags.RendererHasVtxOffset;
+ io.ConfigErrorRecovery = true;
+ io.ConfigErrorRecoveryEnableAssert = false;
+ // Plugins written against pre-1.92 ImGui commonly scoped style pushes to a whole
+ // window and relied on End() to recover the stack. Keep that safe recovery behavior,
+ // but don't flood the editor UI or logs with a diagnostic for every recovered item.
+ io.ConfigErrorRecoveryEnableDebugLog = false;
+ io.ConfigErrorRecoveryEnableTooltip = false;
+ var context = Context;
+ context.ErrorCallback = (void*)Marshal.GetFunctionPointerForDelegate(RecoveredPluginErrorCallback);
+
+ var platformIo = ImGui.GetPlatformIO();
+ platformIo.RendererTextureMaxWidth = GraphicsDevice.GraphicsProfile == GraphicsProfile.HiDef ? 16384 : 4096;
+ platformIo.RendererTextureMaxHeight = GraphicsDevice.GraphicsProfile == GraphicsProfile.HiDef ? 16384 : 4096;
// MonoGame-specific //////////////////////
@@ -327,8 +312,8 @@ private void SetupInput()
///////////////////////////////////////////
// ImGUI provides out-of-the-box clipboard only on Windows. For other platforms, we need to set up the function pointers.
- io.SetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(SetClipboardTextFnDelegate);
- io.GetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(GetClipboardTextFnDelegate);
+ platformIo.PlatformSetClipboardTextFn = (void*)Marshal.GetFunctionPointerForDelegate(SetClipboardTextFnDelegate);
+ platformIo.PlatformGetClipboardTextFn = (void*)Marshal.GetFunctionPointerForDelegate(GetClipboardTextFnDelegate);
}
/*
@@ -498,6 +483,8 @@ private void UpdateInputOwners(GameTime gameTime, MouseState mouse)
///
private void RenderDrawData(ImDrawDataPtr drawData)
{
+ UpdateDynamicTextures(drawData);
+
LastVertexCount = drawData.TotalVtxCount;
LastIndexCount = drawData.TotalIdxCount;
@@ -508,6 +495,7 @@ private void RenderDrawData(ImDrawDataPtr drawData)
var lastBlendState = GraphicsDevice.BlendState;
var lastRasterizerState = GraphicsDevice.RasterizerState;
var lastDepthStencilState = GraphicsDevice.DepthStencilState;
+ var lastSamplerState = GraphicsDevice.SamplerStates[0];
// We are submitting vertex and index buffers without using SpriteBatch.
// We need to end the batch so we are drawing on top of those that are not flushed
@@ -516,6 +504,7 @@ private void RenderDrawData(ImDrawDataPtr drawData)
GraphicsDevice.BlendState = BlendState.NonPremultiplied;
GraphicsDevice.RasterizerState = RasterizerState;
GraphicsDevice.DepthStencilState = DepthStencilState.None;
+ GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
// Handle cases of screen coordinates != from framebuffer coordinates (e.g. retina displays)
drawData.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale);
@@ -539,6 +528,28 @@ private void RenderDrawData(ImDrawDataPtr drawData)
GraphicsDevice.BlendState = lastBlendState;
GraphicsDevice.RasterizerState = lastRasterizerState;
GraphicsDevice.DepthStencilState = lastDepthStencilState;
+ GraphicsDevice.SamplerStates[0] = lastSamplerState;
+ }
+
+ private unsafe void UpdateDynamicTextures(ImDrawDataPtr drawData)
+ {
+ for (var i = 0; i < drawData.Textures.Size; i++)
+ {
+ var textureData = drawData.Textures[i];
+
+ switch (textureData.Status)
+ {
+ case ImTextureStatus.WantCreate:
+ FontAtlas.CreateTexture(GraphicsDevice, textureData);
+ break;
+ case ImTextureStatus.WantUpdates:
+ FontAtlas.UpdateTexture(textureData);
+ break;
+ case ImTextureStatus.WantDestroy:
+ FontAtlas.DestroyTexture(textureData);
+ break;
+ }
+ }
}
///
@@ -640,9 +651,11 @@ private void RenderCommandLists(ImDrawDataPtr drawData)
{
var drawCmd = cmdList.CmdBuffer[cmdi];
- if (!LoadedTextures.ContainsKey(drawCmd.TextureId))
+ var textureId = (IntPtr)drawCmd.GetTexID();
+
+ if (!TryGetTexture(textureId, out var texture))
throw new InvalidOperationException(
- $"Could not find a texture with id '{drawCmd.TextureId}', please check your bindings"
+ $"Could not find a texture with id '{textureId}', please check your bindings"
);
GraphicsDevice.ScissorRectangle = new Rectangle(
@@ -652,7 +665,7 @@ private void RenderCommandLists(ImDrawDataPtr drawData)
(int)(drawCmd.ClipRect.W - drawCmd.ClipRect.Y)
);
- var effect = UpdateEffect(LoadedTextures[drawCmd.TextureId].Texture);
+ var effect = UpdateEffect(texture);
var vertexOffset = vtxOffset + (int)drawCmd.VtxOffset;
var indexOffset = idxOffset + (int)drawCmd.IdxOffset;
@@ -682,6 +695,17 @@ private void RenderCommandLists(ImDrawDataPtr drawData)
GraphicsDevice.ScissorRectangle = lastScissorRectangle;
}
+ private bool TryGetTexture(IntPtr textureId, out Texture2D texture)
+ {
+ if (LoadedTextures.TryGetValue(textureId, out var binding))
+ {
+ texture = binding.Texture;
+ return true;
+ }
+
+ return FontAtlas.TryGetTexture(textureId, out texture);
+ }
+
#endregion Internals
///
@@ -699,7 +723,10 @@ public void Dispose()
KeyboardInputOwner = null;
if (DestroyContext)
+ {
+ FontAtlas.Dispose();
ImGui.DestroyContext(Context);
+ }
foreach (var texture in LoadedTextures.Values)
{
@@ -714,8 +741,6 @@ public void Dispose()
IndexBuffer?.Dispose();
Game.Window.TextInput -= OnWindowOnTextInput;
- if (DestroyContext)
- SharedFontAtlasCache.Release(FontAtlas);
}
private sealed class TextureBinding
@@ -733,25 +758,20 @@ public TextureBinding(Texture2D texture, bool disposeWithRenderer)
private sealed class SharedFontAtlas
{
- public string Key { get; }
-
public ImFontAtlasPtr Atlas { get; }
- public IntPtr TextureId { get; } = new IntPtr(-1);
-
- public Texture2D Texture { get; private set; }
-
- public int ReferenceCount { get; set; }
-
private ImFontPtr DefaultFontPtr { get; set; }
private List FontPointers { get; }
- public unsafe SharedFontAtlas(string key, ImGuiOptions options, float scale)
+ private Dictionary Textures { get; }
+
+ public unsafe SharedFontAtlas(ImGuiOptions options, float scale, ImFontAtlasPtr atlas)
{
- Key = key;
- Atlas = new ImFontAtlasPtr(ImGuiNative.ImFontAtlas_ImFontAtlas());
+ Atlas = atlas;
+ Atlas.TexDesiredFormat = ImTextureFormat.Rgba32;
FontPointers = new List();
+ Textures = new Dictionary();
if (options == null || options.LoadDefaultFont)
DefaultFontPtr = Atlas.AddFontDefault();
@@ -766,12 +786,11 @@ public unsafe SharedFontAtlas(string key, ImGuiOptions options, float scale)
foreach (var fallback in font.Fallbacks)
{
- var config = new ImFontConfigPtr(ImGuiNative.ImFontConfig_ImFontConfig());
+ var config = ImGui.ImFontConfig();
config.MergeMode = true;
- config.FontNo = fallback.Index;
+ config.FontNo = (uint)fallback.Index;
- Atlas.AddFontFromFileTTF(fallback.Path, font.Size * scale, config,
- GetGlyphRanges(Atlas, fallback.GlyphRanges));
+ Atlas.AddFontFromFileTTF(fallback.Path, font.Size * scale, config);
config.Destroy();
}
@@ -782,6 +801,9 @@ public void AssignFontPointers(ImGuiOptions options, out ImFontPtr defaultFontPt
{
defaultFontPtr = DefaultFontPtr;
+ if (defaultFontPtr.IsNull && FontPointers.Count > 0)
+ defaultFontPtr = FontPointers[0];
+
if (options == null)
return;
@@ -789,85 +811,57 @@ public void AssignFontPointers(ImGuiOptions options, out ImFontPtr defaultFontPt
options.Fonts[i].Context = FontPointers[i];
}
- public unsafe void EnsureTexture(GraphicsDevice graphicsDevice)
- {
- if (Texture != null)
- return;
-
- Atlas.GetTexDataAsRGBA32(out byte* pixelData, out var width, out var height, out var bytesPerPixel);
+ public bool TryGetTexture(IntPtr textureId, out Texture2D texture) =>
+ Textures.TryGetValue(textureId, out texture);
- var pixels = new byte[width * height * bytesPerPixel];
- Marshal.Copy(new IntPtr(pixelData), pixels, 0, pixels.Length);
-
- Texture = new Texture2D(graphicsDevice, width, height, false, SurfaceFormat.Color);
- Texture.SetData(pixels);
-
- Atlas.SetTexID(TextureId);
- Atlas.ClearTexData();
- }
-
- public unsafe void Dispose()
+ public unsafe void CreateTexture(GraphicsDevice graphicsDevice, ImTextureDataPtr textureData)
{
- Texture?.Dispose();
- Texture = null;
- ImGuiNative.ImFontAtlas_destroy(Atlas.NativePtr);
+ if (textureData.Format != ImTextureFormat.Rgba32)
+ throw new NotSupportedException($"Unsupported ImGui texture format: {textureData.Format}");
+
+ var id = new IntPtr(Interlocked.Increment(ref NextTextureId));
+ var texture = new Texture2D(graphicsDevice, textureData.Width, textureData.Height, false, SurfaceFormat.Color);
+ Textures.Add(id, texture);
+ UploadTexture(texture, textureData);
+ textureData.SetTexID(id);
+ textureData.SetStatus(ImTextureStatus.Ok);
}
- }
-
- private static class SharedFontAtlasCache
- {
- private static readonly Dictionary Cache = new Dictionary();
- public static SharedFontAtlas Retain(ImGuiOptions options, float scale)
+ public unsafe void UpdateTexture(ImTextureDataPtr textureData)
{
- var key = CreateKey(options, scale);
+ var id = (IntPtr)textureData.TexID;
- if (!Cache.TryGetValue(key, out var atlas))
- {
- atlas = new SharedFontAtlas(key, options, scale);
- Cache.Add(key, atlas);
- }
+ if (!Textures.TryGetValue(id, out var texture))
+ throw new InvalidOperationException($"Could not update ImGui texture with id '{id}'");
- atlas.ReferenceCount++;
- return atlas;
+ UploadTexture(texture, textureData);
+ textureData.SetStatus(ImTextureStatus.Ok);
}
- public static void Release(SharedFontAtlas atlas)
+ public void DestroyTexture(ImTextureDataPtr textureData)
{
- if (atlas == null)
- return;
+ var id = (IntPtr)textureData.TexID;
- atlas.ReferenceCount--;
-
- if (atlas.ReferenceCount > 0)
- return;
+ if (Textures.Remove(id, out var texture))
+ texture.Dispose();
- Cache.Remove(atlas.Key);
- atlas.Dispose();
+ textureData.SetTexID(IntPtr.Zero);
+ textureData.SetStatus(ImTextureStatus.Destroyed);
}
- private static string CreateKey(ImGuiOptions options, float scale)
+ private static unsafe void UploadTexture(Texture2D texture, ImTextureDataPtr textureData)
{
- var key = "scale=" + scale.ToString("R", CultureInfo.InvariantCulture);
-
- if (options == null)
- return key + ";default";
-
- key += ";loadDefault=" + options.LoadDefaultFont;
-
- foreach (var font in options.Fonts)
- {
- key += ";font=" + font.Path + "," + font.Size.ToString(CultureInfo.InvariantCulture);
+ var pixels = new byte[textureData.Width * textureData.Height * textureData.BytesPerPixel];
+ Marshal.Copy((IntPtr)textureData.GetPixels(), pixels, 0, pixels.Length);
+ texture.SetData(pixels);
+ }
- foreach (var fallback in font.Fallbacks)
- {
- key += ";fallback=" + fallback.Path + "," +
- fallback.Index.ToString(CultureInfo.InvariantCulture) + "," +
- fallback.GlyphRanges;
- }
- }
+ public void Dispose()
+ {
+ foreach (var texture in Textures.Values)
+ texture.Dispose();
- return key;
+ Textures.Clear();
}
}
}
diff --git a/Wobble/Graphics/ImGUI/SpriteImGui.cs b/Wobble/Graphics/ImGUI/SpriteImGui.cs
index 5b16376e..087b5f68 100644
--- a/Wobble/Graphics/ImGUI/SpriteImGui.cs
+++ b/Wobble/Graphics/ImGUI/SpriteImGui.cs
@@ -1,5 +1,5 @@
using System;
-using ImGuiNET;
+using Hexa.NET.ImGui;
using Microsoft.Xna.Framework;
namespace Wobble.Graphics.ImGUI
diff --git a/Wobble/Graphics/UI/Debugging/DebugOverlay.cs b/Wobble/Graphics/UI/Debugging/DebugOverlay.cs
index 312be451..d32dd791 100644
--- a/Wobble/Graphics/UI/Debugging/DebugOverlay.cs
+++ b/Wobble/Graphics/UI/Debugging/DebugOverlay.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using ImGuiNET;
+using Hexa.NET.ImGui;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Wobble.Graphics.ImGUI;
@@ -179,7 +179,7 @@ private void RenderTextObjectControls()
highlightedTextObjectId = selectedTextObjectId;
- if (ImGui.BeginChild("SpriteTextPlusObjectList", new Vector2(0, 180), ImGuiChildFlags.Border))
+ if (ImGui.BeginChild("SpriteTextPlusObjectList", new Vector2(0, 180), ImGuiChildFlags.Borders))
{
for (var i = 0; i < objects.Count; i++)
{
@@ -228,7 +228,7 @@ private void RenderDrawableControls()
highlightedDrawableId = selectedDrawableId;
- if (ImGui.BeginChild("DrawableTypeList", new Vector2(0, 260), ImGuiChildFlags.Border))
+ if (ImGui.BeginChild("DrawableTypeList", new Vector2(0, 260), ImGuiChildFlags.Borders))
{
for (var i = 0; i < groups.Count; i++)
RenderDrawableTypeGroup(groups[i]);
diff --git a/Wobble/Wobble.csproj b/Wobble/Wobble.csproj
index 29a13dae..f4c3a017 100644
--- a/Wobble/Wobble.csproj
+++ b/Wobble/Wobble.csproj
@@ -10,7 +10,7 @@
-
+