Skip to content

Instantly share code, notes, and snippets.

@thatcosmonaut
Last active September 26, 2023 21:50
Show Gist options
  • Save thatcosmonaut/0d4e549f6774277bb16c17561f6568fc to your computer and use it in GitHub Desktop.
Save thatcosmonaut/0d4e549f6774277bb16c17561f6568fc to your computer and use it in GitHub Desktop.
MoonWorks + Dear Imgui
#if DEBUG
using MoonWorks;
using MoonWorks.Graphics;
using MoonWorks.Math.Float;
using ImGuiNET;
using Buffer = MoonWorks.Graphics.Buffer;
namespace SamuraiGunn2
{
public class DebugRenderer
{
private GraphicsDevice GraphicsDevice;
private Window Window;
private Data.DebugTextureStorage TextureStorage;
private Texture FontTexture;
private uint VertexCount = 0;
private uint IndexCount = 0;
private Buffer ImGuiVertexBuffer = null;
private Buffer ImGuiIndexBuffer = null;
private GraphicsPipeline ImGuiPipeline;
private Sampler ImGuiSampler;
public DebugRenderer(GraphicsDevice graphicsDevice, Window window, ShaderModules shaderModules, Data.DebugTextureStorage debugTextureStorage)
{
GraphicsDevice = graphicsDevice;
Window = window;
TextureStorage = debugTextureStorage;
ImGuiSampler = new Sampler(GraphicsDevice, SamplerCreateInfo.PointClamp);
ImGuiPipeline = new GraphicsPipeline(
GraphicsDevice,
new GraphicsPipelineCreateInfo
{
AttachmentInfo = new GraphicsPipelineAttachmentInfo(
new ColorAttachmentDescription(
window.SwapchainFormat,
ColorAttachmentBlendState.NonPremultiplied
)
),
DepthStencilState = DepthStencilState.Disable,
VertexShaderInfo = GraphicsShaderInfo.Create<Matrix4x4>(shaderModules.ImguiVertexShader, "main", 0),
FragmentShaderInfo = GraphicsShaderInfo.Create(shaderModules.ImguiFragmentShader, "main", 1),
VertexInputState = VertexInputState.CreateSingleBinding<Position2DTextureColorVertex>(),
PrimitiveType = PrimitiveType.TriangleList,
RasterizerState = RasterizerState.CW_CullNone,
MultisampleState = MultisampleState.None
}
);
var io = ImGui.GetIO();
io.DisplaySize = new System.Numerics.Vector2(window.Width, window.Height); io.DisplayFramebufferScale = System.Numerics.Vector2.One;
BuildFontAtlas();
}
public void Resize()
{
ImGui.GetIO().DisplaySize = new System.Numerics.Vector2(Window.Width, Window.Height);
}
public void Draw(CommandBuffer commandBuffer, Texture renderTexture)
{
ImGui.Render();
var io = ImGui.GetIO();
var drawDataPtr = ImGui.GetDrawData();
UpdateImGuiBuffers(drawDataPtr);
RenderCommandLists(commandBuffer, renderTexture, drawDataPtr, io);
}
private void BuildFontAtlas()
{
var commandBuffer = GraphicsDevice.AcquireCommandBuffer();
var io = ImGui.GetIO();
io.Fonts.GetTexDataAsRGBA32(
out System.IntPtr pixelData,
out int width,
out int height,
out int bytesPerPixel
);
FontTexture = Texture.CreateTexture2D(
GraphicsDevice,
(uint) width,
(uint) height,
TextureFormat.R8G8B8A8,
TextureUsageFlags.Sampler
);
commandBuffer.SetTextureData(FontTexture, pixelData, (uint) (width * height * bytesPerPixel));
GraphicsDevice.Submit(commandBuffer);
io.Fonts.SetTexID(FontTexture.Handle);
io.Fonts.ClearTexData();
TextureStorage.Add(FontTexture);
}
private unsafe void UpdateImGuiBuffers(ImDrawDataPtr drawDataPtr)
{
if (drawDataPtr.TotalVtxCount == 0) { return; }
var commandBuffer = GraphicsDevice.AcquireCommandBuffer();
if (drawDataPtr.TotalVtxCount > VertexCount)
{
ImGuiVertexBuffer?.Dispose();
VertexCount = (uint) (drawDataPtr.TotalVtxCount * 1.5f);
ImGuiVertexBuffer = Buffer.Create<Position2DTextureColorVertex>(
GraphicsDevice,
BufferUsageFlags.Vertex,
VertexCount
);
}
if (drawDataPtr.TotalIdxCount > IndexCount)
{
ImGuiIndexBuffer?.Dispose();
IndexCount = (uint) (drawDataPtr.TotalIdxCount * 1.5f);
ImGuiIndexBuffer = Buffer.Create<ushort>(
GraphicsDevice,
BufferUsageFlags.Index,
IndexCount
);
}
uint vertexOffset = 0;
uint indexOffset = 0;
for (var n = 0; n < drawDataPtr.CmdListsCount; n += 1)
{
var cmdList = drawDataPtr.CmdListsRange[n];
commandBuffer.SetBufferData<Position2DTextureColorVertex>(
ImGuiVertexBuffer,
cmdList.VtxBuffer.Data,
vertexOffset,
(uint) cmdList.VtxBuffer.Size
);
commandBuffer.SetBufferData<ushort>(
ImGuiIndexBuffer,
cmdList.IdxBuffer.Data,
indexOffset,
(uint) cmdList.IdxBuffer.Size
);
vertexOffset += (uint) cmdList.VtxBuffer.Size;
indexOffset += (uint) cmdList.IdxBuffer.Size;
}
GraphicsDevice.Submit(commandBuffer);
}
private void RenderCommandLists(CommandBuffer commandBuffer, Texture renderTexture, ImDrawDataPtr drawDataPtr, ImGuiIOPtr ioPtr)
{
if (ImGuiVertexBuffer == null) { return; }
commandBuffer.BeginRenderPass(
new ColorAttachmentInfo(renderTexture, LoadOp.Load)
);
commandBuffer.BindGraphicsPipeline(ImGuiPipeline);
var vertexUniformOffset = commandBuffer.PushVertexShaderUniforms(
Matrix4x4.CreateOrthographicOffCenter(0, ioPtr.DisplaySize.X, ioPtr.DisplaySize.Y, 0, -1, 1)
);
commandBuffer.BindVertexBuffers(ImGuiVertexBuffer);
commandBuffer.BindIndexBuffer(ImGuiIndexBuffer, IndexElementSize.Sixteen);
uint vertexOffset = 0;
uint indexOffset = 0;
for (int n = 0; n < drawDataPtr.CmdListsCount; n += 1)
{
var cmdList = drawDataPtr.CmdListsRange[n];
for (int cmdIndex = 0; cmdIndex < cmdList.CmdBuffer.Size; cmdIndex += 1)
{
var drawCmd = cmdList.CmdBuffer[cmdIndex];
commandBuffer.BindFragmentSamplers(
new TextureSamplerBinding(TextureStorage.GetTexture(drawCmd.TextureId), ImGuiSampler)
);
var width = drawCmd.ClipRect.Z - (int) drawCmd.ClipRect.X;
var height = drawCmd.ClipRect.W - (int) drawCmd.ClipRect.Y;
if (width <= 0 || height <= 0)
{
continue;
}
commandBuffer.SetScissor(
new Rect(
(int) drawCmd.ClipRect.X,
(int) drawCmd.ClipRect.Y,
(int) width,
(int) height
)
);
commandBuffer.DrawIndexedPrimitives(
vertexOffset,
indexOffset,
drawCmd.ElemCount / 3,
vertexUniformOffset,
0
);
indexOffset += drawCmd.ElemCount;
}
vertexOffset += (uint) cmdList.VtxBuffer.Size;
}
commandBuffer.EndRenderPass();
}
}
}
#endif
#if DEBUG
using System;
using System.Collections.Generic;
using MoonWorks.Graphics;
namespace SamuraiGunn2.Data
{
public class DebugTextureStorage
{
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;
}
var result = PointerToTexture[pointer];
if (!result.TryGetTarget(out var texture))
{
PointerToTexture.Remove(pointer);
return null;
}
return texture;
}
}
}
#endif
#version 450
layout(set = 1, binding = 0) uniform sampler2D tex;
layout(location = 0) in vec2 inTexCoord;
layout(location = 1) in vec4 inColor;
layout(location = 0) out vec4 outColor;
void main()
{
outColor = texture(tex, inTexCoord) * inColor;
}
#version 450
layout(location = 0) in vec2 inPosition;
layout(location = 1) in vec2 inTexCoord;
layout(location = 2) in vec4 inColor;
layout (set = 2, binding = 0) uniform UniformBlock
{
mat4 projectionMatrix;
} Uniforms;
layout(location = 0) out vec2 outTexCoord;
layout(location = 1) out vec4 outColor;
void main() {
gl_Position = Uniforms.projectionMatrix * vec4(inPosition, 0.0, 1.0);
outTexCoord = inTexCoord;
outColor = inColor;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment