Files
2026-04-18 22:31:51 +02:00

134 lines
4.4 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using Intromat.DXExt;
using Intromat.Pipelines;
using SharpDX.Direct3D;
using SharpDX.Direct3D9;
using Splat;
using D3D11 = SharpDX.Direct3D11;
namespace Intromat.Graphics
{
public class DxHost
{
public delegate void RenderDelegate(D3D11.Device device, D3D11.DeviceContext deviceContext);
public readonly ConcurrentQueue<RenderDelegate> _actionQueue;
public readonly D3D11.Device Device;
private readonly Thread _renderThread;
private IntPtr _renderWindowHandle;
public DeviceEx? D3D9Device;
public Dispatcher? RenderDispatcher;
public Dispatcher? MainDispatcher;
private readonly ManualResetEvent _renderThreadStarted = new(false);
public readonly D3D11.UserDefinedAnnotation? _annotation;
public readonly GraphicsAnalysis? _graphicsAnalysis;
public DxHost()
{
#if DEBUG
var flags = D3D11.DeviceCreationFlags.Debug;
#else
var flags = D3D11.DeviceCreationFlags.None;
#endif
Device = new D3D11.Device(DriverType.Hardware, flags);
_annotation = Device.ImmediateContext.QueryInterface<D3D11.UserDefinedAnnotation>();
_graphicsAnalysis = DebugInterface.TryCreateGraphicsAnalysis();
_actionQueue = new();
_renderThread = new(RenderThreadStart) { Name = "WPF SwapChain" };
_renderThread.SetApartmentState(ApartmentState.STA);
_renderThread.Start();
_renderThreadStarted.WaitOne();
var device = Device;
var context = Device.ImmediateContext;
MainDispatcher = Dispatcher.CurrentDispatcher;
Locator.CurrentMutable.Register(() => new DisplayTexturePipeline(device, context));
}
public void Load(Window window)
{
_renderWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
CreateDevice();
}
public void Unload()
{
RenderDispatcher?.BeginInvokeShutdown(DispatcherPriority.Background);
RenderDispatcher = null;
}
private void RenderThreadStart(object? o)
{
RenderDispatcher = Dispatcher.CurrentDispatcher;
RenderDispatcher.BeginInvoke(new Action(UpdateFrameTick));
_renderThreadStarted.Set();
Dispatcher.Run();
}
private void UpdateFrameTick()
{
if (RenderDispatcher == null)
return;
if (_actionQueue.Count == 0)
{
RenderDispatcher.BeginInvoke(new Action(UpdateFrameTick), DispatcherPriority.Background);
return;
}
EnsureTargetDevice();
while (_actionQueue.TryDequeue(out var action))
action(Device, Device.ImmediateContext);
RenderDispatcher?.BeginInvoke(new Action(UpdateFrameTick), DispatcherPriority.Render);
}
public void EnqueueAction(RenderDelegate action)
{
_actionQueue.Enqueue(action);
}
[DllImport("user32.dll")]
private static extern IntPtr MonitorFromWindow(IntPtr hWnd, int flags);
private void EnsureTargetDevice()
{
if (D3D9Device != null)
return;
MainDispatcher!.Invoke(CreateDevice);
}
private void CreateDevice()
{
var d3d9Ex = new Direct3DEx();
var adapter = 0;
if (d3d9Ex.AdapterCount > 1)
{
const int monitorDefaultToNearest = 2;
var hMon = MonitorFromWindow(_renderWindowHandle, monitorDefaultToNearest);
foreach (var a in d3d9Ex.AdaptersEx)
if (a.Monitor == hMon)
adapter = a.Adapter;
}
var parameters = new PresentParameters
{
PresentationInterval = PresentInterval.Immediate,
SwapEffect = SwapEffect.Discard,
Windowed = true
};
D3D9Device = new DeviceEx(d3d9Ex, adapter, DeviceType.Hardware, _renderWindowHandle, CreateFlags.HardwareVertexProcessing | CreateFlags.FpuPreserve | CreateFlags.Multithreaded, parameters);
}
}
}