|
@@ -0,0 +1,206 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Runtime.InteropServices;
|
|
|
+using System.Text;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using System.Windows;
|
|
|
+using System.Windows.Interop;
|
|
|
+using Avalonia.Direct2D1;
|
|
|
+using SharpDX;
|
|
|
+using SharpDX.Direct2D1;
|
|
|
+using SharpDX.Direct3D11;
|
|
|
+using SharpDX.Direct3D9;
|
|
|
+using SharpDX.DXGI;
|
|
|
+using AlphaMode = SharpDX.Direct2D1.AlphaMode;
|
|
|
+using Device = SharpDX.Direct3D11.Device;
|
|
|
+using Format = SharpDX.DXGI.Format;
|
|
|
+using MapFlags = SharpDX.Direct3D11.MapFlags;
|
|
|
+using PresentParameters = SharpDX.DXGI.PresentParameters;
|
|
|
+using RenderTarget = SharpDX.Direct2D1.RenderTarget;
|
|
|
+using Surface = SharpDX.DXGI.Surface;
|
|
|
+using SwapEffect = SharpDX.DXGI.SwapEffect;
|
|
|
+using Usage = SharpDX.Direct3D9.Usage;
|
|
|
+
|
|
|
+namespace Avalonia.Win32.Interop.Wpf
|
|
|
+{
|
|
|
+ class Direct2DImageSurface : IExternalDirect2DRenderTargetSurface
|
|
|
+ {
|
|
|
+ class Pair: IDisposable
|
|
|
+ {
|
|
|
+ public SharpDX.Direct3D9.Surface Texture { get; }
|
|
|
+ public SharpDX.Direct3D11.Resource D3D11Resource { get; }
|
|
|
+ public SharpDX.Direct3D11.Resource StagingResource { get; }
|
|
|
+ public RenderTarget Target { get;}
|
|
|
+ public Size Size { get; }
|
|
|
+
|
|
|
+ public Pair(Size size, Vector dpi)
|
|
|
+ {
|
|
|
+ int width = (int) size.Width;
|
|
|
+ int height = (int) size.Height;
|
|
|
+ using (var texture = new Texture2D(s_dxDevice, new Texture2DDescription
|
|
|
+ {
|
|
|
+ Width = width,
|
|
|
+ Height = height,
|
|
|
+ ArraySize = 1,
|
|
|
+ MipLevels = 1,
|
|
|
+ Format = Format.B8G8R8A8_UNorm,
|
|
|
+ Usage = ResourceUsage.Default,
|
|
|
+ SampleDescription = new SampleDescription(1, 0),
|
|
|
+ BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
|
|
|
+ OptionFlags = ResourceOptionFlags.Shared
|
|
|
+ }))
|
|
|
+ using (var surface = texture.QueryInterface<Surface>())
|
|
|
+ using (var resource = texture.QueryInterface<SharpDX.DXGI.Resource>())
|
|
|
+ {
|
|
|
+ D3D11Resource = texture.QueryInterface<SharpDX.Direct3D11.Resource>();
|
|
|
+ var handle = resource.SharedHandle;
|
|
|
+ using (var texture9 = new Texture(s_d3DDevice, texture.Description.Width,
|
|
|
+ texture.Description.Height, 1,
|
|
|
+ Usage.RenderTarget, SharpDX.Direct3D9.Format.A8R8G8B8, Pool.Default, ref handle))
|
|
|
+ Texture = texture9.GetSurfaceLevel(0);
|
|
|
+ Target = new RenderTarget(AvaloniaLocator.Current.GetService<SharpDX.Direct2D1.Factory>(), surface,
|
|
|
+ new RenderTargetProperties
|
|
|
+ {
|
|
|
+ DpiX = (float) dpi.X,
|
|
|
+ DpiY = (float) dpi.Y,
|
|
|
+ MinLevel = FeatureLevel.Level_10,
|
|
|
+ PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied),
|
|
|
+
|
|
|
+ });
|
|
|
+ }
|
|
|
+ using (var texture = new Texture2D(s_dxDevice, new Texture2DDescription
|
|
|
+ {
|
|
|
+ Width = Math.Min(width, 16),
|
|
|
+ Height = Math.Min(height, 16),
|
|
|
+ ArraySize = 1,
|
|
|
+ MipLevels = 1,
|
|
|
+ Format = Format.B8G8R8A8_UNorm,
|
|
|
+ Usage = ResourceUsage.Staging,
|
|
|
+ SampleDescription = new SampleDescription(1, 0),
|
|
|
+ CpuAccessFlags = CpuAccessFlags.Read
|
|
|
+ }))
|
|
|
+ StagingResource = texture.QueryInterface<SharpDX.Direct3D11.Resource>();
|
|
|
+ Size = size;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ Texture?.Dispose();
|
|
|
+ Target?.Dispose();
|
|
|
+ D3D11Resource?.Dispose();
|
|
|
+ StagingResource?.Dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Flush()
|
|
|
+ {
|
|
|
+
|
|
|
+ s_dxDevice.ImmediateContext.CopySubresourceRegion(D3D11Resource, 0,
|
|
|
+ new ResourceRegion(0, 0, 0, 1, 1, 1), StagingResource, 0, 0, 0, 0);
|
|
|
+ s_dxDevice.ImmediateContext.MapSubresource(StagingResource, 0, MapMode.Read, MapFlags.None);
|
|
|
+ s_dxDevice.ImmediateContext.UnmapSubresource(StagingResource, 0);
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private D3DImage _image;
|
|
|
+ private Pair _backBuffer;
|
|
|
+ private Pair _frontBuffer;
|
|
|
+ private readonly WpfTopLevelImpl _impl;
|
|
|
+ private static Device s_dxDevice;
|
|
|
+ private static Direct3DEx s_d3DContext;
|
|
|
+ private static DeviceEx s_d3DDevice;
|
|
|
+
|
|
|
+
|
|
|
+ [DllImport("user32.dll", SetLastError = false)]
|
|
|
+ private static extern IntPtr GetDesktopWindow();
|
|
|
+ void EnsureDirectX()
|
|
|
+ {
|
|
|
+ if(s_d3DDevice != null)
|
|
|
+ return;
|
|
|
+ s_d3DContext = new Direct3DEx();
|
|
|
+
|
|
|
+ SharpDX.Direct3D9.PresentParameters presentparams = new SharpDX.Direct3D9.PresentParameters
|
|
|
+ {
|
|
|
+ Windowed = true,
|
|
|
+ SwapEffect = SharpDX.Direct3D9.SwapEffect.Discard,
|
|
|
+ DeviceWindowHandle = GetDesktopWindow(),
|
|
|
+ PresentationInterval = PresentInterval.Default
|
|
|
+ };
|
|
|
+ s_dxDevice = s_dxDevice ?? AvaloniaLocator.Current.GetService<SharpDX.DXGI.Device>()
|
|
|
+ .QueryInterface<SharpDX.Direct3D11.Device>();
|
|
|
+ s_d3DDevice = new DeviceEx(s_d3DContext, 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded | CreateFlags.FpuPreserve, presentparams);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public Direct2DImageSurface(WpfTopLevelImpl impl)
|
|
|
+ {
|
|
|
+ _impl = impl;
|
|
|
+ }
|
|
|
+
|
|
|
+ public RenderTarget GetOrCreateRenderTarget()
|
|
|
+ {
|
|
|
+ EnsureDirectX();
|
|
|
+ var scale = _impl.GetScaling();
|
|
|
+ var size = new Size(_impl.ActualWidth * scale.X, _impl.ActualHeight * scale.Y);
|
|
|
+ var dpi = scale * 96;
|
|
|
+
|
|
|
+ if (_backBuffer!=null && _backBuffer.Size == size)
|
|
|
+ return _backBuffer.Target;
|
|
|
+
|
|
|
+ if (_image == null)
|
|
|
+ _image = new DX11Image();
|
|
|
+ _impl.ImageSource = _image;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ RemoveAndDispose(ref _backBuffer);
|
|
|
+ if (size == default(Size))
|
|
|
+ {
|
|
|
+ RemoveAndDispose(ref _frontBuffer);
|
|
|
+ _image.Lock();
|
|
|
+ _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
|
|
|
+ _image.Unlock();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ _backBuffer = new Pair(size, dpi);
|
|
|
+
|
|
|
+ return _backBuffer.Target;
|
|
|
+ }
|
|
|
+
|
|
|
+ void RemoveAndDispose<T>(ref T d) where T : IDisposable
|
|
|
+ {
|
|
|
+ d?.Dispose();
|
|
|
+ d = default(T);
|
|
|
+ }
|
|
|
+
|
|
|
+ void DoSwap()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ void Swap()
|
|
|
+ {
|
|
|
+ var oldFront = _frontBuffer;
|
|
|
+ _frontBuffer = _backBuffer;
|
|
|
+ _backBuffer = oldFront;
|
|
|
+ _frontBuffer.Flush();
|
|
|
+ _image.Lock();
|
|
|
+ _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _frontBuffer?.Texture?.NativePointer ?? IntPtr.Zero, true);
|
|
|
+ _image.AddDirtyRect(new Int32Rect(0, 0, _image.PixelWidth, _image.PixelHeight));
|
|
|
+ _image.Unlock();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void DestroyRenderTarget()
|
|
|
+ {
|
|
|
+ //?
|
|
|
+ }
|
|
|
+
|
|
|
+ public void BeforeDrawing()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void AfterDrawing() => Swap();
|
|
|
+ }
|
|
|
+}
|