Move ImGuiScene into Dalamud project

This commit is contained in:
Soreepeong 2024-07-12 16:17:33 +09:00
parent 5af935bbca
commit faf61477c7
25 changed files with 3711 additions and 29 deletions

6
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "lib/ImGuiScene"]
path = lib/ImGuiScene
url = https://github.com/goatcorp/ImGuiScene
[submodule "lib/FFXIVClientStructs"]
path = lib/FFXIVClientStructs
url = https://github.com/aers/FFXIVClientStructs
@ -10,3 +7,6 @@
[submodule "lib/TsudaKageyu-minhook"]
path = lib/TsudaKageyu-minhook
url = https://github.com/TsudaKageyu/minhook.git
[submodule "lib/ImGui.NET"]
path = lib/ImGui.NET
url = https://github.com/goatcorp/ImGui.NET.git

View file

@ -40,13 +40,7 @@
<ProjectReference Include="..\Dalamud\Dalamud.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj">
<Private>false</Private>
</ProjectReference>
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj">
<ProjectReference Include="..\lib\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj">
<Private>false</Private>
</ProjectReference>
</ItemGroup>

View file

@ -27,11 +27,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.Test", "Dalamud.Tes
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interface", "Interface", "{E15BDA6D-E881-4482-94BA-BE5527E917FF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGui.NET-472", "lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj", "{0483026E-C6CE-4B1A-AA68-46544C08140B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGuiScene", "lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj", "{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SDL2-CS", "lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj", "{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImGui.NET-472", "lib\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj", "{0483026E-C6CE-4B1A-AA68-46544C08140B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dalamud.CorePlugin", "Dalamud.CorePlugin\Dalamud.CorePlugin.csproj", "{4AFDB34A-7467-4D41-B067-53BC4101D9D0}"
EndProject
@ -81,14 +77,6 @@ Global
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Debug|Any CPU.Build.0 = Debug|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.ActiveCfg = Release|x64
{0483026E-C6CE-4B1A-AA68-46544C08140B}.Release|Any CPU.Build.0 = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.ActiveCfg = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Debug|Any CPU.Build.0 = Debug|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.ActiveCfg = Release|x64
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A}.Release|Any CPU.Build.0 = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.ActiveCfg = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Debug|Any CPU.Build.0 = Debug|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.ActiveCfg = Release|x64
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8}.Release|Any CPU.Build.0 = Release|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.ActiveCfg = Debug|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Debug|Any CPU.Build.0 = Debug|x64
{4AFDB34A-7467-4D41-B067-53BC4101D9D0}.Release|Any CPU.ActiveCfg = Release|x64
@ -127,8 +115,6 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0483026E-C6CE-4B1A-AA68-46544C08140B} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{C0E7E797-4FBF-4F46-BC57-463F3719BA7A} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{2F7FF0A8-B619-4572-86C7-71E46FE22FB8} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{C9B87BD7-AF49-41C3-91F1-D550ADEB7833} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{3620414C-7DFC-423E-929F-310E19F5D930} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}
{A6AA1C3F-9470-4922-9D3F-D4549657AB22} = {E15BDA6D-E881-4482-94BA-BE5527E917FF}

View file

@ -79,11 +79,18 @@
</PackageReference>
<PackageReference Include="MinSharp" Version="1.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="PInvoke.DwmApi" Version="0.7.104" />
<PackageReference Include="PInvoke.Kernel32" Version="0.7.104" />
<PackageReference Include="PInvoke.User32" Version="0.7.104" />
<PackageReference Include="PInvoke.Win32" Version="0.7.104" />
<PackageReference Include="Serilog" Version="2.11.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="SharpDX.Direct3D11" Version="4.2.0" />
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
<PackageReference Include="StbiSharp" Version="1.0.13" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -95,12 +102,17 @@
<PackageReference Include="System.Resources.Extensions" Version="8.0.0" />
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.22621.2" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Interface\ImGuiScene\resources\shaders\imgui-frag.hlsl.bytes" LogicalName="imgui-frag.hlsl.bytes" />
<EmbeddedResource Include="Interface\ImGuiScene\resources\shaders\imgui-vertex.hlsl.bytes" LogicalName="imgui-vertex.hlsl.bytes" />
<EmbeddedResource Include="Interface\ImGuiScene\resources\shaders\imgui-frag.glsl" LogicalName="imgui-frag.glsl" />
<EmbeddedResource Include="Interface\ImGuiScene\resources\shaders\imgui-vertex.glsl" LogicalName="imgui-vertex.glsl" />
<EmbeddedResource Include="Interface\ImGuiScene\costura64\stbi.dll" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dalamud.Common\Dalamud.Common.csproj" />
<ProjectReference Include="..\lib\FFXIVClientStructs\FFXIVClientStructs\FFXIVClientStructs.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\deps\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\deps\SDL2-CS\SDL2-CS.csproj" />
<ProjectReference Include="..\lib\ImGuiScene\ImGuiScene\ImGuiScene.csproj" />
<ProjectReference Include="..\lib\ImGui.NET\src\ImGui.NET-472\ImGui.NET-472.csproj" />
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,60 @@
using Dalamud.Interface.Textures.TextureWraps;
using SharpDX.Direct3D11;
/// <summary>
/// DX11 Implementation of <see cref="IDalamudTextureWrap"/>.
/// Provides a simple wrapped view of the disposeable resource as well as the handle for ImGui.
/// </summary>
public class D3DTextureWrap : IDalamudTextureWrap
{
// hold onto this directly for easier dispose etc and in case we need it later
private ShaderResourceView _resourceView = null;
public int Width { get; }
public int Height { get; }
public IntPtr ImGuiHandle => (_resourceView == null) ? IntPtr.Zero : _resourceView.NativePointer;
public D3DTextureWrap(ShaderResourceView texView, int width, int height)
{
_resourceView = texView;
Width = width;
Height = height;
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
_resourceView?.Dispose();
_resourceView = null;
disposedValue = true;
}
}
~D3DTextureWrap()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura IncludeDebugSymbols="false">
<ExcludeAssemblies>
SDL2-CS
ImGui.NET
</ExcludeAssemblies>
<Unmanaged64Assemblies>
stbi
</Unmanaged64Assemblies>
</Costura>
</Weavers>

View file

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeReferences" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if runtime assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="UseRuntimeReferencePaths" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls whether the runtime assemblies are embedded with their full path or only with their assembly name.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeRuntimeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

View file

@ -0,0 +1,75 @@
namespace ImGuiScene
{
/// <summary>
/// Simple encapsulation of framerate limiting behavior, allowing for fully unbounded (no control),
/// Vsync-enabled (sync to monitor refresh), or a specified fixed framerate (vsync disabled, hard time cap)
/// </summary>
public class FramerateLimit
{
/// <summary>
/// The different methods of limiting framerate.
/// </summary>
public enum LimitType
{
/// <summary>
/// No limiting at all.
/// </summary>
Unbounded,
/// <summary>
/// Vsync enabled. Render presentation will be synced to display refresh rate.
/// </summary>
Vsync,
/// <summary>
/// Restrict rendering to a fixed (maximum) number of frames per second.
/// This will disable vsync regardless of the fps value.
/// </summary>
FixedFPS
}
/// <summary>
/// Which type of framerate limiting to apply.
/// </summary>
public LimitType Type { get; }
private readonly int _fps;
/// <summary>
/// The current FPS limit. Only valid with <see cref="LimitType.FixedFPS"/>.
/// </summary>
public int FPS
{
get
{
if (Type != LimitType.FixedFPS)
throw new InvalidOperationException();
return _fps;
}
}
/// <summary>
/// Creates a new framerate limit description.
/// </summary>
/// <param name="limitType">Which type of limiting to apply.</param>
/// <param name="targetFps">Used only with <see cref="LimitType.FixedFPS"/>, the target frames per second to restrict rendering to.</param>
public FramerateLimit(LimitType limitType, int targetFps = 0)
{
if (limitType == LimitType.FixedFPS && targetFps <= 0)
{
limitType = LimitType.Unbounded;
}
Type = limitType;
_fps = targetFps;
}
public override string ToString()
{
var str = Type.ToString();
if (Type == LimitType.FixedFPS)
{
str += $" ({FPS})";
}
return str;
}
}
}

View file

@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
namespace ImGuiScene.ImGui_Impl {
// Custom cimgui functions we use for utility purposes
internal static class Custom {
[DllImport("cimgui", CallingConvention = CallingConvention.Cdecl)]
public static extern void igCustom_ClearStacks();
}
}

View file

@ -0,0 +1,8 @@
namespace ImGuiScene
{
public interface IImGuiInputHandler : IDisposable
{
void NewFrame(int width, int height);
void SetIniPath(string path);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
namespace ImGuiScene
{
/// <summary>
/// A simple shared public interface that all ImGui render implementations follow.
/// </summary>
public interface IImGuiRenderer
{
// FIXME - probably a better way to do this than params object[] !
void Init(params object[] initParams);
void Shutdown();
void NewFrame();
void RenderDrawData(ImGuiNET.ImDrawDataPtr drawData);
}
}

View file

@ -0,0 +1,859 @@
using ImGuiNET;
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using System.Collections.Generic;
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 ImGuiScene
{
/// <summary>
/// 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 follows the general layout of imgui's sample (which is a mess), but has been rather
/// expanded to cover the vast majority of render state, following the example here
/// https://github.com/GPUOpen-LibrariesAndSDKs/CrossfireAPI11/blob/master/amd_lib/src/AMD_SaveRestoreState.cpp
/// Would be nice to organize it better, but it seems to work
/// </summary>
public unsafe class ImGui_Impl_DX11 : IImGuiRenderer
{
private IntPtr _renderNamePtr;
private Device _device;
private DeviceContext _deviceContext;
private List<ShaderResourceView> _fontResourceViews = new();
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);
// TODO: I'll clean this up better later
private class StateBackup : IDisposable
{
private DeviceContext deviceContext;
// IA
public InputLayout InputLayout;
public PrimitiveTopology PrimitiveTopology;
public Buffer IndexBuffer;
public Format IndexBufferFormat;
public int IndexBufferOffset;
public Buffer[] VertexBuffers;
public int[] VertexBufferStrides;
public int[] VertexBufferOffsets;
// RS
public RasterizerState RS;
public Rectangle[] ScissorRects;
public RawViewportF[] Viewports;
// OM
public BlendState BlendState;
public RawColor4 BlendFactor;
public int SampleMask;
public DepthStencilState DepthStencilState;
public int DepthStencilRef;
public DepthStencilView DepthStencilView;
public RenderTargetView[] RenderTargetViews;
// VS
public VertexShader VS;
public Buffer[] VSConstantBuffers;
public SamplerState[] VSSamplers;
public ShaderResourceView[] VSResourceViews;
// HS
public HullShader HS;
public Buffer[] HSConstantBuffers;
public SamplerState[] HSSamplers;
public ShaderResourceView[] HSResourceViews;
// DS
public DomainShader DS;
public Buffer[] DSConstantBuffers;
public SamplerState[] DSSamplers;
public ShaderResourceView[] DSResourceViews;
// GS
public GeometryShader GS;
public Buffer[] GSConstantBuffers;
public SamplerState[] GSSamplers;
public ShaderResourceView[] GSResourceViews;
// PS
public PixelShader PS;
public Buffer[] PSConstantBuffers;
public SamplerState[] PSSamplers;
public ShaderResourceView[] PSResourceViews;
public ComputeShader CS;
public Buffer[] CSConstantBuffers;
public SamplerState[] CSSamplers;
public ShaderResourceView[] CSResourceViews;
public UnorderedAccessView[] CSUAVs;
private bool disposedValue = false; // To detect redundant calls
public StateBackup(DeviceContext deviceContext)
{
this.deviceContext = deviceContext;
this.ScissorRects = new Rectangle[16]; // I couldn't find D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE as a SharpDX enum
this.Viewports = new RawViewportF[16];
this.VertexBuffers = new Buffer[InputAssemblerStage.VertexInputResourceSlotCount];
this.VertexBufferStrides = new int[InputAssemblerStage.VertexInputResourceSlotCount];
this.VertexBufferOffsets = new int[InputAssemblerStage.VertexInputResourceSlotCount];
// IA
this.InputLayout = deviceContext.InputAssembler.InputLayout;
this.deviceContext.InputAssembler.GetIndexBuffer(out this.IndexBuffer, out this.IndexBufferFormat, out this.IndexBufferOffset);
this.PrimitiveTopology = this.deviceContext.InputAssembler.PrimitiveTopology;
this.deviceContext.InputAssembler.GetVertexBuffers(0, InputAssemblerStage.VertexInputResourceSlotCount, this.VertexBuffers, this.VertexBufferStrides, this.VertexBufferOffsets);
// RS
this.RS = this.deviceContext.Rasterizer.State;
this.deviceContext.Rasterizer.GetScissorRectangles<Rectangle>(this.ScissorRects);
this.deviceContext.Rasterizer.GetViewports<RawViewportF>(this.Viewports);
// OM
this.BlendState = this.deviceContext.OutputMerger.GetBlendState(out this.BlendFactor, out this.SampleMask);
this.DepthStencilState = this.deviceContext.OutputMerger.GetDepthStencilState(out this.DepthStencilRef);
this.RenderTargetViews = this.deviceContext.OutputMerger.GetRenderTargets(OutputMergerStage.SimultaneousRenderTargetCount, out this.DepthStencilView);
// VS
this.VS = this.deviceContext.VertexShader.Get();
this.VSSamplers = this.deviceContext.VertexShader.GetSamplers(0, CommonShaderStage.SamplerSlotCount);
this.VSConstantBuffers = this.deviceContext.VertexShader.GetConstantBuffers(0, CommonShaderStage.ConstantBufferApiSlotCount);
this.VSResourceViews = this.deviceContext.VertexShader.GetShaderResources(0, CommonShaderStage.InputResourceSlotCount);
// HS
this.HS = this.deviceContext.HullShader.Get();
this.HSSamplers = this.deviceContext.HullShader.GetSamplers(0, CommonShaderStage.SamplerSlotCount);
this.HSConstantBuffers = this.deviceContext.HullShader.GetConstantBuffers(0, CommonShaderStage.ConstantBufferApiSlotCount);
this.HSResourceViews = this.deviceContext.HullShader.GetShaderResources(0, CommonShaderStage.InputResourceSlotCount);
// DS
this.DS = this.deviceContext.DomainShader.Get();
this.DSSamplers = this.deviceContext.DomainShader.GetSamplers(0, CommonShaderStage.SamplerSlotCount);
this.DSConstantBuffers = this.deviceContext.DomainShader.GetConstantBuffers(0, CommonShaderStage.ConstantBufferApiSlotCount);
this.DSResourceViews = this.deviceContext.DomainShader.GetShaderResources(0, CommonShaderStage.InputResourceSlotCount);
// GS
this.GS = this.deviceContext.GeometryShader.Get();
this.GSSamplers = this.deviceContext.GeometryShader.GetSamplers(0, CommonShaderStage.SamplerSlotCount);
this.GSConstantBuffers = this.deviceContext.GeometryShader.GetConstantBuffers(0, CommonShaderStage.ConstantBufferApiSlotCount);
this.GSResourceViews = this.deviceContext.GeometryShader.GetShaderResources(0, CommonShaderStage.InputResourceSlotCount);
// PS
this.PS = this.deviceContext.PixelShader.Get();
this.PSSamplers = this.deviceContext.PixelShader.GetSamplers(0, CommonShaderStage.SamplerSlotCount);
this.PSConstantBuffers = this.deviceContext.PixelShader.GetConstantBuffers(0, CommonShaderStage.ConstantBufferApiSlotCount);
this.PSResourceViews = this.deviceContext.PixelShader.GetShaderResources(0, CommonShaderStage.InputResourceSlotCount);
// CS
this.CS = this.deviceContext.ComputeShader.Get();
this.CSSamplers = this.deviceContext.ComputeShader.GetSamplers(0, CommonShaderStage.SamplerSlotCount);
this.CSConstantBuffers = this.deviceContext.ComputeShader.GetConstantBuffers(0, CommonShaderStage.ConstantBufferApiSlotCount);
this.CSResourceViews = this.deviceContext.ComputeShader.GetShaderResources(0, CommonShaderStage.InputResourceSlotCount);
this.CSUAVs = this.deviceContext.ComputeShader.GetUnorderedAccessViews(0, ComputeShaderStage.UnorderedAccessViewSlotCount); // should be register count and not slot, but the value is correct
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
// IA
this.deviceContext.InputAssembler.InputLayout = this.InputLayout;
this.deviceContext.InputAssembler.SetIndexBuffer(this.IndexBuffer, this.IndexBufferFormat, this.IndexBufferOffset);
this.deviceContext.InputAssembler.PrimitiveTopology = this.PrimitiveTopology;
this.deviceContext.InputAssembler.SetVertexBuffers(0, this.VertexBuffers, this.VertexBufferStrides, this.VertexBufferOffsets);
// RS
this.deviceContext.Rasterizer.State = this.RS;
this.deviceContext.Rasterizer.SetScissorRectangles(this.ScissorRects);
this.deviceContext.Rasterizer.SetViewports(this.Viewports, this.Viewports.Length);
// OM
this.deviceContext.OutputMerger.SetBlendState(this.BlendState, this.BlendFactor, this.SampleMask);
this.deviceContext.OutputMerger.SetDepthStencilState(this.DepthStencilState, this.DepthStencilRef);
this.deviceContext.OutputMerger.SetRenderTargets(this.DepthStencilView, this.RenderTargetViews);
// VS
this.deviceContext.VertexShader.Set(this.VS);
this.deviceContext.VertexShader.SetSamplers(0, this.VSSamplers);
this.deviceContext.VertexShader.SetConstantBuffers(0, this.VSConstantBuffers);
this.deviceContext.VertexShader.SetShaderResources(0, this.VSResourceViews);
// HS
this.deviceContext.HullShader.Set(this.HS);
this.deviceContext.HullShader.SetSamplers(0, this.HSSamplers);
this.deviceContext.HullShader.SetConstantBuffers(0, this.HSConstantBuffers);
this.deviceContext.HullShader.SetShaderResources(0, this.HSResourceViews);
// DS
this.deviceContext.DomainShader.Set(this.DS);
this.deviceContext.DomainShader.SetSamplers(0, this.DSSamplers);
this.deviceContext.DomainShader.SetConstantBuffers(0, this.DSConstantBuffers);
this.deviceContext.DomainShader.SetShaderResources(0, this.DSResourceViews);
// GS
this.deviceContext.GeometryShader.Set(this.GS);
this.deviceContext.GeometryShader.SetSamplers(0, this.GSSamplers);
this.deviceContext.GeometryShader.SetConstantBuffers(0, this.GSConstantBuffers);
this.deviceContext.GeometryShader.SetShaderResources(0, this.GSResourceViews);
// PS
this.deviceContext.PixelShader.Set(this.PS);
this.deviceContext.PixelShader.SetSamplers(0, this.PSSamplers);
this.deviceContext.PixelShader.SetConstantBuffers(0, this.PSConstantBuffers);
this.deviceContext.PixelShader.SetShaderResources(0, this.PSResourceViews);
// CS
this.deviceContext.ComputeShader.Set(this.CS);
this.deviceContext.ComputeShader.SetSamplers(0, this.CSSamplers);
this.deviceContext.ComputeShader.SetConstantBuffers(0, this.CSConstantBuffers);
this.deviceContext.ComputeShader.SetShaderResources(0, this.CSResourceViews);
this.deviceContext.ComputeShader.SetUnorderedAccessViews(0, this.CSUAVs);
// force free these references immediately, or they hang around too long and calls
// to swapchain->ResizeBuffers() will fail due to outstanding references
// We could force free other things too, but nothing else should cause errors
// and these should get gc'd and disposed eventually
foreach (var rtv in this.RenderTargetViews)
{
rtv?.Dispose();
}
this.RenderTargetViews = null;
this.DepthStencilView = null;
this.VSResourceViews = null;
this.HSResourceViews = null;
this.DSResourceViews = null;
this.GSResourceViews = null;
this.PSResourceViews = null;
this.CSResourceViews = null;
disposedValue = true;
}
}
~StateBackup()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
GC.SuppressFinalize(this);
}
}
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<ImDrawVert>() * _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<ImDrawVert>(),
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<ImDrawVert>() * _vertexBufferSize,
Unsafe.SizeOf<ImDrawVert>() * 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);
var oldState = new StateBackup(_deviceContext);
// 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<ShaderResourceView>(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;
}
oldState.Dispose(); // restores the previous state
oldState = null;
}
public void CreateFontsTexture()
{
var io = ImGui.GetIO();
if (io.Fonts.Textures.Size == 0)
io.Fonts.Build();
for (int textureIndex = 0, textureCount = io.Fonts.Textures.Size;
textureIndex < textureCount;
textureIndex++) {
// Build texture atlas
io.Fonts.GetTexDataAsRGBA32(textureIndex, 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
var fontResourceView = new ShaderResourceView(_device, fontTexture, new ShaderResourceViewDescription {
Format = texDesc.Format,
Dimension = ShaderResourceViewDimension.Texture2D,
Texture2D = { MipLevels = texDesc.MipLevels }
});
// Store our identifier
_fontResourceViews.Add(fontResourceView);
io.Fonts.SetTexID(textureIndex, 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;
}
// Added to support dynamic rebuilding of the font texture
// for adding fonts after initialization time
public void RebuildFontTexture()
{
_fontSampler?.Dispose();
foreach (var fontResourceView in this._fontResourceViews)
fontResourceView.Dispose();
this._fontResourceViews.Clear();
CreateFontsTexture();
}
public void InvalidateDeviceObjects()
{
if (_device == null)
{
return;
}
_fontSampler?.Dispose();
_fontSampler = null;
foreach (var fontResourceView in this._fontResourceViews)
fontResourceView.Dispose();
this._fontResourceViews.Clear();
for (int textureIndex = 0, textureCount = ImGui.GetIO().Fonts.Textures.Size;
textureIndex < textureCount;
textureIndex++)
ImGui.GetIO().Fonts.SetTexID(textureIndex, 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(params object[] initParams)
{
ImGui.GetIO().BackendFlags = ImGui.GetIO().BackendFlags | ImGuiBackendFlags.RendererHasVtxOffset | ImGuiBackendFlags.RendererHasViewports;
// 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 = (Device)initParams[0];
_deviceContext = (DeviceContext)initParams[1];
InitPlatformInterface();
// SharpDX also doesn't allow reference managment
}
public void Shutdown()
{
ShutdownPlatformInterface();
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();
}
}
/** Viewport support **/
private struct ImGuiViewportDataDx11
{
public IntPtr SwapChain;
public IntPtr View;
}
// Viewport interface
private delegate void CreateWindowDelegate(ImGuiViewportPtr ptr);
private delegate void DestroyWindowDelegate(ImGuiViewportPtr ptr);
private delegate void SetWindowSizeDelegate(ImGuiViewportPtr ptr, Vector2 size);
private delegate void RenderWindowDelegate(ImGuiViewportPtr ptr, IntPtr v);
private delegate void SwapBuffersDelegate(ImGuiViewportPtr ptr, IntPtr v);
private CreateWindowDelegate _createWindow;
private DestroyWindowDelegate _destroyWindow;
private SetWindowSizeDelegate _setWindowSize;
private RenderWindowDelegate _renderWindow;
private SwapBuffersDelegate _swapBuffers;
private void InitPlatformInterface()
{
ImGuiPlatformIOPtr ptr = ImGui.GetPlatformIO();
_createWindow = CreateWindow;
_destroyWindow = DestroyWindow;
_setWindowSize = SetWindowSize;
_renderWindow = RenderWindow;
_swapBuffers = SwapBuffers;
ptr.Renderer_CreateWindow = Marshal.GetFunctionPointerForDelegate(_createWindow);
ptr.Renderer_DestroyWindow = Marshal.GetFunctionPointerForDelegate(_destroyWindow);
ptr.Renderer_SetWindowSize = Marshal.GetFunctionPointerForDelegate(_setWindowSize);
ptr.Renderer_RenderWindow = Marshal.GetFunctionPointerForDelegate(_renderWindow);
ptr.Renderer_SwapBuffers = Marshal.GetFunctionPointerForDelegate(_swapBuffers);
}
private void ShutdownPlatformInterface()
{
ImGui.DestroyPlatformWindows();
}
// Viewport functions
public void CreateWindow(ImGuiViewportPtr viewport)
{
var data = (ImGuiViewportDataDx11*) Marshal.AllocHGlobal(Marshal.SizeOf<ImGuiViewportDataDx11>());
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
// Some backend will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND.
IntPtr hWnd = viewport.PlatformHandleRaw;
if (hWnd == IntPtr.Zero)
hWnd = viewport.PlatformHandle;
// Create swapchain
SwapChainDescription desc = new SwapChainDescription
{
ModeDescription = new ModeDescription
{
Width = 0,
Height = 0,
Format = Format.R8G8B8A8_UNorm,
RefreshRate = new Rational(0, 0)
},
SampleDescription = new SampleDescription
{
Count = 1,
Quality = 0
},
Usage = Usage.RenderTargetOutput,
BufferCount = 1,
OutputHandle = hWnd,
IsWindowed = true,
SwapEffect = SwapEffect.Discard,
Flags = SwapChainFlags.None
};
data->SwapChain = CreateSwapChain(desc);
// Create the render target view
using (var backbuffer = new SwapChain(data->SwapChain).GetBackBuffer<Texture2D>(0))
data->View = new RenderTargetView(_device, backbuffer).NativePointer;
viewport.RendererUserData = (IntPtr) data;
}
private IntPtr CreateSwapChain(SwapChainDescription desc) {
// Create a swapchain using the existing game hardware (I think)
using (var dxgi = _device.QueryInterface<SharpDX.DXGI.Device>())
using (var adapter = dxgi.Adapter)
using (var factory = adapter.GetParent<Factory>())
{
return new SwapChain(factory, _device, desc).NativePointer;
}
}
public void DestroyWindow(ImGuiViewportPtr viewport)
{
// This is also called on the main viewport for some reason, and we never set that viewport's RendererUserData
if (viewport.RendererUserData == IntPtr.Zero) return;
var data = (ImGuiViewportDataDx11*) viewport.RendererUserData;
new SwapChain(data->SwapChain).Dispose();
new RenderTargetView(data->View).Dispose();
data->SwapChain = IntPtr.Zero;
data->View = IntPtr.Zero;
Marshal.FreeHGlobal(viewport.RendererUserData);
viewport.RendererUserData = IntPtr.Zero;
}
public void SetWindowSize(ImGuiViewportPtr viewport, Vector2 size)
{
var data = (ImGuiViewportDataDx11*)viewport.RendererUserData;
// Delete our existing view
new RenderTargetView(data->View).Dispose();
var tmpSwap = new SwapChain(data->SwapChain);
// Resize buffers and recreate view
tmpSwap.ResizeBuffers(1, (int)size.X, (int)size.Y, Format.Unknown, SwapChainFlags.None);
using (var backbuffer = tmpSwap.GetBackBuffer<Texture2D>(0))
data->View = new RenderTargetView(_device, backbuffer).NativePointer;
}
public void RenderWindow(ImGuiViewportPtr viewport, IntPtr v)
{
var data = (ImGuiViewportDataDx11*)viewport.RendererUserData;
var tmpRtv = new RenderTargetView(data->View);
this._deviceContext.OutputMerger.SetTargets(tmpRtv);
if ((viewport.Flags & ImGuiViewportFlags.NoRendererClear) != ImGuiViewportFlags.NoRendererClear)
this._deviceContext.ClearRenderTargetView(tmpRtv, new RawColor4(0f, 0f, 0f, 1f));
RenderDrawData(viewport.DrawData);
}
public void SwapBuffers(ImGuiViewportPtr viewport, IntPtr v)
{
var data = (ImGuiViewportDataDx11*)viewport.RendererUserData;
new SwapChain(data->SwapChain).Present(0, PresentFlags.None);
}
}
}

View file

@ -0,0 +1,368 @@
using ImGuiNET;
using PInvoke;
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using StbiSharp;
using System.IO;
using Dalamud.Interface.Textures.TextureWraps;
using ImGuiScene.ImGui_Impl;
using ImGuizmoNET;
using ImPlotNET;
using Device = SharpDX.Direct3D11.Device;
namespace ImGuiScene
{
// This class will likely eventually be unified a bit more with other scenes, but for
// now it should be directly useable
public sealed class RawDX11Scene : IDisposable
{
public Device Device { get; private set; }
public IntPtr WindowHandlePtr { get; private set; }
public SwapChain SwapChain { get; private set; }
public bool UpdateCursor
{
get => this.imguiInput.UpdateCursor;
set => this.imguiInput.UpdateCursor = value;
}
private DeviceContext deviceContext;
private RenderTargetView rtv;
private int targetWidth;
private int targetHeight;
private ImGui_Impl_DX11 imguiRenderer;
private ImGui_Input_Impl_Direct imguiInput;
public delegate void BuildUIDelegate();
public delegate void NewInputFrameDelegate();
public delegate void NewRenderFrameDelegate();
/// <summary>
/// User methods invoked every ImGui frame to construct custom UIs.
/// </summary>
public BuildUIDelegate OnBuildUI;
public NewInputFrameDelegate OnNewInputFrame;
public NewRenderFrameDelegate OnNewRenderFrame;
private string imguiIniPath = null;
public string ImGuiIniPath
{
get { return imguiIniPath; }
set
{
imguiIniPath = value;
imguiInput.SetIniPath(imguiIniPath);
}
}
public RawDX11Scene(IntPtr nativeSwapChain)
{
this.SwapChain = new SwapChain(nativeSwapChain);
this.Device = SwapChain.GetDevice<Device>();
Initialize();
}
// This ctor will work fine, but it's only usefulness over using just the swapchain version
// is that this one will allow you to pass a different device than the swapchain.GetDevice() would
// return. This is mostly useful for render debugging, where the real d3ddevice is hooked and
// where we would like all our work to be done on that hooked device.
// Because we generally will get the swapchain from the internal present() call, we are getting
// the real d3d swapchain and not a hooked version, so GetDevice() will correspondingly return
// the read device and not a hooked verison.
// By passing in the hooked version explicitly here, we can mostly play nice with debug tools
public RawDX11Scene(IntPtr nativeDevice, IntPtr nativeSwapChain)
{
this.Device = new Device(nativeDevice);
this.SwapChain = new SwapChain(nativeSwapChain);
Initialize();
}
private void Initialize()
{
this.deviceContext = this.Device.ImmediateContext;
using (var backbuffer = this.SwapChain.GetBackBuffer<Texture2D>(0))
{
this.rtv = new RenderTargetView(this.Device, backbuffer);
}
// could also do things with GetClientRect() for WindowHandlePtr, not sure if that is necessary
this.targetWidth = this.SwapChain.Description.ModeDescription.Width;
this.targetHeight = this.SwapChain.Description.ModeDescription.Height;
this.WindowHandlePtr = this.SwapChain.Description.OutputHandle;
InitializeImGui();
}
private void InitializeImGui()
{
this.imguiRenderer = new ImGui_Impl_DX11();
var ctx = ImGui.CreateContext();
ImGuizmo.SetImGuiContext(ctx);
ImPlot.SetImGuiContext(ctx);
ImPlot.CreateContext();
ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable;
this.imguiRenderer.Init(this.Device, this.deviceContext);
this.imguiInput = new ImGui_Input_Impl_Direct(WindowHandlePtr);
}
/// <summary>
/// Processes window messages.
/// </summary>
/// <param name="hWnd">Handle of the window.</param>
/// <param name="msg">Type of window message.</param>
/// <param name="wParam">wParam.</param>
/// <param name="lParam">lParam.</param>
/// <returns>Return value.</returns>
public unsafe IntPtr? ProcessWndProcW(IntPtr hWnd, User32.WindowMessage msg, void* wParam, void* lParam) {
return this.imguiInput.ProcessWndProcW(hWnd, msg, wParam, lParam);
}
public void Render()
{
this.deviceContext.OutputMerger.SetRenderTargets(this.rtv);
this.imguiRenderer.NewFrame();
this.OnNewRenderFrame?.Invoke();
this.imguiInput.NewFrame(targetWidth, targetHeight);
this.OnNewInputFrame?.Invoke();
ImGui.NewFrame();
ImGuizmo.BeginFrame();
OnBuildUI?.Invoke();
ImGui.Render();
this.imguiRenderer.RenderDrawData(ImGui.GetDrawData());
this.deviceContext.OutputMerger.SetRenderTargets((RenderTargetView)null);
ImGui.UpdatePlatformWindows();
ImGui.RenderPlatformWindowsDefault();
}
public void OnPreResize()
{
this.deviceContext.OutputMerger.SetRenderTargets((RenderTargetView)null);
this.rtv?.Dispose();
this.rtv = null;
}
public void OnPostResize(int newWidth, int newHeight)
{
using (var backbuffer = this.SwapChain.GetBackBuffer<Texture2D>(0))
{
this.rtv = new RenderTargetView(this.Device, backbuffer);
}
this.targetWidth = newWidth;
this.targetHeight = newHeight;
}
// It is pretty much required that this is called from a handler attached
// to OnNewRenderFrame
public void InvalidateFonts()
{
this.imguiRenderer.RebuildFontTexture();
}
// It is pretty much required that this is called from a handler attached
// to OnNewRenderFrame
public void ClearStacksOnContext() {
Custom.igCustom_ClearStacks();
}
public bool IsImGuiCursor(IntPtr hCursor)
{
return this.imguiInput.IsImGuiCursor(hCursor);
}
public IDalamudTextureWrap LoadImage(string path)
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var ms = new MemoryStream())
{
fs.CopyTo(ms);
var image = Stbi.LoadFromMemory(ms, 4);
return LoadImage_Internal(image);
}
}
public IDalamudTextureWrap LoadImage(byte[] imageBytes)
{
using (var ms = new MemoryStream(imageBytes, 0, imageBytes.Length, false, true))
{
var image = Stbi.LoadFromMemory(ms, 4);
return LoadImage_Internal(image);
}
}
public unsafe IDalamudTextureWrap LoadImageRaw(byte[] imageData, int width, int height, int numChannels = 4)
{
// StbiSharp doesn't expose a constructor, even just to wrap existing data, which means
// short of something awful like below, or creating another wrapper layer, we can't avoid
// adding divergent code paths into CreateTexture
//var mock = new { Width = width, Height = height, NumChannels = numChannels, Data = imageData };
//var image = Unsafe.As<StbiImage>(mock);
//return LoadImage_Internal(image);
fixed (void* pixelData = imageData)
{
return CreateTexture(pixelData, width, height, numChannels);
}
}
private unsafe IDalamudTextureWrap LoadImage_Internal(StbiImage image)
{
fixed (void* pixelData = image.Data)
{
return CreateTexture(pixelData, image.Width, image.Height, image.NumChannels);
}
}
private unsafe IDalamudTextureWrap CreateTexture(void* pixelData, int width, int height, int bytesPerPixel)
{
ShaderResourceView resView = null;
var texDesc = new Texture2DDescription
{
Width = width,
Height = height,
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 texture = new Texture2D(this.Device, texDesc, new DataRectangle(new IntPtr(pixelData), width * bytesPerPixel)))
{
resView = new ShaderResourceView(this.Device, texture, new ShaderResourceViewDescription
{
Format = texDesc.Format,
Dimension = ShaderResourceViewDimension.Texture2D,
Texture2D = { MipLevels = texDesc.MipLevels }
});
}
// no sampler for now because the ImGui implementation we copied doesn't allow for changing it
return new D3DTextureWrap(resView, width, height);
}
public byte[] CaptureScreenshot()
{
using (var backBuffer = this.SwapChain.GetBackBuffer<Texture2D>(0))
{
Texture2DDescription desc = backBuffer.Description;
desc.CpuAccessFlags = CpuAccessFlags.Read;
desc.Usage = ResourceUsage.Staging;
desc.OptionFlags = ResourceOptionFlags.None;
desc.BindFlags = BindFlags.None;
using (var tex = new Texture2D(this.Device, desc))
{
this.deviceContext.CopyResource(backBuffer, tex);
using (var surf = tex.QueryInterface<Surface>())
{
var map = surf.Map(SharpDX.DXGI.MapFlags.Read, out DataStream dataStream);
var pixelData = new byte[surf.Description.Width * surf.Description.Height * surf.Description.Format.SizeOfInBytes()];
var dataCounter = 0;
while (dataCounter < pixelData.Length)
{
//var curPixel = dataStream.Read<uint>();
var x = dataStream.Read<byte>();
var y = dataStream.Read<byte>();
var z = dataStream.Read<byte>();
var w = dataStream.Read<byte>();
pixelData[dataCounter++] = z;
pixelData[dataCounter++] = y;
pixelData[dataCounter++] = x;
pixelData[dataCounter++] = w;
}
// TODO: test this on a thread
//var gch = GCHandle.Alloc(pixelData, GCHandleType.Pinned);
//using (var bitmap = new Bitmap(surf.Description.Width, surf.Description.Height, map.Pitch, PixelFormat.Format32bppRgb, gch.AddrOfPinnedObject()))
//{
// bitmap.Save(path);
//}
//gch.Free();
surf.Unmap();
dataStream.Dispose();
return pixelData;
}
}
}
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
private void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
this.imguiRenderer?.Shutdown();
this.imguiInput?.Dispose();
ImGui.DestroyContext();
this.rtv.Dispose();
// Not actually sure how sharpdx does ref management, but hopefully they
// addref when we create our wrappers, so this should just release that count
// Originally it was thought these lines were needed because it was assumed that SharpDX does
// proper refcounting to handle disposing, but disposing these would cause the game to crash
// on resizing after unloading Dalamud
// this.SwapChain?.Dispose();
// this.deviceContext?.Dispose();
// this.Device?.Dispose();
disposedValue = true;
}
}
~RawDX11Scene()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}

View file

@ -0,0 +1,515 @@
namespace ImGuiScene
{
// Assorted other win32 constants that are seemingly just #defined
// Not an enum like the others because the types vary and there are overlapping values
internal static class Win32Constants
{
public const short HTTRANSPARENT = -1;
public const short HTCLIENT = 1;
public const short XBUTTON1 = 1;
public const int WHEEL_DELTA = 120;
public const int CURSOR_SHOWING = 1;
public const int CURSOR_SUPPRESSED = 2;
}
internal enum WindowLongType : int
{
GWL_EXSTYLE = -20,
GWL_HINSTANCE = -6,
GWL_HWNDPARENT = -8,
GWL_ID = -12,
GWL_STYLE = -16,
GWL_USERDATA = -21,
GWL_WNDPROC = -4
}
internal enum Cursor : int
{
IDC_ARROW = 32512,
IDC_IBEAM = 32513,
IDC_WAIT = 32514,
IDC_CROSS = 32515,
IDC_UPARROW = 32516,
IDC_SIZE = 32640,
IDC_ICON = 32641,
IDC_SIZENWSE = 32642,
IDC_SIZENESW = 32643,
IDC_SIZEWE = 32644,
IDC_SIZENS = 32645,
IDC_SIZEALL = 32646,
IDC_NO = 32648,
IDC_HAND = 32649,
IDC_APPSTARTING = 32650,
IDC_HELP = 32651
}
// taken from https://www.pinvoke.net/default.aspx/Enums/VirtualKeys.html
internal enum VirtualKey : int
{
LeftButton = 0x01,
RightButton = 0x02,
Cancel = 0x03,
MiddleButton = 0x04,
ExtraButton1 = 0x05,
ExtraButton2 = 0x06,
Back = 0x08,
Tab = 0x09,
Clear = 0x0C,
Return = 0x0D,
Shift = 0x10,
Control = 0x11,
Menu = 0x12,
Pause = 0x13,
CapsLock = 0x14,
Kana = 0x15,
Hangeul = 0x15,
Hangul = 0x15,
Junja = 0x17,
Final = 0x18,
Hanja = 0x19,
Kanji = 0x19,
Escape = 0x1B,
Convert = 0x1C,
NonConvert = 0x1D,
Accept = 0x1E,
ModeChange = 0x1F,
Space = 0x20,
Prior = 0x21,
Next = 0x22,
End = 0x23,
Home = 0x24,
Left = 0x25,
Up = 0x26,
Right = 0x27,
Down = 0x28,
Select = 0x29,
Print = 0x2A,
Execute = 0x2B,
Snapshot = 0x2C,
Insert = 0x2D,
Delete = 0x2E,
Help = 0x2F,
N0 = 0x30,
N1 = 0x31,
N2 = 0x32,
N3 = 0x33,
N4 = 0x34,
N5 = 0x35,
N6 = 0x36,
N7 = 0x37,
N8 = 0x38,
N9 = 0x39,
A = 0x41,
B = 0x42,
C = 0x43,
D = 0x44,
E = 0x45,
F = 0x46,
G = 0x47,
H = 0x48,
I = 0x49,
J = 0x4A,
K = 0x4B,
L = 0x4C,
M = 0x4D,
N = 0x4E,
O = 0x4F,
P = 0x50,
Q = 0x51,
R = 0x52,
S = 0x53,
T = 0x54,
U = 0x55,
V = 0x56,
W = 0x57,
X = 0x58,
Y = 0x59,
Z = 0x5A,
LeftWindows = 0x5B,
RightWindows = 0x5C,
Application = 0x5D,
Sleep = 0x5F,
Numpad0 = 0x60,
Numpad1 = 0x61,
Numpad2 = 0x62,
Numpad3 = 0x63,
Numpad4 = 0x64,
Numpad5 = 0x65,
Numpad6 = 0x66,
Numpad7 = 0x67,
Numpad8 = 0x68,
Numpad9 = 0x69,
Multiply = 0x6A,
Add = 0x6B,
Separator = 0x6C,
Subtract = 0x6D,
Decimal = 0x6E,
Divide = 0x6F,
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
F4 = 0x73,
F5 = 0x74,
F6 = 0x75,
F7 = 0x76,
F8 = 0x77,
F9 = 0x78,
F10 = 0x79,
F11 = 0x7A,
F12 = 0x7B,
F13 = 0x7C,
F14 = 0x7D,
F15 = 0x7E,
F16 = 0x7F,
F17 = 0x80,
F18 = 0x81,
F19 = 0x82,
F20 = 0x83,
F21 = 0x84,
F22 = 0x85,
F23 = 0x86,
F24 = 0x87,
NumLock = 0x90,
ScrollLock = 0x91,
NEC_Equal = 0x92,
Fujitsu_Jisho = 0x92,
Fujitsu_Masshou = 0x93,
Fujitsu_Touroku = 0x94,
Fujitsu_Loya = 0x95,
Fujitsu_Roya = 0x96,
LeftShift = 0xA0,
RightShift = 0xA1,
LeftControl = 0xA2,
RightControl = 0xA3,
LeftMenu = 0xA4,
RightMenu = 0xA5,
BrowserBack = 0xA6,
BrowserForward = 0xA7,
BrowserRefresh = 0xA8,
BrowserStop = 0xA9,
BrowserSearch = 0xAA,
BrowserFavorites = 0xAB,
BrowserHome = 0xAC,
VolumeMute = 0xAD,
VolumeDown = 0xAE,
VolumeUp = 0xAF,
MediaNextTrack = 0xB0,
MediaPrevTrack = 0xB1,
MediaStop = 0xB2,
MediaPlayPause = 0xB3,
LaunchMail = 0xB4,
LaunchMediaSelect = 0xB5,
LaunchApplication1 = 0xB6,
LaunchApplication2 = 0xB7,
OEM1 = 0xBA,
OEMPlus = 0xBB,
OEMComma = 0xBC,
OEMMinus = 0xBD,
OEMPeriod = 0xBE,
OEM2 = 0xBF,
OEM3 = 0xC0,
OEM4 = 0xDB,
OEM5 = 0xDC,
OEM6 = 0xDD,
OEM7 = 0xDE,
OEM8 = 0xDF,
OEMAX = 0xE1,
OEM102 = 0xE2,
ICOHelp = 0xE3,
ICO00 = 0xE4,
ProcessKey = 0xE5,
ICOClear = 0xE6,
Packet = 0xE7,
OEMReset = 0xE9,
OEMJump = 0xEA,
OEMPA1 = 0xEB,
OEMPA2 = 0xEC,
OEMPA3 = 0xED,
OEMWSCtrl = 0xEE,
OEMCUSel = 0xEF,
OEMATTN = 0xF0,
OEMFinish = 0xF1,
OEMCopy = 0xF2,
OEMAuto = 0xF3,
OEMENLW = 0xF4,
OEMBackTab = 0xF5,
ATTN = 0xF6,
CRSel = 0xF7,
EXSel = 0xF8,
EREOF = 0xF9,
Play = 0xFA,
Zoom = 0xFB,
Noname = 0xFC,
PA1 = 0xFD,
OEMClear = 0xFE
}
// taken from https://gist.github.com/amgine/2395987
// may be missing some but likely has anything we'd ever need
internal enum WindowsMessage
{
WM_NULL = 0x0000,
WM_CREATE = 0x0001,
WM_DESTROY = 0x0002,
WM_MOVE = 0x0003,
WM_SIZE = 0x0005,
WM_ACTIVATE = 0x0006,
WM_SETFOCUS = 0x0007,
WM_KILLFOCUS = 0x0008,
WM_ENABLE = 0x000A,
WM_SETREDRAW = 0x000B,
WM_SETTEXT = 0x000C,
WM_GETTEXT = 0x000D,
WM_GETTEXTLENGTH = 0x000E,
WM_PAINT = 0x000F,
WM_CLOSE = 0x0010,
WM_QUERYENDSESSION = 0x0011,
WM_QUERYOPEN = 0x0013,
WM_ENDSESSION = 0x0016,
WM_QUIT = 0x0012,
WM_ERASEBKGND = 0x0014,
WM_SYSCOLORCHANGE = 0x0015,
WM_SHOWWINDOW = 0x0018,
WM_WININICHANGE = 0x001A,
WM_SETTINGCHANGE = WM_WININICHANGE,
WM_DEVMODECHANGE = 0x001B,
WM_ACTIVATEAPP = 0x001C,
WM_FONTCHANGE = 0x001D,
WM_TIMECHANGE = 0x001E,
WM_CANCELMODE = 0x001F,
WM_SETCURSOR = 0x0020,
WM_MOUSEACTIVATE = 0x0021,
WM_CHILDACTIVATE = 0x0022,
WM_QUEUESYNC = 0x0023,
WM_GETMINMAXINFO = 0x0024,
WM_PAINTICON = 0x0026,
WM_ICONERASEBKGND = 0x0027,
WM_NEXTDLGCTL = 0x0028,
WM_SPOOLERSTATUS = 0x002A,
WM_DRAWITEM = 0x002B,
WM_MEASUREITEM = 0x002C,
WM_DELETEITEM = 0x002D,
WM_VKEYTOITEM = 0x002E,
WM_CHARTOITEM = 0x002F,
WM_SETFONT = 0x0030,
WM_GETFONT = 0x0031,
WM_SETHOTKEY = 0x0032,
WM_GETHOTKEY = 0x0033,
WM_QUERYDRAGICON = 0x0037,
WM_COMPAREITEM = 0x0039,
WM_GETOBJECT = 0x003D,
WM_COMPACTING = 0x0041,
WM_COMMNOTIFY = 0x0044,
WM_WINDOWPOSCHANGING = 0x0046,
WM_WINDOWPOSCHANGED = 0x0047,
WM_POWER = 0x0048,
WM_COPYDATA = 0x004A,
WM_CANCELJOURNAL = 0x004B,
WM_NOTIFY = 0x004E,
WM_INPUTLANGCHANGEREQUEST = 0x0050,
WM_INPUTLANGCHANGE = 0x0051,
WM_TCARD = 0x0052,
WM_HELP = 0x0053,
WM_USERCHANGED = 0x0054,
WM_NOTIFYFORMAT = 0x0055,
WM_CONTEXTMENU = 0x007B,
WM_STYLECHANGING = 0x007C,
WM_STYLECHANGED = 0x007D,
WM_DISPLAYCHANGE = 0x007E,
WM_GETICON = 0x007F,
WM_SETICON = 0x0080,
WM_NCCREATE = 0x0081,
WM_NCDESTROY = 0x0082,
WM_NCCALCSIZE = 0x0083,
WM_NCHITTEST = 0x0084,
WM_NCPAINT = 0x0085,
WM_NCACTIVATE = 0x0086,
WM_GETDLGCODE = 0x0087,
WM_SYNCPAINT = 0x0088,
WM_NCMOUSEMOVE = 0x00A0,
WM_NCLBUTTONDOWN = 0x00A1,
WM_NCLBUTTONUP = 0x00A2,
WM_NCLBUTTONDBLCLK = 0x00A3,
WM_NCRBUTTONDOWN = 0x00A4,
WM_NCRBUTTONUP = 0x00A5,
WM_NCRBUTTONDBLCLK = 0x00A6,
WM_NCMBUTTONDOWN = 0x00A7,
WM_NCMBUTTONUP = 0x00A8,
WM_NCMBUTTONDBLCLK = 0x00A9,
WM_NCXBUTTONDOWN = 0x00AB,
WM_NCXBUTTONUP = 0x00AC,
WM_NCXBUTTONDBLCLK = 0x00AD,
WM_INPUT_DEVICE_CHANGE = 0x00FE,
WM_INPUT = 0x00FF,
WM_KEYFIRST = 0x0100,
WM_KEYDOWN = 0x0100,
WM_KEYUP = 0x0101,
WM_CHAR = 0x0102,
WM_DEADCHAR = 0x0103,
WM_SYSKEYDOWN = 0x0104,
WM_SYSKEYUP = 0x0105,
WM_SYSCHAR = 0x0106,
WM_SYSDEADCHAR = 0x0107,
WM_UNICHAR = 0x0109,
WM_KEYLAST = 0x0109,
WM_IME_STARTCOMPOSITION = 0x010D,
WM_IME_ENDCOMPOSITION = 0x010E,
WM_IME_COMPOSITION = 0x010F,
WM_IME_KEYLAST = 0x010F,
WM_INITDIALOG = 0x0110,
WM_COMMAND = 0x0111,
WM_SYSCOMMAND = 0x0112,
WM_TIMER = 0x0113,
WM_HSCROLL = 0x0114,
WM_VSCROLL = 0x0115,
WM_INITMENU = 0x0116,
WM_INITMENUPOPUP = 0x0117,
WM_MENUSELECT = 0x011F,
WM_MENUCHAR = 0x0120,
WM_ENTERIDLE = 0x0121,
WM_MENURBUTTONUP = 0x0122,
WM_MENUDRAG = 0x0123,
WM_MENUGETOBJECT = 0x0124,
WM_UNINITMENUPOPUP = 0x0125,
WM_MENUCOMMAND = 0x0126,
WM_CHANGEUISTATE = 0x0127,
WM_UPDATEUISTATE = 0x0128,
WM_QUERYUISTATE = 0x0129,
WM_CTLCOLORMSGBOX = 0x0132,
WM_CTLCOLOREDIT = 0x0133,
WM_CTLCOLORLISTBOX = 0x0134,
WM_CTLCOLORBTN = 0x0135,
WM_CTLCOLORDLG = 0x0136,
WM_CTLCOLORSCROLLBAR = 0x0137,
WM_CTLCOLORSTATIC = 0x0138,
MN_GETHMENU = 0x01E1,
WM_MOUSEFIRST = 0x0200,
WM_MOUSEMOVE = 0x0200,
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_LBUTTONDBLCLK = 0x0203,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205,
WM_RBUTTONDBLCLK = 0x0206,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208,
WM_MBUTTONDBLCLK = 0x0209,
WM_MOUSEWHEEL = 0x020A,
WM_XBUTTONDOWN = 0x020B,
WM_XBUTTONUP = 0x020C,
WM_XBUTTONDBLCLK = 0x020D,
WM_MOUSEHWHEEL = 0x020E,
WM_PARENTNOTIFY = 0x0210,
WM_ENTERMENULOOP = 0x0211,
WM_EXITMENULOOP = 0x0212,
WM_NEXTMENU = 0x0213,
WM_SIZING = 0x0214,
WM_CAPTURECHANGED = 0x0215,
WM_MOVING = 0x0216,
WM_POWERBROADCAST = 0x0218,
WM_DEVICECHANGE = 0x0219,
WM_MDICREATE = 0x0220,
WM_MDIDESTROY = 0x0221,
WM_MDIACTIVATE = 0x0222,
WM_MDIRESTORE = 0x0223,
WM_MDINEXT = 0x0224,
WM_MDIMAXIMIZE = 0x0225,
WM_MDITILE = 0x0226,
WM_MDICASCADE = 0x0227,
WM_MDIICONARRANGE = 0x0228,
WM_MDIGETACTIVE = 0x0229,
WM_MDISETMENU = 0x0230,
WM_ENTERSIZEMOVE = 0x0231,
WM_EXITSIZEMOVE = 0x0232,
WM_DROPFILES = 0x0233,
WM_MDIREFRESHMENU = 0x0234,
WM_IME_SETCONTEXT = 0x0281,
WM_IME_NOTIFY = 0x0282,
WM_IME_CONTROL = 0x0283,
WM_IME_COMPOSITIONFULL = 0x0284,
WM_IME_SELECT = 0x0285,
WM_IME_CHAR = 0x0286,
WM_IME_REQUEST = 0x0288,
WM_IME_KEYDOWN = 0x0290,
WM_IME_KEYUP = 0x0291,
WM_MOUSEHOVER = 0x02A1,
WM_MOUSELEAVE = 0x02A3,
WM_NCMOUSEHOVER = 0x02A0,
WM_NCMOUSELEAVE = 0x02A2,
WM_WTSSESSION_CHANGE = 0x02B1,
WM_TABLET_FIRST = 0x02c0,
WM_TABLET_LAST = 0x02df,
WM_CUT = 0x0300,
WM_COPY = 0x0301,
WM_PASTE = 0x0302,
WM_CLEAR = 0x0303,
WM_UNDO = 0x0304,
WM_RENDERFORMAT = 0x0305,
WM_RENDERALLFORMATS = 0x0306,
WM_DESTROYCLIPBOARD = 0x0307,
WM_DRAWCLIPBOARD = 0x0308,
WM_PAINTCLIPBOARD = 0x0309,
WM_VSCROLLCLIPBOARD = 0x030A,
WM_SIZECLIPBOARD = 0x030B,
WM_ASKCBFORMATNAME = 0x030C,
WM_CHANGECBCHAIN = 0x030D,
WM_HSCROLLCLIPBOARD = 0x030E,
WM_QUERYNEWPALETTE = 0x030F,
WM_PALETTEISCHANGING = 0x0310,
WM_PALETTECHANGED = 0x0311,
WM_HOTKEY = 0x0312,
WM_PRINT = 0x0317,
WM_PRINTCLIENT = 0x0318,
WM_APPCOMMAND = 0x0319,
WM_THEMECHANGED = 0x031A,
WM_CLIPBOARDUPDATE = 0x031D,
WM_DWMCOMPOSITIONCHANGED = 0x031E,
WM_DWMNCRENDERINGCHANGED = 0x031F,
WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320,
WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321,
WM_GETTITLEBARINFOEX = 0x033F,
WM_HANDHELDFIRST = 0x0358,
WM_HANDHELDLAST = 0x035F,
WM_AFXFIRST = 0x0360,
WM_AFXLAST = 0x037F,
WM_PENWINFIRST = 0x0380,
WM_PENWINLAST = 0x038F,
WM_APP = 0x8000,
WM_USER = 0x0400,
WM_REFLECT = WM_USER + 0x1C00,
}
}

View file

@ -0,0 +1,15 @@
using System.Runtime.InteropServices;
namespace ImGuiScene
{
public static unsafe class MemUtil
{
public static T* Allocate<T>() where T : unmanaged {
return (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>());
}
public static void Free(this IntPtr obj) {
Marshal.FreeHGlobal(obj);
}
}
}

View file

@ -0,0 +1,184 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace ImGuiScene
{
// Even though we are importing PInvoke stuff from Nuget, we still need this class
// for some APIs that do not seem to be exposed in any way through those packages.
// In the future, we may be able to use https://github.com/microsoft/cswin32
internal class Win32
{
public enum ImeCommand
{
IMN_CLOSESTATUSWINDOW = 0x0001,
IMN_OPENSTATUSWINDOW = 0x0002,
IMN_CHANGECANDIDATE = 0x0003,
IMN_CLOSECANDIDATE = 0x0004,
IMN_OPENCANDIDATE = 0x0005,
IMN_SETCONVERSIONMODE = 0x0006,
IMN_SETSENTENCEMODE = 0x0007,
IMN_SETOPENSTATUS = 0x0008,
IMN_SETCANDIDATEPOS = 0x0009,
IMN_SETCOMPOSITIONFONT = 0x000A,
IMN_SETCOMPOSITIONWINDOW = 0x000B,
IMN_SETSTATUSWINDOWPOS = 0x000C,
IMN_GUIDELINE = 0x000D,
IMN_PRIVATE = 0x000E
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int X, int Y)
{
this.X = X;
this.Y = Y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct CURSORINFO
{
public Int32 cbSize;
public Int32 flags;
public IntPtr hCursor;
public POINT ptScreenPos;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct COMPOSITIONFORM
{
public uint dwStyle;
public POINT ptCurrentPos;
public RECT rcArea;
public COMPOSITIONFORM(uint dwStyle, POINT ptCurrentPos, RECT rcArea)
{
this.dwStyle = dwStyle;
this.ptCurrentPos = ptCurrentPos;
this.rcArea = rcArea;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort HIWORD(ulong val)
{
// #define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
return (ushort)((val >> 16) & 0xFFFF);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort LOWORD(ulong val)
{
// #define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff))
return (ushort)(val & 0xFFFF);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ushort GET_XBUTTON_WPARAM(ulong val)
{
// #define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam))
return HIWORD(val);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short GET_WHEEL_DELTA_WPARAM(ulong val)
{
// #define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam))
return (short)HIWORD(val);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GET_X_LPARAM(ulong val)
{
// #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
return (int)(short)LOWORD(val);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GET_Y_LPARAM(ulong val)
{
// #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
return (int)(short)HIWORD(val);
}
[DllImport("dwmapi.dll")]
public static extern int DwmIsCompositionEnabled(out bool enabled);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
// [DllImport("Imm32.dll", SetLastError=true)]
// public static extern IntPtr ImmGetContext(IntPtr hWnd);
// [DllImport("Imm32.dll", SetLastError=true)]
// public static extern bool ImmSetCompositionWindow(IntPtr hImc, ref COMPOSITIONFORM lpCompForm);
// [DllImport("Imm32.dll", SetLastError=true)]
// public static extern bool ImmReleaseContext(IntPtr hWnd, IntPtr hImc);
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int x, int y);
[DllImport("user32.dll")]
public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32.dll")]
public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32.dll")]
public static extern IntPtr GetCapture();
[DllImport("user32.dll")]
public static extern IntPtr SetCapture(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
[DllImport("user32.dll")]
public static extern short GetKeyState(VirtualKey nVirtKey);
[DllImport("user32.dll")]
public static extern IntPtr GetCursor();
[DllImport("user32.dll")]
public static extern IntPtr SetCursor(IntPtr handle);
[DllImport("user32.dll")]
public static extern IntPtr LoadCursor(IntPtr hInstance, Cursor lpCursorName);
[DllImport("user32.dll")]
public static extern int ShowCursor(bool bShow);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW", SetLastError = true)]
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, WindowLongType nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", EntryPoint = "CallWindowProcW")]
public static extern long CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, ulong wParam, long lParam);
[DllImport("user32.dll", EntryPoint = "GetCursorInfo")]
private static extern bool GetCursorInfo_Internal(ref CURSORINFO pci);
public static bool GetCursorInfo(out CURSORINFO pci)
{
pci = new CURSORINFO
{
cbSize = Marshal.SizeOf(typeof(CURSORINFO))
};
return GetCursorInfo_Internal(ref pci);
}
}
}

Binary file not shown.

View file

@ -0,0 +1,12 @@
#version 150
uniform sampler2D Texture;
in vec2 Frag_UV;
in vec4 Frag_Color;
out vec4 Out_Color;
void main()
{
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
}

View file

@ -0,0 +1,17 @@
#version 150
uniform mat4 ProjMtx;
in vec2 Position;
in vec2 UV;
in vec4 Color;
out vec2 Frag_UV;
out vec4 Frag_Color;
void main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy, 0, 1);
}

View file

@ -16,6 +16,8 @@ using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using ImGuiScene;
using VirtualKey = Dalamud.Game.ClientState.Keys.VirtualKey;
namespace Dalamud.Interface.Utility;
/// <summary>

1
lib/ImGui.NET Submodule

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

@ -1 +0,0 @@
Subproject commit 2f37349ffd778561a1103a650683116c43edc86c