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 _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(); _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); } } }