Added ImGui.NET

Start working on controller.
This commit is contained in:
max 2024-07-05 21:56:14 +02:00
parent 26eb1da3f0
commit 1e1ed303ad
17 changed files with 862 additions and 1 deletions

3
.gitmodules vendored
View File

@ -13,3 +13,6 @@
[submodule "Nerfed.Runtime/Libraries/dav1dfile"] [submodule "Nerfed.Runtime/Libraries/dav1dfile"]
path = Nerfed.Runtime/Libraries/dav1dfile path = Nerfed.Runtime/Libraries/dav1dfile
url = https://github.com/MoonsideGames/dav1dfile.git url = https://github.com/MoonsideGames/dav1dfile.git
[submodule "Nerfed.Runtime/Libraries/ImGui.NET"]
path = Nerfed.Runtime/Libraries/ImGui.NET
url = https://github.com/ImGuiNET/ImGui.NET.git

Binary file not shown.

Binary file not shown.

View File

@ -1,7 +1,7 @@
using System.Diagnostics;
using Nerfed.Runtime.Audio; using Nerfed.Runtime.Audio;
using Nerfed.Runtime.Graphics; using Nerfed.Runtime.Graphics;
using SDL2; using SDL2;
using System.Diagnostics;
namespace Nerfed.Runtime; namespace Nerfed.Runtime;
@ -39,6 +39,8 @@ public static class Engine
private const string WindowTitle = "Nerfed"; private const string WindowTitle = "Nerfed";
//.. //..
private static Gui.Controller Controller;
internal static void Run(string[] args) internal static void Run(string[] args)
{ {
Timestep = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / TargetTimestep); Timestep = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / TargetTimestep);
@ -65,11 +67,17 @@ internal static void Run(string[] args)
AudioDevice = new AudioDevice(); AudioDevice = new AudioDevice();
Controller = new Gui.Controller(GraphicsDevice, MainWindow, Color.DarkOliveGreen);
Controller.OnGui += () => {
ImGuiNET.ImGui.ShowDemoWindow();
};
while (!quit) while (!quit)
{ {
Tick(); Tick();
} }
Controller.Dispose();
GraphicsDevice.UnclaimWindow(MainWindow); GraphicsDevice.UnclaimWindow(MainWindow);
MainWindow.Dispose(); MainWindow.Dispose();
GraphicsDevice.Dispose(); GraphicsDevice.Dispose();
@ -145,6 +153,7 @@ private static void Tick()
ProcessSDLEvents(); ProcessSDLEvents();
Controller.Update((float)Timestep.TotalSeconds);
// Tick game here... // Tick game here...
AudioDevice.WakeThread(); AudioDevice.WakeThread();
@ -154,6 +163,7 @@ private static void Tick()
double alpha = accumulatedUpdateTime / Timestep; double alpha = accumulatedUpdateTime / Timestep;
// Render here.. // Render here..
Controller.Render();
accumulatedDrawTime -= framerateCapTimeSpan; accumulatedDrawTime -= framerateCapTimeSpan;
} }

View File

@ -0,0 +1,48 @@
using System.Runtime.InteropServices;
using System.Text;
namespace Nerfed.Runtime.Gui;
public static unsafe class Clipboard
{
private static IntPtr clipboard;
private static readonly Dictionary<Delegate, IntPtr> pinned = new();
private static unsafe void Set(void* userdata, byte* text)
{
int len = 0; while (text[len] != 0) len++;
string str = Encoding.UTF8.GetString(text, len);
SDL2.SDL.SDL_SetClipboardText(str);
}
private static unsafe byte* Get(void* userdata)
{
if (clipboard != IntPtr.Zero)
{
NativeMemory.Free((void*) clipboard);
clipboard = IntPtr.Zero;
}
string str = SDL2.SDL.SDL_GetClipboardText();
int length = Encoding.UTF8.GetByteCount(str);
byte* bytes = (byte*)(clipboard = (nint)NativeMemory.Alloc((nuint)(length + 1)));
Encoding.UTF8.GetBytes(str, new Span<byte>(bytes, length));
bytes[length] = 0;
return bytes;
}
// Stops the delegate pointer from being collected
private static IntPtr GetPointerTo<T>(T fn) where T : Delegate
{
if (pinned.TryGetValue(fn, out nint ptr))
return ptr;
ptr = Marshal.GetFunctionPointerForDelegate(fn);
pinned.Add(fn, ptr);
return ptr;
}
public static readonly IntPtr GetFnPtr = GetPointerTo(Get);
public static readonly IntPtr SetFnPtr = GetPointerTo(Set);
}

View File

@ -0,0 +1,661 @@
// 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;
internal class Controller : IDisposable
{
public event Action OnGui;
private readonly string shaderContentPath = Path.Combine(System.AppContext.BaseDirectory, "Assets", "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 TextureStorage textureStorage = new TextureStorage();
private readonly Dictionary<Window, GCHandle> windows = new Dictionary<Window, GCHandle>(16);
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 Controller(GraphicsDevice graphicsDevice, Window mainWindow, Color clearColor, ImGuiConfigFlags configFlags = ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable)
{
this.mainWindow = mainWindow;
this.graphicsDevice = graphicsDevice;
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;
ShaderCreateInfo vertexCreateInfo = new ShaderCreateInfo {
ShaderStage = ShaderStage.Vertex,
ShaderFormat = ShaderFormat.SPIRV,
UniformBufferCount = 1,
};
imGuiVertexShader = new Shader(graphicsDevice, Path.Combine(shaderContentPath, "imgui-vertex.spv"), "main", in vertexCreateInfo);
ShaderCreateInfo fragCreateInfo = new ShaderCreateInfo {
ShaderStage = ShaderStage.Fragment,
ShaderFormat = ShaderFormat.SPIRV,
SamplerCount = 1,
};
imGuiFragmentShader = new Shader(graphicsDevice, Path.Combine(shaderContentPath, "imgui-frag.spv"), "main", in fragCreateInfo);
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;
//io.MouseDrawCursor = true;
if (!OperatingSystem.IsWindows())
{
io.SetClipboardTextFn = Clipboard.SetFnPtr;
io.GetClipboardTextFn = Clipboard.GetFnPtr;
}
ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO();
ImGuiViewportPtr mainViewport = platformIO.Viewports[0];
mainViewport.PlatformHandle = mainWindow.Handle;
GCHandle handle = GCHandle.Alloc(mainWindow);
mainViewport.PlatformUserData = (IntPtr)handle;
windows.Add(mainWindow, handle);
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;
}
public void Update(float deltaTime)
{
if (frameBegun)
{
ImGui.Render();
ImGui.UpdatePlatformWindows();
}
UpdatePerFrameImGuiData(deltaTime);
UpdateInput();
UpdateMonitors();
frameBegun = true;
ImGui.NewFrame();
OnGui?.Invoke();
{ // Debug
ImGuiIOPtr io = ImGui.GetIO();
ImGui.Text($"mouse pos: {io.MousePos}");
}
ImGui.EndFrame();
}
private void UpdatePerFrameImGuiData(float deltaSeconds)
{
ImGuiIOPtr io = ImGui.GetIO();
io.DisplaySize = new Vector2(mainWindow.Width, mainWindow.Height);
io.DisplayFramebufferScale = new Vector2(1, 1);
io.DeltaTime = 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 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];
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
if (!window.Claimed)
{
continue;
}
UpdateImGuiBuffers(vp.DrawData);
CommandBuffer commandBuffer = graphicsDevice.AcquireCommandBuffer();
Texture swapchainTexture = commandBuffer.AcquireSwapchainTexture(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);
}
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.
}
#region Window
private void CreateWindow(ImGuiViewportPtr vp)
{
WindowCreateInfo info = new WindowCreateInfo("Window Title", (uint)vp.Pos.X, (uint)vp.Pos.Y, ScreenMode.Windowed);
//SDL2.SDL.SDL_WindowFlags flags = graphicsDevice.WindowFlags;
//flags |= SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_HIDDEN;
//if ((vp.Flags & ImGuiViewportFlags.NoTaskBarIcon) != 0)
//{
// flags |= SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_SKIP_TASKBAR;
//}
//if ((vp.Flags & ImGuiViewportFlags.NoDecoration) != 0)
//{
// flags |= SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_BORDERLESS;
// info.SystemResizable = false;
//}
//else
//{
// flags |= SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE;
// info.SystemResizable = true;
//}
//if ((vp.Flags & ImGuiViewportFlags.TopMost) != 0)
//{
// flags |= SDL2.SDL.SDL_WindowFlags.SDL_WINDOW_ALWAYS_ON_TOP;
//}
Window window = new Window(graphicsDevice, info);
graphicsDevice.ClaimWindow(window, SwapchainComposition.SDR, PresentMode.Immediate);
GCHandle handle = GCHandle.Alloc(window);
vp.PlatformUserData = (IntPtr)handle;
windows.Add(window, handle);
}
private void DestroyWindow(ImGuiViewportPtr vp)
{
if (vp.PlatformUserData == IntPtr.Zero) return;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
graphicsDevice.UnclaimWindow(window);
if (windows.TryGetValue(window, out GCHandle handle))
{
handle.Free();
windows.Remove(window);
}
graphicsDevice.Wait();
window.Dispose();
}
private void ShowWindow(ImGuiViewportPtr vp)
{
if (vp.PlatformUserData == IntPtr.Zero) return;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
SDL2.SDL.SDL_ShowWindow(window.Handle);
}
private unsafe void GetWindowPos(ImGuiViewportPtr vp, Vector2* outPos)
{
if (vp.PlatformUserData == IntPtr.Zero) return;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
SDL2.SDL.SDL_GetWindowPosition(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;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
SDL2.SDL.SDL_SetWindowPosition(window.Handle, (int)pos.X, (int)pos.Y);
}
private void SetWindowSize(ImGuiViewportPtr vp, Vector2 size)
{
if (vp.PlatformUserData == IntPtr.Zero) return;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
SDL2.SDL.SDL_SetWindowSize(window.Handle, (int)size.X, (int)size.Y);
}
private unsafe void GetWindowSize(ImGuiViewportPtr vp, Vector2* outSize)
{
if (vp.PlatformUserData == IntPtr.Zero) return;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
SDL2.SDL.SDL_GetWindowSize(window.Handle, out int w, out int h);
*outSize = new Vector2(w, h);
}
private void SetWindowFocus(ImGuiViewportPtr vp)
{
if (vp.PlatformUserData == IntPtr.Zero) return;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
//SDL2.SDL.SDL_SetWindowInputFocus(window.Handle);
SDL2.SDL.SDL_RaiseWindow(window.Handle);
}
private byte GetWindowFocus(ImGuiViewportPtr vp)
{
if (vp.PlatformUserData == IntPtr.Zero) return (byte)0;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
SDL2.SDL.SDL_WindowFlags flags = (SDL2.SDL.SDL_WindowFlags)SDL2.SDL.SDL_GetWindowFlags(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 (byte)0;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
SDL2.SDL.SDL_WindowFlags flags = (SDL2.SDL.SDL_WindowFlags)SDL2.SDL.SDL_GetWindowFlags(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;
Window window = (Window)GCHandle.FromIntPtr(vp.PlatformUserData).Target;
byte* titlePtr = (byte*)title;
int count = 0;
while (titlePtr[count] != 0)
{
count += 1;
}
SDL2.SDL.SDL_SetWindowTitle(window.Handle, System.Text.Encoding.ASCII.GetString(titlePtr, count));
}
#endregion
public void Dispose()
{
fontTexture?.Dispose();
imGuiVertexBuffer?.Dispose();
imGuiIndexBuffer?.Dispose();
imGuiFragmentShader?.Dispose();
imGuiVertexShader?.Dispose();
imGuiPipeline?.Dispose();
imGuiSampler?.Dispose();
resourceUploader?.Dispose();
foreach (KeyValuePair<Window, GCHandle> window in windows)
{
graphicsDevice.UnclaimWindow(window.Key);
window.Key.Dispose();
window.Value.Free();
}
windows.Clear();
}
}

View File

@ -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

View File

@ -0,0 +1,13 @@
#version 450
layout (location = 0) in vec4 color;
layout (location = 1) in vec2 texCoord;
layout(set = 2, binding = 0) uniform sampler2D Sampler;
layout (location = 0) out vec4 outputColor;
void main()
{
outputColor = color * texture(Sampler, texCoord);
}

Binary file not shown.

View File

@ -0,0 +1,20 @@
#version 450
layout (location = 0) in vec2 in_position;
layout (location = 1) in vec2 in_texCoord;
layout (location = 2) in vec4 in_color;
layout (set = 1, binding = 0) uniform ProjectionMatrixBuffer
{
mat4 projection_matrix;
};
layout (location = 0) out vec4 color;
layout (location = 1) out vec2 texCoord;
void main()
{
gl_Position = projection_matrix * vec4(in_position, 0, 1);
color = in_color;
texCoord = in_texCoord;
}

Binary file not shown.

View File

@ -0,0 +1,49 @@
using Nerfed.Runtime.Graphics;
using System.Numerics;
using System.Runtime.InteropServices;
namespace Nerfed.Runtime.Gui;
[StructLayout(LayoutKind.Sequential)]
public struct Position2DTextureColorVertex : IVertexType
{
public Vector2 Position;
public Vector2 TexCoord;
public Color Color;
public Position2DTextureColorVertex(
Vector2 position,
Vector2 texcoord,
Color color
)
{
Position = position;
TexCoord = texcoord;
Color = color;
}
public static VertexElementFormat[] Formats { get; } = new VertexElementFormat[3]
{
VertexElementFormat.Vector2,
VertexElementFormat.Vector2,
VertexElementFormat.Color
};
public static uint[] Offsets => new uint[3]
{
0,
8,
16
};
}
[StructLayout(LayoutKind.Sequential)]
public struct TransformVertexUniform
{
public Matrix4x4 ProjectionMatrix;
public TransformVertexUniform(Matrix4x4 projectionMatrix)
{
ProjectionMatrix = projectionMatrix;
}
}

View File

@ -0,0 +1,35 @@
using Nerfed.Runtime.Graphics;
namespace Nerfed.Runtime.Gui;
public class TextureStorage
{
private readonly Dictionary<IntPtr, WeakReference<Texture>> pointerToTexture = new Dictionary<IntPtr, WeakReference<Texture>>();
public IntPtr Add(Texture texture)
{
if (!pointerToTexture.ContainsKey(texture.Handle))
{
pointerToTexture.Add(texture.Handle, new WeakReference<Texture>(texture));
}
return texture.Handle;
}
public Texture GetTexture(IntPtr pointer)
{
if (!pointerToTexture.ContainsKey(pointer))
{
return null;
}
WeakReference<Texture> result = pointerToTexture[pointer];
if (!result.TryGetTarget(out Texture texture))
{
pointerToTexture.Remove(pointer);
return null;
}
return texture;
}
}

@ -0,0 +1 @@
Subproject commit ae493d92a312810b66483af1922babe2eb434a47

View File

@ -12,6 +12,16 @@
<Compile Include="Libraries\dav1dfile\csharp\dav1dfile.cs" /> <Compile Include="Libraries\dav1dfile\csharp\dav1dfile.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="Libraries\ImGui.NET\src\ImGui.NET\ImGui.NET.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\**\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>

View File

@ -5,6 +5,8 @@ VisualStudioVersion = 17.10.35013.160
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Runtime", "Nerfed.Runtime\Nerfed.Runtime.csproj", "{98E09BAF-587F-4238-89BD-7693C036C233}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nerfed.Runtime", "Nerfed.Runtime\Nerfed.Runtime.csproj", "{98E09BAF-587F-4238-89BD-7693C036C233}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGui.NET", "Nerfed.Runtime\Libraries\ImGui.NET\src\ImGui.NET\ImGui.NET.csproj", "{4EC3C399-4E09-4A36-B11E-391F0792C1C8}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -15,6 +17,10 @@ Global
{98E09BAF-587F-4238-89BD-7693C036C233}.Debug|Any CPU.Build.0 = Debug|Any CPU {98E09BAF-587F-4238-89BD-7693C036C233}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98E09BAF-587F-4238-89BD-7693C036C233}.Release|Any CPU.ActiveCfg = Release|Any CPU {98E09BAF-587F-4238-89BD-7693C036C233}.Release|Any CPU.ActiveCfg = Release|Any CPU
{98E09BAF-587F-4238-89BD-7693C036C233}.Release|Any CPU.Build.0 = Release|Any CPU {98E09BAF-587F-4238-89BD-7693C036C233}.Release|Any CPU.Build.0 = Release|Any CPU
{4EC3C399-4E09-4A36-B11E-391F0792C1C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EC3C399-4E09-4A36-B11E-391F0792C1C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EC3C399-4E09-4A36-B11E-391F0792C1C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EC3C399-4E09-4A36-B11E-391F0792C1C8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

BIN
libs/x64/cimgui.dll (Stored with Git LFS) Normal file

Binary file not shown.