mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-30 20:33:40 +01:00
Rewrite ImGuiScene
This commit is contained in:
parent
6bf264acf0
commit
184463a056
41 changed files with 3888 additions and 3890 deletions
|
|
@ -90,7 +90,6 @@
|
|||
<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>
|
||||
|
|
@ -103,11 +102,12 @@
|
|||
<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" />
|
||||
<EmbeddedResource Include="Interface\ImGuiBackend\Renderers\imgui-frag.hlsl.bytes">
|
||||
<LogicalName>imgui-frag.hlsl.bytes</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Interface\ImGuiBackend\Renderers\imgui-vertex.hlsl.bytes">
|
||||
<LogicalName>imgui-vertex.hlsl.bytes</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dalamud.Common\Dalamud.Common.csproj" />
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ internal partial class DragDropManager : IInternalDisposableService, IDragDropMa
|
|||
Service<InterfaceManager.InterfaceManagerWithScene>.GetAsync()
|
||||
.ContinueWith(t =>
|
||||
{
|
||||
this.windowHandlePtr = t.Result.Manager.WindowHandlePtr;
|
||||
this.windowHandlePtr = t.Result.Manager.GameWindowHandle;
|
||||
this.Enable();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
222
Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs
Normal file
222
Dalamud/Interface/ImGuiBackend/Dx11Win32Backend.cs
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Dalamud.Interface.ImGuiBackend.Helpers;
|
||||
using Dalamud.Interface.ImGuiBackend.InputHandler;
|
||||
using Dalamud.Interface.ImGuiBackend.Renderers;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using ImGuizmoNET;
|
||||
|
||||
using ImPlotNET;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend;
|
||||
|
||||
/// <summary>
|
||||
/// Backend for ImGui, using <see cref="Dx11Renderer"/> and <see cref="Win32InputHandler"/>.
|
||||
/// </summary>
|
||||
[SuppressMessage(
|
||||
"StyleCop.CSharp.LayoutRules",
|
||||
"SA1519:Braces should not be omitted from multi-line child statement",
|
||||
Justification = "Multiple fixed/using scopes")]
|
||||
internal sealed unsafe class Dx11Win32Backend : IWin32Backend
|
||||
{
|
||||
private readonly Dx11Renderer imguiRenderer;
|
||||
private readonly Win32InputHandler imguiInput;
|
||||
|
||||
private ComPtr<IDXGISwapChain> swapChainPossiblyWrapped;
|
||||
private ComPtr<IDXGISwapChain> swapChain;
|
||||
private ComPtr<ID3D11Device> device;
|
||||
private ComPtr<ID3D11DeviceContext> deviceContext;
|
||||
|
||||
private int targetWidth;
|
||||
private int targetHeight;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dx11Win32Backend"/> class.
|
||||
/// </summary>
|
||||
/// <param name="swapChain">The pointer to an instance of <see cref="IDXGISwapChain"/>. The reference is copied.</param>
|
||||
public Dx11Win32Backend(IDXGISwapChain* swapChain)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.swapChainPossiblyWrapped = new(swapChain);
|
||||
this.swapChain = new(swapChain);
|
||||
fixed (ComPtr<IDXGISwapChain>* ppSwapChain = &this.swapChain)
|
||||
ReShadePeeler.PeelSwapChain(ppSwapChain);
|
||||
|
||||
fixed (Guid* guid = &IID.IID_ID3D11Device)
|
||||
fixed (ID3D11Device** pp = &this.device.GetPinnableReference())
|
||||
this.swapChain.Get()->GetDevice(guid, (void**)pp).ThrowOnError();
|
||||
|
||||
fixed (ID3D11DeviceContext** pp = &this.deviceContext.GetPinnableReference())
|
||||
this.device.Get()->GetImmediateContext(pp);
|
||||
|
||||
using var buffer = default(ComPtr<ID3D11Resource>);
|
||||
fixed (Guid* guid = &IID.IID_ID3D11Resource)
|
||||
this.swapChain.Get()->GetBuffer(0, guid, (void**)buffer.GetAddressOf()).ThrowOnError();
|
||||
|
||||
var desc = default(DXGI_SWAP_CHAIN_DESC);
|
||||
this.swapChain.Get()->GetDesc(&desc).ThrowOnError();
|
||||
this.targetWidth = (int)desc.BufferDesc.Width;
|
||||
this.targetHeight = (int)desc.BufferDesc.Height;
|
||||
this.WindowHandle = desc.OutputWindow;
|
||||
|
||||
var ctx = ImGui.CreateContext();
|
||||
ImGuizmo.SetImGuiContext(ctx);
|
||||
ImPlot.SetImGuiContext(ctx);
|
||||
ImPlot.CreateContext();
|
||||
|
||||
ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.DockingEnable | ImGuiConfigFlags.ViewportsEnable;
|
||||
|
||||
this.imguiRenderer = new(this.SwapChain, this.Device, this.DeviceContext);
|
||||
this.imguiInput = new(this.WindowHandle);
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="Dx11Win32Backend"/> class.
|
||||
/// </summary>
|
||||
~Dx11Win32Backend() => this.ReleaseUnmanagedResources();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IImGuiBackend.BuildUiDelegate? BuildUi;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IImGuiBackend.NewInputFrameDelegate? NewInputFrame;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event IImGuiBackend.NewRenderFrameDelegate? NewRenderFrame;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool UpdateCursor
|
||||
{
|
||||
get => this.imguiInput.UpdateCursor;
|
||||
set => this.imguiInput.UpdateCursor = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? IniPath
|
||||
{
|
||||
get => this.imguiInput.IniPath;
|
||||
set => this.imguiInput.IniPath = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IImGuiInputHandler InputHandler => this.imguiInput;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IImGuiRenderer Renderer => this.imguiRenderer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to an instance of <see cref="IDXGISwapChain"/>.
|
||||
/// </summary>
|
||||
public IDXGISwapChain* SwapChain => this.swapChain;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to an instance of <see cref="ID3D11Device"/>.
|
||||
/// </summary>
|
||||
public ID3D11Device* Device => this.device;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to an instance of <see cref="ID3D11Device"/>, in <see cref="nint"/>.
|
||||
/// </summary>
|
||||
public nint DeviceHandle => (nint)this.device.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to an instance of <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
public ID3D11DeviceContext* DeviceContext => this.deviceContext;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the window handle.
|
||||
/// </summary>
|
||||
public HWND WindowHandle { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.ReleaseUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public nint? ProcessWndProcW(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam) =>
|
||||
this.imguiInput.ProcessWndProcW(hWnd, msg, wParam, lParam);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Render()
|
||||
{
|
||||
this.imguiRenderer.OnNewFrame();
|
||||
this.NewRenderFrame?.Invoke();
|
||||
this.imguiInput.NewFrame(this.targetWidth, this.targetHeight);
|
||||
this.NewInputFrame?.Invoke();
|
||||
|
||||
ImGui.NewFrame();
|
||||
ImGuizmo.BeginFrame();
|
||||
|
||||
this.BuildUi?.Invoke();
|
||||
|
||||
ImGui.Render();
|
||||
|
||||
this.imguiRenderer.RenderDrawData(ImGui.GetDrawData());
|
||||
|
||||
ImGui.UpdatePlatformWindows();
|
||||
ImGui.RenderPlatformWindowsDefault();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnPreResize() => this.imguiRenderer.OnPreResize();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnPostResize(int newWidth, int newHeight)
|
||||
{
|
||||
this.imguiRenderer.OnPostResize(newWidth, newHeight);
|
||||
this.targetWidth = newWidth;
|
||||
this.targetHeight = newHeight;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void InvalidateFonts() => this.imguiRenderer.RebuildFontTexture();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsImGuiCursor(nint cursorHandle) => this.imguiInput.IsImGuiCursor(cursorHandle);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsAttachedToPresentationTarget(nint targetHandle) =>
|
||||
this.swapChain.Get() == (void*)targetHandle
|
||||
|| this.swapChainPossiblyWrapped.Get() == (void*)targetHandle;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsMainViewportFullScreen()
|
||||
{
|
||||
BOOL fullscreen;
|
||||
this.swapChain.Get()->GetFullscreenState(&fullscreen, null);
|
||||
return fullscreen;
|
||||
}
|
||||
|
||||
private void ReleaseUnmanagedResources()
|
||||
{
|
||||
if (this.device.IsEmpty())
|
||||
return;
|
||||
|
||||
this.imguiRenderer.Dispose();
|
||||
this.imguiInput.Dispose();
|
||||
|
||||
ImGui.DestroyContext();
|
||||
|
||||
this.swapChain.Dispose();
|
||||
this.deviceContext.Dispose();
|
||||
this.device.Dispose();
|
||||
this.swapChainPossiblyWrapped.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,655 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Helpers.D3D11;
|
||||
|
||||
/// <summary>
|
||||
/// Captures states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
internal unsafe struct D3D11DeviceContextStateBackup : IDisposable
|
||||
{
|
||||
private InputAssemblerState inputAssemblerState;
|
||||
private RasterizerState rasterizerState;
|
||||
private OutputMergerState outputMergerState;
|
||||
private VertexShaderState vertexShaderState;
|
||||
private HullShaderState hullShaderState;
|
||||
private DomainShaderState domainShaderState;
|
||||
private GeometryShaderState geometryShaderState;
|
||||
private PixelShaderState pixelShaderState;
|
||||
private ComputeShaderState computeShaderState;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="D3D11DeviceContextStateBackup"/> struct,
|
||||
/// by capturing all states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="featureLevel">The feature level.</param>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
public D3D11DeviceContextStateBackup(D3D_FEATURE_LEVEL featureLevel, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
this.inputAssemblerState = InputAssemblerState.From(ctx);
|
||||
this.rasterizerState = RasterizerState.From(ctx);
|
||||
this.outputMergerState = OutputMergerState.From(featureLevel, ctx);
|
||||
this.vertexShaderState = VertexShaderState.From(ctx);
|
||||
this.hullShaderState = HullShaderState.From(ctx);
|
||||
this.domainShaderState = DomainShaderState.From(ctx);
|
||||
this.geometryShaderState = GeometryShaderState.From(ctx);
|
||||
this.pixelShaderState = PixelShaderState.From(ctx);
|
||||
this.computeShaderState = ComputeShaderState.From(featureLevel, ctx);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.inputAssemblerState.Dispose();
|
||||
this.rasterizerState.Dispose();
|
||||
this.outputMergerState.Dispose();
|
||||
this.vertexShaderState.Dispose();
|
||||
this.hullShaderState.Dispose();
|
||||
this.domainShaderState.Dispose();
|
||||
this.geometryShaderState.Dispose();
|
||||
this.pixelShaderState.Dispose();
|
||||
this.computeShaderState.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Input Assembler states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct InputAssemblerState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT;
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11InputLayout> layout;
|
||||
private ComPtr<ID3D11Buffer> indexBuffer;
|
||||
private DXGI_FORMAT indexFormat;
|
||||
private uint indexOffset;
|
||||
private D3D_PRIMITIVE_TOPOLOGY topology;
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed uint strides[BufferCount];
|
||||
private fixed uint offsets[BufferCount];
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="InputAssemblerState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static InputAssemblerState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(InputAssemblerState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
ctx->IAGetInputLayout(state.layout.GetAddressOf());
|
||||
ctx->IAGetPrimitiveTopology(&state.topology);
|
||||
ctx->IAGetIndexBuffer(state.indexBuffer.GetAddressOf(), &state.indexFormat, &state.indexOffset);
|
||||
ctx->IAGetVertexBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers, state.strides, state.offsets);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (InputAssemblerState* pThis = &this)
|
||||
{
|
||||
ctx->IASetInputLayout(pThis->layout);
|
||||
ctx->IASetPrimitiveTopology(pThis->topology);
|
||||
ctx->IASetIndexBuffer(pThis->indexBuffer, pThis->indexFormat, pThis->indexOffset);
|
||||
ctx->IASetVertexBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers, pThis->strides, pThis->offsets);
|
||||
|
||||
pThis->context.Dispose();
|
||||
pThis->layout.Dispose();
|
||||
pThis->indexBuffer.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Rasterizer states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RasterizerState : IDisposable
|
||||
{
|
||||
private const int Count = TerraFX.Interop.DirectX.D3D11.D3D11_VIEWPORT_AND_SCISSORRECT_MAX_INDEX;
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11RasterizerState> state;
|
||||
private fixed byte viewports[24 * Count];
|
||||
private fixed ulong scissorRects[16 * Count];
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="RasterizerState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static RasterizerState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(RasterizerState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
ctx->RSGetState(state.state.GetAddressOf());
|
||||
uint n = Count;
|
||||
ctx->RSGetViewports(&n, (D3D11_VIEWPORT*)state.viewports);
|
||||
n = Count;
|
||||
ctx->RSGetScissorRects(&n, (RECT*)state.scissorRects);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (RasterizerState* pThis = &this)
|
||||
{
|
||||
ctx->RSSetState(pThis->state);
|
||||
ctx->RSSetViewports(Count, (D3D11_VIEWPORT*)pThis->viewports);
|
||||
ctx->RSSetScissorRects(Count, (RECT*)pThis->scissorRects);
|
||||
|
||||
pThis->context.Dispose();
|
||||
pThis->state.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Output Merger states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct OutputMergerState : IDisposable
|
||||
{
|
||||
private const int RtvCount = TerraFX.Interop.DirectX.D3D11.D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT;
|
||||
private const int UavCountMax = TerraFX.Interop.DirectX.D3D11.D3D11_1_UAV_SLOT_COUNT;
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11BlendState> blendState;
|
||||
private fixed float blendFactor[4];
|
||||
private uint sampleMask;
|
||||
private uint stencilRef;
|
||||
private ComPtr<ID3D11DepthStencilState> depthStencilState;
|
||||
private fixed ulong rtvs[RtvCount]; // ID3D11RenderTargetView*[RtvCount]
|
||||
private ComPtr<ID3D11DepthStencilView> dsv;
|
||||
private fixed ulong uavs[UavCountMax]; // ID3D11UnorderedAccessView*[UavCount]
|
||||
private int uavCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="OutputMergerState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="featureLevel">The feature level.</param>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static OutputMergerState From(D3D_FEATURE_LEVEL featureLevel, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(OutputMergerState);
|
||||
state.uavCount = featureLevel >= D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_11_1
|
||||
? TerraFX.Interop.DirectX.D3D11.D3D11_1_UAV_SLOT_COUNT
|
||||
: TerraFX.Interop.DirectX.D3D11.D3D11_PS_CS_UAV_REGISTER_COUNT;
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
ctx->OMGetBlendState(state.blendState.GetAddressOf(), state.blendFactor, &state.sampleMask);
|
||||
ctx->OMGetDepthStencilState(state.depthStencilState.GetAddressOf(), &state.stencilRef);
|
||||
ctx->OMGetRenderTargetsAndUnorderedAccessViews(
|
||||
RtvCount,
|
||||
(ID3D11RenderTargetView**)state.rtvs,
|
||||
state.dsv.GetAddressOf(),
|
||||
0,
|
||||
(uint)state.uavCount,
|
||||
(ID3D11UnorderedAccessView**)state.uavs);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (OutputMergerState* pThis = &this)
|
||||
{
|
||||
ctx->OMSetBlendState(pThis->blendState, pThis->blendFactor, pThis->sampleMask);
|
||||
ctx->OMSetDepthStencilState(pThis->depthStencilState, pThis->stencilRef);
|
||||
var rtvc = (uint)RtvCount;
|
||||
while (rtvc > 0 && pThis->rtvs[rtvc - 1] == 0)
|
||||
rtvc--;
|
||||
|
||||
var uavlb = rtvc;
|
||||
while (uavlb < this.uavCount && pThis->uavs[uavlb] == 0)
|
||||
uavlb++;
|
||||
|
||||
var uavc = (uint)this.uavCount;
|
||||
while (uavc > uavlb && pThis->uavs[uavc - 1] == 0)
|
||||
uavlb--;
|
||||
uavc -= uavlb;
|
||||
|
||||
ctx->OMSetRenderTargetsAndUnorderedAccessViews(
|
||||
rtvc,
|
||||
(ID3D11RenderTargetView**)pThis->rtvs,
|
||||
pThis->dsv,
|
||||
uavc == 0 ? 0 : uavlb,
|
||||
uavc,
|
||||
uavc == 0 ? null : (ID3D11UnorderedAccessView**)pThis->uavs,
|
||||
null);
|
||||
|
||||
this.context.Reset();
|
||||
this.blendState.Reset();
|
||||
this.depthStencilState.Reset();
|
||||
this.dsv.Reset();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11RenderTargetView>>(pThis->rtvs, RtvCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11UnorderedAccessView>>(pThis->uavs, this.uavCount))
|
||||
b.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Vertex Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct VertexShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11VertexShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="VertexShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static VertexShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(VertexShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->VSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->VSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->VSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->VSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (VertexShaderState* pThis = &this)
|
||||
{
|
||||
ctx->VSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->VSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->VSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->VSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Hull Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct HullShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11HullShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="HullShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static HullShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(HullShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->HSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->HSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->HSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->HSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (HullShaderState* pThis = &this)
|
||||
{
|
||||
ctx->HSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->HSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->HSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->HSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Domain Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DomainShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11DomainShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DomainShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static DomainShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(DomainShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->DSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->DSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->DSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->DSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (DomainShaderState* pThis = &this)
|
||||
{
|
||||
ctx->DSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->DSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->DSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->DSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Geometry Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct GeometryShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11GeometryShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="GeometryShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static GeometryShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(GeometryShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->GSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->GSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->GSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->GSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (GeometryShaderState* pThis = &this)
|
||||
{
|
||||
ctx->GSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->GSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->GSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->GSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Pixel Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct PixelShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int ClassInstanceCount = 256; // According to msdn
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11PixelShader> shader;
|
||||
private fixed ulong insts[ClassInstanceCount];
|
||||
private fixed ulong buffers[BufferCount];
|
||||
private fixed ulong samplers[SamplerCount];
|
||||
private fixed ulong resources[ResourceCount];
|
||||
private uint instCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PixelShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static PixelShaderState From(ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(PixelShaderState);
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = ClassInstanceCount;
|
||||
ctx->PSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->PSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->PSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->PSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (PixelShaderState* pThis = &this)
|
||||
{
|
||||
ctx->PSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->PSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->PSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->PSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures Compute Shader states of a <see cref="ID3D11DeviceContext"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ComputeShaderState : IDisposable
|
||||
{
|
||||
private const int BufferCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT;
|
||||
private const int SamplerCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT;
|
||||
private const int ResourceCount = TerraFX.Interop.DirectX.D3D11.D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT;
|
||||
private const int InstanceCount = 256; // According to msdn
|
||||
private const int UavCountMax = TerraFX.Interop.DirectX.D3D11.D3D11_1_UAV_SLOT_COUNT;
|
||||
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11ComputeShader> shader;
|
||||
private fixed ulong insts[InstanceCount]; // ID3D11ClassInstance*[BufferCount]
|
||||
private fixed ulong buffers[BufferCount]; // ID3D11Buffer*[BufferCount]
|
||||
private fixed ulong samplers[SamplerCount]; // ID3D11SamplerState*[SamplerCount]
|
||||
private fixed ulong resources[ResourceCount]; // ID3D11ShaderResourceView*[ResourceCount]
|
||||
private fixed ulong uavs[UavCountMax]; // ID3D11UnorderedAccessView*[UavCountMax]
|
||||
private uint instCount;
|
||||
private int uavCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="ComputeShaderState"/> from <paramref name="ctx"/>.
|
||||
/// </summary>
|
||||
/// <param name="featureLevel">The feature level.</param>
|
||||
/// <param name="ctx">The device context.</param>
|
||||
/// <returns>The captured state.</returns>
|
||||
public static ComputeShaderState From(D3D_FEATURE_LEVEL featureLevel, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
var state = default(ComputeShaderState);
|
||||
state.uavCount = featureLevel >= D3D_FEATURE_LEVEL.D3D_FEATURE_LEVEL_11_1
|
||||
? TerraFX.Interop.DirectX.D3D11.D3D11_1_UAV_SLOT_COUNT
|
||||
: TerraFX.Interop.DirectX.D3D11.D3D11_PS_CS_UAV_REGISTER_COUNT;
|
||||
state.context.Attach(ctx);
|
||||
ctx->AddRef();
|
||||
state.instCount = InstanceCount;
|
||||
ctx->CSGetShader(state.shader.GetAddressOf(), (ID3D11ClassInstance**)state.insts, &state.instCount);
|
||||
ctx->CSGetConstantBuffers(0, BufferCount, (ID3D11Buffer**)state.buffers);
|
||||
ctx->CSGetSamplers(0, SamplerCount, (ID3D11SamplerState**)state.samplers);
|
||||
ctx->CSGetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)state.resources);
|
||||
ctx->CSGetUnorderedAccessViews(0, (uint)state.uavCount, (ID3D11UnorderedAccessView**)state.uavs);
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
if (ctx is null)
|
||||
return;
|
||||
|
||||
fixed (ComputeShaderState* pThis = &this)
|
||||
{
|
||||
ctx->CSSetShader(pThis->shader, (ID3D11ClassInstance**)pThis->insts, pThis->instCount);
|
||||
ctx->CSSetConstantBuffers(0, BufferCount, (ID3D11Buffer**)pThis->buffers);
|
||||
ctx->CSSetSamplers(0, SamplerCount, (ID3D11SamplerState**)pThis->samplers);
|
||||
ctx->CSSetShaderResources(0, ResourceCount, (ID3D11ShaderResourceView**)pThis->resources);
|
||||
ctx->CSSetUnorderedAccessViews(0, (uint)this.uavCount, (ID3D11UnorderedAccessView**)pThis->uavs, null);
|
||||
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11Buffer>>(pThis->buffers, BufferCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11SamplerState>>(pThis->samplers, SamplerCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ShaderResourceView>>(pThis->resources, ResourceCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11ClassInstance>>(pThis->insts, (int)pThis->instCount))
|
||||
b.Dispose();
|
||||
foreach (ref var b in new Span<ComPtr<ID3D11UnorderedAccessView>>(pThis->uavs, this.uavCount))
|
||||
b.Dispose();
|
||||
pThis->context.Dispose();
|
||||
pThis->shader.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Dalamud/Interface/ImGuiBackend/Helpers/D3D11/Extensions.cs
Normal file
26
Dalamud/Interface/ImGuiBackend/Helpers/D3D11/Extensions.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using System.Text;
|
||||
|
||||
using Dalamud.Utility;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Helpers.D3D11;
|
||||
|
||||
/// <summary>Utility extension methods for D3D11 objects.</summary>
|
||||
internal static class Extensions
|
||||
{
|
||||
/// <summary>Sets the name for debugging.</summary>
|
||||
/// <param name="child">D3D11 object.</param>
|
||||
/// <param name="name">Debug name.</param>
|
||||
/// <typeparam name="T">Object type.</typeparam>
|
||||
public static unsafe void SetDebugName<T>(ref this T child, string name)
|
||||
where T : unmanaged, ID3D11DeviceChild.Interface
|
||||
{
|
||||
var len = Encoding.UTF8.GetByteCount(name);
|
||||
var buf = stackalloc byte[len + 1];
|
||||
Encoding.UTF8.GetBytes(name, new(buf, len + 1));
|
||||
buf[len] = 0;
|
||||
fixed (Guid* pId = &DirectX.WKPDID_D3DDebugObjectName)
|
||||
child.SetPrivateData(pId, (uint)(len + 1), buf).ThrowOnError();
|
||||
}
|
||||
}
|
||||
165
Dalamud/Interface/ImGuiBackend/Helpers/ImGuiViewportHelpers.cs
Normal file
165
Dalamud/Interface/ImGuiBackend/Helpers/ImGuiViewportHelpers.cs
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helpers for using ImGui Viewports.
|
||||
/// </summary>
|
||||
internal static class ImGuiViewportHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate to be called when a window should be created.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void CreateWindowDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when a window should be destroyed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void DestroyWindowDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when a window should be resized.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="size">Size of the new window.</param>
|
||||
public delegate void SetWindowSizeDelegate(ImGuiViewportPtr viewport, Vector2 size);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when a window should be rendered.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="v">Custom user-provided argument from <see cref="ImGui.RenderPlatformWindowsDefault()"/>.</param>
|
||||
public delegate void RenderWindowDelegate(ImGuiViewportPtr viewport, nint v);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when buffers for the window should be swapped.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="v">Custom user-provided argument from <see cref="ImGui.RenderPlatformWindowsDefault()"/>.</param>
|
||||
public delegate void SwapBuffersDelegate(ImGuiViewportPtr viewport, nint v);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window should be showed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void ShowWindowDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window should be updated.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void UpdateWindowDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window position is queried.
|
||||
/// </summary>
|
||||
/// <param name="returnStorage">The return value storage.</param>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>Same value with <paramref name="returnStorage"/>.</returns>
|
||||
public unsafe delegate Vector2* GetWindowPosDelegate(Vector2* returnStorage, ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window should be moved.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="pos">The new position.</param>
|
||||
public delegate void SetWindowPosDelegate(ImGuiViewportPtr viewport, Vector2 pos);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window size is queried.
|
||||
/// </summary>
|
||||
/// <param name="returnStorage">The return value storage.</param>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>Same value with <paramref name="returnStorage"/>.</returns>
|
||||
public unsafe delegate Vector2* GetWindowSizeDelegate(Vector2* returnStorage, ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window should be given focus.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void SetWindowFocusDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window is focused.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>Whether the window is focused.</returns>
|
||||
public delegate bool GetWindowFocusDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when whether the window is minimized is queried.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>Whether the window is minimized.</returns>
|
||||
public delegate bool GetWindowMinimizedDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window title should be changed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="title">The new title.</param>
|
||||
public delegate void SetWindowTitleDelegate(ImGuiViewportPtr viewport, string title);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window alpha should be changed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="alpha">The new alpha.</param>
|
||||
public delegate void SetWindowAlphaDelegate(ImGuiViewportPtr viewport, float alpha);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the IME input position should be changed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <param name="pos">The new position.</param>
|
||||
public delegate void SetImeInputPosDelegate(ImGuiViewportPtr viewport, Vector2 pos);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when the window's DPI scale value is queried.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
/// <returns>The DPI scale.</returns>
|
||||
public delegate float GetWindowDpiScaleDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when viewport is changed.
|
||||
/// </summary>
|
||||
/// <param name="viewport">An instance of <see cref="ImGuiViewportPtr"/>.</param>
|
||||
public delegate void ChangedViewportDelegate(ImGuiViewportPtr viewport);
|
||||
|
||||
/// <summary>
|
||||
/// Disables ImGui from disabling alpha for Viewport window backgrounds.
|
||||
/// </summary>
|
||||
public static unsafe void EnableViewportWindowBackgroundAlpha()
|
||||
{
|
||||
// TODO: patch imgui.cpp:6126, which disables background transparency for extra viewport windows
|
||||
var offset = 0x00007FFB6ADA632C - 0x00007FFB6AD60000;
|
||||
offset += Process.GetCurrentProcess().Modules.Cast<ProcessModule>().First(x => x.ModuleName == "cimgui.dll")
|
||||
.BaseAddress;
|
||||
var b = (byte*)offset;
|
||||
uint old;
|
||||
if (!VirtualProtect(b, 1, PAGE.PAGE_EXECUTE_READWRITE, &old))
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())
|
||||
?? throw new InvalidOperationException($"{nameof(VirtualProtect)} failed.");
|
||||
}
|
||||
|
||||
*b = 0xEB;
|
||||
if (!VirtualProtect(b, 1, old, &old))
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error())
|
||||
?? throw new InvalidOperationException($"{nameof(VirtualProtect)} failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
134
Dalamud/Interface/ImGuiBackend/Helpers/ReShadePeeler.cs
Normal file
134
Dalamud/Interface/ImGuiBackend/Helpers/ReShadePeeler.cs
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Peels ReShade off stuff.
|
||||
/// </summary>
|
||||
[SuppressMessage(
|
||||
"StyleCop.CSharp.LayoutRules",
|
||||
"SA1519:Braces should not be omitted from multi-line child statement",
|
||||
Justification = "Multiple fixed blocks")]
|
||||
internal static unsafe class ReShadePeeler
|
||||
{
|
||||
/// <summary>
|
||||
/// Peels <see cref="IDXGISwapChain"/> if it is wrapped by ReShade.
|
||||
/// </summary>
|
||||
/// <param name="comptr">[inout] The COM pointer to an instance of <see cref="IDXGISwapChain"/>.</param>
|
||||
/// <typeparam name="T">A COM type that is or extends <see cref="IDXGISwapChain"/>.</typeparam>
|
||||
/// <returns><c>true</c> if peeled.</returns>
|
||||
public static bool PeelSwapChain<T>(ComPtr<T>* comptr)
|
||||
where T : unmanaged, IDXGISwapChain.Interface =>
|
||||
PeelIUnknown(comptr, 0x10);
|
||||
|
||||
/// <summary>
|
||||
/// Peels <see cref="ID3D12Device"/> if it is wrapped by ReShade.
|
||||
/// </summary>
|
||||
/// <param name="comptr">[inout] The COM pointer to an instance of <see cref="ID3D12Device"/>.</param>
|
||||
/// <typeparam name="T">A COM type that is or extends <see cref="ID3D12Device"/>.</typeparam>
|
||||
/// <returns><c>true</c> if peeled.</returns>
|
||||
public static bool PeelD3D12Device<T>(ComPtr<T>* comptr)
|
||||
where T : unmanaged, ID3D12Device.Interface =>
|
||||
PeelIUnknown(comptr, 0x10);
|
||||
|
||||
/// <summary>
|
||||
/// Peels <see cref="ID3D12CommandQueue"/> if it is wrapped by ReShade.
|
||||
/// </summary>
|
||||
/// <param name="comptr">[inout] The COM pointer to an instance of <see cref="ID3D12CommandQueue"/>.</param>
|
||||
/// <typeparam name="T">A COM type that is or extends <see cref="ID3D12CommandQueue"/>.</typeparam>
|
||||
/// <returns><c>true</c> if peeled.</returns>
|
||||
public static bool PeelD3D12CommandQueue<T>(ComPtr<T>* comptr)
|
||||
where T : unmanaged, ID3D12CommandQueue.Interface =>
|
||||
PeelIUnknown(comptr, 0x10);
|
||||
|
||||
private static bool PeelIUnknown<T>(ComPtr<T>* comptr, nint offset)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
if (comptr->Get() == null || !IsReShadedComObject(comptr->Get()))
|
||||
return false;
|
||||
|
||||
var punk = new ComPtr<IUnknown>(*(IUnknown**)((nint)comptr->Get() + offset));
|
||||
using var comptr2 = default(ComPtr<T>);
|
||||
if (punk.As(&comptr2).FAILED)
|
||||
return false;
|
||||
comptr2.Swap(comptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool BelongsInReShadeDll(nint ptr)
|
||||
{
|
||||
foreach (ProcessModule processModule in Process.GetCurrentProcess().Modules)
|
||||
{
|
||||
if (ptr < processModule.BaseAddress)
|
||||
continue;
|
||||
|
||||
var dosh = (IMAGE_DOS_HEADER*)processModule.BaseAddress;
|
||||
var nth = (IMAGE_NT_HEADERS64*)(processModule.BaseAddress + dosh->e_lfanew);
|
||||
if (ptr >= processModule.BaseAddress + nth->OptionalHeader.SizeOfImage)
|
||||
continue;
|
||||
|
||||
fixed (byte* pfn0 = "CreateDXGIFactory"u8)
|
||||
fixed (byte* pfn1 = "D2D1CreateDevice"u8)
|
||||
fixed (byte* pfn2 = "D3D10CreateDevice"u8)
|
||||
fixed (byte* pfn3 = "D3D11CreateDevice"u8)
|
||||
fixed (byte* pfn4 = "D3D12CreateDevice"u8)
|
||||
fixed (byte* pfn5 = "glBegin"u8)
|
||||
fixed (byte* pfn6 = "vkCreateDevice"u8)
|
||||
{
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn0) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn1) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn2) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn3) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn4) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn5) == 0)
|
||||
continue;
|
||||
if (GetProcAddress((HMODULE)dosh, (sbyte*)pfn6) == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileInfo = FileVersionInfo.GetVersionInfo(processModule.FileName);
|
||||
|
||||
if (fileInfo.FileDescription == null)
|
||||
continue;
|
||||
|
||||
if (!fileInfo.FileDescription.Contains("GShade") && !fileInfo.FileDescription.Contains("ReShade"))
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsReShadedComObject<T>(T* obj)
|
||||
where T : unmanaged, IUnknown.Interface
|
||||
{
|
||||
try
|
||||
{
|
||||
var vtbl = (nint**)Marshal.ReadIntPtr((nint)obj);
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
if (!BelongsInReShadeDll(Marshal.ReadIntPtr((nint)(vtbl + i))))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Dalamud/Interface/ImGuiBackend/IImGuiBackend.cs
Normal file
72
Dalamud/Interface/ImGuiBackend/IImGuiBackend.cs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
using Dalamud.Interface.ImGuiBackend.InputHandler;
|
||||
using Dalamud.Interface.ImGuiBackend.Renderers;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend;
|
||||
|
||||
/// <summary>Backend for ImGui.</summary>
|
||||
internal interface IImGuiBackend : IDisposable
|
||||
{
|
||||
/// <summary>Delegate to be called when ImGui should be used to layout now.</summary>
|
||||
public delegate void BuildUiDelegate();
|
||||
|
||||
/// <summary>Delegate to be called on new input frame.</summary>
|
||||
public delegate void NewInputFrameDelegate();
|
||||
|
||||
/// <summary>Delegaet to be called on new render frame.</summary>
|
||||
public delegate void NewRenderFrameDelegate();
|
||||
|
||||
/// <summary>User methods invoked every ImGui frame to construct custom UIs.</summary>
|
||||
event BuildUiDelegate? BuildUi;
|
||||
|
||||
/// <summary>User methods invoked every ImGui frame on handling inputs.</summary>
|
||||
event NewInputFrameDelegate? NewInputFrame;
|
||||
|
||||
/// <summary>User methods invoked every ImGui frame on handling renders.</summary>
|
||||
event NewRenderFrameDelegate? NewRenderFrame;
|
||||
|
||||
/// <summary>Gets or sets a value indicating whether the cursor should be overridden with the ImGui cursor.
|
||||
/// </summary>
|
||||
bool UpdateCursor { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the path of ImGui configuration .ini file.</summary>
|
||||
string? IniPath { get; set; }
|
||||
|
||||
/// <summary>Gets the device handle.</summary>
|
||||
nint DeviceHandle { get; }
|
||||
|
||||
/// <summary>Gets the input handler.</summary>
|
||||
IImGuiInputHandler InputHandler { get; }
|
||||
|
||||
/// <summary>Gets the renderer.</summary>
|
||||
IImGuiRenderer Renderer { get; }
|
||||
|
||||
/// <summary>Performs a render cycle.</summary>
|
||||
void Render();
|
||||
|
||||
/// <summary>Handles stuff before resizing happens.</summary>
|
||||
void OnPreResize();
|
||||
|
||||
/// <summary>Handles stuff after resizing happens.</summary>
|
||||
/// <param name="newWidth">The new width.</param>
|
||||
/// <param name="newHeight">The new height.</param>
|
||||
void OnPostResize(int newWidth, int newHeight);
|
||||
|
||||
/// <summary>Invalidates fonts immediately.</summary>
|
||||
/// <remarks>Call this while handling <see cref="NewRenderFrame"/>.</remarks>
|
||||
void InvalidateFonts();
|
||||
|
||||
/// <summary>Determines if <paramref name="cursorHandle"/> is owned by this.</summary>
|
||||
/// <param name="cursorHandle">The cursor.</param>
|
||||
/// <returns>Whether it is the case.</returns>
|
||||
bool IsImGuiCursor(nint cursorHandle);
|
||||
|
||||
/// <summary>Determines if this instance of <see cref="IImGuiBackend"/> is rendering to
|
||||
/// <paramref name="targetHandle"/>. </summary>
|
||||
/// <param name="targetHandle">The present target handle.</param>
|
||||
/// <returns>Whether it is the case.</returns>
|
||||
bool IsAttachedToPresentationTarget(nint targetHandle);
|
||||
|
||||
/// <summary>Determines if the main viewport is full screen. </summary>
|
||||
/// <returns>Whether it is the case.</returns>
|
||||
bool IsMainViewportFullScreen();
|
||||
}
|
||||
15
Dalamud/Interface/ImGuiBackend/IWin32Backend.cs
Normal file
15
Dalamud/Interface/ImGuiBackend/IWin32Backend.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend;
|
||||
|
||||
/// <summary><see cref="IImGuiBackend"/> with Win32 support.</summary>
|
||||
internal interface IWin32Backend : IImGuiBackend
|
||||
{
|
||||
/// <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 nint? ProcessWndProcW(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam);
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
namespace Dalamud.Interface.ImGuiBackend.InputHandler;
|
||||
|
||||
/// <summary>A simple shared public interface that all ImGui input implementations follows.</summary>
|
||||
internal interface IImGuiInputHandler : IDisposable
|
||||
{
|
||||
/// <summary>Gets or sets a value indicating whether the cursor should be overridden with the ImGui cursor.
|
||||
/// </summary>
|
||||
public bool UpdateCursor { get; set; }
|
||||
|
||||
/// <summary>Gets or sets the path of ImGui configuration .ini file.</summary>
|
||||
string? IniPath { get; set; }
|
||||
|
||||
/// <summary>Determines if <paramref name="cursorHandle"/> is owned by this.</summary>
|
||||
/// <param name="cursorHandle">The cursor.</param>
|
||||
/// <returns>Whether it is the case.</returns>
|
||||
public bool IsImGuiCursor(nint cursorHandle);
|
||||
|
||||
/// <summary>Marks the beginning of a new frame.</summary>
|
||||
/// <param name="width">The width of the new frame.</param>
|
||||
/// <param name="height">The height of the new frame.</param>
|
||||
void NewFrame(int width, int height);
|
||||
}
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.InputHandler;
|
||||
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="IImGuiInputHandler"/>, using Win32 APIs.
|
||||
/// </summary>
|
||||
internal sealed partial class Win32InputHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a <see cref="VK"/> to <see cref="ImGuiKey"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">The virtual key.</param>
|
||||
/// <returns>The corresponding <see cref="ImGuiKey"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ImGuiKey VirtualKeyToImGuiKey(int key) => key switch
|
||||
{
|
||||
VK.VK_TAB => ImGuiKey.Tab,
|
||||
VK.VK_LEFT => ImGuiKey.LeftArrow,
|
||||
VK.VK_RIGHT => ImGuiKey.RightArrow,
|
||||
VK.VK_UP => ImGuiKey.UpArrow,
|
||||
VK.VK_DOWN => ImGuiKey.DownArrow,
|
||||
VK.VK_PRIOR => ImGuiKey.PageUp,
|
||||
VK.VK_NEXT => ImGuiKey.PageDown,
|
||||
VK.VK_HOME => ImGuiKey.Home,
|
||||
VK.VK_END => ImGuiKey.End,
|
||||
VK.VK_INSERT => ImGuiKey.Insert,
|
||||
VK.VK_DELETE => ImGuiKey.Delete,
|
||||
VK.VK_BACK => ImGuiKey.Backspace,
|
||||
VK.VK_SPACE => ImGuiKey.Space,
|
||||
VK.VK_RETURN => ImGuiKey.Enter,
|
||||
VK.VK_ESCAPE => ImGuiKey.Escape,
|
||||
VK.VK_OEM_7 => ImGuiKey.Apostrophe,
|
||||
VK.VK_OEM_COMMA => ImGuiKey.Comma,
|
||||
VK.VK_OEM_MINUS => ImGuiKey.Minus,
|
||||
VK.VK_OEM_PERIOD => ImGuiKey.Period,
|
||||
VK.VK_OEM_2 => ImGuiKey.Slash,
|
||||
VK.VK_OEM_1 => ImGuiKey.Semicolon,
|
||||
VK.VK_OEM_PLUS => ImGuiKey.Equal,
|
||||
VK.VK_OEM_4 => ImGuiKey.LeftBracket,
|
||||
VK.VK_OEM_5 => ImGuiKey.Backslash,
|
||||
VK.VK_OEM_6 => ImGuiKey.RightBracket,
|
||||
VK.VK_OEM_3 => ImGuiKey.GraveAccent,
|
||||
VK.VK_CAPITAL => ImGuiKey.CapsLock,
|
||||
VK.VK_SCROLL => ImGuiKey.ScrollLock,
|
||||
VK.VK_NUMLOCK => ImGuiKey.NumLock,
|
||||
VK.VK_SNAPSHOT => ImGuiKey.PrintScreen,
|
||||
VK.VK_PAUSE => ImGuiKey.Pause,
|
||||
VK.VK_NUMPAD0 => ImGuiKey.Keypad0,
|
||||
VK.VK_NUMPAD1 => ImGuiKey.Keypad1,
|
||||
VK.VK_NUMPAD2 => ImGuiKey.Keypad2,
|
||||
VK.VK_NUMPAD3 => ImGuiKey.Keypad3,
|
||||
VK.VK_NUMPAD4 => ImGuiKey.Keypad4,
|
||||
VK.VK_NUMPAD5 => ImGuiKey.Keypad5,
|
||||
VK.VK_NUMPAD6 => ImGuiKey.Keypad6,
|
||||
VK.VK_NUMPAD7 => ImGuiKey.Keypad7,
|
||||
VK.VK_NUMPAD8 => ImGuiKey.Keypad8,
|
||||
VK.VK_NUMPAD9 => ImGuiKey.Keypad9,
|
||||
VK.VK_DECIMAL => ImGuiKey.KeypadDecimal,
|
||||
VK.VK_DIVIDE => ImGuiKey.KeypadDivide,
|
||||
VK.VK_MULTIPLY => ImGuiKey.KeypadMultiply,
|
||||
VK.VK_SUBTRACT => ImGuiKey.KeypadSubtract,
|
||||
VK.VK_ADD => ImGuiKey.KeypadAdd,
|
||||
VK.VK_RETURN + 256 => ImGuiKey.KeypadEnter,
|
||||
VK.VK_LSHIFT => ImGuiKey.LeftShift,
|
||||
VK.VK_LCONTROL => ImGuiKey.LeftCtrl,
|
||||
VK.VK_LMENU => ImGuiKey.LeftAlt,
|
||||
VK.VK_LWIN => ImGuiKey.LeftSuper,
|
||||
VK.VK_RSHIFT => ImGuiKey.RightShift,
|
||||
VK.VK_RCONTROL => ImGuiKey.RightCtrl,
|
||||
VK.VK_RMENU => ImGuiKey.RightAlt,
|
||||
VK.VK_RWIN => ImGuiKey.RightSuper,
|
||||
VK.VK_APPS => ImGuiKey.Menu,
|
||||
'0' => ImGuiKey._0,
|
||||
'1' => ImGuiKey._1,
|
||||
'2' => ImGuiKey._2,
|
||||
'3' => ImGuiKey._3,
|
||||
'4' => ImGuiKey._4,
|
||||
'5' => ImGuiKey._5,
|
||||
'6' => ImGuiKey._6,
|
||||
'7' => ImGuiKey._7,
|
||||
'8' => ImGuiKey._8,
|
||||
'9' => ImGuiKey._9,
|
||||
'A' => ImGuiKey.A,
|
||||
'B' => ImGuiKey.B,
|
||||
'C' => ImGuiKey.C,
|
||||
'D' => ImGuiKey.D,
|
||||
'E' => ImGuiKey.E,
|
||||
'F' => ImGuiKey.F,
|
||||
'G' => ImGuiKey.G,
|
||||
'H' => ImGuiKey.H,
|
||||
'I' => ImGuiKey.I,
|
||||
'J' => ImGuiKey.J,
|
||||
'K' => ImGuiKey.K,
|
||||
'L' => ImGuiKey.L,
|
||||
'M' => ImGuiKey.M,
|
||||
'N' => ImGuiKey.N,
|
||||
'O' => ImGuiKey.O,
|
||||
'P' => ImGuiKey.P,
|
||||
'Q' => ImGuiKey.Q,
|
||||
'R' => ImGuiKey.R,
|
||||
'S' => ImGuiKey.S,
|
||||
'T' => ImGuiKey.T,
|
||||
'U' => ImGuiKey.U,
|
||||
'V' => ImGuiKey.V,
|
||||
'W' => ImGuiKey.W,
|
||||
'X' => ImGuiKey.X,
|
||||
'Y' => ImGuiKey.Y,
|
||||
'Z' => ImGuiKey.Z,
|
||||
VK.VK_F1 => ImGuiKey.F1,
|
||||
VK.VK_F2 => ImGuiKey.F2,
|
||||
VK.VK_F3 => ImGuiKey.F3,
|
||||
VK.VK_F4 => ImGuiKey.F4,
|
||||
VK.VK_F5 => ImGuiKey.F5,
|
||||
VK.VK_F6 => ImGuiKey.F6,
|
||||
VK.VK_F7 => ImGuiKey.F7,
|
||||
VK.VK_F8 => ImGuiKey.F8,
|
||||
VK.VK_F9 => ImGuiKey.F9,
|
||||
VK.VK_F10 => ImGuiKey.F10,
|
||||
VK.VK_F11 => ImGuiKey.F11,
|
||||
VK.VK_F12 => ImGuiKey.F12,
|
||||
_ => ImGuiKey.None,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Maps a <see cref="ImGuiKey"/> to <see cref="VK"/>.
|
||||
/// </summary>
|
||||
/// <param name="key">The ImGui key.</param>
|
||||
/// <returns>The corresponding <see cref="VK"/>.</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int ImGuiKeyToVirtualKey(ImGuiKey key) => key switch
|
||||
{
|
||||
ImGuiKey.Tab => VK.VK_TAB,
|
||||
ImGuiKey.LeftArrow => VK.VK_LEFT,
|
||||
ImGuiKey.RightArrow => VK.VK_RIGHT,
|
||||
ImGuiKey.UpArrow => VK.VK_UP,
|
||||
ImGuiKey.DownArrow => VK.VK_DOWN,
|
||||
ImGuiKey.PageUp => VK.VK_PRIOR,
|
||||
ImGuiKey.PageDown => VK.VK_NEXT,
|
||||
ImGuiKey.Home => VK.VK_HOME,
|
||||
ImGuiKey.End => VK.VK_END,
|
||||
ImGuiKey.Insert => VK.VK_INSERT,
|
||||
ImGuiKey.Delete => VK.VK_DELETE,
|
||||
ImGuiKey.Backspace => VK.VK_BACK,
|
||||
ImGuiKey.Space => VK.VK_SPACE,
|
||||
ImGuiKey.Enter => VK.VK_RETURN,
|
||||
ImGuiKey.Escape => VK.VK_ESCAPE,
|
||||
ImGuiKey.Apostrophe => VK.VK_OEM_7,
|
||||
ImGuiKey.Comma => VK.VK_OEM_COMMA,
|
||||
ImGuiKey.Minus => VK.VK_OEM_MINUS,
|
||||
ImGuiKey.Period => VK.VK_OEM_PERIOD,
|
||||
ImGuiKey.Slash => VK.VK_OEM_2,
|
||||
ImGuiKey.Semicolon => VK.VK_OEM_1,
|
||||
ImGuiKey.Equal => VK.VK_OEM_PLUS,
|
||||
ImGuiKey.LeftBracket => VK.VK_OEM_4,
|
||||
ImGuiKey.Backslash => VK.VK_OEM_5,
|
||||
ImGuiKey.RightBracket => VK.VK_OEM_6,
|
||||
ImGuiKey.GraveAccent => VK.VK_OEM_3,
|
||||
ImGuiKey.CapsLock => VK.VK_CAPITAL,
|
||||
ImGuiKey.ScrollLock => VK.VK_SCROLL,
|
||||
ImGuiKey.NumLock => VK.VK_NUMLOCK,
|
||||
ImGuiKey.PrintScreen => VK.VK_SNAPSHOT,
|
||||
ImGuiKey.Pause => VK.VK_PAUSE,
|
||||
ImGuiKey.Keypad0 => VK.VK_NUMPAD0,
|
||||
ImGuiKey.Keypad1 => VK.VK_NUMPAD1,
|
||||
ImGuiKey.Keypad2 => VK.VK_NUMPAD2,
|
||||
ImGuiKey.Keypad3 => VK.VK_NUMPAD3,
|
||||
ImGuiKey.Keypad4 => VK.VK_NUMPAD4,
|
||||
ImGuiKey.Keypad5 => VK.VK_NUMPAD5,
|
||||
ImGuiKey.Keypad6 => VK.VK_NUMPAD6,
|
||||
ImGuiKey.Keypad7 => VK.VK_NUMPAD7,
|
||||
ImGuiKey.Keypad8 => VK.VK_NUMPAD8,
|
||||
ImGuiKey.Keypad9 => VK.VK_NUMPAD9,
|
||||
ImGuiKey.KeypadDecimal => VK.VK_DECIMAL,
|
||||
ImGuiKey.KeypadDivide => VK.VK_DIVIDE,
|
||||
ImGuiKey.KeypadMultiply => VK.VK_MULTIPLY,
|
||||
ImGuiKey.KeypadSubtract => VK.VK_SUBTRACT,
|
||||
ImGuiKey.KeypadAdd => VK.VK_ADD,
|
||||
ImGuiKey.KeypadEnter => VK.VK_RETURN + 256,
|
||||
ImGuiKey.LeftShift => VK.VK_LSHIFT,
|
||||
ImGuiKey.LeftCtrl => VK.VK_LCONTROL,
|
||||
ImGuiKey.LeftAlt => VK.VK_LMENU,
|
||||
ImGuiKey.LeftSuper => VK.VK_LWIN,
|
||||
ImGuiKey.RightShift => VK.VK_RSHIFT,
|
||||
ImGuiKey.RightCtrl => VK.VK_RCONTROL,
|
||||
ImGuiKey.RightAlt => VK.VK_RMENU,
|
||||
ImGuiKey.RightSuper => VK.VK_RWIN,
|
||||
ImGuiKey.Menu => VK.VK_APPS,
|
||||
ImGuiKey._0 => '0',
|
||||
ImGuiKey._1 => '1',
|
||||
ImGuiKey._2 => '2',
|
||||
ImGuiKey._3 => '3',
|
||||
ImGuiKey._4 => '4',
|
||||
ImGuiKey._5 => '5',
|
||||
ImGuiKey._6 => '6',
|
||||
ImGuiKey._7 => '7',
|
||||
ImGuiKey._8 => '8',
|
||||
ImGuiKey._9 => '9',
|
||||
ImGuiKey.A => 'A',
|
||||
ImGuiKey.B => 'B',
|
||||
ImGuiKey.C => 'C',
|
||||
ImGuiKey.D => 'D',
|
||||
ImGuiKey.E => 'E',
|
||||
ImGuiKey.F => 'F',
|
||||
ImGuiKey.G => 'G',
|
||||
ImGuiKey.H => 'H',
|
||||
ImGuiKey.I => 'I',
|
||||
ImGuiKey.J => 'J',
|
||||
ImGuiKey.K => 'K',
|
||||
ImGuiKey.L => 'L',
|
||||
ImGuiKey.M => 'M',
|
||||
ImGuiKey.N => 'N',
|
||||
ImGuiKey.O => 'O',
|
||||
ImGuiKey.P => 'P',
|
||||
ImGuiKey.Q => 'Q',
|
||||
ImGuiKey.R => 'R',
|
||||
ImGuiKey.S => 'S',
|
||||
ImGuiKey.T => 'T',
|
||||
ImGuiKey.U => 'U',
|
||||
ImGuiKey.V => 'V',
|
||||
ImGuiKey.W => 'W',
|
||||
ImGuiKey.X => 'X',
|
||||
ImGuiKey.Y => 'Y',
|
||||
ImGuiKey.Z => 'Z',
|
||||
ImGuiKey.F1 => VK.VK_F1,
|
||||
ImGuiKey.F2 => VK.VK_F2,
|
||||
ImGuiKey.F3 => VK.VK_F3,
|
||||
ImGuiKey.F4 => VK.VK_F4,
|
||||
ImGuiKey.F5 => VK.VK_F5,
|
||||
ImGuiKey.F6 => VK.VK_F6,
|
||||
ImGuiKey.F7 => VK.VK_F7,
|
||||
ImGuiKey.F8 => VK.VK_F8,
|
||||
ImGuiKey.F9 => VK.VK_F9,
|
||||
ImGuiKey.F10 => VK.VK_F10,
|
||||
ImGuiKey.F11 => VK.VK_F11,
|
||||
ImGuiKey.F12 => VK.VK_F12,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsGamepadKey(ImGuiKey key) => (int)key is >= 617 and <= 640;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsModKey(ImGuiKey key) =>
|
||||
key is ImGuiKey.LeftShift
|
||||
or ImGuiKey.RightShift
|
||||
or ImGuiKey.ModShift
|
||||
or ImGuiKey.LeftCtrl
|
||||
or ImGuiKey.ModCtrl
|
||||
or ImGuiKey.LeftAlt
|
||||
or ImGuiKey.RightAlt
|
||||
or ImGuiKey.ModAlt;
|
||||
|
||||
private static void AddKeyEvent(ImGuiKey key, bool down, int nativeKeycode, int nativeScancode = -1)
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
io.AddKeyEvent(key, down);
|
||||
io.SetKeyEventNativeData(key, nativeKeycode, nativeScancode);
|
||||
}
|
||||
|
||||
private static void UpdateKeyModifiers()
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
io.AddKeyEvent(ImGuiKey.ModCtrl, IsVkDown(VK.VK_CONTROL));
|
||||
io.AddKeyEvent(ImGuiKey.ModShift, IsVkDown(VK.VK_SHIFT));
|
||||
io.AddKeyEvent(ImGuiKey.ModAlt, IsVkDown(VK.VK_MENU));
|
||||
io.AddKeyEvent(ImGuiKey.ModSuper, IsVkDown(VK.VK_APPS));
|
||||
}
|
||||
|
||||
private static void UpAllKeys()
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
for (var i = (int)ImGuiKey.NamedKey_BEGIN; i < (int)ImGuiKey.NamedKey_END; i++)
|
||||
io.AddKeyEvent((ImGuiKey)i, false);
|
||||
}
|
||||
|
||||
private static void UpAllMouseButton()
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
for (var i = 0; i < io.MouseDown.Count; i++)
|
||||
io.MouseDown[i] = false;
|
||||
}
|
||||
|
||||
private static bool IsVkDown(int key) => (TerraFX.Interop.Windows.Windows.GetKeyState(key) & 0x8000) != 0;
|
||||
|
||||
private static int GetButton(uint msg, WPARAM wParam) => msg switch
|
||||
{
|
||||
WM.WM_LBUTTONUP or WM.WM_LBUTTONDOWN or WM.WM_LBUTTONDBLCLK => 0,
|
||||
WM.WM_RBUTTONUP or WM.WM_RBUTTONDOWN or WM.WM_RBUTTONDBLCLK => 1,
|
||||
WM.WM_MBUTTONUP or WM.WM_MBUTTONDOWN or WM.WM_MBUTTONDBLCLK => 2,
|
||||
WM.WM_XBUTTONUP or WM.WM_XBUTTONDOWN or WM.WM_XBUTTONDBLCLK =>
|
||||
TerraFX.Interop.Windows.Windows.GET_XBUTTON_WPARAM(wParam) == TerraFX.Interop.Windows.Windows.XBUTTON1 ? 3 : 4,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
private static void ViewportFlagsToWin32Styles(ImGuiViewportFlags flags, out int style, out int exStyle)
|
||||
{
|
||||
style = (int)(flags.HasFlag(ImGuiViewportFlags.NoDecoration) ? WS.WS_POPUP : WS.WS_OVERLAPPEDWINDOW);
|
||||
exStyle =
|
||||
(int)(flags.HasFlag(ImGuiViewportFlags.NoTaskBarIcon) ? WS.WS_EX_TOOLWINDOW : (uint)WS.WS_EX_APPWINDOW);
|
||||
exStyle |= WS.WS_EX_NOREDIRECTIONBITMAP;
|
||||
if (flags.HasFlag(ImGuiViewportFlags.TopMost))
|
||||
exStyle |= WS.WS_EX_TOPMOST;
|
||||
}
|
||||
}
|
||||
1016
Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs
Normal file
1016
Dalamud/Interface/ImGuiBackend/InputHandler/Win32InputHandler.cs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,374 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Interface.ImGuiBackend.Helpers;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Renderers;
|
||||
|
||||
/// <summary>
|
||||
/// Deals with rendering ImGui using DirectX 11.
|
||||
/// See https://github.com/ocornut/imgui/blob/master/examples/imgui_impl_dx11.cpp for the original implementation.
|
||||
/// </summary>
|
||||
[SuppressMessage(
|
||||
"StyleCop.CSharp.LayoutRules",
|
||||
"SA1519:Braces should not be omitted from multi-line child statement",
|
||||
Justification = "Multiple fixed/using scopes")]
|
||||
internal unsafe partial class Dx11Renderer
|
||||
{
|
||||
private class ViewportHandler : IDisposable
|
||||
{
|
||||
private readonly Dx11Renderer renderer;
|
||||
|
||||
[SuppressMessage("ReSharper", "NotAccessedField.Local", Justification = "Keeping reference alive")]
|
||||
private readonly ImGuiViewportHelpers.CreateWindowDelegate cwd;
|
||||
|
||||
public ViewportHandler(Dx11Renderer renderer)
|
||||
{
|
||||
this.renderer = renderer;
|
||||
|
||||
var pio = ImGui.GetPlatformIO();
|
||||
pio.Renderer_CreateWindow = Marshal.GetFunctionPointerForDelegate(this.cwd = this.OnCreateWindow);
|
||||
pio.Renderer_DestroyWindow = (nint)(delegate* unmanaged<ImGuiViewportPtr, void>)&OnDestroyWindow;
|
||||
pio.Renderer_SetWindowSize = (nint)(delegate* unmanaged<ImGuiViewportPtr, Vector2, void>)&OnSetWindowSize;
|
||||
pio.Renderer_RenderWindow = (nint)(delegate* unmanaged<ImGuiViewportPtr, nint, void>)&OnRenderWindow;
|
||||
pio.Renderer_SwapBuffers = (nint)(delegate* unmanaged<ImGuiViewportPtr, nint, void>)&OnSwapBuffers;
|
||||
}
|
||||
|
||||
~ViewportHandler() => ReleaseUnmanagedResources();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ReleaseUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private static void ReleaseUnmanagedResources()
|
||||
{
|
||||
var pio = ImGui.GetPlatformIO();
|
||||
pio.Renderer_CreateWindow = nint.Zero;
|
||||
pio.Renderer_DestroyWindow = nint.Zero;
|
||||
pio.Renderer_SetWindowSize = nint.Zero;
|
||||
pio.Renderer_RenderWindow = nint.Zero;
|
||||
pio.Renderer_SwapBuffers = nint.Zero;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static void OnDestroyWindow(ImGuiViewportPtr viewport)
|
||||
{
|
||||
if (viewport.RendererUserData == nint.Zero)
|
||||
return;
|
||||
ViewportData.Attach(viewport.RendererUserData).Dispose();
|
||||
viewport.RendererUserData = nint.Zero;
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static void OnSetWindowSize(ImGuiViewportPtr viewport, Vector2 size) =>
|
||||
ViewportData.Attach(viewport.RendererUserData).ResizeBuffers((int)size.X, (int)size.Y, true);
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static void OnRenderWindow(ImGuiViewportPtr viewport, nint v) =>
|
||||
ViewportData.Attach(viewport.RendererUserData).Draw(viewport.DrawData, true);
|
||||
|
||||
[UnmanagedCallersOnly]
|
||||
private static void OnSwapBuffers(ImGuiViewportPtr viewport, nint v) =>
|
||||
ViewportData.Attach(viewport.RendererUserData).PresentIfSwapChainAvailable();
|
||||
|
||||
private void OnCreateWindow(ImGuiViewportPtr viewport)
|
||||
{
|
||||
// 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.
|
||||
var hWnd = viewport.PlatformHandleRaw;
|
||||
if (hWnd == 0)
|
||||
hWnd = viewport.PlatformHandle;
|
||||
try
|
||||
{
|
||||
viewport.RendererUserData = ViewportData.CreateDComposition(this.renderer, (HWND)hWnd).Handle;
|
||||
}
|
||||
catch
|
||||
{
|
||||
viewport.RendererUserData = ViewportData.Create(this.renderer, (HWND)hWnd).Handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ViewportData : IDisposable
|
||||
{
|
||||
private readonly Dx11Renderer parent;
|
||||
|
||||
private GCHandle selfGcHandle;
|
||||
private ComPtr<IDXGISwapChain> swapChain;
|
||||
private ComPtr<ID3D11Texture2D> renderTarget;
|
||||
private ComPtr<ID3D11RenderTargetView> renderTargetView;
|
||||
private ComPtr<IDCompositionVisual> dcompVisual;
|
||||
private ComPtr<IDCompositionTarget> dcompTarget;
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public ViewportData(
|
||||
Dx11Renderer parent,
|
||||
IDXGISwapChain* swapChain,
|
||||
int width,
|
||||
int height,
|
||||
IDCompositionVisual* dcompVisual,
|
||||
IDCompositionTarget* dcompTarget)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.swapChain = new(swapChain);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
if (dcompVisual is not null)
|
||||
this.dcompVisual = new(dcompVisual);
|
||||
if (dcompTarget is not null)
|
||||
this.dcompTarget = new(dcompTarget);
|
||||
this.selfGcHandle = GCHandle.Alloc(this);
|
||||
}
|
||||
|
||||
public IDXGISwapChain* SwapChain => this.swapChain;
|
||||
|
||||
public nint Handle => GCHandle.ToIntPtr(this.selfGcHandle);
|
||||
|
||||
private DXGI_FORMAT RtvFormat => this.parent.rtvFormat;
|
||||
|
||||
public static ViewportData Attach(nint handle) =>
|
||||
(ViewportData)GCHandle.FromIntPtr(handle).Target ?? throw new InvalidOperationException();
|
||||
|
||||
public static ViewportData Create(
|
||||
Dx11Renderer renderer,
|
||||
IDXGISwapChain* swapChain,
|
||||
IDCompositionVisual* dcompVisual,
|
||||
IDCompositionTarget* dcompTarget)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
swapChain->GetDesc(&desc).ThrowOnError();
|
||||
return new(
|
||||
renderer,
|
||||
swapChain,
|
||||
(int)desc.BufferDesc.Width,
|
||||
(int)desc.BufferDesc.Height,
|
||||
dcompVisual,
|
||||
dcompTarget);
|
||||
}
|
||||
|
||||
public static ViewportData CreateDComposition(Dx11Renderer renderer, HWND hWnd)
|
||||
{
|
||||
if (renderer.dcompDevice.IsEmpty())
|
||||
throw new NotSupportedException();
|
||||
|
||||
var mvsd = default(DXGI_SWAP_CHAIN_DESC);
|
||||
renderer.mainViewport.SwapChain->GetDesc(&mvsd).ThrowOnError();
|
||||
|
||||
using var dxgiFactory = default(ComPtr<IDXGIFactory4>);
|
||||
fixed (Guid* piidFactory = &IID.IID_IDXGIFactory4)
|
||||
{
|
||||
#if DEBUG
|
||||
DirectX.CreateDXGIFactory2(
|
||||
DXGI.DXGI_CREATE_FACTORY_DEBUG,
|
||||
piidFactory,
|
||||
(void**)dxgiFactory.GetAddressOf()).ThrowOnError();
|
||||
#else
|
||||
DirectX.CreateDXGIFactory1(piidFactory, (void**)dxgiFactory.GetAddressOf()).ThrowOnError();
|
||||
#endif
|
||||
}
|
||||
|
||||
RECT rc;
|
||||
if (!GetWindowRect(hWnd, &rc) || rc.right == rc.left || rc.bottom == rc.top)
|
||||
rc = new(0, 0, 4, 4);
|
||||
|
||||
using var swapChain1 = default(ComPtr<IDXGISwapChain1>);
|
||||
var sd1 = new DXGI_SWAP_CHAIN_DESC1
|
||||
{
|
||||
Width = (uint)(rc.right - rc.left),
|
||||
Height = (uint)(rc.bottom - rc.top),
|
||||
Format = renderer.rtvFormat,
|
||||
Stereo = false,
|
||||
SampleDesc = new(1, 0),
|
||||
BufferUsage = DXGI.DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
||||
BufferCount = Math.Max(2u, mvsd.BufferCount),
|
||||
Scaling = DXGI_SCALING.DXGI_SCALING_STRETCH,
|
||||
SwapEffect = DXGI_SWAP_EFFECT.DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
|
||||
AlphaMode = DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED,
|
||||
Flags = 0,
|
||||
};
|
||||
dxgiFactory.Get()->CreateSwapChainForComposition(
|
||||
(IUnknown*)renderer.device.Get(),
|
||||
&sd1,
|
||||
null,
|
||||
swapChain1.GetAddressOf()).ThrowOnError();
|
||||
|
||||
if (ReShadePeeler.PeelSwapChain(&swapChain1))
|
||||
{
|
||||
swapChain1.Get()->ResizeBuffers(sd1.BufferCount, sd1.Width, sd1.Height, sd1.Format, sd1.Flags)
|
||||
.ThrowOnError();
|
||||
}
|
||||
|
||||
using var dcTarget = default(ComPtr<IDCompositionTarget>);
|
||||
renderer.dcompDevice.Get()->CreateTargetForHwnd(hWnd, BOOL.TRUE, dcTarget.GetAddressOf());
|
||||
|
||||
using var dcVisual = default(ComPtr<IDCompositionVisual>);
|
||||
renderer.dcompDevice.Get()->CreateVisual(dcVisual.GetAddressOf()).ThrowOnError();
|
||||
|
||||
dcVisual.Get()->SetContent((IUnknown*)swapChain1.Get()).ThrowOnError();
|
||||
dcTarget.Get()->SetRoot(dcVisual).ThrowOnError();
|
||||
renderer.dcompDevice.Get()->Commit().ThrowOnError();
|
||||
|
||||
using var swapChain = default(ComPtr<IDXGISwapChain>);
|
||||
swapChain1.As(&swapChain).ThrowOnError();
|
||||
return Create(renderer, swapChain, dcVisual, dcTarget);
|
||||
}
|
||||
|
||||
public static ViewportData Create(Dx11Renderer renderer, HWND hWnd)
|
||||
{
|
||||
using var dxgiFactory = default(ComPtr<IDXGIFactory>);
|
||||
fixed (Guid* piidFactory = &IID.IID_IDXGIFactory)
|
||||
{
|
||||
#if DEBUG
|
||||
DirectX.CreateDXGIFactory2(
|
||||
DXGI.DXGI_CREATE_FACTORY_DEBUG,
|
||||
piidFactory,
|
||||
(void**)dxgiFactory.GetAddressOf()).ThrowOnError();
|
||||
#else
|
||||
DirectX.CreateDXGIFactory(piidFactory, (void**)dxgiFactory.GetAddressOf()).ThrowOnError();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Create swapchain
|
||||
using var swapChain = default(ComPtr<IDXGISwapChain>);
|
||||
var desc = new DXGI_SWAP_CHAIN_DESC
|
||||
{
|
||||
BufferDesc =
|
||||
{
|
||||
Format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
},
|
||||
SampleDesc = new(1, 0),
|
||||
BufferUsage = DXGI.DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
||||
BufferCount = 1,
|
||||
OutputWindow = hWnd,
|
||||
Windowed = true,
|
||||
SwapEffect = DXGI_SWAP_EFFECT.DXGI_SWAP_EFFECT_DISCARD,
|
||||
};
|
||||
dxgiFactory.Get()->CreateSwapChain((IUnknown*)renderer.device.Get(), &desc, swapChain.GetAddressOf())
|
||||
.ThrowOnError();
|
||||
|
||||
if (ReShadePeeler.PeelSwapChain(&swapChain))
|
||||
{
|
||||
swapChain.Get()->ResizeBuffers(
|
||||
desc.BufferCount,
|
||||
desc.BufferDesc.Width,
|
||||
desc.BufferDesc.Height,
|
||||
desc.BufferDesc.Format,
|
||||
desc.Flags)
|
||||
.ThrowOnError();
|
||||
}
|
||||
|
||||
return Create(renderer, swapChain, null, null);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!this.selfGcHandle.IsAllocated)
|
||||
return;
|
||||
|
||||
this.ResetBuffers();
|
||||
this.dcompVisual.Reset();
|
||||
this.dcompTarget.Reset();
|
||||
this.swapChain.Reset();
|
||||
this.selfGcHandle.Free();
|
||||
}
|
||||
|
||||
public void Draw(ImDrawDataPtr drawData, bool clearRenderTarget)
|
||||
{
|
||||
if (this.width < 1 || this.height < 1)
|
||||
return;
|
||||
|
||||
this.EnsureRenderTarget();
|
||||
this.parent.RenderDrawDataInternal(this.renderTargetView, drawData, clearRenderTarget);
|
||||
}
|
||||
|
||||
public void PresentIfSwapChainAvailable()
|
||||
{
|
||||
if (this.width < 1 || this.height < 1)
|
||||
return;
|
||||
|
||||
if (!this.swapChain.IsEmpty())
|
||||
this.swapChain.Get()->Present(0, 0).ThrowOnError();
|
||||
}
|
||||
|
||||
public void ResetBuffers()
|
||||
{
|
||||
this.renderTargetView.Reset();
|
||||
this.renderTarget.Reset();
|
||||
}
|
||||
|
||||
public void ResizeBuffers(int newWidth, int newHeight, bool resizeSwapChain)
|
||||
{
|
||||
this.ResetBuffers();
|
||||
|
||||
this.width = newWidth;
|
||||
this.height = newHeight;
|
||||
if (this.width < 1 || this.height < 1)
|
||||
return;
|
||||
|
||||
if (resizeSwapChain && !this.swapChain.IsEmpty())
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
this.swapChain.Get()->GetDesc(&desc).ThrowOnError();
|
||||
this.swapChain.Get()->ResizeBuffers(
|
||||
desc.BufferCount,
|
||||
(uint)newWidth,
|
||||
(uint)newHeight,
|
||||
DXGI_FORMAT.DXGI_FORMAT_UNKNOWN,
|
||||
desc.Flags).ThrowOnError();
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureRenderTarget()
|
||||
{
|
||||
if (!this.renderTarget.IsEmpty() && !this.renderTargetView.IsEmpty())
|
||||
return;
|
||||
|
||||
this.ResetBuffers();
|
||||
|
||||
fixed (ID3D11Texture2D** pprt = &this.renderTarget.GetPinnableReference())
|
||||
fixed (ID3D11RenderTargetView** pprtv = &this.renderTargetView.GetPinnableReference())
|
||||
{
|
||||
if (this.swapChain.IsEmpty())
|
||||
{
|
||||
var desc = new D3D11_TEXTURE2D_DESC
|
||||
{
|
||||
Width = (uint)this.width,
|
||||
Height = (uint)this.height,
|
||||
MipLevels = 1,
|
||||
ArraySize = 1,
|
||||
Format = this.RtvFormat,
|
||||
SampleDesc = new(1, 0),
|
||||
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
|
||||
BindFlags = (uint)D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE,
|
||||
CPUAccessFlags = 0,
|
||||
MiscFlags = (uint)D3D11_RESOURCE_MISC_FLAG.D3D11_RESOURCE_MISC_SHARED_NTHANDLE,
|
||||
};
|
||||
this.parent.device.Get()->CreateTexture2D(&desc, null, pprt).ThrowOnError();
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (Guid* piid = &IID.IID_ID3D11Texture2D)
|
||||
{
|
||||
this.swapChain.Get()->GetBuffer(0u, piid, (void**)pprt)
|
||||
.ThrowOnError();
|
||||
}
|
||||
}
|
||||
|
||||
this.parent.device.Get()->CreateRenderTargetView((ID3D11Resource*)*pprt, null, pprtv).ThrowOnError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
664
Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs
Normal file
664
Dalamud/Interface/ImGuiBackend/Renderers/Dx11Renderer.cs
Normal file
|
|
@ -0,0 +1,664 @@
|
|||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Interface.ImGuiBackend.Helpers;
|
||||
using Dalamud.Interface.ImGuiBackend.Helpers.D3D11;
|
||||
using Dalamud.Interface.Textures;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Interface.Textures.TextureWraps.Internal;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Renderers;
|
||||
|
||||
/// <summary>
|
||||
/// Deals with rendering ImGui using DirectX 11.
|
||||
/// See https://github.com/ocornut/imgui/blob/master/examples/imgui_impl_dx11.cpp for the original implementation.
|
||||
/// </summary>
|
||||
[SuppressMessage(
|
||||
"StyleCop.CSharp.LayoutRules",
|
||||
"SA1519:Braces should not be omitted from multi-line child statement",
|
||||
Justification = "Multiple fixed/using scopes")]
|
||||
internal unsafe partial class Dx11Renderer : IImGuiRenderer
|
||||
{
|
||||
private readonly List<IDalamudTextureWrap> fontTextures = new();
|
||||
private readonly D3D_FEATURE_LEVEL featureLevel;
|
||||
private readonly ViewportHandler viewportHandler;
|
||||
private readonly nint renderNamePtr;
|
||||
private readonly DXGI_FORMAT rtvFormat;
|
||||
private readonly ViewportData mainViewport;
|
||||
|
||||
private bool releaseUnmanagedResourceCalled;
|
||||
|
||||
private ComPtr<ID3D11Device> device;
|
||||
private ComPtr<ID3D11DeviceContext> context;
|
||||
private ComPtr<ID3D11VertexShader> vertexShader;
|
||||
private ComPtr<ID3D11PixelShader> pixelShader;
|
||||
private ComPtr<ID3D11SamplerState> sampler;
|
||||
private ComPtr<ID3D11InputLayout> inputLayout;
|
||||
private ComPtr<ID3D11Buffer> vertexConstantBuffer;
|
||||
private ComPtr<ID3D11BlendState> blendState;
|
||||
private ComPtr<ID3D11RasterizerState> rasterizerState;
|
||||
private ComPtr<ID3D11DepthStencilState> depthStencilState;
|
||||
private ComPtr<ID3D11Buffer> vertexBuffer;
|
||||
private ComPtr<ID3D11Buffer> indexBuffer;
|
||||
private int vertexBufferSize;
|
||||
private int indexBufferSize;
|
||||
|
||||
private ComPtr<IDCompositionDevice> dcompDevice;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Dx11Renderer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="swapChain">The swap chain.</param>
|
||||
/// <param name="device">A pointer to an instance of <see cref="ID3D11Device"/>.</param>
|
||||
/// <param name="context">A pointer to an instance of <see cref="ID3D11DeviceContext"/>.</param>
|
||||
public Dx11Renderer(IDXGISwapChain* swapChain, ID3D11Device* device, ID3D11DeviceContext* context)
|
||||
{
|
||||
var io = ImGui.GetIO();
|
||||
if (ImGui.GetIO().NativePtr->BackendRendererName is not null)
|
||||
throw new InvalidOperationException("ImGui backend renderer seems to be have been already attached.");
|
||||
try
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
swapChain->GetDesc(&desc).ThrowOnError();
|
||||
this.rtvFormat = desc.BufferDesc.Format;
|
||||
this.device = new(device);
|
||||
this.context = new(context);
|
||||
this.featureLevel = device->GetFeatureLevel();
|
||||
|
||||
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset | ImGuiBackendFlags.RendererHasViewports;
|
||||
|
||||
this.renderNamePtr = Marshal.StringToHGlobalAnsi("imgui_impl_dx11_c#");
|
||||
io.NativePtr->BackendRendererName = (byte*)this.renderNamePtr;
|
||||
|
||||
if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable))
|
||||
{
|
||||
try
|
||||
{
|
||||
fixed (IDCompositionDevice** pp = &this.dcompDevice.GetPinnableReference())
|
||||
fixed (Guid* piidDCompositionDevice = &IID.IID_IDCompositionDevice)
|
||||
DirectX.DCompositionCreateDevice(null, piidDCompositionDevice, (void**)pp).ThrowOnError();
|
||||
|
||||
ImGuiViewportHelpers.EnableViewportWindowBackgroundAlpha();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// don't care; not using DComposition then
|
||||
}
|
||||
|
||||
this.viewportHandler = new(this);
|
||||
}
|
||||
|
||||
this.mainViewport = ViewportData.Create(this, swapChain, null, null);
|
||||
ImGui.GetPlatformIO().Viewports[0].RendererUserData = this.mainViewport.Handle;
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.ReleaseUnmanagedResources();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="Dx11Renderer"/> class.
|
||||
/// </summary>
|
||||
~Dx11Renderer() => this.ReleaseUnmanagedResources();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.ReleaseUnmanagedResources();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnNewFrame()
|
||||
{
|
||||
this.EnsureDeviceObjects();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnPreResize() => this.mainViewport.ResetBuffers();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void OnPostResize(int width, int height) => this.mainViewport.ResizeBuffers(width, height, false);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RenderDrawData(ImDrawDataPtr drawData) =>
|
||||
this.mainViewport.Draw(drawData, this.mainViewport.SwapChain == null);
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds font texture.
|
||||
/// </summary>
|
||||
public void RebuildFontTexture()
|
||||
{
|
||||
foreach (var fontResourceView in this.fontTextures)
|
||||
fontResourceView.Dispose();
|
||||
this.fontTextures.Clear();
|
||||
|
||||
this.CreateFontsTexture();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDalamudTextureWrap CreateTexture2D(
|
||||
ReadOnlySpan<byte> data,
|
||||
RawImageSpecification specs,
|
||||
bool cpuRead,
|
||||
bool cpuWrite,
|
||||
bool allowRenderTarget,
|
||||
[CallerMemberName] string debugName = "")
|
||||
{
|
||||
if (cpuRead && cpuWrite)
|
||||
throw new ArgumentException("cpuRead and cpuWrite cannot be set at the same time.");
|
||||
|
||||
var cpuaf = default(D3D11_CPU_ACCESS_FLAG);
|
||||
if (cpuRead)
|
||||
cpuaf |= D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ;
|
||||
if (cpuWrite)
|
||||
cpuaf |= D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE;
|
||||
|
||||
D3D11_USAGE usage;
|
||||
if (cpuRead)
|
||||
usage = D3D11_USAGE.D3D11_USAGE_STAGING;
|
||||
else if (cpuWrite)
|
||||
usage = D3D11_USAGE.D3D11_USAGE_DYNAMIC;
|
||||
else
|
||||
usage = D3D11_USAGE.D3D11_USAGE_DEFAULT;
|
||||
|
||||
var texDesc = new D3D11_TEXTURE2D_DESC
|
||||
{
|
||||
Width = (uint)specs.Width,
|
||||
Height = (uint)specs.Height,
|
||||
MipLevels = 1,
|
||||
ArraySize = 1,
|
||||
Format = specs.Format,
|
||||
SampleDesc = new(1, 0),
|
||||
Usage = usage,
|
||||
BindFlags = (uint)(D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE |
|
||||
(allowRenderTarget ? D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET : 0)),
|
||||
CPUAccessFlags = (uint)cpuaf,
|
||||
MiscFlags = 0,
|
||||
};
|
||||
using var texture = default(ComPtr<ID3D11Texture2D>);
|
||||
if (data.IsEmpty)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(this.device.Get()->CreateTexture2D(&texDesc, null, texture.GetAddressOf()));
|
||||
}
|
||||
else
|
||||
{
|
||||
fixed (void* dataPtr = data)
|
||||
{
|
||||
var subrdata = new D3D11_SUBRESOURCE_DATA { pSysMem = dataPtr, SysMemPitch = (uint)specs.Pitch };
|
||||
Marshal.ThrowExceptionForHR(
|
||||
this.device.Get()->CreateTexture2D(&texDesc, &subrdata, texture.GetAddressOf()));
|
||||
}
|
||||
}
|
||||
|
||||
texture.Get()->SetDebugName($"Texture:{debugName}:SRV");
|
||||
|
||||
using var srvTemp = default(ComPtr<ID3D11ShaderResourceView>);
|
||||
var srvDesc = new D3D11_SHADER_RESOURCE_VIEW_DESC(
|
||||
texture,
|
||||
D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D);
|
||||
this.device.Get()->CreateShaderResourceView((ID3D11Resource*)texture.Get(), &srvDesc, srvTemp.GetAddressOf())
|
||||
.ThrowOnError();
|
||||
srvTemp.Get()->SetDebugName($"Texture:{debugName}:SRV");
|
||||
|
||||
return new UnknownTextureWrap((IUnknown*)srvTemp.Get(), specs.Width, specs.Height, true);
|
||||
}
|
||||
|
||||
private void RenderDrawDataInternal(
|
||||
ID3D11RenderTargetView* renderTargetView,
|
||||
ImDrawDataPtr drawData,
|
||||
bool clearRenderTarget)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (drawData.DisplaySize.X <= 0 || drawData.DisplaySize.Y <= 0)
|
||||
return;
|
||||
|
||||
using var oldState = new D3D11DeviceContextStateBackup(this.featureLevel, this.context.Get());
|
||||
|
||||
// Setup desired DX state
|
||||
this.SetupRenderState(drawData);
|
||||
|
||||
this.context.Get()->OMSetRenderTargets(1, &renderTargetView, null);
|
||||
if (clearRenderTarget)
|
||||
{
|
||||
var color = default(Vector4);
|
||||
this.context.Get()->ClearRenderTargetView(renderTargetView, (float*)&color);
|
||||
}
|
||||
|
||||
if (!drawData.Valid || drawData.CmdListsCount == 0)
|
||||
return;
|
||||
|
||||
var cmdLists = new Span<ImDrawListPtr>(drawData.NativePtr->CmdLists, drawData.NativePtr->CmdListsCount);
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (this.vertexBufferSize < drawData.TotalVtxCount)
|
||||
this.vertexBuffer.Dispose();
|
||||
if (this.vertexBuffer.Get() is null)
|
||||
{
|
||||
this.vertexBufferSize = drawData.TotalVtxCount + 5000;
|
||||
var desc = new D3D11_BUFFER_DESC(
|
||||
(uint)(sizeof(ImDrawVert) * this.vertexBufferSize),
|
||||
(uint)D3D11_BIND_FLAG.D3D11_BIND_VERTEX_BUFFER,
|
||||
D3D11_USAGE.D3D11_USAGE_DYNAMIC,
|
||||
(uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE);
|
||||
var buffer = default(ID3D11Buffer*);
|
||||
this.device.Get()->CreateBuffer(&desc, null, &buffer).ThrowOnError();
|
||||
this.vertexBuffer.Attach(buffer);
|
||||
}
|
||||
|
||||
if (this.indexBufferSize < drawData.TotalIdxCount)
|
||||
this.indexBuffer.Dispose();
|
||||
if (this.indexBuffer.Get() is null)
|
||||
{
|
||||
this.indexBufferSize = drawData.TotalIdxCount + 5000;
|
||||
var desc = new D3D11_BUFFER_DESC(
|
||||
(uint)(sizeof(ushort) * this.indexBufferSize),
|
||||
(uint)D3D11_BIND_FLAG.D3D11_BIND_INDEX_BUFFER,
|
||||
D3D11_USAGE.D3D11_USAGE_DYNAMIC,
|
||||
(uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE);
|
||||
var buffer = default(ID3D11Buffer*);
|
||||
this.device.Get()->CreateBuffer(&desc, null, &buffer).ThrowOnError();
|
||||
this.indexBuffer.Attach(buffer);
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
try
|
||||
{
|
||||
var vertexData = default(D3D11_MAPPED_SUBRESOURCE);
|
||||
var indexData = default(D3D11_MAPPED_SUBRESOURCE);
|
||||
this.context.Get()->Map(
|
||||
(ID3D11Resource*)this.vertexBuffer.Get(),
|
||||
0,
|
||||
D3D11_MAP.D3D11_MAP_WRITE_DISCARD,
|
||||
0,
|
||||
&vertexData).ThrowOnError();
|
||||
this.context.Get()->Map(
|
||||
(ID3D11Resource*)this.indexBuffer.Get(),
|
||||
0,
|
||||
D3D11_MAP.D3D11_MAP_WRITE_DISCARD,
|
||||
0,
|
||||
&indexData).ThrowOnError();
|
||||
|
||||
var targetVertices = new Span<ImDrawVert>(vertexData.pData, this.vertexBufferSize);
|
||||
var targetIndices = new Span<ushort>(indexData.pData, this.indexBufferSize);
|
||||
foreach (ref var cmdList in cmdLists)
|
||||
{
|
||||
var vertices = new ImVectorWrapper<ImDrawVert>(&cmdList.NativePtr->VtxBuffer);
|
||||
var indices = new ImVectorWrapper<ushort>(&cmdList.NativePtr->IdxBuffer);
|
||||
|
||||
vertices.DataSpan.CopyTo(targetVertices);
|
||||
indices.DataSpan.CopyTo(targetIndices);
|
||||
|
||||
targetVertices = targetVertices[vertices.Length..];
|
||||
targetIndices = targetIndices[indices.Length..];
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.context.Get()->Unmap((ID3D11Resource*)this.vertexBuffer.Get(), 0);
|
||||
this.context.Get()->Unmap((ID3D11Resource*)this.indexBuffer.Get(), 0);
|
||||
}
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer.
|
||||
// Our visible imgui space lies from DisplayPos (LT) to DisplayPos+DisplaySize (RB).
|
||||
// DisplayPos is (0,0) for single viewport apps.
|
||||
try
|
||||
{
|
||||
var data = default(D3D11_MAPPED_SUBRESOURCE);
|
||||
this.context.Get()->Map(
|
||||
(ID3D11Resource*)this.vertexConstantBuffer.Get(),
|
||||
0,
|
||||
D3D11_MAP.D3D11_MAP_WRITE_DISCARD,
|
||||
0,
|
||||
&data).ThrowOnError();
|
||||
*(Matrix4x4*)data.pData = Matrix4x4.CreateOrthographicOffCenter(
|
||||
drawData.DisplayPos.X,
|
||||
drawData.DisplayPos.X + drawData.DisplaySize.X,
|
||||
drawData.DisplayPos.Y + drawData.DisplaySize.Y,
|
||||
drawData.DisplayPos.Y,
|
||||
1f,
|
||||
0f);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.context.Get()->Unmap((ID3D11Resource*)this.vertexConstantBuffer.Get(), 0);
|
||||
}
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
var vertexOffset = 0;
|
||||
var indexOffset = 0;
|
||||
var clipOff = new Vector4(drawData.DisplayPos, drawData.DisplayPos.X, drawData.DisplayPos.Y);
|
||||
foreach (ref var cmdList in cmdLists)
|
||||
{
|
||||
var cmds = new ImVectorWrapper<ImDrawCmd>(&cmdList.NativePtr->CmdBuffer);
|
||||
foreach (ref var cmd in cmds.DataSpan)
|
||||
{
|
||||
var clipV4 = cmd.ClipRect - clipOff;
|
||||
var clipRect = new RECT((int)clipV4.X, (int)clipV4.Y, (int)clipV4.Z, (int)clipV4.W);
|
||||
|
||||
// Skip the draw if nothing would be visible
|
||||
if (clipRect.left >= clipRect.right || clipRect.top >= clipRect.bottom)
|
||||
continue;
|
||||
|
||||
this.context.Get()->RSSetScissorRects(1, &clipRect);
|
||||
|
||||
if (cmd.UserCallback == nint.Zero)
|
||||
{
|
||||
// Bind texture and draw
|
||||
var srv = (ID3D11ShaderResourceView*)cmd.TextureId;
|
||||
this.context.Get()->PSSetShader(this.pixelShader, null, 0);
|
||||
this.context.Get()->PSSetSamplers(0, 1, this.sampler.GetAddressOf());
|
||||
this.context.Get()->PSSetShaderResources(0, 1, &srv);
|
||||
this.context.Get()->DrawIndexed(
|
||||
cmd.ElemCount,
|
||||
(uint)(cmd.IdxOffset + indexOffset),
|
||||
(int)(cmd.VtxOffset + vertexOffset));
|
||||
}
|
||||
}
|
||||
|
||||
indexOffset += cmdList.IdxBuffer.Size;
|
||||
vertexOffset += cmdList.VtxBuffer.Size;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds fonts as necessary, and uploads the built data onto the GPU.<br />
|
||||
/// No-op if it has already been done.
|
||||
/// </summary>
|
||||
private void CreateFontsTexture()
|
||||
{
|
||||
if (this.device.IsEmpty())
|
||||
throw new ObjectDisposedException(nameof(Dx11Renderer));
|
||||
|
||||
if (this.fontTextures.Any())
|
||||
return;
|
||||
|
||||
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 byte* fontPixels,
|
||||
out var width,
|
||||
out var height,
|
||||
out var bytespp);
|
||||
|
||||
var tex = this.CreateTexture2D(
|
||||
new(fontPixels, width * height * bytespp),
|
||||
new(width, height, (int)DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM, width * bytespp),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
$"Font#{textureIndex}");
|
||||
io.Fonts.SetTexID(textureIndex, tex.ImGuiHandle);
|
||||
this.fontTextures.Add(tex);
|
||||
}
|
||||
|
||||
io.Fonts.ClearTexData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the device context's render state to what we would use for rendering ImGui by default.
|
||||
/// </summary>
|
||||
/// <param name="drawData">The relevant ImGui draw data.</param>
|
||||
private void SetupRenderState(ImDrawDataPtr drawData)
|
||||
{
|
||||
var ctx = this.context.Get();
|
||||
ctx->IASetInputLayout(this.inputLayout);
|
||||
var buffer = this.vertexBuffer.Get();
|
||||
var stride = (uint)sizeof(ImDrawVert);
|
||||
var offset = 0u;
|
||||
ctx->IASetVertexBuffers(0, 1, &buffer, &stride, &offset);
|
||||
ctx->IASetIndexBuffer(this.indexBuffer, DXGI_FORMAT.DXGI_FORMAT_R16_UINT, 0);
|
||||
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
|
||||
var viewport = new D3D11_VIEWPORT(0, 0, drawData.DisplaySize.X, drawData.DisplaySize.Y);
|
||||
ctx->RSSetState(this.rasterizerState);
|
||||
ctx->RSSetViewports(1, &viewport);
|
||||
|
||||
var blendColor = default(Vector4);
|
||||
ctx->OMSetBlendState(this.blendState, (float*)&blendColor, 0xffffffff);
|
||||
ctx->OMSetDepthStencilState(this.depthStencilState, 0);
|
||||
|
||||
ctx->VSSetShader(this.vertexShader.Get(), null, 0);
|
||||
buffer = this.vertexConstantBuffer.Get();
|
||||
ctx->VSSetConstantBuffers(0, 1, &buffer);
|
||||
|
||||
// PS handled later
|
||||
|
||||
ctx->GSSetShader(null, null, 0);
|
||||
ctx->HSSetShader(null, null, 0);
|
||||
ctx->DSSetShader(null, null, 0);
|
||||
ctx->CSSetShader(null, null, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates objects from the device as necessary.<br />
|
||||
/// No-op if objects already are built.
|
||||
/// </summary>
|
||||
private void EnsureDeviceObjects()
|
||||
{
|
||||
if (this.device.IsEmpty())
|
||||
throw new ObjectDisposedException(nameof(Dx11Renderer));
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
// Create the vertex shader
|
||||
if (this.vertexShader.IsEmpty() || this.inputLayout.IsEmpty())
|
||||
{
|
||||
using var stream = assembly.GetManifestResourceStream("imgui-vertex.hlsl.bytes")!;
|
||||
var array = ArrayPool<byte>.Shared.Rent((int)stream.Length);
|
||||
stream.ReadExactly(array, 0, (int)stream.Length);
|
||||
fixed (byte* pArray = array)
|
||||
fixed (ID3D11VertexShader** ppShader = &this.vertexShader.GetPinnableReference())
|
||||
fixed (ID3D11InputLayout** ppInputLayout = &this.inputLayout.GetPinnableReference())
|
||||
fixed (void* pszPosition = "POSITION"u8)
|
||||
fixed (void* pszTexCoord = "TEXCOORD"u8)
|
||||
fixed (void* pszColor = "COLOR"u8)
|
||||
{
|
||||
this.device.Get()->CreateVertexShader(pArray, (nuint)stream.Length, null, ppShader).ThrowOnError();
|
||||
|
||||
var ied = stackalloc D3D11_INPUT_ELEMENT_DESC[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
SemanticName = (sbyte*)pszPosition,
|
||||
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT,
|
||||
AlignedByteOffset = uint.MaxValue,
|
||||
},
|
||||
new()
|
||||
{
|
||||
SemanticName = (sbyte*)pszTexCoord,
|
||||
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT,
|
||||
AlignedByteOffset = uint.MaxValue,
|
||||
},
|
||||
new()
|
||||
{
|
||||
SemanticName = (sbyte*)pszColor,
|
||||
Format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
AlignedByteOffset = uint.MaxValue,
|
||||
},
|
||||
};
|
||||
this.device.Get()->CreateInputLayout(ied, 3, pArray, (nuint)stream.Length, ppInputLayout)
|
||||
.ThrowOnError();
|
||||
}
|
||||
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
|
||||
// Create the pixel shader
|
||||
if (this.pixelShader.IsEmpty())
|
||||
{
|
||||
using var stream = assembly.GetManifestResourceStream("imgui-frag.hlsl.bytes")!;
|
||||
var array = ArrayPool<byte>.Shared.Rent((int)stream.Length);
|
||||
stream.ReadExactly(array, 0, (int)stream.Length);
|
||||
fixed (byte* pArray = array)
|
||||
fixed (ID3D11PixelShader** ppShader = &this.pixelShader.GetPinnableReference())
|
||||
this.device.Get()->CreatePixelShader(pArray, (nuint)stream.Length, null, ppShader).ThrowOnError();
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
|
||||
// Create the sampler state
|
||||
if (this.sampler.IsEmpty())
|
||||
{
|
||||
var samplerDesc = new D3D11_SAMPLER_DESC
|
||||
{
|
||||
Filter = D3D11_FILTER.D3D11_FILTER_MIN_MAG_MIP_LINEAR,
|
||||
AddressU = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP,
|
||||
AddressV = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP,
|
||||
AddressW = D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP,
|
||||
MipLODBias = 0,
|
||||
MaxAnisotropy = 0,
|
||||
ComparisonFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_ALWAYS,
|
||||
MinLOD = 0,
|
||||
MaxLOD = 0,
|
||||
};
|
||||
|
||||
fixed (ID3D11SamplerState** ppSampler = &this.sampler.GetPinnableReference())
|
||||
this.device.Get()->CreateSamplerState(&samplerDesc, ppSampler).ThrowOnError();
|
||||
}
|
||||
|
||||
// Create the constant buffer
|
||||
if (this.vertexConstantBuffer.IsEmpty())
|
||||
{
|
||||
var bufferDesc = new D3D11_BUFFER_DESC(
|
||||
(uint)sizeof(Matrix4x4),
|
||||
(uint)D3D11_BIND_FLAG.D3D11_BIND_CONSTANT_BUFFER,
|
||||
D3D11_USAGE.D3D11_USAGE_DYNAMIC,
|
||||
(uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE);
|
||||
fixed (ID3D11Buffer** ppBuffer = &this.vertexConstantBuffer.GetPinnableReference())
|
||||
this.device.Get()->CreateBuffer(&bufferDesc, null, ppBuffer).ThrowOnError();
|
||||
}
|
||||
|
||||
// Create the blending setup
|
||||
if (this.blendState.IsEmpty())
|
||||
{
|
||||
var blendStateDesc = new D3D11_BLEND_DESC
|
||||
{
|
||||
RenderTarget =
|
||||
{
|
||||
e0 =
|
||||
{
|
||||
BlendEnable = true,
|
||||
SrcBlend = D3D11_BLEND.D3D11_BLEND_SRC_ALPHA,
|
||||
DestBlend = D3D11_BLEND.D3D11_BLEND_INV_SRC_ALPHA,
|
||||
BlendOp = D3D11_BLEND_OP.D3D11_BLEND_OP_ADD,
|
||||
SrcBlendAlpha = D3D11_BLEND.D3D11_BLEND_INV_DEST_ALPHA,
|
||||
DestBlendAlpha = D3D11_BLEND.D3D11_BLEND_ONE,
|
||||
BlendOpAlpha = D3D11_BLEND_OP.D3D11_BLEND_OP_ADD,
|
||||
RenderTargetWriteMask = (byte)D3D11_COLOR_WRITE_ENABLE.D3D11_COLOR_WRITE_ENABLE_ALL,
|
||||
},
|
||||
},
|
||||
};
|
||||
fixed (ID3D11BlendState** ppBlendState = &this.blendState.GetPinnableReference())
|
||||
this.device.Get()->CreateBlendState(&blendStateDesc, ppBlendState).ThrowOnError();
|
||||
}
|
||||
|
||||
// Create the rasterizer state
|
||||
if (this.rasterizerState.IsEmpty())
|
||||
{
|
||||
var rasterizerDesc = new D3D11_RASTERIZER_DESC
|
||||
{
|
||||
FillMode = D3D11_FILL_MODE.D3D11_FILL_SOLID,
|
||||
CullMode = D3D11_CULL_MODE.D3D11_CULL_NONE,
|
||||
ScissorEnable = true,
|
||||
DepthClipEnable = true,
|
||||
};
|
||||
fixed (ID3D11RasterizerState** ppRasterizerState = &this.rasterizerState.GetPinnableReference())
|
||||
this.device.Get()->CreateRasterizerState(&rasterizerDesc, ppRasterizerState).ThrowOnError();
|
||||
}
|
||||
|
||||
// Create the depth-stencil State
|
||||
if (this.depthStencilState.IsEmpty())
|
||||
{
|
||||
var dsDesc = new D3D11_DEPTH_STENCIL_DESC
|
||||
{
|
||||
DepthEnable = false,
|
||||
DepthWriteMask = D3D11_DEPTH_WRITE_MASK.D3D11_DEPTH_WRITE_MASK_ALL,
|
||||
DepthFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_ALWAYS,
|
||||
StencilEnable = false,
|
||||
StencilReadMask = byte.MaxValue,
|
||||
StencilWriteMask = byte.MaxValue,
|
||||
FrontFace =
|
||||
{
|
||||
StencilFailOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
|
||||
StencilDepthFailOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
|
||||
StencilPassOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
|
||||
StencilFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_ALWAYS,
|
||||
},
|
||||
BackFace =
|
||||
{
|
||||
StencilFailOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
|
||||
StencilDepthFailOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
|
||||
StencilPassOp = D3D11_STENCIL_OP.D3D11_STENCIL_OP_KEEP,
|
||||
StencilFunc = D3D11_COMPARISON_FUNC.D3D11_COMPARISON_ALWAYS,
|
||||
},
|
||||
};
|
||||
fixed (ID3D11DepthStencilState** ppDepthStencilState = &this.depthStencilState.GetPinnableReference())
|
||||
this.device.Get()->CreateDepthStencilState(&dsDesc, ppDepthStencilState).ThrowOnError();
|
||||
}
|
||||
|
||||
this.CreateFontsTexture();
|
||||
}
|
||||
|
||||
private void ReleaseUnmanagedResources()
|
||||
{
|
||||
if (this.releaseUnmanagedResourceCalled)
|
||||
return;
|
||||
this.releaseUnmanagedResourceCalled = true;
|
||||
|
||||
this.mainViewport.Dispose();
|
||||
ImGui.GetPlatformIO().Viewports[0].RendererUserData = nint.Zero;
|
||||
ImGui.DestroyPlatformWindows();
|
||||
|
||||
this.viewportHandler.Dispose();
|
||||
|
||||
var io = ImGui.GetIO();
|
||||
if (io.NativePtr->BackendRendererName == (void*)this.renderNamePtr)
|
||||
io.NativePtr->BackendRendererName = null;
|
||||
if (this.renderNamePtr != 0)
|
||||
Marshal.FreeHGlobal(this.renderNamePtr);
|
||||
|
||||
foreach (var fontResourceView in this.fontTextures)
|
||||
fontResourceView.Dispose();
|
||||
|
||||
foreach (var i in Enumerable.Range(0, io.Fonts.Textures.Size))
|
||||
io.Fonts.SetTexID(i, nint.Zero);
|
||||
|
||||
this.device.Reset();
|
||||
this.context.Reset();
|
||||
this.vertexShader.Reset();
|
||||
this.pixelShader.Reset();
|
||||
this.sampler.Reset();
|
||||
this.inputLayout.Reset();
|
||||
this.vertexConstantBuffer.Reset();
|
||||
this.blendState.Reset();
|
||||
this.rasterizerState.Reset();
|
||||
this.depthStencilState.Reset();
|
||||
this.vertexBuffer.Reset();
|
||||
this.indexBuffer.Reset();
|
||||
this.dcompDevice.Reset();
|
||||
}
|
||||
}
|
||||
43
Dalamud/Interface/ImGuiBackend/Renderers/IImGuiRenderer.cs
Normal file
43
Dalamud/Interface/ImGuiBackend/Renderers/IImGuiRenderer.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Dalamud.Interface.Textures;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Dalamud.Interface.ImGuiBackend.Renderers;
|
||||
|
||||
/// <summary>A simple shared public interface that all ImGui render implementations follow.</summary>
|
||||
internal interface IImGuiRenderer : IDisposable
|
||||
{
|
||||
/// <summary>Load an image from a span of bytes of specified format.</summary>
|
||||
/// <param name="data">The data to load.</param>
|
||||
/// <param name="specs">Texture specifications.</param>
|
||||
/// <param name="cpuRead">Whether to support reading from CPU, while disabling reading from GPU.</param>
|
||||
/// <param name="cpuWrite">Whether to support writing from CPU, while disabling writing from GPU.</param>
|
||||
/// <param name="allowRenderTarget">Whether to allow rendering to this texture.</param>
|
||||
/// <param name="debugName">Name for debugging.</param>
|
||||
/// <returns>A texture, ready to use in ImGui.</returns>
|
||||
IDalamudTextureWrap CreateTexture2D(
|
||||
ReadOnlySpan<byte> data,
|
||||
RawImageSpecification specs,
|
||||
bool cpuRead,
|
||||
bool cpuWrite,
|
||||
bool allowRenderTarget,
|
||||
[CallerMemberName] string debugName = "");
|
||||
|
||||
/// <summary>Notifies that the window is about to be resized.</summary>
|
||||
void OnPreResize();
|
||||
|
||||
/// <summary>Notifies that the window has been resized.</summary>
|
||||
/// <param name="width">The new window width.</param>
|
||||
/// <param name="height">The new window height.</param>
|
||||
void OnPostResize(int width, int height);
|
||||
|
||||
/// <summary>Marks the beginning of a new frame.</summary>
|
||||
void OnNewFrame();
|
||||
|
||||
/// <summary>Renders the draw data.</summary>
|
||||
/// <param name="drawData">The draw data.</param>
|
||||
void RenderDrawData(ImDrawDataPtr drawData);
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
<?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>
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
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
|
|
@ -1,14 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,859 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,368 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,515 +0,0 @@
|
|||
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,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
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.
|
|
@ -1,12 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#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);
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ using Dalamud.Game.ClientState.GamePad;
|
|||
using Dalamud.Game.ClientState.Keys;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Hooking.WndProcHook;
|
||||
using Dalamud.Interface.ImGuiBackend;
|
||||
using Dalamud.Interface.ImGuiNotification.Internal;
|
||||
using Dalamud.Interface.Internal.ManagedAsserts;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
|
|
@ -27,12 +28,12 @@ using Dalamud.Utility.Timing;
|
|||
|
||||
using ImGuiNET;
|
||||
|
||||
using ImGuiScene;
|
||||
|
||||
using PInvoke;
|
||||
|
||||
using SharpDX;
|
||||
using SharpDX.DXGI;
|
||||
using TerraFX.Interop.DirectX;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
using static TerraFX.Interop.Windows.Windows;
|
||||
|
||||
// general dev notes, here because it's easiest
|
||||
|
||||
|
|
@ -65,7 +66,7 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
public const float DefaultFontSizePx = (DefaultFontSizePt * 4.0f) / 3.0f;
|
||||
|
||||
private static readonly ModuleLog Log = new("INTERFACE");
|
||||
|
||||
|
||||
private readonly ConcurrentBag<IDeferredDisposable> deferredDisposeTextures = new();
|
||||
private readonly ConcurrentBag<IDisposable> deferredDisposeDisposables = new();
|
||||
|
||||
|
|
@ -78,42 +79,39 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
private readonly ConcurrentQueue<Action> runBeforeImGuiRender = new();
|
||||
private readonly ConcurrentQueue<Action> runAfterImGuiRender = new();
|
||||
|
||||
private RawDX11Scene? scene;
|
||||
private IWin32Backend? scene;
|
||||
|
||||
private Hook<SetCursorDelegate>? setCursorHook;
|
||||
private Hook<DxgiPresentDelegate>? dxgiPresentHook;
|
||||
private Hook<ReshadeOnPresentDelegate>? reshadeOnPresentHook;
|
||||
private Hook<PresentDelegate>? presentHook;
|
||||
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
|
||||
|
||||
private IFontAtlas? dalamudAtlas;
|
||||
private ILockedImFont? defaultFontResourceLock;
|
||||
|
||||
// can't access imgui IO before first present call
|
||||
private bool lastWantCapture = false;
|
||||
private HWND gameWindowHandle;
|
||||
private bool lastWantCapture;
|
||||
private bool isOverrideGameCursor = true;
|
||||
private IntPtr gameWindowHandle;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private InterfaceManager()
|
||||
{
|
||||
}
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate IntPtr DxgiPresentDelegate(IntPtr swapChain, uint syncInterval, uint presentFlags);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate void ReshadeOnPresentDelegate(nint swapChain, uint flags, nint presentParams);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate IntPtr ResizeBuffersDelegate(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags);
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
private unsafe delegate HRESULT PresentDelegate(IDXGISwapChain* swapChain, uint syncInterval, uint presentFlags);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
private delegate IntPtr SetCursorDelegate(IntPtr hCursor);
|
||||
private unsafe delegate HRESULT ResizeBuffersDelegate(
|
||||
IDXGISwapChain* swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
||||
private delegate HCURSOR SetCursorDelegate(HCURSOR hCursor);
|
||||
|
||||
/// <summary>
|
||||
/// This event gets called each frame to facilitate ImGui drawing.
|
||||
/// </summary>
|
||||
public event RawDX11Scene.BuildUIDelegate? Draw;
|
||||
public event IImGuiBackend.BuildUiDelegate? Draw;
|
||||
|
||||
/// <summary>
|
||||
/// This event gets called when ResizeBuffers is called.
|
||||
|
|
@ -181,17 +179,7 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
/// <summary>
|
||||
/// Gets the DX11 scene.
|
||||
/// </summary>
|
||||
public RawDX11Scene? Scene => this.scene;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the D3D11 device instance.
|
||||
/// </summary>
|
||||
public SharpDX.Direct3D11.Device? Device => this.scene?.Device;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address handle to the main process window.
|
||||
/// </summary>
|
||||
public IntPtr WindowHandlePtr => this.scene?.WindowHandlePtr ?? IntPtr.Zero;
|
||||
public IImGuiBackend? Scene => this.scene;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not the game's cursor should be overridden with the ImGui cursor.
|
||||
|
|
@ -224,20 +212,24 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
/// <summary>
|
||||
/// Gets a value indicating the native handle of the game main window.
|
||||
/// </summary>
|
||||
public IntPtr GameWindowHandle
|
||||
public unsafe HWND GameWindowHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.gameWindowHandle == 0)
|
||||
{
|
||||
nint gwh = 0;
|
||||
while ((gwh = NativeFunctions.FindWindowEx(0, gwh, "FFXIVGAME", 0)) != 0)
|
||||
var gwh = default(HWND);
|
||||
fixed (char* pClass = "FFXIVGAME")
|
||||
{
|
||||
_ = User32.GetWindowThreadProcessId(gwh, out var pid);
|
||||
if (pid == Environment.ProcessId && User32.IsWindowVisible(gwh))
|
||||
while ((gwh = FindWindowExW(default, gwh, (ushort*)pClass, default)) != default)
|
||||
{
|
||||
this.gameWindowHandle = gwh;
|
||||
break;
|
||||
uint pid;
|
||||
_ = GetWindowThreadProcessId(gwh, &pid);
|
||||
if (pid == Environment.ProcessId && IsWindowVisible(gwh))
|
||||
{
|
||||
this.gameWindowHandle = gwh;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -298,8 +290,7 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
{
|
||||
this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc;
|
||||
Interlocked.Exchange(ref this.setCursorHook, null)?.Dispose();
|
||||
Interlocked.Exchange(ref this.dxgiPresentHook, null)?.Dispose();
|
||||
Interlocked.Exchange(ref this.reshadeOnPresentHook, null)?.Dispose();
|
||||
Interlocked.Exchange(ref this.presentHook, null)?.Dispose();
|
||||
Interlocked.Exchange(ref this.resizeBuffersHook, null)?.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
@ -423,27 +414,27 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
/// Get video memory information.
|
||||
/// </summary>
|
||||
/// <returns>The currently used video memory, or null if not available.</returns>
|
||||
public (long Used, long Available)? GetD3dMemoryInfo()
|
||||
public unsafe (long Used, long Available)? GetD3dMemoryInfo()
|
||||
{
|
||||
if (this.Device == null)
|
||||
if (this.scene?.DeviceHandle is 0 or null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var dxgiDev = this.Device.QueryInterfaceOrNull<SharpDX.DXGI.Device>();
|
||||
var dxgiAdapter = dxgiDev?.Adapter.QueryInterfaceOrNull<Adapter4>();
|
||||
if (dxgiAdapter == null)
|
||||
return null;
|
||||
using var device = default(ComPtr<IDXGIDevice>);
|
||||
using var adapter = default(ComPtr<IDXGIAdapter>);
|
||||
using var adapter4 = default(ComPtr<IDXGIAdapter4>);
|
||||
|
||||
var memInfo = dxgiAdapter.QueryVideoMemoryInfo(0, MemorySegmentGroup.Local);
|
||||
return (memInfo.CurrentUsage, memInfo.CurrentReservation);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
if (new ComPtr<IUnknown>((IUnknown*)this.scene.DeviceHandle).As(&device).FAILED)
|
||||
return null;
|
||||
|
||||
return null;
|
||||
if (device.Get()->GetAdapter(adapter.GetAddressOf()).FAILED)
|
||||
return null;
|
||||
|
||||
if (adapter.As(&adapter4).FAILED)
|
||||
return null;
|
||||
|
||||
var vmi = default(DXGI_QUERY_VIDEO_MEMORY_INFO);
|
||||
adapter4.Get()->QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP.DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &vmi);
|
||||
return ((long)vmi.CurrentUsage, (long)vmi.CurrentReservation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -452,23 +443,23 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
/// </summary>
|
||||
public void ClearStacks()
|
||||
{
|
||||
this.scene?.ClearStacksOnContext();
|
||||
ImGuiHelpers.ClearStacksOnContext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggle Windows 11 immersive mode on the game window.
|
||||
/// </summary>
|
||||
/// <param name="enabled">Value.</param>
|
||||
internal void SetImmersiveMode(bool enabled)
|
||||
internal unsafe void SetImmersiveMode(bool enabled)
|
||||
{
|
||||
if (this.GameWindowHandle == 0)
|
||||
throw new InvalidOperationException("Game window is not yet ready.");
|
||||
var value = enabled ? 1 : 0;
|
||||
((Result)NativeFunctions.DwmSetWindowAttribute(
|
||||
this.GameWindowHandle,
|
||||
NativeFunctions.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
ref value,
|
||||
sizeof(int))).CheckError();
|
||||
var value = enabled ? 1u : 0u;
|
||||
DwmSetWindowAttribute(
|
||||
this.GameWindowHandle,
|
||||
(uint)DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||
&value,
|
||||
sizeof(int)).ThrowOnError();
|
||||
}
|
||||
|
||||
private static InterfaceManager WhenFontsReady()
|
||||
|
|
@ -483,7 +474,7 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void RenderImGui(RawDX11Scene scene)
|
||||
private static void RenderImGui(IImGuiBackend backend)
|
||||
{
|
||||
var conf = Service<DalamudConfiguration>.Get();
|
||||
|
||||
|
|
@ -491,22 +482,22 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
ImGuiHelpers.NewFrame();
|
||||
|
||||
// Enable viewports if there are no issues.
|
||||
if (conf.IsDisableViewport || scene.SwapChain.IsFullScreen || ImGui.GetPlatformIO().Monitors.Size == 1)
|
||||
if (conf.IsDisableViewport || backend.IsMainViewportFullScreen() || ImGui.GetPlatformIO().Monitors.Size == 1)
|
||||
ImGui.GetIO().ConfigFlags &= ~ImGuiConfigFlags.ViewportsEnable;
|
||||
else
|
||||
ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.ViewportsEnable;
|
||||
|
||||
scene.Render();
|
||||
backend.Render();
|
||||
}
|
||||
|
||||
private void InitScene(IntPtr swapChain)
|
||||
private unsafe void InitScene(IDXGISwapChain* swapChain)
|
||||
{
|
||||
RawDX11Scene newScene;
|
||||
IWin32Backend newBackend;
|
||||
using (Timings.Start("IM Scene Init"))
|
||||
{
|
||||
try
|
||||
{
|
||||
newScene = new RawDX11Scene(swapChain);
|
||||
newBackend = new Dx11Win32Backend(swapChain);
|
||||
}
|
||||
catch (DllNotFoundException ex)
|
||||
{
|
||||
|
|
@ -517,7 +508,8 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
IntPtr.Zero,
|
||||
"Dalamud plugins require the Microsoft Visual C++ Redistributable to be installed.\nPlease install the runtime from the official Microsoft website or disable Dalamud.\n\nDo you want to download the redistributable now?",
|
||||
"Dalamud Error",
|
||||
User32.MessageBoxOptions.MB_YESNO | User32.MessageBoxOptions.MB_TOPMOST | User32.MessageBoxOptions.MB_ICONERROR);
|
||||
User32.MessageBoxOptions.MB_YESNO | User32.MessageBoxOptions.MB_TOPMOST |
|
||||
User32.MessageBoxOptions.MB_ICONERROR);
|
||||
|
||||
if (res == User32.MessageBoxResult.IDYES)
|
||||
{
|
||||
|
|
@ -538,14 +530,18 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
var startInfo = Service<Dalamud>.Get().StartInfo;
|
||||
var configuration = Service<DalamudConfiguration>.Get();
|
||||
|
||||
var iniFileInfo = new FileInfo(Path.Combine(Path.GetDirectoryName(startInfo.ConfigurationPath)!, "dalamudUI.ini"));
|
||||
var iniFileInfo = new FileInfo(
|
||||
Path.Combine(Path.GetDirectoryName(startInfo.ConfigurationPath)!, "dalamudUI.ini"));
|
||||
|
||||
try
|
||||
{
|
||||
if (iniFileInfo.Length > 1200000)
|
||||
{
|
||||
Log.Warning("dalamudUI.ini was over 1mb, deleting");
|
||||
iniFileInfo.CopyTo(Path.Combine(iniFileInfo.DirectoryName!, $"dalamudUI-{DateTimeOffset.Now.ToUnixTimeSeconds()}.ini"));
|
||||
iniFileInfo.CopyTo(
|
||||
Path.Combine(
|
||||
iniFileInfo.DirectoryName!,
|
||||
$"dalamudUI-{DateTimeOffset.Now.ToUnixTimeSeconds()}.ini"));
|
||||
iniFileInfo.Delete();
|
||||
}
|
||||
}
|
||||
|
|
@ -554,16 +550,18 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
Log.Error(ex, "Could not delete dalamudUI.ini");
|
||||
}
|
||||
|
||||
newScene.UpdateCursor = this.isOverrideGameCursor;
|
||||
newScene.ImGuiIniPath = iniFileInfo.FullName;
|
||||
newScene.OnBuildUI += this.Display;
|
||||
newScene.OnNewInputFrame += this.OnNewInputFrame;
|
||||
newBackend.UpdateCursor = this.isOverrideGameCursor;
|
||||
newBackend.IniPath = iniFileInfo.FullName;
|
||||
newBackend.BuildUi += this.Display;
|
||||
newBackend.NewInputFrame += this.OnNewInputFrame;
|
||||
|
||||
StyleModel.TransferOldModels();
|
||||
|
||||
if (configuration.SavedStyles == null || configuration.SavedStyles.All(x => x.Name != StyleModelV1.DalamudStandard.Name))
|
||||
if (configuration.SavedStyles == null ||
|
||||
configuration.SavedStyles.All(x => x.Name != StyleModelV1.DalamudStandard.Name))
|
||||
{
|
||||
configuration.SavedStyles = new List<StyleModel> { StyleModelV1.DalamudStandard, StyleModelV1.DalamudClassic };
|
||||
configuration.SavedStyles = new List<StyleModel>
|
||||
{ StyleModelV1.DalamudStandard, StyleModelV1.DalamudClassic };
|
||||
configuration.ChosenStyle = StyleModelV1.DalamudStandard.Name;
|
||||
}
|
||||
else if (configuration.SavedStyles.Count == 1)
|
||||
|
|
@ -619,97 +617,64 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
Log.Information("[IM] Scene & ImGui setup OK!");
|
||||
}
|
||||
|
||||
this.scene = newScene;
|
||||
this.scene = newBackend;
|
||||
Service<InterfaceManagerWithScene>.Provide(new(this));
|
||||
|
||||
this.wndProcHookManager.PreWndProc += this.WndProcHookManagerOnPreWndProc;
|
||||
}
|
||||
|
||||
private unsafe void WndProcHookManagerOnPreWndProc(WndProcEventArgs args)
|
||||
private void WndProcHookManagerOnPreWndProc(WndProcEventArgs args)
|
||||
{
|
||||
var r = this.scene?.ProcessWndProcW(args.Hwnd, (User32.WindowMessage)args.Message, args.WParam, args.LParam);
|
||||
var r = this.scene?.ProcessWndProcW(args.Hwnd, args.Message, args.WParam, args.LParam);
|
||||
if (r is not null)
|
||||
args.SuppressWithValue(r.Value);
|
||||
}
|
||||
|
||||
private void ReshadeOnPresentDetour(nint swapChain, uint flags, nint presentParams)
|
||||
/*
|
||||
* NOTE(goat): When hooking ReShade DXGISwapChain::runtime_present, this is missing the syncInterval arg.
|
||||
* Seems to work fine regardless, I guess, so whatever.
|
||||
*/
|
||||
private unsafe HRESULT PresentDetour(IDXGISwapChain* swapChain, uint syncInterval, uint presentFlags)
|
||||
{
|
||||
if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
|
||||
Debug.Assert(this.presentHook is not null, "How did PresentDetour get called when presentHook is null?");
|
||||
|
||||
if (this.scene is null)
|
||||
{
|
||||
this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
|
||||
return;
|
||||
this.InitScene(swapChain);
|
||||
if (this.scene is null)
|
||||
throw new InvalidOperationException("InitScene did not set this.scene?");
|
||||
}
|
||||
|
||||
Debug.Assert(this.reshadeOnPresentHook is not null, "this.reshadeOnPresentHook is not null");
|
||||
Debug.Assert(this.dalamudAtlas is not null, "this.dalamudAtlas is not null");
|
||||
if (!this.scene.IsAttachedToPresentationTarget((nint)swapChain))
|
||||
return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||
|
||||
if (this.scene == null)
|
||||
this.InitScene(swapChain);
|
||||
|
||||
Debug.Assert(this.scene is not null, "InitScene did not set the scene field, but did not throw an exception.");
|
||||
|
||||
if (!this.dalamudAtlas!.HasBuiltAtlas)
|
||||
// Do not do anything yet if no font atlas has been built yet.
|
||||
if (this.dalamudAtlas?.HasBuiltAtlas is not true)
|
||||
{
|
||||
if (this.dalamudAtlas.BuildTask.Exception != null)
|
||||
if (this.dalamudAtlas?.BuildTask.Exception != null)
|
||||
{
|
||||
// TODO: Can we do something more user-friendly here? Unload instead?
|
||||
Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts");
|
||||
Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud");
|
||||
}
|
||||
|
||||
this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
|
||||
return;
|
||||
return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||
}
|
||||
|
||||
this.CumulativePresentCalls++;
|
||||
this.IsMainThreadInPresent = true;
|
||||
|
||||
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
||||
action.InvokeSafely();
|
||||
|
||||
this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
|
||||
|
||||
this.CumulativePresentCalls++;
|
||||
this.PreImGuiRender();
|
||||
RenderImGui(this.scene!);
|
||||
this.PostImGuiRender();
|
||||
this.IsMainThreadInPresent = false;
|
||||
|
||||
return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||
}
|
||||
|
||||
private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags)
|
||||
private void PreImGuiRender()
|
||||
{
|
||||
if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
|
||||
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||
|
||||
Debug.Assert(this.dxgiPresentHook is not null, "How did PresentDetour get called when presentHook is null?");
|
||||
Debug.Assert(this.dalamudAtlas is not null, "dalamudAtlas should have been set already");
|
||||
|
||||
if (this.scene == null)
|
||||
this.InitScene(swapChain);
|
||||
|
||||
Debug.Assert(this.scene is not null, "InitScene did not set the scene field, but did not throw an exception.");
|
||||
|
||||
if (!this.dalamudAtlas!.HasBuiltAtlas)
|
||||
{
|
||||
if (this.dalamudAtlas.BuildTask.Exception != null)
|
||||
{
|
||||
// TODO: Can we do something more user-friendly here? Unload instead?
|
||||
Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts");
|
||||
Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud");
|
||||
}
|
||||
|
||||
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||
}
|
||||
|
||||
this.CumulativePresentCalls++;
|
||||
this.IsMainThreadInPresent = true;
|
||||
|
||||
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
||||
action.InvokeSafely();
|
||||
|
||||
RenderImGui(this.scene!);
|
||||
this.PostImGuiRender();
|
||||
this.IsMainThreadInPresent = false;
|
||||
|
||||
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||
}
|
||||
|
||||
private void PostImGuiRender()
|
||||
|
|
@ -761,13 +726,14 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
GlyphMaxAdvanceX = DefaultFontSizePx,
|
||||
})));
|
||||
this.IconFontFixedWidthHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
|
||||
e => e.OnPreBuild(tk => tk.AddDalamudAssetFont(
|
||||
DalamudAsset.FontAwesomeFreeSolid,
|
||||
new()
|
||||
{
|
||||
SizePx = Service<FontAtlasFactory>.Get().DefaultFontSpec.SizePx,
|
||||
GlyphRanges = new ushort[] { 0x20, 0x20, 0x00 },
|
||||
})));
|
||||
e => e.OnPreBuild(
|
||||
tk => tk.AddDalamudAssetFont(
|
||||
DalamudAsset.FontAwesomeFreeSolid,
|
||||
new()
|
||||
{
|
||||
SizePx = Service<FontAtlasFactory>.Get().DefaultFontSpec.SizePx,
|
||||
GlyphRanges = [0x20, 0x20, 0x00],
|
||||
})));
|
||||
this.MonoFontHandle = (FontHandle)this.dalamudAtlas.NewDelegateFontHandle(
|
||||
e => e.OnPreBuild(
|
||||
tk => tk.AddDalamudAssetFont(
|
||||
|
|
@ -799,10 +765,7 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
() =>
|
||||
{
|
||||
// Update the ImGui default font.
|
||||
unsafe
|
||||
{
|
||||
ImGui.GetIO().NativePtr->FontDefault = fontLocked.ImFont;
|
||||
}
|
||||
ImGui.GetIO().NativePtr->FontDefault = fontLocked.ImFont;
|
||||
|
||||
// Update the reference to the resources of the default font.
|
||||
this.defaultFontResourceLock?.Dispose();
|
||||
|
|
@ -813,7 +776,7 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
|
||||
_ = this.dalamudAtlas.BuildFontsAsync();
|
||||
|
||||
|
|
@ -843,56 +806,54 @@ internal class InterfaceManager : IInternalDisposableService
|
|||
this.ResizeBuffersDetour);
|
||||
Log.Verbose($"ResizeBuffers address {Util.DescribeAddress(this.resizeBuffersHook!.Address)}");
|
||||
|
||||
if (SwapChainHelper.ReshadeOnPresent is null)
|
||||
{
|
||||
var addr = (nint)SwapChainHelper.GameDeviceSwapChainVtbl->Present;
|
||||
this.dxgiPresentHook = Hook<DxgiPresentDelegate>.FromAddress(addr, this.PresentDetour);
|
||||
Log.Verbose($"ReShade::DXGISwapChain::on_present address {Util.DescribeAddress(addr)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var addr = (nint)SwapChainHelper.ReshadeOnPresent;
|
||||
this.reshadeOnPresentHook = Hook<ReshadeOnPresentDelegate>.FromAddress(addr, this.ReshadeOnPresentDetour);
|
||||
Log.Verbose($"IDXGISwapChain::Present address {Util.DescribeAddress(addr)}");
|
||||
}
|
||||
this.presentHook = Hook<PresentDelegate>.FromAddress(
|
||||
(nint)SwapChainHelper.GameDeviceSwapChainVtbl->Present,
|
||||
this.PresentDetour);
|
||||
Log.Verbose(
|
||||
$"IDXGISwapChain::Present address {Util.DescribeAddress(SwapChainHelper.GameDeviceSwapChainVtbl->Present)}");
|
||||
|
||||
this.setCursorHook.Enable();
|
||||
this.dxgiPresentHook?.Enable();
|
||||
this.reshadeOnPresentHook?.Enable();
|
||||
this.presentHook.Enable();
|
||||
this.resizeBuffersHook.Enable();
|
||||
}
|
||||
|
||||
private IntPtr ResizeBuffersDetour(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags)
|
||||
private unsafe HRESULT ResizeBuffersDetour(
|
||||
IDXGISwapChain* swapChain,
|
||||
uint bufferCount,
|
||||
uint width,
|
||||
uint height,
|
||||
uint newFormat,
|
||||
uint swapChainFlags)
|
||||
{
|
||||
if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
|
||||
return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
||||
|
||||
#if DEBUG
|
||||
Log.Verbose($"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}");
|
||||
Log.Verbose(
|
||||
$"Calling resizebuffers swap@{(nint)swapChain:X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}");
|
||||
#endif
|
||||
|
||||
this.ResizeBuffers?.InvokeSafely();
|
||||
|
||||
// We have to ensure we're working with the main swapchain, as other viewports might be resizing as well.
|
||||
if (this.scene?.IsAttachedToPresentationTarget((nint)swapChain) is not true)
|
||||
return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
||||
|
||||
this.scene?.OnPreResize();
|
||||
|
||||
var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
||||
if (ret.ToInt64() == 0x887A0001)
|
||||
{
|
||||
if (ret == DXGI.DXGI_ERROR_INVALID_CALL)
|
||||
Log.Error("invalid call to resizeBuffers");
|
||||
}
|
||||
|
||||
this.scene?.OnPostResize((int)width, (int)height);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private IntPtr SetCursorDetour(IntPtr hCursor)
|
||||
private HCURSOR SetCursorDetour(HCURSOR hCursor)
|
||||
{
|
||||
if (this.lastWantCapture && (!this.scene?.IsImGuiCursor(hCursor) ?? false) && this.OverrideGameCursor)
|
||||
return IntPtr.Zero;
|
||||
return default;
|
||||
|
||||
return this.setCursorHook?.IsDisposed is not false
|
||||
? User32.SetCursor(new(hCursor, false)).DangerousGetHandle()
|
||||
? SetCursor(hCursor)
|
||||
: this.setCursorHook.Original(hCursor);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Logging.Internal;
|
||||
|
|
@ -318,8 +317,8 @@ internal sealed partial class FontAtlasFactory
|
|||
if (this.disposed)
|
||||
return;
|
||||
|
||||
r.Result.OnNewRenderFrame += this.ImGuiSceneOnNewRenderFrame;
|
||||
this.disposables.Add(() => r.Result.OnNewRenderFrame -= this.ImGuiSceneOnNewRenderFrame);
|
||||
r.Result.NewRenderFrame += this.ImGuiSceneOnNewRenderFrame;
|
||||
this.disposables.Add(() => r.Result.NewRenderFrame -= this.ImGuiSceneOnNewRenderFrame);
|
||||
}
|
||||
|
||||
if (this.AutoRebuildMode == FontAtlasAutoRebuildMode.OnNewFrame)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Dalamud.Data;
|
|||
using Dalamud.Game;
|
||||
using Dalamud.Interface.FontIdentifier;
|
||||
using Dalamud.Interface.GameFonts;
|
||||
using Dalamud.Interface.ImGuiBackend;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.Internal;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
|
|
@ -19,8 +20,6 @@ using Dalamud.Utility;
|
|||
|
||||
using ImGuiNET;
|
||||
|
||||
using ImGuiScene;
|
||||
|
||||
using Lumina.Data.Files;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
|
@ -151,9 +150,9 @@ internal sealed partial class FontAtlasFactory
|
|||
public TextureManager TextureManager => Service<TextureManager>.Get();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the async task for <see cref="RawDX11Scene"/> inside <see cref="InterfaceManager"/>.
|
||||
/// Gets the async task for <see cref="IImGuiBackend"/> inside <see cref="InterfaceManager"/>.
|
||||
/// </summary>
|
||||
public Task<RawDX11Scene> SceneTask { get; }
|
||||
public Task<IImGuiBackend> SceneTask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the default glyph ranges (glyph ranges of <see cref="GameFontFamilyAndSize.Axis12"/>).
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ internal sealed partial class TextureManager
|
|||
/// <param name="device">The device.</param>
|
||||
public void Setup(ID3D11Device* device)
|
||||
{
|
||||
var assembly = typeof(ImGuiScene.ImGui_Impl_DX11).Assembly;
|
||||
var assembly = typeof(SimpleDrawerImpl).Assembly;
|
||||
|
||||
// Create the vertex shader
|
||||
if (this.vertexShader.IsEmpty() || this.inputLayout.IsEmpty())
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ internal sealed partial class TextureManager
|
|||
private unsafe TextureManager(InterfaceManager.InterfaceManagerWithScene withScene)
|
||||
{
|
||||
using var failsafe = new DisposeSafety.ScopedFinalizer();
|
||||
failsafe.Add(this.device = new((ID3D11Device*)withScene.Manager.Device!.NativePointer));
|
||||
failsafe.Add(this.device = new((ID3D11Device*)withScene.Manager.Scene!.DeviceHandle));
|
||||
failsafe.Add(this.dynamicPriorityTextureLoader = new(Math.Max(1, Environment.ProcessorCount - 1)));
|
||||
failsafe.Add(this.sharedTextureManager = new(this));
|
||||
failsafe.Add(this.wicManager = new(this));
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ using ImGuiNET;
|
|||
|
||||
using Serilog;
|
||||
|
||||
using SharpDX.Direct3D11;
|
||||
|
||||
namespace Dalamud.Interface;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -120,7 +118,7 @@ public interface IUiBuilder
|
|||
/// <summary>
|
||||
/// Gets the game's active Direct3D device.
|
||||
/// </summary>
|
||||
Device Device { get; }
|
||||
SharpDX.Direct3D11.Device Device { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the game's main window handle.
|
||||
|
|
@ -259,6 +257,8 @@ public sealed class UiBuilder : IDisposable, IUiBuilder
|
|||
private IFontHandle? monoFontHandle;
|
||||
private IFontHandle? iconFontFixedWidthHandle;
|
||||
|
||||
private SharpDX.Direct3D11.Device? sdxDevice;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UiBuilder"/> class and registers it.
|
||||
/// You do not have to call this manually.
|
||||
|
|
@ -418,12 +418,13 @@ public sealed class UiBuilder : IDisposable, IUiBuilder
|
|||
/// <summary>
|
||||
/// Gets the game's active Direct3D device.
|
||||
/// </summary>
|
||||
public Device Device => this.InterfaceManagerWithScene!.Device!;
|
||||
public SharpDX.Direct3D11.Device Device =>
|
||||
this.sdxDevice ??= new(this.InterfaceManagerWithScene!.Scene!.DeviceHandle);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the game's main window handle.
|
||||
/// </summary>
|
||||
public IntPtr WindowHandlePtr => this.InterfaceManagerWithScene!.WindowHandlePtr;
|
||||
public nint WindowHandlePtr => this.InterfaceManagerWithScene!.GameWindowHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this plugin should hide its UI automatically when the game's UI is hidden.
|
||||
|
|
|
|||
|
|
@ -8,13 +8,12 @@ using System.Text;
|
|||
using System.Text.Unicode;
|
||||
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using Dalamud.Interface.ImGuiBackend.InputHandler;
|
||||
using Dalamud.Interface.ManagedFontAtlas;
|
||||
using Dalamud.Interface.ManagedFontAtlas.Internals;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
|
||||
using VirtualKey = Dalamud.Game.ClientState.Keys.VirtualKey;
|
||||
|
||||
|
|
@ -23,7 +22,7 @@ namespace Dalamud.Interface.Utility;
|
|||
/// <summary>
|
||||
/// Class containing various helper methods for use with ImGui inside Dalamud.
|
||||
/// </summary>
|
||||
public static class ImGuiHelpers
|
||||
public static partial class ImGuiHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the main viewport.
|
||||
|
|
@ -373,7 +372,7 @@ public static class ImGuiHelpers
|
|||
/// <returns>The ImGuiKey that corresponds to this VirtualKey, or <c>ImGuiKey.None</c> otherwise.</returns>
|
||||
public static ImGuiKey VirtualKeyToImGuiKey(VirtualKey key)
|
||||
{
|
||||
return ImGui_Input_Impl_Direct.VirtualKeyToImGuiKey((int)key);
|
||||
return Win32InputHandler.VirtualKeyToImGuiKey((int)key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -383,7 +382,7 @@ public static class ImGuiHelpers
|
|||
/// <returns>The VirtualKey that corresponds to this ImGuiKey, or <c>VirtualKey.NO_KEY</c> otherwise.</returns>
|
||||
public static VirtualKey ImGuiKeyToVirtualKey(ImGuiKey key)
|
||||
{
|
||||
return (VirtualKey)ImGui_Input_Impl_Direct.ImGuiKeyToVirtualKey(key);
|
||||
return (VirtualKey)Win32InputHandler.ImGuiKeyToVirtualKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -587,6 +586,12 @@ public static class ImGuiHelpers
|
|||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the stack in the current ImGui context.
|
||||
/// </summary>
|
||||
[LibraryImport("cimgui", EntryPoint = "igCustom_ClearStacks")]
|
||||
internal static partial void ClearStacksOnContext();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to validate that <paramref name="fontPtr"/> is valid.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Dalamud.Interface.ImGuiBackend.Renderers;
|
||||
using Dalamud.Memory;
|
||||
|
||||
using ImGuiScene;
|
||||
using Lumina.Data.Files;
|
||||
|
||||
using TerraFX.Interop.DirectX;
|
||||
|
||||
namespace Dalamud.Utility;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -13,7 +15,9 @@ namespace Dalamud.Utility;
|
|||
public static class TexFileExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the image data formatted for <see cref="RawDX11Scene.LoadImageRaw"/>.
|
||||
/// Returns the image data formatted for <see cref="IImGuiRenderer.CreateTexture2D"/>,
|
||||
/// using <see cref="DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM"/>.<br />
|
||||
/// <b>Consider using <see cref="TexFile.ImageData"/> with <see cref="DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM"/>.</b>
|
||||
/// </summary>
|
||||
/// <param name="texFile">The TexFile to format.</param>
|
||||
/// <returns>The formatted image data.</returns>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue