314 lines
11 KiB
C#
314 lines
11 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Interaction logic for DxView.xaml
|
|
/// </summary>
|
|
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<DxHost>()!;
|
|
_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<Resource>();
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |