630 lines
27 KiB
C#
630 lines
27 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using ImGuiNET;
|
|
using SharpDX.D3DCompiler;
|
|
using SharpDX.Direct3D11;
|
|
using SharpDX.DXGI;
|
|
using SharpDX.Mathematics.Interop;
|
|
using T3.Core.Operator.Slots;
|
|
using T3.Core.Rendering;
|
|
using T3.Core.Resource;
|
|
using T3.Core.SystemUi;
|
|
using T3.Editor.App;
|
|
using T3.Editor.Gui;
|
|
using T3.Editor.Gui.UiHelpers;
|
|
using T3.Editor.Gui.Windows;
|
|
using T3.Editor.Gui.Windows.Analyze;
|
|
using T3.Editor.SystemUi;
|
|
using T3.SystemUi;
|
|
using Buffer = SharpDX.Direct3D11.Buffer;
|
|
using Device = SharpDX.Direct3D11.Device;
|
|
using Vector2 = System.Numerics.Vector2;
|
|
|
|
namespace T3.Editor.UiContentDrawing;
|
|
|
|
/// <summary>
|
|
/// Renders draw data generated by ImGui with SharpDX render form
|
|
/// </summary>
|
|
internal sealed class WindowsUiContentDrawer : IUiContentDrawer<Device>
|
|
{
|
|
private IntPtr _imguiContext;
|
|
private object _contextLock;
|
|
private readonly ShaderResourceView _customImageView = new ShaderResourceView(IntPtr.Zero);
|
|
|
|
#region Init
|
|
public void Initialize(Device device, int width, int height, object contextLock, out IntPtr imguiContext)
|
|
{
|
|
if (device == null)
|
|
{
|
|
Log.Error("Can't initialize window without device.");
|
|
BlockingWindow.Instance.ShowMessageBox("Can't initialize rendering device.", "Graphics error");
|
|
EditorUi.Instance.ExitApplication();
|
|
imguiContext = IntPtr.Zero;
|
|
return;
|
|
}
|
|
|
|
_device = device;
|
|
_deviceContext = device.ImmediateContext;
|
|
_windowWidth = width;
|
|
_windowHeight = height;
|
|
_contextLock = contextLock;
|
|
|
|
lock (_contextLock)
|
|
{
|
|
IntPtr previousContext;
|
|
|
|
try
|
|
{
|
|
previousContext = ImGui.GetCurrentContext();
|
|
}
|
|
catch
|
|
{
|
|
// ignored
|
|
previousContext = IntPtr.Zero;
|
|
}
|
|
|
|
_imguiContext = ImGui.CreateContext();
|
|
ImGui.SetCurrentContext(_imguiContext);
|
|
|
|
SetPerFrameImGuiData(1f / 60f);
|
|
|
|
var io = ImGui.GetIO();
|
|
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors
|
|
| ImGuiBackendFlags.RendererHasVtxOffset;
|
|
io.ConfigFlags |= ImGuiConfigFlags.DockingEnable;
|
|
|
|
// ImGui 1.91 error-recovery: stack imbalances (Push/Pop, Begin/End, etc.)
|
|
// are auto-fixed and logged. Recoverable errors appear as:
|
|
// - Red warning bar in the editor (ConfigErrorRecoveryEnableTooltip)
|
|
// - [imgui-error] lines in Rider's terminal/stderr (ConfigErrorRecoveryEnableDebugLog)
|
|
// Hard asserts (zero-size buttons, invalid flags) are caught by our
|
|
// try/catch(SEHException) around the render loop.
|
|
//
|
|
// TODO: To get recoverable errors into the managed Log.Warning, build a tiny
|
|
// native shim that hooks ImGuiContext.ErrorCallback and forwards messages to
|
|
// a managed delegate via P/Invoke. See imgui.cpp:10611.
|
|
io.ConfigErrorRecovery = true;
|
|
io.ConfigErrorRecoveryEnableDebugLog = true;
|
|
io.ConfigErrorRecoveryEnableTooltip = true;
|
|
io.ConfigErrorRecoveryEnableAssert = false; // default is true; suppress native dialog
|
|
// io.ConfigDebugHighlightIdConflicts = false; // enable this to suppress ID conflict overlay
|
|
|
|
// restore previous context
|
|
if (previousContext != IntPtr.Zero)
|
|
{
|
|
ImGui.SetCurrentContext(previousContext);
|
|
}
|
|
}
|
|
|
|
imguiContext = _imguiContext;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Rendering
|
|
public void RenderCallback()
|
|
{
|
|
// An attempt to prevent System.ObjectDisposedException
|
|
if (Program.IsShuttingDown)
|
|
return;
|
|
|
|
// Frame-pacing wait via DXGI's FrameLatencyWaitableObject. Must run *before* any other
|
|
// per-frame work so the loop is locked to the swap chain's signalled cadence rather than
|
|
// queueing up speculatively. No-op if the waitable wasn't enabled at swap-chain creation.
|
|
ProgramWindows.Main.WaitForFrameLatency();
|
|
|
|
lock (_contextLock)
|
|
{
|
|
ImGui.SetCurrentContext(_imguiContext);
|
|
UiContentUpdate.SetupResourcesAndFontsWithScaling();
|
|
|
|
if (ProgramWindows.Main.IsMinimized && UserSettings.Config.SuspendRenderingWhenHidden)
|
|
{
|
|
Thread.Sleep(100);
|
|
return;
|
|
}
|
|
|
|
UiContentUpdate.TakeMeasurement();
|
|
ImGui.GetIO().DisplaySize = ProgramWindows.Main.Size;
|
|
|
|
ProgramWindows.HandleFullscreenToggle();
|
|
|
|
DirtyFlag.IncrementGlobalTicks();
|
|
T3Metrics.UiRenderingStarted();
|
|
|
|
if (!string.IsNullOrEmpty(Program.NewImGuiLayoutDefinition))
|
|
{
|
|
ImGui.LoadIniSettingsFromMemory(Program.NewImGuiLayoutDefinition);
|
|
Program.NewImGuiLayoutDefinition = string.Empty;
|
|
}
|
|
|
|
ImGui.NewFrame();
|
|
|
|
// Render 2nd view
|
|
ProgramWindows.Viewer.SetVisible(T3Ui.ShowSecondaryRenderWindow);
|
|
|
|
if (T3Ui.ShowSecondaryRenderWindow)
|
|
{
|
|
var viewer = ProgramWindows.Viewer;
|
|
ProgramWindows.Viewer.PrepareRenderingFrame();
|
|
|
|
ProgramWindows.SetVertexShader(SharedResources.FullScreenVertexShaderResource);
|
|
ProgramWindows.SetPixelShader(SharedResources.FullScreenPixelShaderResource);
|
|
|
|
if (UserSettings.Config.MirrorUiOnSecondView)
|
|
{
|
|
ProgramWindows.RebuildUiCopyTextureIfRequired();
|
|
ProgramWindows.CopyUiContentToShareTexture();
|
|
|
|
if (ProgramWindows.UiCopyTextureSrv != null && !ProgramWindows.UiCopyTextureSrv.IsDisposed)
|
|
{
|
|
ProgramWindows.SetRasterizerState(SharedResources.ViewWindowRasterizerState);
|
|
ProgramWindows.SetPixelShaderSRV(ProgramWindows.UiCopyTextureSrv);
|
|
ProgramWindows.DrawTextureToSecondaryRenderOutput();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (viewer.Texture is { IsDisposed: false })
|
|
{
|
|
if (_viewWindowBackgroundSrv == null ||
|
|
_viewWindowBackgroundSrv.Resource.NativePointer != viewer.Texture.NativePointer)
|
|
{
|
|
_viewWindowBackgroundSrv?.Dispose();
|
|
_viewWindowBackgroundSrv = new ShaderResourceView(Program.Device, viewer.Texture);
|
|
}
|
|
|
|
ProgramWindows.SetRasterizerState(SharedResources.ViewWindowRasterizerState);
|
|
ProgramWindows.SetPixelShaderSRV(_viewWindowBackgroundSrv);
|
|
ProgramWindows.DrawTextureToSecondaryRenderOutput();
|
|
}
|
|
else
|
|
{
|
|
Log.Debug($"Null {nameof(ShaderResourceView)} for 2nd render view");
|
|
}
|
|
}
|
|
}
|
|
|
|
ProgramWindows.Main.PrepareRenderingFrame();
|
|
|
|
// Clear the main window buffer for next frame
|
|
try
|
|
{
|
|
T3Ui.ProcessFrame();
|
|
ProgramWindows.RefreshViewport();
|
|
|
|
ImGui.Render();
|
|
ProgramWindows.Main.Form.ApplyRequestedCursor();
|
|
RenderDrawData(ImGui.GetDrawData());
|
|
}
|
|
catch (SEHException e)
|
|
{
|
|
// A native ImGui assertion fired (e.g. SetCursorPos extent check,
|
|
// invalid BeginChild flags, empty-stack pop). Log the full managed
|
|
// stack trace so the call site is visible in the console/log file
|
|
// alongside the [imgui-error] line that ImGui prints to stderr.
|
|
Log.Error($"ImGui native assertion failed (frame skipped):\n{e}");
|
|
|
|
// Try to end the frame so the next NewFrame doesn't assert on
|
|
// "NewFrame called without Render".
|
|
try { ImGui.EndFrame(); } catch { /* best-effort cleanup */ }
|
|
}
|
|
}
|
|
|
|
T3Metrics.UiRenderingCompleted();
|
|
|
|
ProgramWindows.Present(T3Ui.UseVSync, T3Ui.ShowSecondaryRenderWindow);
|
|
}
|
|
|
|
public void InitializeScaling()
|
|
{
|
|
ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.DpiEnableScaleFonts;
|
|
ImGui.GetIO().ConfigFlags |= ImGuiConfigFlags.DpiEnableScaleViewports;
|
|
|
|
unsafe
|
|
{
|
|
// Disable ImGui ini file settings
|
|
ImGui.GetIO().NativePtr->IniFilename = null;
|
|
}
|
|
}
|
|
|
|
public void RenderDrawData(ImDrawDataPtr drawData)
|
|
{
|
|
lock (_contextLock)
|
|
{
|
|
PrepareData(drawData);
|
|
DrawData(drawData);
|
|
}
|
|
}
|
|
|
|
private void PrepareData(ImDrawDataPtr drawData)
|
|
{
|
|
if (_vb == null || _vertexBufferSize < drawData.TotalVtxCount)
|
|
{
|
|
DisposeObj(ref _vb);
|
|
_vertexBufferSize = drawData.TotalVtxCount + 5000;
|
|
_vb = new Buffer(_device,
|
|
new BufferDescription()
|
|
{
|
|
SizeInBytes = _vertexBufferSize * Unsafe.SizeOf<ImDrawVert>(),
|
|
Usage = ResourceUsage.Dynamic,
|
|
BindFlags = BindFlags.VertexBuffer,
|
|
CpuAccessFlags = CpuAccessFlags.Write
|
|
});
|
|
}
|
|
|
|
if (_ib == null || _indexBufferSize < drawData.TotalIdxCount)
|
|
{
|
|
DisposeObj(ref _ib);
|
|
_indexBufferSize = drawData.TotalIdxCount + 10000;
|
|
_ib = new Buffer(_device,
|
|
new BufferDescription()
|
|
{
|
|
SizeInBytes = _indexBufferSize * Unsafe.SizeOf<ushort>(),
|
|
Usage = ResourceUsage.Dynamic,
|
|
BindFlags = BindFlags.IndexBuffer,
|
|
CpuAccessFlags = CpuAccessFlags.Write
|
|
});
|
|
}
|
|
|
|
// Copy and convert all vertices into a single contiguous buffer
|
|
_deviceContext.MapSubresource(_vb, MapMode.WriteDiscard, SharpDX.Direct3D11.MapFlags.None, out var vbStream);
|
|
_deviceContext.MapSubresource(_ib, MapMode.WriteDiscard, SharpDX.Direct3D11.MapFlags.None, out var ibStream);
|
|
for (int n = 0; n < drawData.CmdListsCount; n++)
|
|
{
|
|
ImDrawListPtr cmdList = drawData.CmdLists[n];
|
|
vbStream.WriteRange(cmdList.VtxBuffer.Data, (uint)(cmdList.VtxBuffer.Size * Unsafe.SizeOf<ImDrawVert>()));
|
|
ibStream.WriteRange(cmdList.IdxBuffer.Data, (uint)(cmdList.IdxBuffer.Size * Unsafe.SizeOf<ushort>()));
|
|
}
|
|
|
|
vbStream.Dispose();
|
|
ibStream.Dispose();
|
|
_deviceContext.UnmapSubresource(_vb, 0);
|
|
_deviceContext.UnmapSubresource(_ib, 0);
|
|
}
|
|
|
|
private readonly SharpDX.Mathematics.Interop.RawRectangle[] _prevScissorRects = new SharpDX.Mathematics.Interop.RawRectangle[16];
|
|
|
|
//Note : mrvux : unless you use multi viewport, you can set to 1, I leave 16 for safety here since it's only called once per frame, better than accumulating GC
|
|
private readonly SharpDX.Mathematics.Interop.RawViewportF[] _prevViewports = new SharpDX.Mathematics.Interop.RawViewportF[16];
|
|
|
|
private void DrawData(ImDrawDataPtr drawData)
|
|
{
|
|
// Setup orthographic projection matrix into our constant buffer
|
|
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
|
|
var imGuiIO = ImGui.GetIO();
|
|
var projectionMatrix = Matrix4x4.CreateOrthographicOffCenter(0.0f, imGuiIO.DisplaySize.X, imGuiIO.DisplaySize.Y, 0.0f, -1.0f, 1.0f);
|
|
|
|
ResourceUtils.WriteDynamicBufferData<Matrix4x4>(_deviceContext, _vertexConstantBuffer, projectionMatrix);
|
|
|
|
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
|
|
_deviceContext.Rasterizer.GetScissorRectangles(_prevScissorRects);
|
|
_deviceContext.Rasterizer.GetViewports(_prevViewports);
|
|
var prevRasterizerState = _deviceContext.Rasterizer.State;
|
|
var prevBlendState = _deviceContext.OutputMerger.BlendState;
|
|
var prevBlendFactor = _deviceContext.OutputMerger.BlendFactor;
|
|
var prevSampleMask = _deviceContext.OutputMerger.BlendSampleMask;
|
|
var prevDepthStencilState = _deviceContext.OutputMerger.DepthStencilState;
|
|
var prevStencilRef = _deviceContext.OutputMerger.DepthStencilReference;
|
|
var prevPsShaderResource = _deviceContext.PixelShader.GetShaderResources(0, 1)[0];
|
|
var prevPsSampler = _deviceContext.PixelShader.GetSamplers(0, 1);
|
|
var prevPs = _deviceContext.PixelShader.Get();
|
|
var prevVs = _deviceContext.VertexShader.Get();
|
|
var prevHs = _deviceContext.HullShader.Get();
|
|
var prevGs = _deviceContext.GeometryShader.Get();
|
|
var prevDs = _deviceContext.DomainShader.Get();
|
|
var prevVsConstantBuffer = _deviceContext.VertexShader.GetConstantBuffers(0, 1);
|
|
var prevPrimitiveTopology = _deviceContext.InputAssembler.PrimitiveTopology;
|
|
|
|
_deviceContext.InputAssembler.GetIndexBuffer(out var prevIndexBuffer, out var prevIndexBufferFormat, out var prevIndexBufferOffset);
|
|
Buffer[] prevVertexBuffer = new Buffer[1];
|
|
int[] prevVertexBufferOffset = new int[1], prevVertexBufferStride = new int[1];
|
|
_deviceContext.InputAssembler.GetVertexBuffers(0, 1, prevVertexBuffer, prevVertexBufferOffset, prevVertexBufferStride);
|
|
var prevInputLayout = _deviceContext.InputAssembler.InputLayout;
|
|
|
|
// Setup viewport
|
|
_deviceContext.Rasterizer.SetViewport(0, 0, drawData.DisplaySize.X, drawData.DisplaySize.Y);
|
|
|
|
// Bind shader and vertex buffers
|
|
int stride = Unsafe.SizeOf<ImDrawVert>();
|
|
int offset = 0;
|
|
_deviceContext.InputAssembler.InputLayout = _inputLayout;
|
|
_deviceContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(_vb, stride, offset));
|
|
_deviceContext.InputAssembler.SetIndexBuffer(_ib, Format.R16_UInt, 0);
|
|
_deviceContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;
|
|
_deviceContext.VertexShader.SetShader(_vertexShader, null, 0);
|
|
_deviceContext.VertexShader.SetConstantBuffer(0, _vertexConstantBuffer);
|
|
_deviceContext.PixelShader.SetShader(_pixelShader, null, 0);
|
|
_deviceContext.PixelShader.SetSampler(0, _fontSampler);
|
|
|
|
//make sure we have no tessel/gs
|
|
_deviceContext.HullShader.Set(null);
|
|
_deviceContext.DomainShader.Set(null);
|
|
_deviceContext.GeometryShader.Set(null);
|
|
|
|
// Setup render state
|
|
// Note : mrvux do not use properties for blend state / blend, since the native functions are packed with the
|
|
_deviceContext.OutputMerger.SetBlendState(_blendState, new RawColor4(0.0f, 0.0f, 0.0f, 0.0f)); //sample mask to -1, no GC
|
|
_deviceContext.OutputMerger.SetDepthStencilState(_depthStencilState, 0);
|
|
_deviceContext.Rasterizer.State = _rasterizerState;
|
|
|
|
// Render command lists
|
|
int vtxOffset = 0;
|
|
int idxOffset = 0;
|
|
Vector2 pos = drawData.DisplayPos;
|
|
for (int n = 0; n < drawData.CmdListsCount; n++)
|
|
{
|
|
var cmdList = drawData.CmdLists[n];
|
|
for (int cmdI = 0; cmdI < cmdList.CmdBuffer.Size; cmdI++)
|
|
{
|
|
var cmd = cmdList.CmdBuffer[cmdI];
|
|
if (cmd.UserCallback != IntPtr.Zero)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
else
|
|
{
|
|
_deviceContext.Rasterizer.SetScissorRectangle((int)(cmd.ClipRect.X - pos.X), (int)(cmd.ClipRect.Y - pos.Y),
|
|
(int)(cmd.ClipRect.Z - pos.X), (int)(cmd.ClipRect.W - pos.Y));
|
|
|
|
//This set native pointer without using QueryInterface or new, which allows a "GC free" cast
|
|
|
|
_customImageView.NativePointer = cmd.TextureId;
|
|
_deviceContext.PixelShader.SetShaderResource(0, _customImageView);
|
|
_deviceContext.DrawIndexed((int)cmd.ElemCount,
|
|
idxOffset + (int)cmd.IdxOffset,
|
|
vtxOffset + (int)cmd.VtxOffset);
|
|
|
|
//Set to IntPtr.Zero since that would create issue when disposing (on application Exit)
|
|
//Internally it only resets the pointer and deref the device if it was queried (which in this case, did not)
|
|
_customImageView.NativePointer = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
idxOffset += cmdList.IdxBuffer.Size;
|
|
vtxOffset += cmdList.VtxBuffer.Size;
|
|
}
|
|
|
|
// Restore modified DX state
|
|
_deviceContext.Rasterizer.SetScissorRectangles(_prevScissorRects);
|
|
_deviceContext.Rasterizer.SetViewports(_prevViewports);
|
|
_deviceContext.Rasterizer.State = prevRasterizerState;
|
|
_deviceContext.OutputMerger.BlendState = prevBlendState;
|
|
_deviceContext.OutputMerger.BlendFactor = prevBlendFactor;
|
|
_deviceContext.OutputMerger.BlendSampleMask = prevSampleMask;
|
|
_deviceContext.OutputMerger.DepthStencilState = prevDepthStencilState;
|
|
_deviceContext.OutputMerger.DepthStencilReference = prevStencilRef;
|
|
_deviceContext.PixelShader.SetShaderResources(0, prevPsShaderResource);
|
|
_deviceContext.PixelShader.SetSamplers(0, prevPsSampler);
|
|
_deviceContext.PixelShader.Set(prevPs);
|
|
_deviceContext.VertexShader.Set(prevVs);
|
|
_deviceContext.DomainShader.Set(prevDs);
|
|
_deviceContext.HullShader.Set(prevHs);
|
|
_deviceContext.GeometryShader.Set(prevGs);
|
|
_deviceContext.VertexShader.SetConstantBuffers(0, prevVsConstantBuffer);
|
|
_deviceContext.InputAssembler.PrimitiveTopology = prevPrimitiveTopology;
|
|
_deviceContext.InputAssembler.SetIndexBuffer(prevIndexBuffer, prevIndexBufferFormat, prevIndexBufferOffset);
|
|
_deviceContext.InputAssembler.SetVertexBuffers(0, prevVertexBuffer, prevVertexBufferOffset, prevVertexBufferStride);
|
|
_deviceContext.InputAssembler.InputLayout = prevInputLayout;
|
|
}
|
|
|
|
private void SetPerFrameImGuiData(float deltaSeconds)
|
|
{
|
|
ImGuiIOPtr io = ImGui.GetIO();
|
|
io.DisplaySize = new Vector2(_windowWidth / _scaleFactor.X, _windowHeight / _scaleFactor.Y);
|
|
io.DisplayFramebufferScale = _scaleFactor;
|
|
io.DeltaTime = deltaSeconds; // DeltaTime is in seconds.
|
|
}
|
|
#endregion
|
|
|
|
private bool _initialized;
|
|
|
|
#region setup
|
|
[SuppressMessage("ReSharper", "RedundantArgumentDefaultValue")]
|
|
public bool CreateDeviceObjectsAndFonts()
|
|
{
|
|
if (_device == null)
|
|
return false;
|
|
|
|
if (!_initialized)
|
|
{
|
|
if (!CreateShaders())
|
|
return false;
|
|
|
|
_initialized = true;
|
|
}
|
|
|
|
lock (_contextLock)
|
|
{
|
|
FontAtlasGenerator.CreateFontAtlasWithIcons(_device, _imguiContext, out _fontTextureView, out _fontSampler);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool CreateShaders()
|
|
{
|
|
if (_fontSampler == null)
|
|
DisposeDeviceObjects();
|
|
|
|
// Create the vertex shader
|
|
string vertexShader =
|
|
@"cbuffer vertexBuffer : register(b0)
|
|
{
|
|
float4x4 ProjectionMatrix;
|
|
};
|
|
struct VS_INPUT
|
|
{
|
|
float2 pos : POSITION;
|
|
float4 col : COLOR0;
|
|
float2 uv : TEXCOORD0;
|
|
};
|
|
|
|
struct PS_INPUT
|
|
{
|
|
float4 pos : SV_POSITION;
|
|
float4 col : COLOR0;
|
|
float2 uv : TEXCOORD0;
|
|
};
|
|
|
|
PS_INPUT main(VS_INPUT input)
|
|
{
|
|
PS_INPUT output;
|
|
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));
|
|
output.col = input.col;
|
|
output.uv = input.uv;
|
|
return output;
|
|
}";
|
|
|
|
_vertexShaderBlob = ShaderBytecode.Compile(vertexShader, "main", "vs_4_0", ShaderFlags.None, EffectFlags.None);
|
|
if (_vertexShaderBlob == null)
|
|
return false;
|
|
|
|
_vertexShader = new VertexShader(_device, _vertexShaderBlob);
|
|
if (_vertexShader == null)
|
|
return false;
|
|
|
|
// Create the input layout
|
|
_inputLayout = new InputLayout(_device, ShaderSignature.GetInputSignature(_vertexShaderBlob),
|
|
new[]
|
|
{
|
|
new InputElement("POSITION", 0, Format.R32G32_Float, 0, 0),
|
|
new InputElement("TEXCOORD", 0, Format.R32G32_Float, 8, 0),
|
|
new InputElement("COLOR", 0, Format.R8G8B8A8_UNorm, 16, 0)
|
|
});
|
|
|
|
// Create the constant buffer
|
|
_vertexConstantBuffer = ResourceUtils.CreateDynamicConstantBuffer(_device, Unsafe.SizeOf<Matrix4x4>());
|
|
|
|
// Create the pixel shader
|
|
string pixelShader =
|
|
@"struct PS_INPUT
|
|
{
|
|
float4 pos : SV_POSITION;
|
|
float4 col : COLOR0;
|
|
float2 uv : TEXCOORD0;
|
|
};
|
|
sampler sampler0;
|
|
Texture2D texture0;
|
|
|
|
float4 main(PS_INPUT input) : SV_Target
|
|
{
|
|
float4 out_col = input.col * texture0.Sample(sampler0, input.uv);
|
|
return out_col;
|
|
}";
|
|
|
|
_pixelShaderBlob = ShaderBytecode.Compile(pixelShader, "main", "ps_4_0", ShaderFlags.None, EffectFlags.None);
|
|
if (_pixelShaderBlob == null)
|
|
return false;
|
|
|
|
_pixelShader = new PixelShader(_device, _pixelShaderBlob);
|
|
if (_pixelShader == null)
|
|
return false;
|
|
|
|
// Create the blending setup
|
|
var blendDesc = new BlendStateDescription() { AlphaToCoverageEnable = false, IndependentBlendEnable = false };
|
|
blendDesc.RenderTarget[0].IsBlendEnabled = true;
|
|
blendDesc.RenderTarget[0].SourceBlend = BlendOption.SourceAlpha;
|
|
blendDesc.RenderTarget[0].DestinationBlend = BlendOption.InverseSourceAlpha;
|
|
blendDesc.RenderTarget[0].BlendOperation = BlendOperation.Add;
|
|
blendDesc.RenderTarget[0].SourceAlphaBlend = BlendOption.InverseSourceAlpha;
|
|
blendDesc.RenderTarget[0].DestinationAlphaBlend = BlendOption.Zero;
|
|
blendDesc.RenderTarget[0].AlphaBlendOperation = BlendOperation.Add;
|
|
blendDesc.RenderTarget[0].RenderTargetWriteMask = ColorWriteMaskFlags.All;
|
|
_blendState = new BlendState(_device, blendDesc);
|
|
|
|
// Create the rasterizer state
|
|
var rasterizerDesc = new RasterizerStateDescription()
|
|
{
|
|
FillMode = FillMode.Solid,
|
|
CullMode = CullMode.None,
|
|
IsScissorEnabled = true,
|
|
IsDepthClipEnabled = true
|
|
};
|
|
_rasterizerState = new RasterizerState(_device, rasterizerDesc);
|
|
|
|
// Create depth-stencil State
|
|
var depthStencilDesc = new DepthStencilStateDescription()
|
|
{
|
|
IsDepthEnabled = false,
|
|
DepthWriteMask = DepthWriteMask.All,
|
|
DepthComparison = Comparison.Always,
|
|
IsStencilEnabled = false
|
|
};
|
|
depthStencilDesc.FrontFace.FailOperation =
|
|
depthStencilDesc.FrontFace.DepthFailOperation = depthStencilDesc.FrontFace.PassOperation = StencilOperation.Keep;
|
|
|
|
depthStencilDesc.BackFace = depthStencilDesc.FrontFace;
|
|
_depthStencilState = new DepthStencilState(_device, depthStencilDesc);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
{
|
|
DisposeDeviceObjects();
|
|
}
|
|
|
|
private void DisposeObj<T>(ref T obj) where T : class, IDisposable
|
|
{
|
|
obj?.Dispose();
|
|
obj = null;
|
|
}
|
|
|
|
private void DisposeDeviceObjects()
|
|
{
|
|
if (_device == null)
|
|
return;
|
|
|
|
try
|
|
{
|
|
// Sadly a resource leak causes this to trigger memory exceptions.
|
|
// So disabled for now
|
|
|
|
DisposeObj(ref _fontSampler);
|
|
DisposeObj(ref _fontTextureView);
|
|
DisposeObj(ref _ib);
|
|
DisposeObj(ref _vb);
|
|
DisposeObj(ref _blendState);
|
|
DisposeObj(ref _depthStencilState);
|
|
DisposeObj(ref _rasterizerState);
|
|
DisposeObj(ref _pixelShader);
|
|
DisposeObj(ref _pixelShaderBlob);
|
|
DisposeObj(ref _vertexConstantBuffer);
|
|
DisposeObj(ref _inputLayout);
|
|
DisposeObj(ref _vertexShader);
|
|
DisposeObj(ref _vertexShaderBlob);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.Warning($"Failed to dispose resources :{e.Message}");
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
private Device _device;
|
|
private DeviceContext _deviceContext;
|
|
private Buffer _vb;
|
|
private Buffer _ib;
|
|
private ShaderBytecode _vertexShaderBlob;
|
|
private VertexShader _vertexShader;
|
|
private InputLayout _inputLayout;
|
|
private Buffer _vertexConstantBuffer;
|
|
private ShaderBytecode _pixelShaderBlob;
|
|
private PixelShader _pixelShader;
|
|
private SamplerState _fontSampler;
|
|
private ShaderResourceView _fontTextureView;
|
|
private RasterizerState _rasterizerState;
|
|
private BlendState _blendState;
|
|
private DepthStencilState _depthStencilState;
|
|
private int _vertexBufferSize = 5000, _indexBufferSize = 1000;
|
|
|
|
private int _windowWidth;
|
|
private int _windowHeight;
|
|
private static ShaderResourceView _viewWindowBackgroundSrv;
|
|
|
|
private readonly Vector2 _scaleFactor = Vector2.One;
|
|
} |