From f44468a3ab6f2abb0d1cb5250ebe835b952dcaa2 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 11 Jan 2017 10:47:48 -0600 Subject: [PATCH] Added swap chain backed render target. --- Avalonia.sln.DotSettings | 19 +++ .../Avalonia.Direct2D1.csproj | 20 +-- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 36 ++++- .../Avalonia.Direct2D1/HwndRenderTarget.cs | 56 ++++++++ .../Media/DrawingContext.cs | 8 +- .../SwapChainRenderTarget.cs | 134 ++++++++++++++++++ src/Windows/Avalonia.Direct2D1/app.config | 4 +- .../Avalonia.Direct2D1/packages.config | 7 +- 8 files changed, 265 insertions(+), 19 deletions(-) create mode 100644 src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs create mode 100644 src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs diff --git a/Avalonia.sln.DotSettings b/Avalonia.sln.DotSettings index bf98899847..16c9218a7e 100644 --- a/Avalonia.sln.DotSettings +++ b/Avalonia.sln.DotSettings @@ -2,6 +2,24 @@ ExplicitlyExcluded ExplicitlyExcluded HINT + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="set_" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="_" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /> <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="False" Prefix="I" Suffix="" Style="AaBb" /> @@ -10,6 +28,7 @@ <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="False" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="s_" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="s_" Suffix="" Style="aaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj index 9f9558ff76..95ccc98692 100644 --- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj +++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj @@ -37,17 +37,17 @@ true - - ..\..\..\packages\SharpDX.3.1.0\lib\net45\SharpDX.dll - True + + ..\..\..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll - - ..\..\..\packages\SharpDX.Direct2D1.3.1.0\lib\net45\SharpDX.Direct2D1.dll - True + + ..\..\..\packages\SharpDX.Direct2D1.3.1.1\lib\net45\SharpDX.Direct2D1.dll - - ..\..\..\packages\SharpDX.DXGI.3.1.0\lib\net45\SharpDX.DXGI.dll - True + + ..\..\..\packages\SharpDX.Direct3D11.3.1.1\lib\net45\SharpDX.Direct3D11.dll + + + ..\..\..\packages\SharpDX.DXGI.3.1.1\lib\net45\SharpDX.DXGI.dll @@ -62,6 +62,7 @@ + @@ -79,6 +80,7 @@ + diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index b43eef2fa9..f86fa0b93a 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -8,6 +8,7 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Controls; using Avalonia.Rendering; +using SharpDX.Direct3D11; namespace Avalonia { @@ -29,20 +30,47 @@ namespace Avalonia.Direct2D1 private static readonly SharpDX.Direct2D1.Factory s_d2D1Factory = #if DEBUG - new SharpDX.Direct2D1.Factory(SharpDX.Direct2D1.FactoryType.SingleThreaded, SharpDX.Direct2D1.DebugLevel.Error); + new SharpDX.Direct2D1.Factory(SharpDX.Direct2D1.FactoryType.MultiThreaded, SharpDX.Direct2D1.DebugLevel.Error); #else - new SharpDX.Direct2D1.Factory(SharpDX.Direct2D1.FactoryType.SingleThreaded, SharpDX.Direct2D1.DebugLevel.None); + new SharpDX.Direct2D1.Factory(SharpDX.Direct2D1.FactoryType.MultiThreaded, SharpDX.Direct2D1.DebugLevel.None); #endif private static readonly SharpDX.DirectWrite.Factory s_dwfactory = new SharpDX.DirectWrite.Factory(); private static readonly SharpDX.WIC.ImagingFactory s_imagingFactory = new SharpDX.WIC.ImagingFactory(); + private static readonly SharpDX.DXGI.Device s_device; + + static Direct2D1Platform() + { + var featureLevels = new[] + { + SharpDX.Direct3D.FeatureLevel.Level_12_1, + SharpDX.Direct3D.FeatureLevel.Level_12_0, + SharpDX.Direct3D.FeatureLevel.Level_11_1, + SharpDX.Direct3D.FeatureLevel.Level_11_0, + SharpDX.Direct3D.FeatureLevel.Level_10_1, + SharpDX.Direct3D.FeatureLevel.Level_10_0, + SharpDX.Direct3D.FeatureLevel.Level_9_3, + SharpDX.Direct3D.FeatureLevel.Level_9_2, + SharpDX.Direct3D.FeatureLevel.Level_9_1, + }; + + using (var d3dDevice = new SharpDX.Direct3D11.Device( + SharpDX.Direct3D.DriverType.Hardware, + SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport | SharpDX.Direct3D11.DeviceCreationFlags.VideoSupport, + featureLevels)) + { + s_device = d3dDevice.QueryInterface(); + } + } + public static void Initialize() => AvaloniaLocator.CurrentMutable .Bind().ToConstant(s_instance) .Bind().ToConstant(s_instance) .BindToSelf(s_d2D1Factory) .BindToSelf(s_dwfactory) - .BindToSelf(s_imagingFactory); + .BindToSelf(s_imagingFactory) + .BindToSelf(s_device); public IBitmapImpl CreateBitmap(int width, int height) { @@ -70,7 +98,7 @@ namespace Avalonia.Direct2D1 { if (handle.HandleDescriptor == "HWND") { - return new RenderTarget(handle.Handle); + return new HwndRenderTarget(handle.Handle); } else { diff --git a/src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs new file mode 100644 index 0000000000..5c0c460dcb --- /dev/null +++ b/src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Win32.Interop; +using SharpDX; +using SharpDX.DXGI; + +namespace Avalonia.Direct2D1 +{ + class HwndRenderTarget : SwapChainRenderTarget + { + private readonly IntPtr _hwnd; + + public HwndRenderTarget(IntPtr hwnd) + { + _hwnd = hwnd; + } + + protected override SwapChain1 CreateSwapChain(Factory2 dxgiFactory, SwapChainDescription1 swapChainDesc) + { + return new SwapChain1(dxgiFactory, Device, _hwnd, ref swapChainDesc); + } + + protected override Size2F GetWindowDpi() + { + if (UnmanagedMethods.ShCoreAvailable) + { + uint dpix, dpiy; + + var monitor = UnmanagedMethods.MonitorFromWindow( + _hwnd, + UnmanagedMethods.MONITOR.MONITOR_DEFAULTTONEAREST); + + if (UnmanagedMethods.GetDpiForMonitor( + monitor, + UnmanagedMethods.MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, + out dpix, + out dpiy) == 0) + { + return new Size2F(dpix, dpiy); + } + } + + return new Size2F(96, 96); + } + + protected override Size2 GetWindowSize() + { + UnmanagedMethods.RECT rc; + UnmanagedMethods.GetClientRect(_hwnd, out rc); + return new Size2(rc.right - rc.left, rc.bottom - rc.top); + } + } +} diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs index 75a0f43d9f..decf3c6fc6 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs @@ -27,6 +27,8 @@ namespace Avalonia.Direct2D1.Media /// private SharpDX.DirectWrite.Factory _directWriteFactory; + private SharpDX.DXGI.SwapChain1 _swapChain; + /// /// Initializes a new instance of the class. /// @@ -34,10 +36,12 @@ namespace Avalonia.Direct2D1.Media /// The DirectWrite factory. public DrawingContext( SharpDX.Direct2D1.RenderTarget renderTarget, - SharpDX.DirectWrite.Factory directWriteFactory) + SharpDX.DirectWrite.Factory directWriteFactory, + SharpDX.DXGI.SwapChain1 swapChain = null) { _renderTarget = renderTarget; _directWriteFactory = directWriteFactory; + _swapChain = swapChain; _renderTarget.BeginDraw(); } @@ -60,6 +64,8 @@ namespace Avalonia.Direct2D1.Media try { _renderTarget.EndDraw(); + + _swapChain?.Present(1, SharpDX.DXGI.PresentFlags.None); } catch (SharpDXException ex) when((uint)ex.HResult == 0x8899000C) // D2DERR_RECREATE_TARGET { diff --git a/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs b/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs new file mode 100644 index 0000000000..a7b2f532c2 --- /dev/null +++ b/src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Media; +using Avalonia.Platform; +using Avalonia.Win32.Interop; +using SharpDX; +using SharpDX.Direct2D1; +using SharpDX.DXGI; +using AlphaMode = SharpDX.Direct2D1.AlphaMode; +using Device = SharpDX.Direct2D1.Device; +using Factory = SharpDX.Direct2D1.Factory; +using Factory2 = SharpDX.DXGI.Factory2; + +namespace Avalonia.Direct2D1 +{ + public abstract class SwapChainRenderTarget : IRenderTarget + { + private Size2 _savedSize; + private Size2F _savedDpi; + private DeviceContext _deviceContext; + private SwapChain1 _swapChain; + + protected SwapChainRenderTarget() + { + Device = AvaloniaLocator.Current.GetService(); + Direct2DFactory = AvaloniaLocator.Current.GetService(); + DirectWriteFactory = AvaloniaLocator.Current.GetService(); + } + + + /// + /// Gets the Direct2D factory. + /// + public Factory Direct2DFactory + { + get; + } + + /// + /// Gets the DirectWrite factory. + /// + public SharpDX.DirectWrite.Factory DirectWriteFactory + { + get; + } + + protected SharpDX.DXGI.Device Device { get; } + + /// + /// Creates a drawing context for a rendering session. + /// + /// An . + public DrawingContext CreateDrawingContext() + { + var size = GetWindowSize(); + var dpi = GetWindowDpi(); + + if (size != _savedSize || dpi != _savedDpi) + { + _savedSize = size; + _savedDpi = dpi; + CreateSwapChain(); + } + + return new DrawingContext(new Media.DrawingContext(_deviceContext, DirectWriteFactory, _swapChain)); + } + + public void Dispose() + { + _deviceContext.Dispose(); + _swapChain.Dispose(); + } + + private void CreateSwapChain() + { + using (var d2dDevice = new Device(Device)) + using (var dxgiAdaptor = Device.Adapter) + using (var dxgiFactory = dxgiAdaptor.GetParent()) + { + _deviceContext?.Dispose(); + _deviceContext = new DeviceContext(d2dDevice, DeviceContextOptions.None); + + var swapChainDesc = new SwapChainDescription1 + { + Width = _savedSize.Width, + Height = _savedSize.Height, + Format = Format.B8G8R8A8_UNorm, + Stereo = false, + SampleDescription = new SampleDescription + { + Count = 1, + Quality = 0, + }, + Usage = Usage.RenderTargetOutput, + BufferCount = 2, + Scaling = Scaling.None, + SwapEffect = SwapEffect.FlipSequential, + Flags = 0, + }; + + var dpi = Direct2DFactory.DesktopDpi; + + _swapChain?.Dispose(); + _swapChain = CreateSwapChain(dxgiFactory, swapChainDesc); + + using (var dxgiBackBuffer = _swapChain.GetBackBuffer(0)) + using (var d2dBackBuffer = new Bitmap1( + _deviceContext, + dxgiBackBuffer, + new BitmapProperties1( + new PixelFormat + { + AlphaMode = AlphaMode.Ignore, + Format = Format.B8G8R8A8_UNorm + }, + _savedDpi.Width, + _savedDpi.Height, + BitmapOptions.Target | BitmapOptions.CannotDraw))) + { + _deviceContext.Target = d2dBackBuffer; + } + } + } + + protected abstract SwapChain1 CreateSwapChain(Factory2 dxgiFactory, SwapChainDescription1 swapChainDesc); + + protected abstract Size2F GetWindowDpi(); + + protected abstract Size2 GetWindowSize(); + } +} diff --git a/src/Windows/Avalonia.Direct2D1/app.config b/src/Windows/Avalonia.Direct2D1/app.config index 743de168f3..60a1012655 100644 --- a/src/Windows/Avalonia.Direct2D1/app.config +++ b/src/Windows/Avalonia.Direct2D1/app.config @@ -8,11 +8,11 @@ - + - + diff --git a/src/Windows/Avalonia.Direct2D1/packages.config b/src/Windows/Avalonia.Direct2D1/packages.config index 57031c2b9d..780e6014e5 100644 --- a/src/Windows/Avalonia.Direct2D1/packages.config +++ b/src/Windows/Avalonia.Direct2D1/packages.config @@ -1,6 +1,7 @@  - - - + + + + \ No newline at end of file