From 932f489faa80fae28f13e899c330816f5560f549 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 10 Dec 2016 17:26:16 +0100 Subject: [PATCH] Use the Direct2D 1.1 render target initialization. To prevent D2D from auto-stretching our render target to the window size (which causes problems now we're multi-threaded) we have to move to the D2D1.1 render target initialization. I used the info here to work out how to do this: https://katyscode.wordpress.com/2013/01/23/migrating-existing-direct2d-applications-to-use-direct2d-1-1-functionality-in-windows-7/ - it's not exactly simple! --- samples/BindingTest/App.config | 4 +- samples/ControlCatalog.Desktop/App.config | 8 + samples/RenderTest/App.config | 8 + samples/TestApplication/App.config | 4 +- samples/VirtualizationTest/App.config | 8 + samples/XamlTestApplication/App.config | 4 +- samples/interop/WindowsInteropTest/App.config | 14 +- .../Avalonia.Direct2D1.csproj | 13 +- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 2 +- .../Avalonia.Direct2D1/HwndRenderTarget.cs | 178 ++++++++++++++++++ .../Media/DrawingContextImpl.cs | 10 +- .../Avalonia.Direct2D1/Media/TileBrushImpl.cs | 1 + .../Avalonia.Direct2D1/RenderTarget.cs | 83 -------- src/Windows/Avalonia.Direct2D1/app.config | 4 +- .../Avalonia.Direct2D1/packages.config | 5 +- .../App.config | 14 +- tests/Avalonia.Direct2D1.UnitTests/app.config | 4 +- tests/Avalonia.RenderTests/app.config | 4 +- 18 files changed, 263 insertions(+), 105 deletions(-) create mode 100644 src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs diff --git a/samples/BindingTest/App.config b/samples/BindingTest/App.config index 98b10dfce2..d9f870f27b 100644 --- a/samples/BindingTest/App.config +++ b/samples/BindingTest/App.config @@ -7,11 +7,11 @@ - + - + diff --git a/samples/ControlCatalog.Desktop/App.config b/samples/ControlCatalog.Desktop/App.config index 2231c71e63..10a06d8258 100644 --- a/samples/ControlCatalog.Desktop/App.config +++ b/samples/ControlCatalog.Desktop/App.config @@ -9,6 +9,14 @@ + + + + + + + + diff --git a/samples/RenderTest/App.config b/samples/RenderTest/App.config index 2231c71e63..10a06d8258 100644 --- a/samples/RenderTest/App.config +++ b/samples/RenderTest/App.config @@ -9,6 +9,14 @@ + + + + + + + + diff --git a/samples/TestApplication/App.config b/samples/TestApplication/App.config index 2c29a77dc3..94fe888cf0 100644 --- a/samples/TestApplication/App.config +++ b/samples/TestApplication/App.config @@ -11,11 +11,11 @@ - + - + diff --git a/samples/VirtualizationTest/App.config b/samples/VirtualizationTest/App.config index 2231c71e63..10a06d8258 100644 --- a/samples/VirtualizationTest/App.config +++ b/samples/VirtualizationTest/App.config @@ -9,6 +9,14 @@ + + + + + + + + diff --git a/samples/XamlTestApplication/App.config b/samples/XamlTestApplication/App.config index 2e9ac285c7..b53eb3c9ad 100644 --- a/samples/XamlTestApplication/App.config +++ b/samples/XamlTestApplication/App.config @@ -11,11 +11,11 @@ - + - + diff --git a/samples/interop/WindowsInteropTest/App.config b/samples/interop/WindowsInteropTest/App.config index 8324aa6ff1..801821bf0d 100644 --- a/samples/interop/WindowsInteropTest/App.config +++ b/samples/interop/WindowsInteropTest/App.config @@ -1,6 +1,18 @@ - + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj index f99efa1678..94499bc507 100644 --- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj +++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj @@ -37,16 +37,20 @@ true - - ..\..\..\packages\SharpDX.3.1.0\lib\net45\SharpDX.dll + + ..\..\..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll True ..\..\..\packages\SharpDX.Direct2D1.3.1.0\lib\net45\SharpDX.Direct2D1.dll True - - ..\..\..\packages\SharpDX.DXGI.3.1.0\lib\net45\SharpDX.DXGI.dll + + ..\..\..\packages\SharpDX.Direct3D11.3.1.1\lib\net45\SharpDX.Direct3D11.dll + True + + + ..\..\..\packages\SharpDX.DXGI.3.1.1\lib\net45\SharpDX.DXGI.dll True @@ -79,6 +83,7 @@ + diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 61f15fe8da..a60965923a 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -83,7 +83,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..7adae884cc --- /dev/null +++ b/src/Windows/Avalonia.Direct2D1/HwndRenderTarget.cs @@ -0,0 +1,178 @@ +// Copyright (c) The Avalonia Project. All rights reserved. +// Licensed under the MIT license. See licence.md file in the project root for full license information. + +using System; +using Avalonia.Direct2D1.Media; +using Avalonia.Media; +using Avalonia.Platform; +using Avalonia.Win32.Interop; +using SharpDX; +using SharpDX.Direct2D1; + +namespace Avalonia.Direct2D1 +{ + public class HwndRenderTarget : IRenderTarget + { + private readonly IntPtr _hwnd; + private readonly SharpDX.Direct3D11.Device _d3dDevice; + private DeviceContext _deviceContext; + private SharpDX.DXGI.SwapChain1 _swapChain; + private Size2 _savedSize; + private Size2F _savedDpi; + + /// + /// Initializes a new instance of the class. + /// + /// The window handle. + public HwndRenderTarget(IntPtr hwnd) + { + _hwnd = hwnd; + Direct2DFactory = AvaloniaLocator.Current.GetService(); + DirectWriteFactory = AvaloniaLocator.Current.GetService(); + + 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, + }; + + _d3dDevice = new SharpDX.Direct3D11.Device( + SharpDX.Direct3D.DriverType.Hardware, + SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport, + featureLevels); + + CreateSwapChain(); + } + + /// + /// Gets the Direct2D factory. + /// + public Factory Direct2DFactory + { + get; + } + + /// + /// Gets the DirectWrite factory. + /// + public SharpDX.DirectWrite.Factory DirectWriteFactory + { + get; + } + + /// + /// Creates a drawing context for a rendering session. + /// + /// An . + public IDrawingContextImpl CreateDrawingContext() + { + var size = GetWindowSize(); + var dpi = GetWindowDpi(); + + if (size != _savedSize || dpi != _savedDpi) + { + _savedSize = size; + _savedDpi = dpi; + CreateSwapChain(); + } + + return new DrawingContextImpl(_deviceContext, DirectWriteFactory, _swapChain); + } + + public void Dispose() + { + _deviceContext.Dispose(); + _swapChain.Dispose(); + _d3dDevice.Dispose(); + } + + private void CreateSwapChain() + { + using (var dxgiDevice = _d3dDevice.QueryInterface()) + using (var d2dDevice = new Device(dxgiDevice)) + using (var dxgiAdaptor = dxgiDevice.Adapter) + using (var dxgiFactory = dxgiAdaptor.GetParent()) + { + _deviceContext?.Dispose(); + _deviceContext = new DeviceContext(d2dDevice, DeviceContextOptions.None); + + var swapChainDesc = new SharpDX.DXGI.SwapChainDescription1 + { + Width = 0, + Height = 0, + Format = SharpDX.DXGI.Format.B8G8R8A8_UNorm, + Stereo = false, + SampleDescription = new SharpDX.DXGI.SampleDescription + { + Count = 1, + Quality = 0, + }, + Usage = SharpDX.DXGI.Usage.RenderTargetOutput, + BufferCount = 2, + Scaling = SharpDX.DXGI.Scaling.None, + SwapEffect = SharpDX.DXGI.SwapEffect.FlipSequential, + Flags = 0, + }; + + var dpi = Direct2DFactory.DesktopDpi; + + _swapChain?.Dispose(); + _swapChain = new SharpDX.DXGI.SwapChain1(dxgiFactory, _d3dDevice, _hwnd, ref swapChainDesc); + + using (var dxgiBackBuffer = _swapChain.GetBackBuffer(0)) + using (var d2dBackBuffer = new Bitmap1( + _deviceContext, + dxgiBackBuffer, + new BitmapProperties1( + new PixelFormat + { + AlphaMode = AlphaMode.Ignore, + Format = SharpDX.DXGI.Format.B8G8R8A8_UNorm + }, + dpi.Width, + dpi.Height, + BitmapOptions.Target | BitmapOptions.CannotDraw))) + { + _deviceContext.Target = d2dBackBuffer; + } + } + } + + private 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); + } + + private 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/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index a3eb22b7e8..5b82cfc0c2 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -19,6 +19,7 @@ namespace Avalonia.Direct2D1.Media public class DrawingContextImpl : IDrawingContextImpl, IDisposable { private readonly SharpDX.Direct2D1.RenderTarget _renderTarget; + private readonly SharpDX.DXGI.SwapChain1 _swapChain; private SharpDX.DirectWrite.Factory _directWriteFactory; /// @@ -28,9 +29,11 @@ namespace Avalonia.Direct2D1.Media /// The DirectWrite factory. public DrawingContextImpl( SharpDX.Direct2D1.RenderTarget renderTarget, - SharpDX.DirectWrite.Factory directWriteFactory) + SharpDX.DirectWrite.Factory directWriteFactory, + SharpDX.DXGI.SwapChain1 swapChain = null) { _renderTarget = renderTarget; + _swapChain = swapChain; _directWriteFactory = directWriteFactory; _renderTarget.BeginDraw(); } @@ -60,6 +63,11 @@ namespace Avalonia.Direct2D1.Media try { _renderTarget.EndDraw(); + + if (_swapChain != null) + { + _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/Media/TileBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs index 87f886008c..e567018264 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TileBrushImpl.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using Avalonia.Media; using Avalonia.RenderHelpers; using SharpDX.Direct2D1; diff --git a/src/Windows/Avalonia.Direct2D1/RenderTarget.cs b/src/Windows/Avalonia.Direct2D1/RenderTarget.cs index a81f67e2ce..5c535982f9 100644 --- a/src/Windows/Avalonia.Direct2D1/RenderTarget.cs +++ b/src/Windows/Avalonia.Direct2D1/RenderTarget.cs @@ -5,53 +5,18 @@ using System; using Avalonia.Direct2D1.Media; using Avalonia.Media; using Avalonia.Platform; -using Avalonia.Win32.Interop; -using SharpDX; using SharpDX.Direct2D1; -using SharpDX.Mathematics.Interop; -using DrawingContext = Avalonia.Media.DrawingContext; using DwFactory = SharpDX.DirectWrite.Factory; namespace Avalonia.Direct2D1 { public class RenderTarget : IRenderTarget { - private readonly IntPtr _hwnd; - private Size2 _savedSize; - private Size2F _savedDpi; - /// /// The render target. /// private readonly SharpDX.Direct2D1.RenderTarget _renderTarget; - /// - /// Initializes a new instance of the class. - /// - /// The window handle. - public RenderTarget(IntPtr hwnd) - { - _hwnd = hwnd; - Direct2DFactory = AvaloniaLocator.Current.GetService(); - DirectWriteFactory = AvaloniaLocator.Current.GetService(); - - RenderTargetProperties renderTargetProperties = new RenderTargetProperties - { - }; - - HwndRenderTargetProperties hwndProperties = new HwndRenderTargetProperties - { - Hwnd = hwnd, - PixelSize = _savedSize = GetWindowSize(), - PresentOptions = PresentOptions.Immediately, - }; - - _renderTarget = new WindowRenderTarget( - Direct2DFactory, - renderTargetProperties, - hwndProperties); - } - /// /// Initializes a new instance of the class. /// @@ -85,24 +50,6 @@ namespace Avalonia.Direct2D1 /// An . public IDrawingContextImpl CreateDrawingContext() { - var window = _renderTarget as WindowRenderTarget; - - if (window != null) - { - var size = GetWindowSize(); - var dpi = GetWindowDpi(); - - if (size != _savedSize) - { - window.Resize(_savedSize = size); - } - - if (dpi != _savedDpi) - { - window.DotsPerInch = _savedDpi = dpi; - } - } - return new DrawingContextImpl(_renderTarget, DirectWriteFactory); } @@ -110,35 +57,5 @@ namespace Avalonia.Direct2D1 { _renderTarget.Dispose(); } - - private 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); - } - - private 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/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..bc2a37c3b5 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 diff --git a/tests/Avalonia.DesignerSupport.TestApp/App.config b/tests/Avalonia.DesignerSupport.TestApp/App.config index 8324aa6ff1..801821bf0d 100644 --- a/tests/Avalonia.DesignerSupport.TestApp/App.config +++ b/tests/Avalonia.DesignerSupport.TestApp/App.config @@ -1,6 +1,18 @@ - + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Avalonia.Direct2D1.UnitTests/app.config b/tests/Avalonia.Direct2D1.UnitTests/app.config index 1723f9c94a..6af30494f2 100644 --- a/tests/Avalonia.Direct2D1.UnitTests/app.config +++ b/tests/Avalonia.Direct2D1.UnitTests/app.config @@ -4,11 +4,11 @@ - + - + diff --git a/tests/Avalonia.RenderTests/app.config b/tests/Avalonia.RenderTests/app.config index 920d33a751..dfd1350df6 100644 --- a/tests/Avalonia.RenderTests/app.config +++ b/tests/Avalonia.RenderTests/app.config @@ -8,11 +8,11 @@ - + - +