robert
92cf24fe9f
- Shader building now inspects the spir-v for descriptor sets and writes the info to the output binary
639 lines
24 KiB
C#
639 lines
24 KiB
C#
// ImGuiController with docking and viewport support for MoonWorks/Refresh.
|
|
// Based on the example im ImGui.NET and MoonWorksDearImGuiScaffold.
|
|
|
|
using ImGuiNET;
|
|
using Nerfed.Runtime.Graphics;
|
|
using System.Numerics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Nerfed.Runtime.Gui;
|
|
|
|
public class GuiController : IDisposable
|
|
{
|
|
public event Action OnGui;
|
|
|
|
private readonly string shaderContentPath = Path.Combine(System.AppContext.BaseDirectory, "Content", "Shaders");
|
|
|
|
private readonly GraphicsDevice graphicsDevice;
|
|
private readonly Window mainWindow;
|
|
private readonly Color clearColor;
|
|
|
|
private readonly Platform_CreateWindow createWindow;
|
|
private readonly Platform_DestroyWindow destroyWindow;
|
|
private readonly Platform_GetWindowPos getWindowPos;
|
|
private readonly Platform_ShowWindow showWindow;
|
|
private readonly Platform_SetWindowPos setWindowPos;
|
|
private readonly Platform_SetWindowSize setWindowSize;
|
|
private readonly Platform_GetWindowSize getWindowSize;
|
|
private readonly Platform_SetWindowFocus setWindowFocus;
|
|
private readonly Platform_GetWindowFocus getWindowFocus;
|
|
private readonly Platform_GetWindowMinimized getWindowMinimized;
|
|
private readonly Platform_SetWindowTitle setWindowTitle;
|
|
|
|
private readonly ResourceUploader resourceUploader;
|
|
private readonly GraphicsPipeline imGuiPipeline;
|
|
private readonly Shader imGuiVertexShader;
|
|
private readonly Shader imGuiFragmentShader;
|
|
private readonly Sampler imGuiSampler;
|
|
private readonly GuiTextureStorage textureStorage = new GuiTextureStorage();
|
|
private readonly GuiViewportWindow mainViewportWindow;
|
|
|
|
private Texture fontTexture = null;
|
|
private uint vertexCount = 0;
|
|
private uint indexCount = 0;
|
|
private Graphics.Buffer imGuiVertexBuffer = null;
|
|
private Graphics.Buffer imGuiIndexBuffer = null;
|
|
private bool frameBegun = false;
|
|
|
|
public GuiController(GraphicsDevice graphicsDevice, Window mainWindow, Color clearColor, ImGuiConfigFlags configFlags = ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable)
|
|
{
|
|
this.graphicsDevice = graphicsDevice;
|
|
this.mainWindow = mainWindow;
|
|
this.clearColor = clearColor;
|
|
|
|
resourceUploader = new ResourceUploader(graphicsDevice);
|
|
|
|
ImGui.CreateContext();
|
|
|
|
ImGuiIOPtr io = ImGui.GetIO();
|
|
io.DisplaySize = new Vector2(mainWindow.Width, mainWindow.Height);
|
|
io.DisplayFramebufferScale = Vector2.One;
|
|
|
|
imGuiVertexShader = ResourceManager.Load<Shader>("Shaders/ImGui.vert");
|
|
imGuiFragmentShader = ResourceManager.Load<Shader>("Shaders/ImGui.frag");
|
|
|
|
imGuiSampler = new Sampler(graphicsDevice, SamplerCreateInfo.LinearClamp);
|
|
|
|
imGuiPipeline = new GraphicsPipeline(
|
|
graphicsDevice,
|
|
new GraphicsPipelineCreateInfo
|
|
{
|
|
AttachmentInfo = new GraphicsPipelineAttachmentInfo(
|
|
new ColorAttachmentDescription(
|
|
mainWindow.SwapchainFormat,
|
|
ColorAttachmentBlendState.NonPremultiplied
|
|
)
|
|
),
|
|
DepthStencilState = DepthStencilState.Disable,
|
|
MultisampleState = MultisampleState.None,
|
|
PrimitiveType = PrimitiveType.TriangleList,
|
|
RasterizerState = RasterizerState.CW_CullNone,
|
|
VertexInputState = VertexInputState.CreateSingleBinding<Position2DTextureColorVertex>(),
|
|
VertexShader = imGuiVertexShader,
|
|
FragmentShader = imGuiFragmentShader,
|
|
}
|
|
);
|
|
|
|
BuildFontAtlas();
|
|
|
|
io.ConfigFlags = configFlags;
|
|
|
|
if (!OperatingSystem.IsWindows())
|
|
{
|
|
io.SetClipboardTextFn = GuiClipboard.SetFnPtr;
|
|
io.GetClipboardTextFn = GuiClipboard.GetFnPtr;
|
|
}
|
|
|
|
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
|
|
ImGuiViewportPtr mainViewport = platformIO.Viewports[0];
|
|
mainViewport.PlatformHandle = mainWindow.Handle;
|
|
mainViewportWindow = new GuiViewportWindow(graphicsDevice, mainViewport, mainWindow);
|
|
|
|
unsafe
|
|
{
|
|
createWindow = CreateWindow;
|
|
destroyWindow = DestroyWindow;
|
|
getWindowPos = GetWindowPos;
|
|
showWindow = ShowWindow;
|
|
setWindowPos = SetWindowPos;
|
|
setWindowSize = SetWindowSize;
|
|
getWindowSize = GetWindowSize;
|
|
setWindowFocus = SetWindowFocus;
|
|
getWindowFocus = GetWindowFocus;
|
|
getWindowMinimized = GetWindowMinimized;
|
|
setWindowTitle = SetWindowTitle;
|
|
|
|
platformIO.Platform_CreateWindow = Marshal.GetFunctionPointerForDelegate(createWindow);
|
|
platformIO.Platform_DestroyWindow = Marshal.GetFunctionPointerForDelegate(destroyWindow);
|
|
platformIO.Platform_ShowWindow = Marshal.GetFunctionPointerForDelegate(showWindow);
|
|
platformIO.Platform_SetWindowPos = Marshal.GetFunctionPointerForDelegate(setWindowPos);
|
|
platformIO.Platform_SetWindowSize = Marshal.GetFunctionPointerForDelegate(setWindowSize);
|
|
platformIO.Platform_SetWindowFocus = Marshal.GetFunctionPointerForDelegate(setWindowFocus);
|
|
platformIO.Platform_GetWindowFocus = Marshal.GetFunctionPointerForDelegate(getWindowFocus);
|
|
platformIO.Platform_GetWindowMinimized = Marshal.GetFunctionPointerForDelegate(getWindowMinimized);
|
|
platformIO.Platform_SetWindowTitle = Marshal.GetFunctionPointerForDelegate(setWindowTitle);
|
|
|
|
ImGuiNative.ImGuiPlatformIO_Set_Platform_GetWindowPos(platformIO.NativePtr, Marshal.GetFunctionPointerForDelegate(getWindowPos));
|
|
ImGuiNative.ImGuiPlatformIO_Set_Platform_GetWindowSize(platformIO.NativePtr, Marshal.GetFunctionPointerForDelegate(getWindowSize));
|
|
}
|
|
|
|
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
|
|
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
|
|
io.BackendFlags |= ImGuiBackendFlags.PlatformHasViewports;
|
|
io.BackendFlags |= ImGuiBackendFlags.RendererHasViewports;
|
|
|
|
UpdatePerFrameImGuiData(1.0 / 60.0);
|
|
ImGui.NewFrame();
|
|
frameBegun = true;
|
|
}
|
|
|
|
public void Update(double deltaTime)
|
|
{
|
|
if (frameBegun)
|
|
{
|
|
ImGui.Render();
|
|
ImGui.UpdatePlatformWindows();
|
|
}
|
|
|
|
UpdatePerFrameImGuiData(deltaTime);
|
|
UpdateInput();
|
|
UpdateCursor();
|
|
UpdateMonitors();
|
|
|
|
frameBegun = true;
|
|
ImGui.NewFrame();
|
|
|
|
OnGui?.Invoke();
|
|
|
|
ImGui.EndFrame();
|
|
}
|
|
|
|
private void UpdatePerFrameImGuiData(double deltaSeconds)
|
|
{
|
|
ImGuiIOPtr io = ImGui.GetIO();
|
|
io.DisplaySize = new Vector2(mainWindow.Width, mainWindow.Height);
|
|
io.DisplayFramebufferScale = new Vector2(1, 1);
|
|
io.DeltaTime = (float)deltaSeconds; // DeltaTime is in seconds.
|
|
}
|
|
|
|
private void UpdateInput()
|
|
{
|
|
ImGuiIOPtr io = ImGui.GetIO();
|
|
|
|
if ((ImGui.GetIO().ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0)
|
|
{
|
|
// For viewports we use the global mouse position.
|
|
_ = SDL2.SDL.SDL_GetGlobalMouseState(out int x, out int y);
|
|
io.MousePos = new Vector2(x, y);
|
|
}
|
|
else
|
|
{
|
|
// Without viewports we need to use the relative position.
|
|
//_ = SDL2.SDL.SDL_GetMouseState(out int x, out int y);
|
|
io.MousePos = Mouse.Position;
|
|
}
|
|
|
|
io.MouseDown[0] = Mouse.IsButtonDown(MouseButton.Left);
|
|
io.MouseDown[1] = Mouse.IsButtonDown(MouseButton.Right);
|
|
io.MouseDown[2] = Mouse.IsButtonDown(MouseButton.Middle);
|
|
|
|
io.MouseWheel = Mouse.GetWheel();
|
|
|
|
io.AddKeyEvent(ImGuiKey.A, Keyboard.IsKeyDown(Key.A));
|
|
io.AddKeyEvent(ImGuiKey.Z, Keyboard.IsKeyDown(Key.Z));
|
|
io.AddKeyEvent(ImGuiKey.Y, Keyboard.IsKeyDown(Key.Y));
|
|
io.AddKeyEvent(ImGuiKey.X, Keyboard.IsKeyDown(Key.X));
|
|
io.AddKeyEvent(ImGuiKey.C, Keyboard.IsKeyDown(Key.C));
|
|
io.AddKeyEvent(ImGuiKey.V, Keyboard.IsKeyDown(Key.V));
|
|
|
|
io.AddKeyEvent(ImGuiKey.Tab, Keyboard.IsKeyDown(Key.Tab));
|
|
io.AddKeyEvent(ImGuiKey.LeftArrow, Keyboard.IsKeyDown(Key.Left));
|
|
io.AddKeyEvent(ImGuiKey.RightArrow, Keyboard.IsKeyDown(Key.Right));
|
|
io.AddKeyEvent(ImGuiKey.UpArrow, Keyboard.IsKeyDown(Key.Up));
|
|
io.AddKeyEvent(ImGuiKey.DownArrow, Keyboard.IsKeyDown(Key.Down));
|
|
io.AddKeyEvent(ImGuiKey.Enter, Keyboard.IsKeyDown(Key.Enter));
|
|
io.AddKeyEvent(ImGuiKey.Escape, Keyboard.IsKeyDown(Key.Escape));
|
|
io.AddKeyEvent(ImGuiKey.Delete, Keyboard.IsKeyDown(Key.Delete));
|
|
io.AddKeyEvent(ImGuiKey.Backspace, Keyboard.IsKeyDown(Key.Backspace));
|
|
io.AddKeyEvent(ImGuiKey.Home, Keyboard.IsKeyDown(Key.Home));
|
|
io.AddKeyEvent(ImGuiKey.End, Keyboard.IsKeyDown(Key.End));
|
|
io.AddKeyEvent(ImGuiKey.PageDown, Keyboard.IsKeyDown(Key.PageDown));
|
|
io.AddKeyEvent(ImGuiKey.PageUp, Keyboard.IsKeyDown(Key.PageUp));
|
|
io.AddKeyEvent(ImGuiKey.Insert, Keyboard.IsKeyDown(Key.Insert));
|
|
|
|
io.AddKeyEvent(ImGuiKey.ModCtrl, Keyboard.IsKeyDown(Key.LeftControl) || Keyboard.IsKeyDown(Key.RightControl));
|
|
io.AddKeyEvent(ImGuiKey.ModShift, Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift));
|
|
io.AddKeyEvent(ImGuiKey.ModAlt, Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt));
|
|
io.AddKeyEvent(ImGuiKey.ModSuper, Keyboard.IsKeyDown(Key.LeftSuper) || Keyboard.IsKeyDown(Key.RightSuper));
|
|
|
|
ReadOnlySpan<char> input = Keyboard.GetTextInput();
|
|
if (!input.IsEmpty)
|
|
{
|
|
foreach (char c in input)
|
|
{
|
|
if (c == '\t')
|
|
{
|
|
break;
|
|
}
|
|
|
|
io.AddInputCharacter(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateCursor()
|
|
{
|
|
ImGuiIOPtr io = ImGui.GetIO();
|
|
|
|
if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ImGuiMouseCursor imGuiCursor = ImGui.GetMouseCursor();
|
|
|
|
if (imGuiCursor == ImGuiMouseCursor.None || io.MouseDrawCursor)
|
|
{
|
|
_ = SDL2.SDL.SDL_ShowCursor(0);
|
|
}
|
|
else
|
|
{
|
|
nint sdlCursor = imGuiCursor switch
|
|
{
|
|
ImGuiMouseCursor.Arrow => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW),
|
|
ImGuiMouseCursor.TextInput => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_IBEAM),
|
|
ImGuiMouseCursor.ResizeAll => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEALL),
|
|
ImGuiMouseCursor.ResizeNS => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENS),
|
|
ImGuiMouseCursor.ResizeEW => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEWE),
|
|
ImGuiMouseCursor.ResizeNESW => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENESW),
|
|
ImGuiMouseCursor.ResizeNWSE => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENWSE),
|
|
ImGuiMouseCursor.Hand => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_HAND),
|
|
ImGuiMouseCursor.NotAllowed => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_NO),
|
|
_ => SDL2.SDL.SDL_CreateSystemCursor(SDL2.SDL.SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW),
|
|
};
|
|
SDL2.SDL.SDL_SetCursor(sdlCursor);
|
|
_ = SDL2.SDL.SDL_ShowCursor(1);
|
|
}
|
|
}
|
|
|
|
private unsafe void UpdateMonitors()
|
|
{
|
|
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
|
|
Marshal.FreeHGlobal(platformIO.NativePtr->Monitors.Data);
|
|
int videoDisplayCount = SDL2.SDL.SDL_GetNumVideoDisplays();
|
|
IntPtr data = Marshal.AllocHGlobal(Unsafe.SizeOf<ImGuiPlatformMonitor>() * videoDisplayCount);
|
|
platformIO.NativePtr->Monitors = new ImVector(videoDisplayCount, videoDisplayCount, data);
|
|
|
|
for (int i = 0; i < videoDisplayCount; i++)
|
|
{
|
|
_ = SDL2.SDL.SDL_GetDisplayUsableBounds(i, out SDL2.SDL.SDL_Rect usableBounds);
|
|
_ = SDL2.SDL.SDL_GetDisplayBounds(i, out SDL2.SDL.SDL_Rect bounds);
|
|
_ = SDL2.SDL.SDL_GetDisplayDPI(i, out float ddpi, out float hdpi, out float vdpi);
|
|
ImGuiPlatformMonitorPtr monitor = platformIO.Monitors[i];
|
|
float standardDpi = 96f; // Standard DPI typically used
|
|
monitor.DpiScale = hdpi / standardDpi;
|
|
monitor.MainPos = new Vector2(bounds.x, bounds.y);
|
|
monitor.MainSize = new Vector2(bounds.w, bounds.h);
|
|
monitor.WorkPos = new Vector2(usableBounds.x, usableBounds.y);
|
|
monitor.WorkSize = new Vector2(usableBounds.w, usableBounds.h);
|
|
}
|
|
}
|
|
|
|
public void Render()
|
|
{
|
|
if (!frameBegun)
|
|
{
|
|
return;
|
|
}
|
|
|
|
frameBegun = false;
|
|
|
|
if ((ImGui.GetIO().ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0)
|
|
{
|
|
ImGui.Render();
|
|
ImGui.UpdatePlatformWindows();
|
|
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
|
|
|
|
for (int i = 0; i < platformIO.Viewports.Size; i++)
|
|
{
|
|
ImGuiViewportPtr vp = platformIO.Viewports[i];
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
|
|
if (!window.Window.Claimed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
UpdateImGuiBuffers(vp.DrawData);
|
|
|
|
CommandBuffer commandBuffer = graphicsDevice.AcquireCommandBuffer();
|
|
Texture swapchainTexture = commandBuffer.AcquireSwapchainTexture(window.Window);
|
|
|
|
if (swapchainTexture != null)
|
|
{
|
|
RenderCommandLists(commandBuffer, swapchainTexture, vp.DrawData);
|
|
graphicsDevice.Submit(commandBuffer);
|
|
//graphicsDevice.Wait();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui.Render();
|
|
|
|
if (!mainWindow.Claimed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ImDrawDataPtr drawDataPtr = ImGui.GetDrawData();
|
|
UpdateImGuiBuffers(drawDataPtr);
|
|
|
|
CommandBuffer commandBuffer = graphicsDevice.AcquireCommandBuffer();
|
|
Texture swapchainTexture = commandBuffer.AcquireSwapchainTexture(mainWindow);
|
|
|
|
if (swapchainTexture != null)
|
|
{
|
|
RenderCommandLists(commandBuffer, swapchainTexture, drawDataPtr);
|
|
graphicsDevice.Submit(commandBuffer);
|
|
//graphicsDevice.Wait();
|
|
}
|
|
}
|
|
}
|
|
|
|
private unsafe void UpdateImGuiBuffers(ImDrawDataPtr drawDataPtr)
|
|
{
|
|
if (drawDataPtr.TotalVtxCount == 0 || drawDataPtr.CmdListsCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (drawDataPtr.TotalVtxCount > vertexCount)
|
|
{
|
|
imGuiVertexBuffer?.Dispose();
|
|
|
|
vertexCount = (uint)(drawDataPtr.TotalVtxCount * 1.5f);
|
|
imGuiVertexBuffer = Graphics.Buffer.Create<Position2DTextureColorVertex>(
|
|
graphicsDevice,
|
|
BufferUsageFlags.Vertex,
|
|
vertexCount
|
|
);
|
|
}
|
|
|
|
if (drawDataPtr.TotalIdxCount > indexCount)
|
|
{
|
|
imGuiIndexBuffer?.Dispose();
|
|
|
|
indexCount = (uint)(drawDataPtr.TotalIdxCount * 1.5f);
|
|
imGuiIndexBuffer = Graphics.Buffer.Create<ushort>(
|
|
graphicsDevice,
|
|
BufferUsageFlags.Index,
|
|
indexCount
|
|
);
|
|
}
|
|
|
|
uint vertexOffset = 0;
|
|
uint indexOffset = 0;
|
|
|
|
for (int n = 0; n < drawDataPtr.CmdListsCount; n++)
|
|
{
|
|
ImDrawListPtr cmdList = drawDataPtr.CmdLists[n];
|
|
|
|
resourceUploader.SetBufferData(
|
|
imGuiVertexBuffer,
|
|
vertexOffset,
|
|
new Span<Position2DTextureColorVertex>(cmdList.VtxBuffer.Data.ToPointer(), cmdList.VtxBuffer.Size),
|
|
n == 0
|
|
);
|
|
|
|
resourceUploader.SetBufferData(
|
|
imGuiIndexBuffer,
|
|
indexOffset,
|
|
new Span<ushort>(cmdList.IdxBuffer.Data.ToPointer(), cmdList.IdxBuffer.Size),
|
|
n == 0
|
|
);
|
|
|
|
vertexOffset += (uint)cmdList.VtxBuffer.Size;
|
|
indexOffset += (uint)cmdList.IdxBuffer.Size;
|
|
}
|
|
|
|
resourceUploader.Upload();
|
|
}
|
|
|
|
private void RenderCommandLists(CommandBuffer commandBuffer, Texture renderTexture, ImDrawDataPtr drawDataPtr)
|
|
{
|
|
Vector2 pos = drawDataPtr.DisplayPos;
|
|
|
|
RenderPass renderPass = commandBuffer.BeginRenderPass(
|
|
new ColorAttachmentInfo(renderTexture, false, clearColor)
|
|
);
|
|
|
|
renderPass.BindGraphicsPipeline(imGuiPipeline);
|
|
|
|
// It is possible that the buffers are null (for example nothing is in our main windows viewport, then we exixt early but still clear it).
|
|
if (imGuiVertexBuffer == null || imGuiIndexBuffer == null)
|
|
{
|
|
commandBuffer.EndRenderPass(renderPass);
|
|
return;
|
|
}
|
|
|
|
Matrix4x4 projectionMatrix = Matrix4x4.CreateOrthographicOffCenter(
|
|
pos.X,
|
|
pos.X + drawDataPtr.DisplaySize.X,
|
|
pos.Y + drawDataPtr.DisplaySize.Y,
|
|
pos.Y,
|
|
-1.0f,
|
|
1.0f
|
|
);
|
|
TransformVertexUniform vertexUniform = new TransformVertexUniform(projectionMatrix);
|
|
|
|
renderPass.BindVertexBuffer(imGuiVertexBuffer);
|
|
renderPass.BindIndexBuffer(imGuiIndexBuffer, IndexElementSize.Sixteen);
|
|
|
|
commandBuffer.PushVertexUniformData(in vertexUniform);
|
|
|
|
uint vertexOffset = 0;
|
|
uint indexOffset = 0;
|
|
|
|
for (int n = 0; n < drawDataPtr.CmdListsCount; n++)
|
|
{
|
|
ImDrawListPtr cmdList = drawDataPtr.CmdLists[n];
|
|
|
|
for (int cmdIndex = 0; cmdIndex < cmdList.CmdBuffer.Size; cmdIndex++)
|
|
{
|
|
ImDrawCmdPtr drawCmd = cmdList.CmdBuffer[cmdIndex];
|
|
|
|
Texture texture = textureStorage.GetTexture(drawCmd.TextureId);
|
|
|
|
if (texture == null)
|
|
{
|
|
Log.Error("Texture or drawCmd.TextureId became null. Fit it!");
|
|
continue;
|
|
}
|
|
|
|
renderPass.BindFragmentSampler(new TextureSamplerBinding(texture, imGuiSampler));
|
|
|
|
float width = drawCmd.ClipRect.Z - (int)drawCmd.ClipRect.X;
|
|
float height = drawCmd.ClipRect.W - (int)drawCmd.ClipRect.Y;
|
|
|
|
if (width <= 0 || height <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
renderPass.SetScissor(
|
|
new Rect(
|
|
(int)drawCmd.ClipRect.X - (int)pos.X,
|
|
(int)drawCmd.ClipRect.Y - (int)pos.Y,
|
|
(int)drawCmd.ClipRect.Z - (int)drawCmd.ClipRect.X,
|
|
(int)drawCmd.ClipRect.W - (int)drawCmd.ClipRect.Y
|
|
)
|
|
);
|
|
|
|
renderPass.DrawIndexedPrimitives(vertexOffset, indexOffset, drawCmd.ElemCount / 3);
|
|
|
|
indexOffset += drawCmd.ElemCount;
|
|
}
|
|
|
|
vertexOffset += (uint)cmdList.VtxBuffer.Size;
|
|
}
|
|
|
|
commandBuffer.EndRenderPass(renderPass);
|
|
}
|
|
|
|
#region Resources
|
|
private unsafe void BuildFontAtlas()
|
|
{
|
|
ResourceUploader resourceUploader = new ResourceUploader(graphicsDevice);
|
|
|
|
ImGuiIOPtr io = ImGui.GetIO();
|
|
|
|
io.Fonts.GetTexDataAsRGBA32(
|
|
out nint pixelData,
|
|
out int width,
|
|
out int height,
|
|
out int bytesPerPixel
|
|
);
|
|
|
|
Texture fontTexture = resourceUploader.CreateTexture2D(
|
|
new Span<byte>((void*)pixelData, width * height * bytesPerPixel),
|
|
(uint)width,
|
|
(uint)height
|
|
);
|
|
|
|
resourceUploader.Upload();
|
|
resourceUploader.Dispose();
|
|
|
|
io.Fonts.SetTexID(fontTexture.Handle);
|
|
io.Fonts.ClearTexData();
|
|
|
|
textureStorage.Add(fontTexture); // <-- The fontTexture seems to get lost after some time (CG?).
|
|
this.fontTexture = fontTexture; // <-- So we also keep a reference to make sure it doesn't happen.
|
|
}
|
|
#endregion
|
|
|
|
#region Window
|
|
private void CreateWindow(ImGuiViewportPtr vp)
|
|
{
|
|
GuiViewportWindow window = new GuiViewportWindow(graphicsDevice, vp);
|
|
}
|
|
|
|
private void DestroyWindow(ImGuiViewportPtr vp)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
window.Dispose();
|
|
|
|
vp.PlatformUserData = IntPtr.Zero;
|
|
}
|
|
|
|
private void ShowWindow(ImGuiViewportPtr vp)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
SDL2.SDL.SDL_ShowWindow(window.Window.Handle);
|
|
}
|
|
|
|
private unsafe void GetWindowPos(ImGuiViewportPtr vp, Vector2* outPos)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
SDL2.SDL.SDL_GetWindowPosition(window.Window.Handle, out int x, out int y);
|
|
*outPos = new Vector2(x, y);
|
|
}
|
|
|
|
private void SetWindowPos(ImGuiViewportPtr vp, Vector2 pos)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
SDL2.SDL.SDL_SetWindowPosition(window.Window.Handle, (int)pos.X, (int)pos.Y);
|
|
}
|
|
|
|
private void SetWindowSize(ImGuiViewportPtr vp, Vector2 size)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
SDL2.SDL.SDL_SetWindowSize(window.Window.Handle, (int)size.X, (int)size.Y);
|
|
}
|
|
|
|
private unsafe void GetWindowSize(ImGuiViewportPtr vp, Vector2* outSize)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
SDL2.SDL.SDL_GetWindowSize(window.Window.Handle, out int w, out int h);
|
|
*outSize = new Vector2(w, h);
|
|
}
|
|
|
|
private void SetWindowFocus(ImGuiViewportPtr vp)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
//SDL2.SDL.SDL_SetWindowInputFocus(window.Handle);
|
|
SDL2.SDL.SDL_RaiseWindow(window.Window.Handle);
|
|
}
|
|
|
|
private byte GetWindowFocus(ImGuiViewportPtr vp)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return (byte)0;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
SDL2.SDL.SDL_WindowFlags flags = (SDL2.SDL.SDL_WindowFlags)SDL2.SDL.SDL_GetWindowFlags(window.Window.Handle);
|
|
|
|
return (flags & SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_INPUT_FOCUS) != 0 ? (byte)1 : (byte)0;
|
|
}
|
|
|
|
private byte GetWindowMinimized(ImGuiViewportPtr vp)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return 0;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
SDL2.SDL.SDL_WindowFlags flags = (SDL2.SDL.SDL_WindowFlags)SDL2.SDL.SDL_GetWindowFlags(window.Window.Handle);
|
|
|
|
return (flags & SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_MINIMIZED) != 0 ? (byte)1 : (byte)0;
|
|
}
|
|
|
|
private unsafe void SetWindowTitle(ImGuiViewportPtr vp, IntPtr title)
|
|
{
|
|
if (vp.PlatformUserData == IntPtr.Zero) return;
|
|
|
|
GuiViewportWindow window = (GuiViewportWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
|
|
byte* titlePtr = (byte*)title;
|
|
int count = 0;
|
|
while (titlePtr[count] != 0)
|
|
{
|
|
count += 1;
|
|
}
|
|
SDL2.SDL.SDL_SetWindowTitle(window.Window.Handle, System.Text.Encoding.ASCII.GetString(titlePtr, count));
|
|
}
|
|
#endregion
|
|
|
|
public void Dispose()
|
|
{
|
|
fontTexture?.Dispose();
|
|
imGuiVertexBuffer?.Dispose();
|
|
imGuiIndexBuffer?.Dispose();
|
|
ResourceManager.Unload(imGuiVertexShader);
|
|
ResourceManager.Unload(imGuiFragmentShader);
|
|
imGuiPipeline?.Dispose();
|
|
imGuiSampler?.Dispose();
|
|
resourceUploader?.Dispose();
|
|
}
|
|
} |