Direct2DImageSurface.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. using System;
  2. using System.Runtime.InteropServices;
  3. using System.Windows;
  4. using System.Windows.Interop;
  5. using Avalonia.Direct2D1;
  6. using SharpDX.Direct2D1;
  7. using SharpDX.Direct3D11;
  8. using SharpDX.Direct3D9;
  9. using SharpDX.DXGI;
  10. using AlphaMode = SharpDX.Direct2D1.AlphaMode;
  11. using Device = SharpDX.Direct3D11.Device;
  12. using Format = SharpDX.DXGI.Format;
  13. using Query = SharpDX.Direct3D11.Query;
  14. using QueryType = SharpDX.Direct3D11.QueryType;
  15. using RenderTarget = SharpDX.Direct2D1.RenderTarget;
  16. using Surface = SharpDX.DXGI.Surface;
  17. using Usage = SharpDX.Direct3D9.Usage;
  18. namespace Avalonia.Win32.Interop.Wpf
  19. {
  20. class Direct2DImageSurface : IExternalDirect2DRenderTargetSurface, IDisposable
  21. {
  22. class SwapBuffer: IDisposable
  23. {
  24. private readonly Query _event;
  25. private readonly SharpDX.Direct3D11.Resource _resource;
  26. private readonly SharpDX.Direct3D11.Resource _sharedResource;
  27. public SharpDX.Direct3D9.Surface Texture { get; }
  28. public RenderTarget Target { get;}
  29. public IntSize Size { get; }
  30. public SwapBuffer(IntSize size, Vector dpi)
  31. {
  32. int width = (int) size.Width;
  33. int height = (int) size.Height;
  34. _event = new Query(s_dxDevice, new QueryDescription {Type = QueryType.Event});
  35. using (var texture = new Texture2D(s_dxDevice, new Texture2DDescription
  36. {
  37. Width = width,
  38. Height = height,
  39. ArraySize = 1,
  40. MipLevels = 1,
  41. Format = Format.B8G8R8A8_UNorm,
  42. Usage = ResourceUsage.Default,
  43. SampleDescription = new SampleDescription(2, 0),
  44. BindFlags = BindFlags.RenderTarget,
  45. }))
  46. using (var surface = texture.QueryInterface<Surface>())
  47. {
  48. _resource = texture.QueryInterface<SharpDX.Direct3D11.Resource>();
  49. Target = new RenderTarget(Direct2D1Platform.Direct2D1Factory, surface,
  50. new RenderTargetProperties
  51. {
  52. DpiX = (float) dpi.X,
  53. DpiY = (float) dpi.Y,
  54. MinLevel = FeatureLevel.Level_10,
  55. PixelFormat = new PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied),
  56. });
  57. }
  58. using (var texture = new Texture2D(s_dxDevice, new Texture2DDescription
  59. {
  60. Width = width,
  61. Height = height,
  62. ArraySize = 1,
  63. MipLevels = 1,
  64. Format = Format.B8G8R8A8_UNorm,
  65. Usage = ResourceUsage.Default,
  66. SampleDescription = new SampleDescription(1, 0),
  67. BindFlags = BindFlags.RenderTarget|BindFlags.ShaderResource,
  68. OptionFlags = ResourceOptionFlags.Shared,
  69. }))
  70. using (var resource = texture.QueryInterface<SharpDX.DXGI.Resource>())
  71. {
  72. _sharedResource = texture.QueryInterface<SharpDX.Direct3D11.Resource>();
  73. var handle = resource.SharedHandle;
  74. using (var texture9 = new Texture(s_d3DDevice, texture.Description.Width,
  75. texture.Description.Height, 1,
  76. Usage.RenderTarget, SharpDX.Direct3D9.Format.A8R8G8B8, Pool.Default, ref handle))
  77. Texture = texture9.GetSurfaceLevel(0);
  78. }
  79. Size = size;
  80. }
  81. public void Dispose()
  82. {
  83. Texture?.Dispose();
  84. Target?.Dispose();
  85. _resource?.Dispose();
  86. _sharedResource?.Dispose();
  87. _event?.Dispose();
  88. }
  89. public void Flush()
  90. {
  91. s_dxDevice.ImmediateContext.ResolveSubresource(_resource, 0, _sharedResource, 0, Format.B8G8R8A8_UNorm);
  92. s_dxDevice.ImmediateContext.Flush();
  93. s_dxDevice.ImmediateContext.End(_event);
  94. s_dxDevice.ImmediateContext.GetData(_event).Dispose();
  95. }
  96. }
  97. private D3DImage _image;
  98. private SwapBuffer _backBuffer;
  99. private readonly WpfTopLevelImpl _impl;
  100. private static Device s_dxDevice;
  101. private static Direct3DEx s_d3DContext;
  102. private static DeviceEx s_d3DDevice;
  103. private Vector _oldDpi;
  104. [DllImport("user32.dll", SetLastError = false)]
  105. private static extern IntPtr GetDesktopWindow();
  106. static void EnsureDirectX()
  107. {
  108. if(s_d3DDevice != null)
  109. return;
  110. s_d3DContext = new Direct3DEx();
  111. SharpDX.Direct3D9.PresentParameters presentparams = new SharpDX.Direct3D9.PresentParameters
  112. {
  113. Windowed = true,
  114. SwapEffect = SharpDX.Direct3D9.SwapEffect.Discard,
  115. DeviceWindowHandle = GetDesktopWindow(),
  116. PresentationInterval = PresentInterval.Default
  117. };
  118. s_dxDevice = s_dxDevice ?? AvaloniaLocator.Current.GetService<SharpDX.DXGI.Device>()
  119. .QueryInterface<SharpDX.Direct3D11.Device>();
  120. s_d3DDevice = new DeviceEx(s_d3DContext, 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.HardwareVertexProcessing | CreateFlags.Multithreaded | CreateFlags.FpuPreserve, presentparams);
  121. }
  122. public Direct2DImageSurface(WpfTopLevelImpl impl)
  123. {
  124. _impl = impl;
  125. }
  126. public RenderTarget GetOrCreateRenderTarget()
  127. {
  128. EnsureDirectX();
  129. var scale = _impl.GetScaling();
  130. var size = new IntSize(_impl.ActualWidth * scale.X, _impl.ActualHeight * scale.Y);
  131. var dpi = scale * 96;
  132. if (_backBuffer!=null && _backBuffer.Size == size)
  133. return _backBuffer.Target;
  134. if (_image == null || _oldDpi.X != dpi.X || _oldDpi.Y != dpi.Y)
  135. {
  136. _image = new D3DImage(dpi.X, dpi.Y);
  137. _oldDpi = dpi;
  138. }
  139. _impl.ImageSource = _image;
  140. RemoveAndDispose(ref _backBuffer);
  141. if (size == default(IntSize))
  142. {
  143. _image.Lock();
  144. _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
  145. _image.Unlock();
  146. return null;
  147. }
  148. _backBuffer = new SwapBuffer(size, dpi);
  149. return _backBuffer.Target;
  150. }
  151. static void RemoveAndDispose<T>(ref T d) where T : IDisposable
  152. {
  153. d?.Dispose();
  154. d = default(T);
  155. }
  156. void Swap()
  157. {
  158. _backBuffer.Flush();
  159. _image.Lock();
  160. _image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _backBuffer?.Texture?.NativePointer ?? IntPtr.Zero, true);
  161. _image.AddDirtyRect(new Int32Rect(0, 0, _image.PixelWidth, _image.PixelHeight));
  162. _image.Unlock();
  163. }
  164. public void DestroyRenderTarget()
  165. {
  166. RemoveAndDispose(ref _backBuffer);
  167. }
  168. public void BeforeDrawing()
  169. {
  170. }
  171. public void AfterDrawing() => Swap();
  172. public void Dispose()
  173. {
  174. RemoveAndDispose(ref _backBuffer);
  175. }
  176. }
  177. }