using System; using System.Threading; using System.Windows; using System.Windows.Forms; using System.Windows.Interop; using System.Windows.Media; using Intromat.Graphics; using SharpDX.DXGI; using Splat; using D3D9 = SharpDX.Direct3D9; using D3D11 = SharpDX.Direct3D11; namespace Intromat.Views { /// /// Interaction logic for DxView.xaml /// public partial class DxView { private readonly D3DImage _d3dImage; private readonly DxHost _dxHost; private int _backBufferHeight = -1; private bool _backBufferMultiSample; private D3D11.Texture2D? _backBufferTexture; private D3D11.RenderTargetView? _backBufferView; private int _backBufferWidth = -1; private D3D9.Surface? _d3d9Surface; private D3D9.Texture? _d3d9Texture; private D3D11.Texture2D? _depthBufferTexture; private D3D11.DepthStencilView? _depthBufferView; private TimeSpan _lastRender; private volatile IntPtr _nextFrame = IntPtr.Zero; private bool _supportRemoteDesktop; private D3D11.Texture2D? _transferBuffer; private D3D11.Texture2D? _transferBuffer2; private D3D11.Query? _transferCompleteQuery; private DxHost.RenderDelegate? _updateHandler; public DxView() { InitializeComponent(); _dxHost = Locator.Current.GetService()!; _d3dImage = new D3DImage(); _swapChainImage.Source = _d3dImage; SizeChanged += HostSizeChanged; Loaded += OnLoaded; Unloaded += OnUnloaded; Dispatcher.ShutdownStarted += OnUnloaded; } public int ViewHeight { get; private set; } public int ViewWidth { get; private set; } private void OnLoaded(object? sender, RoutedEventArgs e) { CompositionTarget.Rendering += CompositionTargetRendering; CheckHostSize(); } private void OnUnloaded(object? sender, EventArgs e) { CompositionTarget.Rendering -= CompositionTargetRendering; } public void SetUpdateHandler(DxHost.RenderDelegate? handler) { _updateHandler = handler; } private void CompositionTargetRendering(object? sender, EventArgs e) { lock (this) { var args = (RenderingEventArgs)e; if (_lastRender == args.RenderingTime) return; _lastRender = args.RenderingTime; var nextFrame = _nextFrame; if (nextFrame != IntPtr.Zero) { _nextFrame = IntPtr.Zero; var image = _d3dImage; var hasFrontBuffer = image.IsFrontBufferAvailable; if (_supportRemoteDesktop || hasFrontBuffer) { image.Lock(); image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, nextFrame, _supportRemoteDesktop); var targetWidth = image.PixelWidth; var targetHeight = image.PixelHeight; image.AddDirtyRect(new Int32Rect(0, 0, targetWidth, targetHeight)); image.Unlock(); if (hasFrontBuffer) _supportRemoteDesktop = false; } else { _supportRemoteDesktop = SystemInformation.TerminalServerSession; } } } } public void Render() { _dxHost.EnqueueAction(Render); } public void Render(D3D11.Device device, D3D11.DeviceContext context) { lock (this) { EnsureBuffers(); if (_backBufferTexture == null || _transferBuffer == null || _updateHandler == null) return; context.OutputMerger.SetTargets(_depthBufferView, _backBufferView); context.Rasterizer.SetViewport(0, 0, ViewWidth, ViewHeight); try { _updateHandler(device, context); } catch (Exception e) { Console.WriteLine(e.ToString()); } if (_backBufferMultiSample) { context.ResolveSubresource(_backBufferTexture, 0, _transferBuffer2, 0, Format.B8G8R8A8_UNorm_SRgb); context.CopyResource(_transferBuffer2, _transferBuffer); } else { context.CopyResource(_backBufferTexture, _transferBuffer); } context.End(_transferCompleteQuery); while (!context.GetData(_transferCompleteQuery, out int completed) || completed == 0) Thread.Yield(); _nextFrame = _d3d9Surface!.NativePointer; } } private void HostSizeChanged(object sender, SizeChangedEventArgs e) { CheckHostSize(); } private void CheckHostSize() { lock (this) { double scaleX = 1.0f; double scaleY = 1.0f; if (PresentationSource.FromVisual(this)?.CompositionTarget is HwndTarget hwndTarget) { scaleX = hwndTarget.TransformToDevice.M11; scaleY = hwndTarget.TransformToDevice.M22; } var width = (int)Math.Ceiling(ActualWidth); var height = (int)Math.Ceiling(ActualHeight); width = width > 1 ? width : 1; height = height > 1 ? height : 1; if (ViewWidth == width && ViewHeight == height) return; ViewWidth = width; ViewHeight = height; Render(); } } private void EnsureBuffers() { lock (this) { if (_backBufferHeight != ViewHeight || _backBufferWidth != ViewWidth) { _backBufferHeight = ViewHeight; _backBufferWidth = ViewWidth; if (_d3d9Surface != null) { _d3d9Surface.Dispose(); _d3d9Surface = null; } if (_d3d9Texture != null) { _d3d9Texture.Dispose(); _d3d9Texture = null; } if (_transferBuffer != null) { _transferBuffer.Dispose(); _transferBuffer = null; } if (_transferBuffer2 != null) { _transferBuffer2.Dispose(); _transferBuffer2 = null; } if (_backBufferTexture != null) // TODO: test { _backBufferTexture.Dispose(); _backBufferTexture = null; } if (_backBufferView != null) { _backBufferView.Dispose(); _backBufferView = null; } if (_depthBufferTexture != null) // TODO: test { _depthBufferTexture.Dispose(); _depthBufferTexture = null; } if (_depthBufferView != null) { _depthBufferView.Dispose(); _depthBufferView = null; } if (_transferCompleteQuery != null) { _transferCompleteQuery.Dispose(); _transferCompleteQuery = null; } } if (_transferBuffer == null && _dxHost.D3D9Device != null) { var device = _dxHost.Device; var textureDesc = new D3D11.Texture2DDescription { ArraySize = 1, MipLevels = 1, Width = _backBufferWidth, Height = _backBufferHeight, SampleDescription = new SampleDescription(1, 0), // TAM Usage = D3D11.ResourceUsage.Default, CpuAccessFlags = D3D11.CpuAccessFlags.None, OptionFlags = D3D11.ResourceOptionFlags.Shared, BindFlags = D3D11.BindFlags.RenderTarget | D3D11.BindFlags.ShaderResource, Format = Format.B8G8R8A8_UNorm }; _transferBuffer = new D3D11.Texture2D(device, textureDesc); textureDesc.Format = Format.B8G8R8A8_UNorm_SRgb; textureDesc.OptionFlags = D3D11.ResourceOptionFlags.None; _transferBuffer2 = new D3D11.Texture2D(device, textureDesc); _backBufferMultiSample = true; textureDesc.SampleDescription = new SampleDescription(4, 0); textureDesc.BindFlags = D3D11.BindFlags.RenderTarget; _backBufferTexture = new D3D11.Texture2D(device, textureDesc); _backBufferView = new D3D11.RenderTargetView(device, _backBufferTexture); textureDesc.Format = Format.R32_Typeless; textureDesc.BindFlags = D3D11.BindFlags.DepthStencil; textureDesc.OptionFlags = D3D11.ResourceOptionFlags.None; var depthDesc = new D3D11.DepthStencilViewDescription { Format = Format.D32_Float, Flags = D3D11.DepthStencilViewFlags.None, Dimension = D3D11.DepthStencilViewDimension.Texture2DMultisampled, Texture2DMS = new D3D11.DepthStencilViewDescription.Texture2DMultisampledResource() }; _depthBufferTexture = new D3D11.Texture2D(device, textureDesc); _depthBufferView = new D3D11.DepthStencilView(device, _depthBufferTexture, depthDesc); var dxgiRes = _transferBuffer.QueryInterface(); var handle = dxgiRes.SharedHandle; _d3d9Texture = new D3D9.Texture(_dxHost.D3D9Device, _backBufferWidth, _backBufferHeight, 1, D3D9.Usage.RenderTarget, D3D9.Format.A8R8G8B8, D3D9.Pool.Default, ref handle); _d3d9Surface = _d3d9Texture.GetSurfaceLevel(0); var queryDesc = new D3D11.QueryDescription { Type = D3D11.QueryType.Event }; _transferCompleteQuery = new D3D11.Query(device, queryDesc); } } } } }