瀏覽代碼

Initial implementation for Direct2D rendering for WPF integration

Nikita Tsukanov 8 年之前
父節點
當前提交
76c90df2da

+ 1 - 0
build/SharpDX.props

@@ -3,6 +3,7 @@
     <PackageReference Include="SharpDX" Version="3.1.1" />
     <PackageReference Include="SharpDX.Direct2D1" Version="3.1.1" />
     <PackageReference Include="SharpDX.Direct3D11" Version="3.1.1" />
+    <PackageReference Include="SharpDX.Direct3D9" Version="3.1.1" Condition="'$(UseDirect3D9)' == 'true'" />
     <PackageReference Include="SharpDX.DXGI" Version="3.1.1" />
   </ItemGroup>
 </Project>

+ 1 - 1
samples/interop/WindowsInteropTest/Program.cs

@@ -15,7 +15,7 @@ namespace WindowsInteropTest
         {
             System.Windows.Forms.Application.EnableVisualStyles();
             System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
-            AppBuilder.Configure<App>().UseWin32().UseSkia().SetupWithoutStarting();
+            AppBuilder.Configure<App>().UseWin32().UseDirect2D1().SetupWithoutStarting();
             System.Windows.Forms.Application.Run(new SelectorForm());
         }
     }

+ 4 - 7
src/Windows/Avalonia.Direct2D1/ExternalRenderTarget.cs

@@ -15,7 +15,6 @@ namespace Avalonia.Direct2D1
     {
         private readonly IExternalDirect2DRenderTargetSurface _externalRenderTargetProvider;
         private readonly DirectWriteFactory _dwFactory;
-        private SharpDX.Direct2D1.RenderTarget _target;
         public ExternalRenderTarget(IExternalDirect2DRenderTargetSurface externalRenderTargetProvider,
             DirectWriteFactory dwFactory)
         {
@@ -25,15 +24,14 @@ namespace Avalonia.Direct2D1
 
         public void Dispose()
         {
-            _target?.Dispose();
-            _target = null;
+            _externalRenderTargetProvider.DestroyRenderTarget();
         }
 
         public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
         {
-            _target = _target ?? _externalRenderTargetProvider.CreateRenderTarget();
+            var target =  _externalRenderTargetProvider.GetOrCreateRenderTarget();
             _externalRenderTargetProvider.BeforeDrawing();
-            return new DrawingContextImpl(visualBrushRenderer, _target, _dwFactory, null, () =>
+            return new DrawingContextImpl(visualBrushRenderer, target, _dwFactory, null, () =>
             {
                 try
                 {
@@ -41,8 +39,7 @@ namespace Avalonia.Direct2D1
                 }
                 catch (SharpDXException ex) when ((uint) ex.HResult == 0x8899000C) // D2DERR_RECREATE_TARGET
                 {
-                    _target?.Dispose();
-                    _target = null;
+                    _externalRenderTargetProvider.DestroyRenderTarget();
                 }
             });
         }

+ 2 - 1
src/Windows/Avalonia.Direct2D1/IExternalDirect2DRenderTargetSurface.cs

@@ -8,7 +8,8 @@ namespace Avalonia.Direct2D1
 {
     public interface IExternalDirect2DRenderTargetSurface
     {
-        SharpDX.Direct2D1.RenderTarget CreateRenderTarget();
+        SharpDX.Direct2D1.RenderTarget GetOrCreateRenderTarget();
+        void DestroyRenderTarget();
         void BeforeDrawing();
         void AfterDrawing();
     }

+ 9 - 0
src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj

@@ -49,6 +49,7 @@
     </Compile>
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Wpf\CursorShim.cs" />
+    <Compile Include="Wpf\Direct2DImageSurface.cs" />
     <Compile Include="Wpf\WpfInteropExtensions.cs" />
     <Compile Include="Wpf\WpfAvaloniaHost.cs" />
     <Compile Include="Wpf\WpfMouseDevice.cs" />
@@ -104,10 +105,18 @@
       <Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
       <Name>Avalonia.Markup</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Avalonia.Direct2D1\Avalonia.Direct2D1.csproj">
+      <Project>{3e908f67-5543-4879-a1dc-08eace79b3cd}</Project>
+      <Name>Avalonia.Direct2D1</Name>
+    </ProjectReference>
     <ProjectReference Include="..\Avalonia.Win32\Avalonia.Win32.csproj">
       <Project>{811a76cf-1cf6-440f-963b-bbe31bd72a82}</Project>
       <Name>Avalonia.Win32</Name>
     </ProjectReference>
   </ItemGroup>
+  <PropertyGroup>
+    <UseDirect3D9>true</UseDirect3D9>
+  </PropertyGroup>
+  <Import Project="..\..\..\build\SharpDX.props" />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>

+ 206 - 0
src/Windows/Avalonia.Win32.Interop/Wpf/Direct2DImageSurface.cs

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

+ 9 - 2
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@@ -60,7 +60,7 @@ namespace Avalonia.Win32.Interop.Wpf
             PresentationSource.AddSourceChangedHandler(this, OnSourceChanged);
             _hook = WndProc;
             _ttl = this;
-            _surfaces = new object[] {new WritableBitmapSurface(this)};
+            _surfaces = new object[] {new WritableBitmapSurface(this), new Direct2DImageSurface(this)};
             _mouse = new WpfMouseDevice(this);
             _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
 
@@ -224,6 +224,13 @@ namespace Avalonia.Win32.Interop.Wpf
         Action<double> ITopLevelImpl.ScalingChanged { get; set; }
         Action ITopLevelImpl.Closed { get; set; }
         public new event Action LostFocus;
-        
+
+        internal Vector GetScaling()
+        {
+            var src = PresentationSource.FromVisual(this)?.CompositionTarget;
+            if (src == null)
+                return new Vector(1, 1);
+            return new Vector(src.TransformToDevice.M11, src.TransformToDevice.M22);
+        }
     }
 }

+ 1 - 9
src/Windows/Avalonia.Win32.Interop/Wpf/WritableBitmapSurface.cs

@@ -24,7 +24,7 @@ namespace Avalonia.Win32.Interop.Wpf
 
         public ILockedFramebuffer Lock()
         {
-            var scale = GetScaling();
+            var scale = _impl.GetScaling();
             var size = new Size(_impl.ActualWidth * scale.X, _impl.ActualHeight * scale.Y);
             var dpi = scale * 96;
             if (_bitmap == null || _bitmap.PixelWidth != (int) size.Width || _bitmap.PixelHeight != (int) size.Height)
@@ -69,13 +69,5 @@ namespace Avalonia.Win32.Interop.Wpf
             public Vector Dpi { get; }
             public PixelFormat Format => PixelFormat.Bgra8888;
         }
-
-        Vector GetScaling()
-        {
-            var src = PresentationSource.FromVisual(_impl)?.CompositionTarget;
-            if (src == null)
-                return new Vector(1, 1);
-            return new Vector(src.TransformToDevice.M11, src.TransformToDevice.M22);
-        }
     }
 }