From b8ce2d4001737679f2255b8ee935583f2db4041d Mon Sep 17 00:00:00 2001 From: goaaats Date: Mon, 7 Apr 2025 20:05:11 +0200 Subject: [PATCH] Add standalone testbed --- Dalamud.sln | 7 + .../Internal/SeStringRenderer.cs | 2 - imgui/StandaloneImGuiTestbed/ImGuiBackend.cs | 638 ++++++++++++++++++ imgui/StandaloneImGuiTestbed/Program.cs | 61 ++ .../Shaders/GLSL/imgui-frag.glsl | 13 + .../Shaders/GLSL/imgui-vertex.glsl | 20 + .../Shaders/HLSL/imgui-frag.hlsl | 15 + .../Shaders/HLSL/imgui-frag.hlsl.bytes | Bin 0 -> 740 bytes .../Shaders/HLSL/imgui-vertex.hlsl | 27 + .../Shaders/HLSL/imgui-vertex.hlsl.bytes | Bin 0 -> 996 bytes .../Shaders/Metal/imgui-frag.metal | 18 + .../Shaders/Metal/imgui-frag.metallib | Bin 0 -> 2804 bytes .../Shaders/Metal/imgui-vertex.metal | 27 + .../Shaders/Metal/imgui-vertex.metallib | Bin 0 -> 3000 bytes .../Shaders/SPIR-V/generate-spirv.bat | 2 + .../Shaders/SPIR-V/imgui-frag.glsl | 16 + .../Shaders/SPIR-V/imgui-frag.spv | Bin 0 -> 868 bytes .../Shaders/SPIR-V/imgui-vertex.glsl | 28 + .../Shaders/SPIR-V/imgui-vertex.spv | Bin 0 -> 1308 bytes .../StandaloneImGuiTestbed.csproj | 40 ++ imgui/StandaloneImGuiTestbed/Testbed.cs | 36 + 21 files changed, 948 insertions(+), 2 deletions(-) create mode 100644 imgui/StandaloneImGuiTestbed/ImGuiBackend.cs create mode 100644 imgui/StandaloneImGuiTestbed/Program.cs create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/GLSL/imgui-frag.glsl create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/GLSL/imgui-vertex.glsl create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-frag.hlsl create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-frag.hlsl.bytes create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-vertex.hlsl create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-vertex.hlsl.bytes create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-frag.metal create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-frag.metallib create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-vertex.metal create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-vertex.metallib create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/generate-spirv.bat create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-frag.glsl create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-frag.spv create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-vertex.glsl create mode 100644 imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-vertex.spv create mode 100644 imgui/StandaloneImGuiTestbed/StandaloneImGuiTestbed.csproj create mode 100644 imgui/StandaloneImGuiTestbed/Testbed.cs diff --git a/Dalamud.sln b/Dalamud.sln index 22953b287..f469adee7 100644 --- a/Dalamud.sln +++ b/Dalamud.sln @@ -70,6 +70,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dalamud.Bindings.ImPlot", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bindings", "Bindings", "{A217B3DF-607A-4EFB-B107-3C4809348043}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandaloneImGuiTestbed", "imgui\StandaloneImGuiTestbed\StandaloneImGuiTestbed.csproj", "{4702A911-2513-478C-A434-2776393FDE77}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -156,6 +158,10 @@ Global {9C70BD06-D52C-425E-9C14-5D66BC6046EF}.Debug|Any CPU.Build.0 = Debug|x64 {9C70BD06-D52C-425E-9C14-5D66BC6046EF}.Release|Any CPU.ActiveCfg = Release|x64 {9C70BD06-D52C-425E-9C14-5D66BC6046EF}.Release|Any CPU.Build.0 = Release|x64 + {4702A911-2513-478C-A434-2776393FDE77}.Debug|Any CPU.ActiveCfg = Debug|x64 + {4702A911-2513-478C-A434-2776393FDE77}.Debug|Any CPU.Build.0 = Debug|x64 + {4702A911-2513-478C-A434-2776393FDE77}.Release|Any CPU.ActiveCfg = Release|x64 + {4702A911-2513-478C-A434-2776393FDE77}.Release|Any CPU.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -177,6 +183,7 @@ Global {B0AA8737-33A3-4766-8CBE-A48F2EF283BA} = {A217B3DF-607A-4EFB-B107-3C4809348043} {5E6EDD75-AE95-43A6-9D67-95B840EB4B71} = {A217B3DF-607A-4EFB-B107-3C4809348043} {9C70BD06-D52C-425E-9C14-5D66BC6046EF} = {A217B3DF-607A-4EFB-B107-3C4809348043} + {4702A911-2513-478C-A434-2776393FDE77} = {A217B3DF-607A-4EFB-B107-3C4809348043} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {79B65AC9-C940-410E-AB61-7EA7E12C7599} diff --git a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs index 7c18e59d1..785e7deb8 100644 --- a/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs +++ b/Dalamud/Interface/ImGuiSeStringRenderer/Internal/SeStringRenderer.cs @@ -154,7 +154,6 @@ internal unsafe class SeStringRenderer : IInternalDisposableService // Handle cases where ImGui.AlignTextToFramePadding has been called. var context = ImGui.GetCurrentContext(); var currLineTextBaseOffset = 0f; - /* if (!context.IsNull) { var currentWindow = context.CurrentWindow; @@ -163,7 +162,6 @@ internal unsafe class SeStringRenderer : IInternalDisposableService currLineTextBaseOffset = currentWindow.DC.CurrLineTextBaseOffset; } } - */ var itemSize = size; if (currLineTextBaseOffset != 0f) diff --git a/imgui/StandaloneImGuiTestbed/ImGuiBackend.cs b/imgui/StandaloneImGuiTestbed/ImGuiBackend.cs new file mode 100644 index 000000000..5cf147ec3 --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/ImGuiBackend.cs @@ -0,0 +1,638 @@ +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Dalamud.Bindings.ImGui; + +using Veldrid; +using Veldrid.Sdl2; + +namespace StandaloneImGuiTestbed; + +public class ImGuiBackend : IDisposable +{ + private GraphicsDevice gd; + private bool frameBegun; + + // Veldrid objects + private DeviceBuffer? vertexBuffer; + private DeviceBuffer? indexBuffer; + private DeviceBuffer? projMatrixBuffer; + private Texture? fontTexture; + private TextureView? fontTextureView; + private Shader? vertexShader; + private Shader? fragmentShader; + private ResourceLayout? layout; + private ResourceLayout? textureLayout; + private Pipeline? pipeline; + private ResourceSet? mainResourceSet; + private ResourceSet? fontTextureResourceSet; + + private readonly IntPtr fontAtlasId = (IntPtr)1; + private bool controlDown; + private bool shiftDown; + private bool altDown; + private bool winKeyDown; + + private IntPtr iniPathPtr; + + private int windowWidth; + private int windowHeight; + private Vector2 scaleFactor = Vector2.One; + + // Image trackers + private readonly Dictionary setsByView = new(); + + private readonly Dictionary autoViewsByTexture = new(); + + private readonly Dictionary viewsById = new Dictionary(); + private readonly List ownedResources = new List(); + private int lastAssignedID = 100; + + private delegate void SetClipboardTextDelegate(IntPtr userData, string text); + + private delegate string GetClipboardTextDelegate(); + + // variables because they need to exist for the program duration without being gc'd + private SetClipboardTextDelegate setText; + private GetClipboardTextDelegate getText; + + /// + /// Constructs a new ImGuiBackend. + /// + public unsafe ImGuiBackend(GraphicsDevice gd, OutputDescription outputDescription, int width, int height, FileInfo iniPath, float fontPxSize) + { + this.gd = gd; + windowWidth = width; + windowHeight = height; + + ImGui.CreateContext(); + + ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.NavEnableGamepad; + ImGui.GetIO().BackendFlags |= ImGuiBackendFlags.HasGamepad; + + SetIniPath(iniPath.FullName); + + setText = SetClipboardText; + getText = GetClipboardText; + + var io = ImGui.GetIO(); + io.SetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(setText).ToPointer(); + io.GetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(getText).ToPointer(); + io.ClipboardUserData = null; + + CreateDeviceResources(gd, outputDescription, fontPxSize); + SetKeyMappings(); + + SetPerFrameImGuiData(1f / 60f); + + ImGui.NewFrame(); + frameBegun = true; + } + + private void SetIniPath(string iniPath) + { + if (iniPathPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(iniPathPtr); + } + + iniPathPtr = Marshal.StringToHGlobalAnsi(iniPath); + + unsafe + { + var io = ImGui.GetIO(); + io.IniFilename = (byte*)iniPathPtr.ToPointer(); + } + } + + private static void SetClipboardText(IntPtr userData, string text) + { + // text always seems to have an extra newline, but I'll leave it for now + Sdl2Native.SDL_SetClipboardText(text); + } + + private static string GetClipboardText() + { + return Sdl2Native.SDL_GetClipboardText(); + } + + public void WindowResized(int width, int height) + { + windowWidth = width; + windowHeight = height; + } + + public void DestroyDeviceObjects() + { + Dispose(); + } + + public void CreateDeviceResources(GraphicsDevice gd, OutputDescription outputDescription, float fontPxSize) + { + this.gd = gd; + var factory = gd.ResourceFactory; + vertexBuffer = factory.CreateBuffer(new BufferDescription(10000, BufferUsage.VertexBuffer | BufferUsage.Dynamic)); + vertexBuffer.Name = "ImGui.NET Vertex Buffer"; + indexBuffer = factory.CreateBuffer(new BufferDescription(2000, BufferUsage.IndexBuffer | BufferUsage.Dynamic)); + indexBuffer.Name = "ImGui.NET Index Buffer"; + + var ioFonts = ImGui.GetIO().Fonts; + + ImGui.GetIO().Fonts.Clear(); + ImGui.GetIO().Fonts.AddFontDefault(); + ImGui.GetIO().Fonts.Build(); + + RecreateFontDeviceTexture(gd); + + projMatrixBuffer = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); + projMatrixBuffer.Name = "ImGui.NET Projection Buffer"; + + var vertexShaderBytes = LoadEmbeddedShaderCode(gd.ResourceFactory, "imgui-vertex", ShaderStages.Vertex); + var fragmentShaderBytes = LoadEmbeddedShaderCode(gd.ResourceFactory, "imgui-frag", ShaderStages.Fragment); + vertexShader = factory.CreateShader(new ShaderDescription(ShaderStages.Vertex, vertexShaderBytes, gd.BackendType == GraphicsBackend.Metal ? "VS" : "main")); + fragmentShader = factory.CreateShader(new ShaderDescription(ShaderStages.Fragment, fragmentShaderBytes, gd.BackendType == GraphicsBackend.Metal ? "FS" : "main")); + + var vertexLayouts = new VertexLayoutDescription[] + { + new VertexLayoutDescription( + new VertexElementDescription("in_position", VertexElementSemantic.Position, VertexElementFormat.Float2), + new VertexElementDescription("in_texCoord", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2), + new VertexElementDescription("in_color", VertexElementSemantic.Color, VertexElementFormat.Byte4_Norm)) + }; + + layout = factory.CreateResourceLayout(new ResourceLayoutDescription( + new ResourceLayoutElementDescription("ProjectionMatrixBuffer", ResourceKind.UniformBuffer, ShaderStages.Vertex), + new ResourceLayoutElementDescription("MainSampler", ResourceKind.Sampler, ShaderStages.Fragment))); + textureLayout = factory.CreateResourceLayout(new ResourceLayoutDescription( + new ResourceLayoutElementDescription("MainTexture", ResourceKind.TextureReadOnly, ShaderStages.Fragment))); + + var pd = new GraphicsPipelineDescription( + BlendStateDescription.SingleAlphaBlend, + new DepthStencilStateDescription(false, false, ComparisonKind.Always), + new RasterizerStateDescription(FaceCullMode.None, PolygonFillMode.Solid, FrontFace.Clockwise, false, true), + PrimitiveTopology.TriangleList, + new ShaderSetDescription(vertexLayouts, new[] { vertexShader, fragmentShader }), + new ResourceLayout[] { layout, textureLayout }, + outputDescription, + ResourceBindingModel.Default); + pipeline = factory.CreateGraphicsPipeline(ref pd); + + mainResourceSet = factory.CreateResourceSet(new ResourceSetDescription(layout, + projMatrixBuffer, + gd.PointSampler)); + + fontTextureResourceSet = factory.CreateResourceSet(new ResourceSetDescription(textureLayout, fontTextureView)); + } + + /// + /// Gets or creates a handle for a texture to be drawn with ImGui. + /// Pass the returned handle to Image() or ImageButton(). + /// + public IntPtr GetOrCreateImGuiBinding(ResourceFactory factory, TextureView textureView) + { + if (!setsByView.TryGetValue(textureView, out var rsi)) + { + var resourceSet = factory.CreateResourceSet(new ResourceSetDescription(textureLayout, textureView)); + rsi = new ResourceSetInfo(this.GetNextImGuiBindingId(), resourceSet); + + setsByView.Add(textureView, rsi); + viewsById.Add(rsi.ImGuiBinding, rsi); + ownedResources.Add(resourceSet); + } + + return rsi.ImGuiBinding; + } + + private IntPtr GetNextImGuiBindingId() + { + var newId = lastAssignedID++; + return (IntPtr)newId; + } + + /// + /// Gets or creates a handle for a texture to be drawn with ImGui. + /// Pass the returned handle to Image() or ImageButton(). + /// + public IntPtr GetOrCreateImGuiBinding(ResourceFactory factory, Texture texture) + { + if (!autoViewsByTexture.TryGetValue(texture, out var textureView)) + { + textureView = factory.CreateTextureView(texture); + autoViewsByTexture.Add(texture, textureView); + ownedResources.Add(textureView); + } + + return GetOrCreateImGuiBinding(factory, textureView); + } + + /// + /// Retrieves the shader texture binding for the given helper handle. + /// + public ResourceSet GetImageResourceSet(IntPtr imGuiBinding) + { + if (!viewsById.TryGetValue(imGuiBinding, out var tvi)) + { + throw new InvalidOperationException("No registered ImGui binding with id " + imGuiBinding.ToString()); + } + + return tvi.ResourceSet; + } + + public void ClearCachedImageResources() + { + foreach (var resource in ownedResources) + { + resource.Dispose(); + } + + ownedResources.Clear(); + setsByView.Clear(); + viewsById.Clear(); + autoViewsByTexture.Clear(); + lastAssignedID = 100; + } + + public static byte[] GetEmbeddedResourceBytes(string resourceName) + { + var assembly = typeof(ImGuiBackend).Assembly; + + using var s = assembly.GetManifestResourceStream(resourceName); + if (s == null) + throw new ArgumentException($"Resource {resourceName} not found", nameof(resourceName)); + + var ret = new byte[s.Length]; + s.ReadExactly(ret, 0, (int)s.Length); + return ret; + } + + private byte[] LoadEmbeddedShaderCode(ResourceFactory factory, string name, ShaderStages stage) + { + switch (factory.BackendType) + { + case GraphicsBackend.Direct3D11: + { + var resourceName = name + ".hlsl.bytes"; + return GetEmbeddedResourceBytes(resourceName); + } + + case GraphicsBackend.OpenGL: + { + var resourceName = name + ".glsl"; + return GetEmbeddedResourceBytes(resourceName); + } + + case GraphicsBackend.Vulkan: + { + var resourceName = name + ".spv"; + return GetEmbeddedResourceBytes(resourceName); + } + + case GraphicsBackend.Metal: + { + var resourceName = name + ".metallib"; + return GetEmbeddedResourceBytes(resourceName); + } + + default: + throw new NotImplementedException(); + } + } + + /// + /// Recreates the device texture used to render text. + /// + public unsafe void RecreateFontDeviceTexture(GraphicsDevice gd) + { + var io = ImGui.GetIO(); + // Build + byte* pixels = null; + int width, height, bytesPerPixel; + io.Fonts.GetTexDataAsRGBA32(0, &pixels, &width, &height, &bytesPerPixel); + // Store our identifier + io.Fonts.SetTexID(0, new ImTextureID(this.fontAtlasId)); + + fontTexture = gd.ResourceFactory.CreateTexture(TextureDescription.Texture2D( + (uint)width, + (uint)height, + 1, + 1, + PixelFormat.R8_G8_B8_A8_UNorm, + TextureUsage.Sampled)); + fontTexture.Name = "ImGui.NET Font Texture"; + gd.UpdateTexture( + fontTexture, + new IntPtr(pixels), + (uint)(bytesPerPixel * width * height), + 0, + 0, + 0, + (uint)width, + (uint)height, + 1, + 0, + 0); + fontTextureView = gd.ResourceFactory.CreateTextureView(fontTexture); + + io.Fonts.ClearTexData(); + } + + /// + /// Renders the ImGui draw list data. + /// This method requires a because it may create new DeviceBuffers if the size of vertex + /// or index data has increased beyond the capacity of the existing buffers. + /// A is needed to submit drawing and resource update commands. + /// + public void Render(GraphicsDevice gd, CommandList cl) + { + if (frameBegun) + { + frameBegun = false; + ImGui.Render(); + RenderImDrawData(ImGui.GetDrawData(), gd, cl); + } + } + + /// + /// Updates ImGui input and IO configuration state. + /// + public void Update(float deltaSeconds, InputSnapshot snapshot) + { + if (frameBegun) + { + ImGui.Render(); + } + + SetPerFrameImGuiData(deltaSeconds); + UpdateImGuiInput(snapshot); + + frameBegun = true; + ImGui.NewFrame(); + } + + /// + /// Sets per-frame data based on the associated window. + /// This is called by Update(float). + /// + private void SetPerFrameImGuiData(float deltaSeconds) + { + var io = ImGui.GetIO(); + io.DisplaySize = new Vector2( + windowWidth / scaleFactor.X, + windowHeight / scaleFactor.Y); + io.DisplayFramebufferScale = scaleFactor; + io.DeltaTime = deltaSeconds; // DeltaTime is in seconds. + } + + private void UpdateImGuiInput(InputSnapshot snapshot) + { + var io = ImGui.GetIO(); + + var mousePosition = snapshot.MousePosition; + + // Determine if any of the mouse buttons were pressed during this snapshot period, even if they are no longer held. + var leftPressed = false; + var middlePressed = false; + var rightPressed = false; + + foreach (var me in snapshot.MouseEvents) + { + if (me.Down) + { + switch (me.MouseButton) + { + case MouseButton.Left: + leftPressed = true; + break; + + case MouseButton.Middle: + middlePressed = true; + break; + + case MouseButton.Right: + rightPressed = true; + break; + } + } + } + + io.MouseDown[0] = leftPressed || snapshot.IsMouseDown(MouseButton.Left); + io.MouseDown[1] = rightPressed || snapshot.IsMouseDown(MouseButton.Right); + io.MouseDown[2] = middlePressed || snapshot.IsMouseDown(MouseButton.Middle); + io.MousePos = mousePosition; + io.MouseWheel = snapshot.WheelDelta; + + var keyCharPresses = snapshot.KeyCharPresses; + + for (var i = 0; i < keyCharPresses.Count; i++) + { + var c = keyCharPresses[i]; + io.AddInputCharacter(c); + } + + var keyEvents = snapshot.KeyEvents; + + for (var i = 0; i < keyEvents.Count; i++) + { + var keyEvent = keyEvents[i]; + io.KeysDown[(int)keyEvent.Key] = keyEvent.Down; + + if (keyEvent.Key == Key.ControlLeft) + { + controlDown = keyEvent.Down; + } + + if (keyEvent.Key == Key.ShiftLeft) + { + shiftDown = keyEvent.Down; + } + + if (keyEvent.Key == Key.AltLeft) + { + altDown = keyEvent.Down; + } + + if (keyEvent.Key == Key.WinLeft) + { + winKeyDown = keyEvent.Down; + } + } + + io.KeyCtrl = controlDown; + io.KeyAlt = altDown; + io.KeyShift = shiftDown; + io.KeySuper = winKeyDown; + } + + private static void SetKeyMappings() + { + var io = ImGui.GetIO(); + io.KeyMap[(int)ImGuiKey.Tab] = (int)Key.Tab; + io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)Key.Left; + io.KeyMap[(int)ImGuiKey.RightArrow] = (int)Key.Right; + io.KeyMap[(int)ImGuiKey.UpArrow] = (int)Key.Up; + io.KeyMap[(int)ImGuiKey.DownArrow] = (int)Key.Down; + io.KeyMap[(int)ImGuiKey.PageUp] = (int)Key.PageUp; + io.KeyMap[(int)ImGuiKey.PageDown] = (int)Key.PageDown; + io.KeyMap[(int)ImGuiKey.Home] = (int)Key.Home; + io.KeyMap[(int)ImGuiKey.End] = (int)Key.End; + io.KeyMap[(int)ImGuiKey.Delete] = (int)Key.Delete; + io.KeyMap[(int)ImGuiKey.Backspace] = (int)Key.BackSpace; + io.KeyMap[(int)ImGuiKey.Enter] = (int)Key.Enter; + io.KeyMap[(int)ImGuiKey.Escape] = (int)Key.Escape; + io.KeyMap[(int)ImGuiKey.Space] = (int)Key.Space; + io.KeyMap[(int)ImGuiKey.KeypadEnter] = (int)Key.KeypadEnter; + io.KeyMap[(int)ImGuiKey.A] = (int)Key.A; + io.KeyMap[(int)ImGuiKey.C] = (int)Key.C; + io.KeyMap[(int)ImGuiKey.V] = (int)Key.V; + io.KeyMap[(int)ImGuiKey.X] = (int)Key.X; + io.KeyMap[(int)ImGuiKey.Y] = (int)Key.Y; + io.KeyMap[(int)ImGuiKey.Z] = (int)Key.Z; + } + + private unsafe void RenderImDrawData(ImDrawDataPtr drawData, GraphicsDevice gd, CommandList cl) + { + uint vertexOffsetInVertices = 0; + uint indexOffsetInElements = 0; + + if (drawData.CmdListsCount == 0) + { + return; + } + + var totalVbSize = (uint)(drawData.TotalVtxCount * Unsafe.SizeOf()); + + if (totalVbSize > vertexBuffer!.SizeInBytes) + { + gd.DisposeWhenIdle(vertexBuffer); + vertexBuffer = gd.ResourceFactory.CreateBuffer(new BufferDescription((uint)(totalVbSize * 1.5f), BufferUsage.VertexBuffer | BufferUsage.Dynamic)); + } + + var totalIbSize = (uint)(drawData.TotalIdxCount * sizeof(ushort)); + + if (totalIbSize > indexBuffer!.SizeInBytes) + { + gd.DisposeWhenIdle(indexBuffer); + indexBuffer = gd.ResourceFactory.CreateBuffer(new BufferDescription((uint)(totalIbSize * 1.5f), BufferUsage.IndexBuffer | BufferUsage.Dynamic)); + } + + for (var i = 0; i < drawData.CmdListsCount; i++) + { + ImDrawListPtr cmdList = drawData.CmdLists[i]; + + cl.UpdateBuffer( + vertexBuffer, + vertexOffsetInVertices * (uint)Unsafe.SizeOf(), + new IntPtr(cmdList.VtxBuffer.Data), + (uint)(cmdList.VtxBuffer.Size * Unsafe.SizeOf())); + + cl.UpdateBuffer( + indexBuffer, + indexOffsetInElements * sizeof(ushort), + new IntPtr(cmdList.IdxBuffer.Data), + (uint)(cmdList.IdxBuffer.Size * sizeof(ushort))); + + vertexOffsetInVertices += (uint)cmdList.VtxBuffer.Size; + indexOffsetInElements += (uint)cmdList.IdxBuffer.Size; + } + + // Setup orthographic projection matrix into our constant buffer + var io = ImGui.GetIO(); + var mvp = Matrix4x4.CreateOrthographicOffCenter( + 0f, + io.DisplaySize.X, + io.DisplaySize.Y, + 0.0f, + -1.0f, + 1.0f); + + this.gd.UpdateBuffer(this.projMatrixBuffer, 0, ref mvp); + + cl.SetVertexBuffer(0, vertexBuffer); + cl.SetIndexBuffer(indexBuffer, IndexFormat.UInt16); + cl.SetPipeline(pipeline); + cl.SetGraphicsResourceSet(0, mainResourceSet); + + drawData.ScaleClipRects(io.DisplayFramebufferScale); + + // Render command lists + var globalIdxOffset = 0; + var globalVtxOffset = 0; + + for (var n = 0; n < drawData.CmdListsCount; n++) + { + ImDrawListPtr cmdList = drawData.CmdLists[n]; + + for (var cmdI = 0; cmdI < cmdList.CmdBuffer.Size; cmdI++) + { + var pcmd = cmdList.CmdBuffer[cmdI]; + + if (pcmd.UserCallback != null) + { + throw new NotImplementedException(); + } + else + { + if (pcmd.TextureId != IntPtr.Zero) + { + if (pcmd.TextureId == this.fontAtlasId) + { + cl.SetGraphicsResourceSet(1, fontTextureResourceSet); + } + else + { + cl.SetGraphicsResourceSet(1, GetImageResourceSet((nint)pcmd.TextureId.Handle)); + } + } + + cl.SetScissorRect( + 0, + (uint)pcmd.ClipRect.X, + (uint)pcmd.ClipRect.Y, + (uint)(pcmd.ClipRect.Z - pcmd.ClipRect.X), + (uint)(pcmd.ClipRect.W - pcmd.ClipRect.Y)); + + cl.DrawIndexed(pcmd.ElemCount, 1, pcmd.IdxOffset + (uint)globalIdxOffset, (int)pcmd.VtxOffset + globalVtxOffset, 0); + } + } + + globalIdxOffset += cmdList.IdxBuffer.Size; + globalVtxOffset += cmdList.VtxBuffer.Size; + } + } + + /// + /// Frees all graphics resources used by the renderer. + /// + public void Dispose() + { + vertexBuffer?.Dispose(); + indexBuffer?.Dispose(); + projMatrixBuffer?.Dispose(); + fontTexture?.Dispose(); + fontTextureView?.Dispose(); + vertexShader?.Dispose(); + fragmentShader?.Dispose(); + layout?.Dispose(); + textureLayout?.Dispose(); + pipeline?.Dispose(); + mainResourceSet?.Dispose(); + + foreach (var resource in ownedResources) + { + resource.Dispose(); + } + } + + private struct ResourceSetInfo + { + public readonly IntPtr ImGuiBinding; + public readonly ResourceSet ResourceSet; + + public ResourceSetInfo(IntPtr imGuiBinding, ResourceSet resourceSet) + { + ImGuiBinding = imGuiBinding; + ResourceSet = resourceSet; + } + } +} diff --git a/imgui/StandaloneImGuiTestbed/Program.cs b/imgui/StandaloneImGuiTestbed/Program.cs new file mode 100644 index 000000000..417c05880 --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Program.cs @@ -0,0 +1,61 @@ +using Veldrid; +using Veldrid.Sdl2; +using Veldrid.StartupUtilities; + +namespace StandaloneImGuiTestbed; + +class Program +{ + static void Main(string[] args) + { + Sdl2Window window; + GraphicsDevice gd; + + ImGuiBackend? backend = null; + + VeldridStartup.CreateWindowAndGraphicsDevice( + new WindowCreateInfo(50, 50, 1280, 800, WindowState.Normal, "Dalamud Standalone ImGui Testbed"), + new GraphicsDeviceOptions(false, null, true, ResourceBindingModel.Improved, true, true), + out window, + out gd); + + window.Resized += () => + { + gd.MainSwapchain.Resize((uint)window.Width, (uint)window.Height); + backend!.WindowResized(window.Width, window.Height); + }; + + var cl = gd.ResourceFactory.CreateCommandList(); + backend = new ImGuiBackend(gd, gd.MainSwapchain.Framebuffer.OutputDescription, window.Width, window.Height, new FileInfo("imgui.ini"), 21.0f); + + var testbed = new Testbed(); + + while (window.Exists) + { + Thread.Sleep(50); + + var snapshot = window.PumpEvents(); + + if (!window.Exists) + break; + + backend.Update(1f / 60f, snapshot); + + testbed.Draw(); + + cl.Begin(); + cl.SetFramebuffer(gd.MainSwapchain.Framebuffer); + cl.ClearColorTarget(0, new RgbaFloat(0, 0, 0, 1f)); + backend.Render(gd, cl); + cl.End(); + gd.SubmitCommands(cl); + gd.SwapBuffers(gd.MainSwapchain); + } + + // Clean up Veldrid resources + gd.WaitForIdle(); + backend.Dispose(); + cl.Dispose(); + gd.Dispose(); + } +} diff --git a/imgui/StandaloneImGuiTestbed/Shaders/GLSL/imgui-frag.glsl b/imgui/StandaloneImGuiTestbed/Shaders/GLSL/imgui-frag.glsl new file mode 100644 index 000000000..85e5ee981 --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Shaders/GLSL/imgui-frag.glsl @@ -0,0 +1,13 @@ +#version 330 core + +uniform sampler2D FontTexture; + +in vec4 color; +in vec2 texCoord; + +out vec4 outputColor; + +void main() +{ + outputColor = color * texture(FontTexture, texCoord); +} diff --git a/imgui/StandaloneImGuiTestbed/Shaders/GLSL/imgui-vertex.glsl b/imgui/StandaloneImGuiTestbed/Shaders/GLSL/imgui-vertex.glsl new file mode 100644 index 000000000..997ce0d1e --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Shaders/GLSL/imgui-vertex.glsl @@ -0,0 +1,20 @@ +#version 330 core + +uniform ProjectionMatrixBuffer +{ + mat4 projection_matrix; +}; + +in vec2 in_position; +in vec2 in_texCoord; +in vec4 in_color; + +out vec4 color; +out vec2 texCoord; + +void main() +{ + gl_Position = projection_matrix * vec4(in_position, 0, 1); + color = in_color; + texCoord = in_texCoord; +} diff --git a/imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-frag.hlsl b/imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-frag.hlsl new file mode 100644 index 000000000..63d175f0c --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-frag.hlsl @@ -0,0 +1,15 @@ +struct PS_INPUT +{ + float4 pos : SV_POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +Texture2D FontTexture : register(t0); +sampler FontSampler : register(s0); + +float4 FS(PS_INPUT input) : SV_Target +{ + float4 out_col = input.col * FontTexture.Sample(FontSampler, input.uv); + return out_col; +} \ No newline at end of file diff --git a/imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-frag.hlsl.bytes b/imgui/StandaloneImGuiTestbed/Shaders/HLSL/imgui-frag.hlsl.bytes new file mode 100644 index 0000000000000000000000000000000000000000..2a9fbf5a36bd803ed3dd9c596e2428b2d2daabee GIT binary patch literal 740 zcma)3Jxjw-6uq?;i>S>abSfDfDu@^nbPz%FVGFe>Nh(1Etr}~kH6>OM6wys^b(Zc~ zoSdBWPdJOepp$}7&rKSzL&XcH@4R#Gd*}2;vDf6c$3^co^Y#5+^&dtP7f&%F`WPZg zU|j${fjY!Ckb`b0s`d;mDoC`kz;y}41J?fo3u4>? zd@pAh2D&ILL2n$Z=AN|7N~2kKyntKIdF#w`Xrs38xu@=7OUxS6Vy<8oMDwU};CQ0! zHkvhzi_+YzM0s<)SjRn_AqkWKF7}RcrNFmY>=o+qbx9N)p7A`sPYPuyWuysFb8-#2rr-Nq2-<2O7`Qf!MYcLnT- z>@=RI8S-)&xde)L1R?NXJ`c{>9GvNh;oK48at4*??uF=DE>BX2!k*Tf*RH~SSsrFy{N~PwP!bCkGjfNqH zMDY2|u8>%&ZUz34UkS}ocg)P>yqxLnmbS}*Y5Dty)fz}_Vmu~UFTK2rdOVjzfjpp% z8qEM%Q&h$H=G~}oe$jcS-d^-^ylf%sEK4C(v>eAxisj@SR~*c%@htFh-dn#`FC72$ z?yO~=`cLx={8-+~#`{P$>3wj+%cP1CG{~dN0($I4r49Fq*k>jp5MTo<#ryi+V$C^| zya}E6+Cgd4R5i?%nM1639GrD{&vt6lxMEXO{Yt;`dM5L6kuIIrI%Mf=?P2$*GmxTs xuGVxt-)0j3XuXF$tahNL_p#BB^XBl3USXkd&KUqT8aexD)4GoT^1SnZ*efKJYZL$g literal 0 HcmV?d00001 diff --git a/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-frag.metal b/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-frag.metal new file mode 100644 index 000000000..ff3cbe6bd --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-frag.metal @@ -0,0 +1,18 @@ +#include +using namespace metal; + +struct PS_INPUT +{ + float4 pos [[ position ]]; + float4 col; + float2 uv; +}; + +fragment float4 FS( + PS_INPUT input [[ stage_in ]], + texture2d FontTexture [[ texture(0) ]], + sampler FontSampler [[ sampler(0) ]]) +{ + float4 out_col = input.col * FontTexture.sample(FontSampler, input.uv); + return out_col; +} \ No newline at end of file diff --git a/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-frag.metallib b/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-frag.metallib new file mode 100644 index 0000000000000000000000000000000000000000..18a3d73b29857311fec32b8878dc105bd4932324 GIT binary patch literal 2804 zcmZuzeN0=|6~D&D&wzQ)B&3C4=!0YxQCMOOAvVUH?FR%F(wcy(WXk4e;5o4PDqNtcCMib zBz>~)z31F>e&?L~@q2GiYm-UjPeW;F`B}aj*kst<7npCn z(Xywu!d;1$AxinMX|2vegHqAGXky9lTW@B?;U@my;xMWMcv>6Hs z17^X%5PE6oYxA~ooDCJ=M2wu8O?}TH#(*XiFGiG78K+i z6?F?K?wH9(D$0SVhi39-gnR^;EV+oGNTi=+^ni0Qp&JcoCmGEsqa791TxO~-iuHrR zno*-R##E1r4d4q<@_UQe_o0Hk3m}NPA0h7SwI_o$(~K5{beEZeDW>{^ zpl(pC9Syt&*qn zhe_$92>D;IEJ6}}C|L6WqfIgeQAQgQ>!*V?I4hjLg76{*Y|tYG_WH4kreRYctAs8p z)es(ek z1!W9>s9DZj$xoReL03N>QGClkDe>Y2_tW5e#wpjQ??>t1gPl^K+Ua0~xRG`a;6&ty86$(>ODBKqQ^&gqKD-`Fv}OTLT)M=A1nNFSZPzGr_6!{Q({aN17iJ@Z$RLQBQm0= zA|fkvMxM8GbW_utduT7`_H?0J!P|c1dGEebXqv)3v+I(gKO)y z**Lew#tH46KL+5m3SC~#(&6d`f$J!(Xm{Cb zy_FW*7HqW&1N6_V)ZN}%uNsU}d4PnC1;_j?%B=+1C+ygH0f*ohCMBG?_P4Ref6Z+sgCmotL(Z z*RlyvRne1q1|9~mA<56Sk&rSz%oqNxjt fC$$IGZ^-#v|Lulf&3u%0biM4A)TbYKK70NTxO%1L literal 0 HcmV?d00001 diff --git a/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-vertex.metal b/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-vertex.metal new file mode 100644 index 000000000..73d649e8b --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-vertex.metal @@ -0,0 +1,27 @@ +#include +using namespace metal; + +struct VS_INPUT +{ + float2 pos [[ attribute(0) ]]; + float2 uv [[ attribute(1) ]]; + float4 col [[ attribute(2) ]]; +}; + +struct PS_INPUT +{ + float4 pos [[ position ]]; + float4 col; + float2 uv; +}; + +vertex PS_INPUT VS( + VS_INPUT input [[ stage_in ]], + constant float4x4 &ProjectionMatrix [[ buffer(1) ]]) +{ + PS_INPUT output; + output.pos = ProjectionMatrix * float4(input.pos.xy, 0.f, 1.f); + output.col = input.col; + output.uv = input.uv; + return output; +} \ No newline at end of file diff --git a/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-vertex.metallib b/imgui/StandaloneImGuiTestbed/Shaders/Metal/imgui-vertex.metallib new file mode 100644 index 0000000000000000000000000000000000000000..2998b94808b1e017fbbea1d82135ea29d217cb11 GIT binary patch literal 3000 zcmZuzdr(tX8b9HN-0<+`s&oqyvKMT-u&BKR3W*7Bl8Y8r=mue2-L*pkB$Adq43{+E z{&B-i4OVt1D&1z*&NhtMopGkqLCaWLo!wv@%P@?~v~Jh#Iu;bE?)u2KxOT?QbiWf2 zq4rF2&iNka_dV`8-#HD<^(KjshSJbN8_il6cP-NX#qrYOxM6Wbl7)$Uk(Mvg!V zOyadK85_0|XlD~@-fi9r%vxhpErZ@|de7SR#h7ZEZG32mem68Cst)~9@96iGN#kOtDxr=t59CmKq(Iij#k7L;SQcZI)j603Z&1(^XJnk-pgl->_ z2yO08lqP^+cRC29QW@5jF0Q#6e>xR;4$u*Z;X7z1E`Id|U=YA-&1Rnw==5KvWNNTSfxx$C zu^G+L)NNLA1JSc#>T8<17N>4Z$>wS5R-AIO)Jz;p1#OVfO>$Z!)eR=}lU#mW)DI>! zv4kElBZMBH!h}Kbl^NsQO;Sz^^%YIsF;h;YxEH4G8WcAeh0{#k4pVo~l&HT1P@?WE ztQGY^P6r_Igf1rPra0|5mp@48Vw`RypgSw-MkFBBLz&IK2U&as==Qitop%-?!!j`uaeZ90ef;gi4m4Q1geAm zfX~y~paC6Z5P~047u9Ak?XOFZ7R7?lT@Mb0bJof^z}YY+*rClHN`a z(LXN>$`4#L#FdiV?Dh17Qc|2eUe&v-$F3q0yGN5A24`%6gnjY- zpr$Eo6!0NS?5q|cT#Ru+Gpr95zW&ymml-qP1luJGcvz%XMd-ZM_2Gex=U%)Z<#udk z)68i$vmi+~b>d|wVV~`Rv0N@F&(A3BIg;vrP7_QFm;|!NPj*H2WzQkS!XYWoHmS~y z6T z%~UykzThhCze2nn?d(BZ2%t2K5PGO_xDq9Ed=_$l2U*ntTa2s{LY{EQVo=9NB-6VB)n!1l`8PPgEq?{d6PNw=RR^>#Y5)R{pRrXmJS08O8 z)sH4LPwQyE;cFdA}D_p)X?h& zIQGZ-0bLXDCpk!^{B;A;3mxpUg6l-3VOOesz>9jHR z^*b9FpUvClaJv`{tKL+`6gzFawbO3fm?AfoSClkhhSlNS)aG`29G$jJ?KYR&>+EW2 zcXU}>J8jEFc54^k;)YCa_9Nn2a`rf|`@;(1>$`7oPoGdJ^lwLS+&9KJyxrJSIr1+vt?4fH9 zR%X94EbAOL-in=gSS;P~`&AP^Y53v+6JFyqYaG}9BCE^UOGssJJ|+9~!OOdH+gpW> ziPHg(SS5&>%dFqYkn1w)%}-TUo~eAR;*`4Tqmn=_pRxUD*U8eO$F}e9I<{uOcw)Fh z`}Aq{gZDoge*e_-XVf2lI8yaNi6oED1iifduZr8-N+0%$$Fce`(_kR)ZOOXkMU3O6 ziDPTV;i5VL`x!r`vNFBJa6ZZ2U7iTMP!ezwv{Q+`Ipu|+RX2^ee9RRV+$=1(I#z(J z3_@E{IpQ|ZFlN9%W=ENU&)~iZvTSx>wNREuOO4+iX9+{AOt=7#Flhf}5n2sn>g;3& z!7@UBg9n@2>2i47yv@ZkZ5>u`lf#M64Od#qG@Fe0{*umJrZ0^h2vB$9RAshbAbm*8 z{;g%QOuFrbkLlb`8O~CChW;I2mlJFqW8T@_XNj5)6VXiBPt(%+`^th`q^+aR6)yC| zN+Y*Td-8LopeQN)aTfheL@i@;UGfSxVx#*krvvMR&ksyz+4K8YS}pIFq~4RiK2Q`F uHzw9jL%wj^$1K$79TZ*E-WO9cHRH3jT0?($Xdpc(FNs$MlDI@Hwf_T}>(@O1 literal 0 HcmV?d00001 diff --git a/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/generate-spirv.bat b/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/generate-spirv.bat new file mode 100644 index 000000000..62d1d996b --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/generate-spirv.bat @@ -0,0 +1,2 @@ +glslangvalidator -V imgui-vertex.glsl -o imgui-vertex.spv -S vert +glslangvalidator -V imgui-frag.glsl -o imgui-frag.spv -S frag diff --git a/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-frag.glsl b/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-frag.glsl new file mode 100644 index 000000000..f94fa4859 --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-frag.glsl @@ -0,0 +1,16 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout(set = 1, binding = 0) uniform texture2D FontTexture; +layout(set = 0, binding = 1) uniform sampler FontSampler; + +layout (location = 0) in vec4 color; +layout (location = 1) in vec2 texCoord; +layout (location = 0) out vec4 outputColor; + +void main() +{ + outputColor = color * texture(sampler2D(FontTexture, FontSampler), texCoord); +} \ No newline at end of file diff --git a/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-frag.spv b/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..5d3d96f32e08822f4621e0f813cf814e668b2a67 GIT binary patch literal 868 zcmY+B+e!jq6oq%$bh3kX)(&*I7m11>3M$AOuQKX#nDoaib7Y)>UZ5B2sk#YT-=BhQ z*zQ?tuYaG+O&-mdP1wB6+OGXs(I&;1TD?)P4ANHR|x{up9PFhM^%};K&2atiOJ&vh;DCa)p!3S~-Ods;t2$LV_xf}Js ztGXCBJMh;t|GlB_X>t4VP1WPJVD7N5-&sZeA^3|S<|c6D=M||v%7fVfuB7WNDpC(T zZX-;MrC|asJ;3aVl{IBi5#5Ss%wu$5_Q$q0(Yhiv*keJ@Wr$kD)~p>J7E&nIc9xh+Qr|2gD# S^i3~vSDJ)d!~JN|bNMgo13~lv literal 0 HcmV?d00001 diff --git a/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-vertex.glsl b/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-vertex.glsl new file mode 100644 index 000000000..6249a36dd --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-vertex.glsl @@ -0,0 +1,28 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec2 in_position; +layout (location = 1) in vec2 in_texCoord; +layout (location = 2) in vec4 in_color; + +layout (binding = 0) uniform ProjectionMatrixBuffer +{ + mat4 projection_matrix; +}; + +layout (location = 0) out vec4 color; +layout (location = 1) out vec2 texCoord; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = projection_matrix * vec4(in_position, 0, 1); + color = in_color; + texCoord = in_texCoord; +} diff --git a/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-vertex.spv b/imgui/StandaloneImGuiTestbed/Shaders/SPIR-V/imgui-vertex.spv new file mode 100644 index 0000000000000000000000000000000000000000..b40ec8ca53630b09e586eda3ada5c2196a047e3e GIT binary patch literal 1308 zcmZ9KOK;Oa6orR4E^UF9Qu^RkoCXRKQdOaXkPx7jMY;%5pb}flB-3C>?Z|eeF8K@m zC4LnfB+fVXgqDeJX3n{fIrn;kR`;qgSInZBH&0EyR!mEbG1ttT+k5@v{%%=~cK2WG z5wToh-Aso0Bv&~X!+aFuDaSD{$$p>b#YjiTF+au4=p(W8$*RAAF+KGH$Bd6O z6?$QXGU7e%I`%TBN8q$mgE(sRRD*ud$nv;TLx<^c>jIAc!{p-k_2><(C*bnw%i~o= z5!aChUKrqQ5(!`;P9_BXX4R$TRS?ve@$oZg(n}(?7S9YH)Slef$uo<;~n*x z5q+SKd~hU#!{581>MYnMa*w8yrTpC)9Wo6 z{WHHeilpyV89bPKHe~epQ91N;x8bzSxO)x9y#uL7|9u&;_?QWE_^BHB9>} + + + Exe + enable + enable + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/imgui/StandaloneImGuiTestbed/Testbed.cs b/imgui/StandaloneImGuiTestbed/Testbed.cs new file mode 100644 index 000000000..9025d4b52 --- /dev/null +++ b/imgui/StandaloneImGuiTestbed/Testbed.cs @@ -0,0 +1,36 @@ +using Dalamud.Bindings.ImGui; + +namespace StandaloneImGuiTestbed; + +public class Testbed +{ + private bool showDemoWindow = false; + + public unsafe void Draw() + { + if (ImGui.Begin("Testbed")) + { + ImGui.Text("Hello!"); + + if (ImGui.Button("Open demo")) + { + this.showDemoWindow = true; + } + + if (this.showDemoWindow) + { + ImGui.ShowDemoWindow(ref this.showDemoWindow); + } + + if (ImGui.Button("Access context")) + { + var context = ImGui.GetCurrentContext(); + var currentWindow = context.CurrentWindow; + ref var dc = ref currentWindow.DC; // BREAKPOINT HERE, currentWindow will be invalid + dc.CurrLineTextBaseOffset = 0; + } + + ImGui.End(); + } + } +}