diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 000000000..d419ce197
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "lib/ImGui.NET"]
+ path = lib/ImGui.NET
+ url = https://github.com/ff-meli/ImGui.NET.git
diff --git a/Dalamud.Injector/Dalamud.Injector.csproj b/Dalamud.Injector/Dalamud.Injector.csproj
index 0ac4cccf8..d0901d863 100644
--- a/Dalamud.Injector/Dalamud.Injector.csproj
+++ b/Dalamud.Injector/Dalamud.Injector.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/Dalamud.Injector/Program.cs b/Dalamud.Injector/Program.cs
index db9ceb692..6d7ad17c3 100644
--- a/Dalamud.Injector/Program.cs
+++ b/Dalamud.Injector/Program.cs
@@ -4,6 +4,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
+using System.Threading;
using System.Windows.Forms;
using Dalamud.DiscordBot;
using Dalamud.Game.Chat;
@@ -34,6 +35,7 @@ namespace Dalamud.Injector {
process = Process.Start(
"C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\ffxiv_dx11.exe",
"DEV.TestSID=0 DEV.UseSqPack=1 DEV.DataPathType=1 DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 SYS.Region=0 language=1 version=1.0.0.0 DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0");
+ Thread.Sleep(10000);
break;
default:
process = Process.GetProcessById(pid);
diff --git a/Dalamud.sln b/Dalamud.sln
index ac5182893..596579eec 100644
--- a/Dalamud.sln
+++ b/Dalamud.sln
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud", "Dalamud\Dalamud.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Injector", "Dalamud.Injector\Dalamud.Injector.csproj", "{5B832F73-5F54-4ADC-870F-D0095EF72C9A}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImGui.NET-472", "lib\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj", "{0483026E-C6CE-4B1A-AA68-46544C08140B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -31,6 +33,14 @@ Global
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|Any CPU.Build.0 = Release|Any CPU
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x64.ActiveCfg = Release|x64
{5B832F73-5F54-4ADC-870F-D0095EF72C9A}.Release|x64.Build.0 = Release|x64
+ {0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|x64.Build.0 = Debug|Any CPU
+ {0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.ActiveCfg = Release|Any CPU
+ {0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs
index 23174f86a..8d5bbe5b9 100644
--- a/Dalamud/Dalamud.cs
+++ b/Dalamud/Dalamud.cs
@@ -14,7 +14,9 @@ using Dalamud.Game.ClientState.Actors.Types;
using Dalamud.Game.ClientState.Actors.Types.NonPlayer;
using Dalamud.Game.Command;
using Dalamud.Game.Internal;
+using Dalamud.Game.Internal.DXGI;
using Dalamud.Game.Internal.Gui;
+using Dalamud.Game.Internal.Network;
using Dalamud.Game.Network;
using Dalamud.Plugin;
using Serilog;
@@ -49,6 +51,8 @@ namespace Dalamud {
internal readonly WinSockHandlers WinSock2;
+ internal readonly SwapChain deviceHandler;
+
public Dalamud(DalamudStartInfo info) {
this.StartInfo = info;
this.Configuration = DalamudConfiguration.Load(info.ConfigurationPath);
@@ -81,6 +85,8 @@ namespace Dalamud {
this.WinSock2 = new WinSockHandlers();
+ this.deviceHandler = new SwapChain(this, this.sigScanner);
+
try {
this.PluginManager.LoadPlugins();
} catch (Exception ex) {
diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 7d65ccb68..3d07c6589 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -1,7 +1,7 @@
AnyCPU
- net471
+ net48
8.0
AnyCPU;x64
@@ -41,13 +41,19 @@
-
+
+
+
+
+
+
+
diff --git a/Dalamud/Game/Internal/DXGI/ImGuiImplDx11.cs b/Dalamud/Game/Internal/DXGI/ImGuiImplDx11.cs
new file mode 100644
index 000000000..a51216719
--- /dev/null
+++ b/Dalamud/Game/Internal/DXGI/ImGuiImplDx11.cs
@@ -0,0 +1,450 @@
+using ImGuiNET;
+using SharpDX;
+using SharpDX.Direct3D;
+using SharpDX.Direct3D11;
+using SharpDX.DXGI;
+using SharpDX.Mathematics.Interop;
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Buffer = SharpDX.Direct3D11.Buffer;
+using Device = SharpDX.Direct3D11.Device;
+using MapFlags = SharpDX.Direct3D11.MapFlags;
+
+namespace Dalamud.Game.Internal.DXGI
+{
+ ///
+ /// Currently undocumented because it is a horrible mess.
+ /// A near-direct port of https://github.com/ocornut/imgui/blob/master/examples/imgui_impl_dx11.cpp
+ /// State backup was removed because ImGui does it poorly and SharpDX makes it worse; state caching should
+ /// be the responsibility of the main render application anyway (which for most uses of this class does not
+ /// exist at all)
+ ///
+ public class ImGuiImplDx11
+ {
+ private IntPtr _renderNamePtr;
+ private Device _device;
+ private DeviceContext _deviceContext;
+ private ShaderResourceView _fontResourceView;
+ private SamplerState _fontSampler;
+ private VertexShader _vertexShader;
+ private PixelShader _pixelShader;
+ private InputLayout _inputLayout;
+ private Buffer _vertexConstantBuffer;
+ private BlendState _blendState;
+ private RasterizerState _rasterizerState;
+ private DepthStencilState _depthStencilState;
+ private Buffer _vertexBuffer;
+ private Buffer _indexBuffer;
+ private int _vertexBufferSize;
+ private int _indexBufferSize;
+ private VertexBufferBinding _vertexBinding;
+ // so we don't make a temporary object every frame
+ private RawColor4 _blendColor = new RawColor4(0, 0, 0, 0);
+
+ public void SetupRenderState(ImDrawDataPtr drawData)
+ {
+ // Setup viewport
+ _deviceContext.Rasterizer.SetViewport(0, 0, drawData.DisplaySize.X, drawData.DisplaySize.Y);
+
+ // Setup shader and vertex buffers
+ _deviceContext.InputAssembler.InputLayout = _inputLayout;
+ _vertexBinding.Buffer = _vertexBuffer;
+ _deviceContext.InputAssembler.SetVertexBuffers(0, _vertexBinding);
+ _deviceContext.InputAssembler.SetIndexBuffer(_indexBuffer, Format.R16_UInt, 0);
+ _deviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
+ _deviceContext.VertexShader.Set(_vertexShader);
+ _deviceContext.VertexShader.SetConstantBuffer(0, _vertexConstantBuffer);
+ _deviceContext.PixelShader.Set(_pixelShader);
+ _deviceContext.PixelShader.SetSampler(0, _fontSampler);
+ _deviceContext.GeometryShader.Set(null);
+ _deviceContext.HullShader.Set(null);
+ _deviceContext.DomainShader.Set(null);
+ _deviceContext.ComputeShader.Set(null);
+
+ // Setup blend state
+ _deviceContext.OutputMerger.BlendState = _blendState;
+ _deviceContext.OutputMerger.BlendFactor = _blendColor;
+ _deviceContext.OutputMerger.DepthStencilState = _depthStencilState;
+ _deviceContext.Rasterizer.State = _rasterizerState;
+ }
+
+ public void RenderDrawData(ImDrawDataPtr drawData)
+ {
+ // Avoid rendering when minimized
+ if (drawData.DisplaySize.X <= 0 || drawData.DisplaySize.Y <= 0)
+ {
+ return;
+ }
+
+ if (!drawData.Valid || drawData.CmdListsCount == 0)
+ {
+ return;
+ }
+
+ //drawData.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale);
+
+ // Create and grow vertex/index buffers if needed
+ if (_vertexBuffer == null || _vertexBufferSize < drawData.TotalVtxCount)
+ {
+ _vertexBuffer?.Dispose();
+ _vertexBufferSize = drawData.TotalVtxCount + 5000;
+
+ _vertexBuffer = new Buffer(_device, new BufferDescription
+ {
+ Usage = ResourceUsage.Dynamic,
+ SizeInBytes = Unsafe.SizeOf() * _vertexBufferSize,
+ BindFlags = BindFlags.VertexBuffer,
+ CpuAccessFlags = CpuAccessFlags.Write,
+ OptionFlags = ResourceOptionFlags.None
+ });
+
+ // (Re)make this here rather than every frame
+ _vertexBinding = new VertexBufferBinding
+ {
+ Buffer = _vertexBuffer,
+ Stride = Unsafe.SizeOf(),
+ Offset = 0
+ };
+ }
+
+ if (_indexBuffer == null || _indexBufferSize < drawData.TotalIdxCount)
+ {
+ _indexBuffer?.Dispose();
+ _indexBufferSize = drawData.TotalIdxCount + 10000;
+
+ _indexBuffer = new Buffer(_device, new BufferDescription
+ {
+ Usage = ResourceUsage.Dynamic,
+ SizeInBytes = sizeof(ushort) * _indexBufferSize, // ImGui.NET doesn't provide an ImDrawIdx, but their sample uses ushort
+ BindFlags = BindFlags.IndexBuffer,
+ CpuAccessFlags = CpuAccessFlags.Write
+ });
+ }
+
+ // Upload vertex/index data into a single contiguous GPU buffer
+ int vertexOffset = 0, indexOffset = 0;
+ var vertexData = _deviceContext.MapSubresource(_vertexBuffer, 0, MapMode.WriteDiscard, MapFlags.None).DataPointer;
+ var indexData = _deviceContext.MapSubresource(_indexBuffer, 0, MapMode.WriteDiscard, MapFlags.None).DataPointer;
+
+ for (int n = 0; n < drawData.CmdListsCount; n++)
+ {
+ var cmdList = drawData.CmdListsRange[n];
+ unsafe
+ {
+ System.Buffer.MemoryCopy(cmdList.VtxBuffer.Data.ToPointer(),
+ (ImDrawVert*)vertexData + vertexOffset,
+ Unsafe.SizeOf() * _vertexBufferSize,
+ Unsafe.SizeOf() * cmdList.VtxBuffer.Size);
+
+ System.Buffer.MemoryCopy(cmdList.IdxBuffer.Data.ToPointer(),
+ (ushort*)indexData + indexOffset,
+ sizeof(ushort) * _indexBufferSize,
+ sizeof(ushort) * cmdList.IdxBuffer.Size);
+
+ vertexOffset += cmdList.VtxBuffer.Size;
+ indexOffset += cmdList.IdxBuffer.Size;
+ }
+ }
+ _deviceContext.UnmapSubresource(_vertexBuffer, 0);
+ _deviceContext.UnmapSubresource(_indexBuffer, 0);
+
+ // Setup orthographic projection matrix into our constant buffer
+ // Our visible imgui space lies from drawData.DisplayPos (top left) to drawData.DisplayPos+drawData.DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
+ var L = drawData.DisplayPos.X;
+ var R = drawData.DisplayPos.X + drawData.DisplaySize.X;
+ var T = drawData.DisplayPos.Y;
+ var B = drawData.DisplayPos.Y + drawData.DisplaySize.Y;
+ var mvp = new float[]
+ {
+ 2f/(R-L), 0, 0, 0,
+ 0, 2f/(T-B), 0, 0,
+ 0, 0, 0.5f, 0,
+ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1f
+ };
+
+ var constantBuffer = _deviceContext.MapSubresource(_vertexConstantBuffer, 0, MapMode.WriteDiscard, MapFlags.None).DataPointer;
+ unsafe
+ {
+ fixed (void* mvpPtr = mvp)
+ {
+ System.Buffer.MemoryCopy(mvpPtr, constantBuffer.ToPointer(), 16 * sizeof(float), 16 * sizeof(float));
+ }
+ }
+ _deviceContext.UnmapSubresource(_vertexConstantBuffer, 0);
+
+ // Setup desired DX state
+ SetupRenderState(drawData);
+
+ // Render command lists
+ // (Because we merged all buffers into a single one, we maintain our own offset into them)
+ vertexOffset = 0;
+ indexOffset = 0;
+ var clipOff = drawData.DisplayPos;
+ for (int n = 0; n < drawData.CmdListsCount; n++)
+ {
+ var cmdList = drawData.CmdListsRange[n];
+ for (int cmd = 0; cmd < cmdList.CmdBuffer.Size; cmd++)
+ {
+ var pcmd = cmdList.CmdBuffer[cmd];
+ if (pcmd.UserCallback != IntPtr.Zero)
+ {
+ // TODO
+ throw new NotImplementedException();
+ }
+ else
+ {
+ // Apply scissor/clipping rectangle
+ _deviceContext.Rasterizer.SetScissorRectangle((int)(pcmd.ClipRect.X - clipOff.X), (int)(pcmd.ClipRect.Y - clipOff.Y), (int)(pcmd.ClipRect.Z - clipOff.X), (int)(pcmd.ClipRect.W - clipOff.Y));
+
+ // Bind texture, Draw
+ // TODO: might be nice to store samplers for loaded textures so that we can look them up and apply them here
+ // rather than just always using the font sampler
+ var textureSrv = ShaderResourceView.FromPointer(pcmd.TextureId);
+ _deviceContext.PixelShader.SetShaderResource(0, textureSrv);
+ _deviceContext.DrawIndexed((int)pcmd.ElemCount, (int)(pcmd.IdxOffset + indexOffset), (int)(pcmd.VtxOffset + vertexOffset));
+ }
+ }
+
+ indexOffset += cmdList.IdxBuffer.Size;
+ vertexOffset += cmdList.VtxBuffer.Size;
+ }
+ }
+
+ public void CreateFontsTexture()
+ {
+ var io = ImGui.GetIO();
+
+ // Build texture atlas
+ io.Fonts.GetTexDataAsRGBA32(out IntPtr fontPixels, out int fontWidth, out int fontHeight, out int fontBytesPerPixel);
+
+ // Upload texture to graphics system
+ var texDesc = new Texture2DDescription
+ {
+ Width = fontWidth,
+ Height = fontHeight,
+ MipLevels = 1,
+ ArraySize = 1,
+ Format = Format.R8G8B8A8_UNorm,
+ SampleDescription = new SampleDescription(1, 0),
+ Usage = ResourceUsage.Immutable,
+ BindFlags = BindFlags.ShaderResource,
+ CpuAccessFlags = CpuAccessFlags.None,
+ OptionFlags = ResourceOptionFlags.None
+ };
+
+ using (var fontTexture = new Texture2D(_device, texDesc, new DataRectangle(fontPixels, fontWidth * fontBytesPerPixel)))
+ {
+ // Create texture view
+ _fontResourceView = new ShaderResourceView(_device, fontTexture, new ShaderResourceViewDescription
+ {
+ Format = texDesc.Format,
+ Dimension = ShaderResourceViewDimension.Texture2D,
+ Texture2D = { MipLevels = texDesc.MipLevels }
+ });
+ }
+
+ // Store our identifier
+ io.Fonts.SetTexID(_fontResourceView.NativePointer);
+ io.Fonts.ClearTexData();
+
+ // Create texture sampler
+ _fontSampler = new SamplerState(_device, new SamplerStateDescription
+ {
+ Filter = Filter.MinMagMipLinear,
+ AddressU = TextureAddressMode.Wrap,
+ AddressV = TextureAddressMode.Wrap,
+ AddressW = TextureAddressMode.Wrap,
+ MipLodBias = 0,
+ ComparisonFunction = Comparison.Always,
+ MinimumLod = 0,
+ MaximumLod = 0
+ });
+ }
+
+ public bool CreateDeviceObjects()
+ {
+ if (_device == null)
+ {
+ return false;
+ }
+
+ if (_fontSampler != null)
+ {
+ InvalidateDeviceObjects();
+ }
+
+ // Create the vertex shader
+ byte[] shaderData;
+
+ var assembly = Assembly.GetExecutingAssembly();
+ using (var stream = assembly.GetManifestResourceStream("imgui-vertex.hlsl.bytes"))
+ {
+ shaderData = new byte[stream.Length];
+ stream.Read(shaderData, 0, shaderData.Length);
+ }
+
+ _vertexShader = new VertexShader(_device, shaderData);
+
+ // Create the input layout
+ _inputLayout = new InputLayout(_device, shaderData, new[]
+ {
+ new InputElement("POSITION", 0, Format.R32G32_Float, 0),
+ new InputElement("TEXCOORD", 0, Format.R32G32_Float, 0),
+ new InputElement("COLOR", 0, Format.R8G8B8A8_UNorm, 0)
+ });
+
+ // Create the constant buffer
+ _vertexConstantBuffer = new Buffer(_device, new BufferDescription
+ {
+ Usage = ResourceUsage.Dynamic,
+ BindFlags = BindFlags.ConstantBuffer,
+ CpuAccessFlags = CpuAccessFlags.Write,
+ OptionFlags = ResourceOptionFlags.None,
+ SizeInBytes = 16 * sizeof(float)
+ });
+
+ // Create the pixel shader
+ using (var stream = assembly.GetManifestResourceStream("imgui-frag.hlsl.bytes"))
+ {
+ shaderData = new byte[stream.Length];
+ stream.Read(shaderData, 0, shaderData.Length);
+ }
+
+ _pixelShader = new PixelShader(_device, shaderData);
+
+ // Create the blending setup
+ // ...of course this was setup in a way that can't be done inline
+ var blendStateDesc = new BlendStateDescription();
+ blendStateDesc.AlphaToCoverageEnable = false;
+ blendStateDesc.RenderTarget[0].IsBlendEnabled = true;
+ blendStateDesc.RenderTarget[0].SourceBlend = BlendOption.SourceAlpha;
+ blendStateDesc.RenderTarget[0].DestinationBlend = BlendOption.InverseSourceAlpha;
+ blendStateDesc.RenderTarget[0].BlendOperation = BlendOperation.Add;
+ blendStateDesc.RenderTarget[0].SourceAlphaBlend = BlendOption.InverseSourceAlpha;
+ blendStateDesc.RenderTarget[0].DestinationAlphaBlend = BlendOption.Zero;
+ blendStateDesc.RenderTarget[0].AlphaBlendOperation = BlendOperation.Add;
+ blendStateDesc.RenderTarget[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;
+ _blendState = new BlendState(_device, blendStateDesc);
+
+ // Create the rasterizer state
+ _rasterizerState = new RasterizerState(_device, new RasterizerStateDescription
+ {
+ FillMode = FillMode.Solid,
+ CullMode = CullMode.None,
+ IsScissorEnabled = true,
+ IsDepthClipEnabled = true
+ });
+
+ // Create the depth-stencil State
+ _depthStencilState = new DepthStencilState(_device, new DepthStencilStateDescription
+ {
+ IsDepthEnabled = false,
+ DepthWriteMask = DepthWriteMask.All,
+ DepthComparison = Comparison.Always,
+ IsStencilEnabled = false,
+ FrontFace =
+ {
+ FailOperation = StencilOperation.Keep,
+ DepthFailOperation = StencilOperation.Keep,
+ PassOperation = StencilOperation.Keep,
+ Comparison = Comparison.Always
+ },
+ BackFace =
+ {
+ FailOperation = StencilOperation.Keep,
+ DepthFailOperation = StencilOperation.Keep,
+ PassOperation = StencilOperation.Keep,
+ Comparison = Comparison.Always
+ }
+ });
+
+ CreateFontsTexture();
+
+ return true;
+ }
+
+ public void InvalidateDeviceObjects()
+ {
+ if (_device == null)
+ {
+ return;
+ }
+
+ _fontSampler?.Dispose();
+ _fontSampler = null;
+
+ _fontResourceView?.Dispose();
+ _fontResourceView = null;
+ ImGui.GetIO().Fonts.SetTexID(IntPtr.Zero);
+
+ _indexBuffer?.Dispose();
+ _indexBuffer = null;
+
+ _vertexBuffer?.Dispose();
+ _vertexBuffer = null;
+
+ _blendState?.Dispose();
+ _blendState = null;
+
+ _depthStencilState?.Dispose();
+ _depthStencilState = null;
+
+ _rasterizerState?.Dispose();
+ _rasterizerState = null;
+
+ _pixelShader?.Dispose();
+ _pixelShader = null;
+
+ _vertexConstantBuffer?.Dispose();
+ _vertexConstantBuffer = null;
+
+ _inputLayout?.Dispose();
+ _inputLayout = null;
+
+ _vertexShader?.Dispose();
+ _vertexShader = null;
+ }
+
+ public void Init(Device dev, DeviceContext ctx)
+ {
+ ImGui.GetIO().BackendFlags = ImGui.GetIO().BackendFlags | ImGuiBackendFlags.RendererHasVtxOffset;
+
+ // BackendRendererName is readonly (and null) in ImGui.NET for some reason, but we can hack it via its internal pointer
+ _renderNamePtr = Marshal.StringToHGlobalAnsi("imgui_impl_dx11_c#");
+ unsafe
+ {
+ ImGui.GetIO().NativePtr->BackendRendererName = (byte*)_renderNamePtr.ToPointer();
+ }
+
+ _device = dev;
+ _deviceContext = ctx;
+
+ // SharpDX also doesn't allow reference managment
+ }
+
+ public void Shutdown()
+ {
+ InvalidateDeviceObjects();
+
+ // we don't own these, so no Dispose()
+ _device = null;
+ _deviceContext = null;
+
+ if (_renderNamePtr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(_renderNamePtr);
+ _renderNamePtr = IntPtr.Zero;
+ }
+ }
+
+ public void NewFrame()
+ {
+ if (_fontSampler == null)
+ {
+ CreateDeviceObjects();
+ }
+ }
+ }
+}
diff --git a/Dalamud/Game/Internal/DXGI/SwapChain.cs b/Dalamud/Game/Internal/DXGI/SwapChain.cs
new file mode 100644
index 000000000..f49fc7647
--- /dev/null
+++ b/Dalamud/Game/Internal/DXGI/SwapChain.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Dalamud.Hooking;
+using ImGuiNET;
+using Serilog;
+using SharpDX.Direct3D11;
+
+namespace Dalamud.Game.Internal.DXGI {
+ public sealed class SwapChain : IDisposable {
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ private delegate IntPtr PresentDelegate(IntPtr swapChain, uint a2, uint a3);
+
+ private readonly Hook presentHook;
+
+ private SwapChainAddressResolver Address { get; }
+
+ private readonly Dalamud dalamud;
+
+ private const string deviceguid = "3d3e0379-f9de-4d58-bb6c-18d62992f1a6";
+
+ public SwapChain(Dalamud dalamud, SigScanner scanner) {
+ this.dalamud = dalamud;
+ Address = new SwapChainAddressResolver();
+ Address.Setup(scanner);
+
+ Log.Verbose("===== S W A P C H A I N =====");
+ Log.Verbose("Present address {Present}", Address.Present);
+
+ this.presentHook =
+ new Hook(Address.Present,
+ new PresentDelegate(PresentDetour),
+ this);
+ Enable();
+ }
+
+ public void Enable() {
+ this.presentHook.Enable();
+ }
+
+ public void Dispose() {
+ this.presentHook.Dispose();
+ }
+
+ private ImGuiImplDx11 impl;
+
+ private IntPtr PresentDetour(IntPtr swapChain, uint a2, uint a3) {
+
+
+ if (this.impl == null) {
+ var ret = this.presentHook.Original(swapChain, a2, a3);
+
+ Log.Debug($"CDXGISwapChain::Present: swapChain->{swapChain.ToInt64():X} a2->{a2:X} a3->{a3:X} RET=>{ret.ToInt64():X}");
+
+ var s = new SharpDX.DXGI.SwapChain(swapChain);
+ var d = s.GetDevice();
+ var ctx = d.ImmediateContext;
+ //s.GetDevice(Guid.Parse(deviceguid), out var device);
+
+ Log.Verbose($"DEVICE: {d.NativePointer.ToInt64():X} CONTEXT: {ctx.NativePointer.ToInt64():X}");
+
+ ImGui.CreateContext();
+ ImGui.StyleColorsDark();
+
+ this.impl = new ImGuiImplDx11();
+ this.impl.Init(d, d.ImmediateContext);
+
+ Log.Debug("Init OK");
+
+ return ret;
+ } else {
+ this.impl.NewFrame();
+ Log.Debug("IMPL NewFrame OK");
+ ImGui.NewFrame();
+ Log.Debug("NewFrame OK");
+
+ ImGui.ShowDemoWindow();
+ Log.Debug("ShowDemoWindow OK");
+
+ ImGui.Render();
+ Log.Debug("Render OK");
+
+ this.impl.RenderDrawData(ImGui.GetDrawData());
+ Log.Debug("RenderDrawData OK");
+
+ return this.presentHook.Original(swapChain, a2, a3);
+ }
+ }
+ }
+}
diff --git a/Dalamud/Game/Internal/DXGI/SwapChainAddressResolver.cs b/Dalamud/Game/Internal/DXGI/SwapChainAddressResolver.cs
new file mode 100644
index 000000000..95f6dd924
--- /dev/null
+++ b/Dalamud/Game/Internal/DXGI/SwapChainAddressResolver.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using Serilog;
+
+namespace Dalamud.Game.Internal.DXGI
+{
+ public sealed class SwapChainAddressResolver : BaseAddressResolver {
+ public IntPtr Present { get; private set; }
+
+ protected override void Setup64Bit(SigScanner sig) {
+ var module = Process.GetCurrentProcess().Modules.Cast().First(m => m.ModuleName == "dxgi.dll");
+
+ Log.Debug($"Found DXGI: {module.BaseAddress.ToInt64():X}");
+
+ var scanner = new SigScanner(module);
+ Present = scanner.ScanModule("48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 56 48 8D 6C 24 ??");
+ }
+ }
+}
diff --git a/lib/ImGui.NET b/lib/ImGui.NET
new file mode 160000
index 000000000..85b836ac6
--- /dev/null
+++ b/lib/ImGui.NET
@@ -0,0 +1 @@
+Subproject commit 85b836ac617a39560043c4add1e82e14ef0b1157