Window.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. using Ryujinx.Common.Configuration;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Metal.Effects;
  5. using Ryujinx.Graphics.Metal.SharpMetalExtensions;
  6. using SharpMetal.ObjectiveCCore;
  7. using SharpMetal.QuartzCore;
  8. using System;
  9. using System.Runtime.Versioning;
  10. using AntiAliasing = Ryujinx.Graphics.GAL.AntiAliasing;
  11. using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
  12. namespace Ryujinx.Graphics.Metal
  13. {
  14. [SupportedOSPlatform("macos")]
  15. class Window : IWindow, IDisposable
  16. {
  17. public bool ScreenCaptureRequested { get; set; }
  18. private readonly MetalRenderer _renderer;
  19. private CAMetalLayer _metalLayer;
  20. private int _width;
  21. private int _height;
  22. private int _requestedWidth;
  23. private int _requestedHeight;
  24. // private bool _vsyncEnabled;
  25. private AntiAliasing _currentAntiAliasing;
  26. private bool _updateEffect;
  27. private IPostProcessingEffect _effect;
  28. private IScalingFilter _scalingFilter;
  29. private bool _isLinear;
  30. // private float _scalingFilterLevel;
  31. private bool _updateScalingFilter;
  32. private ScalingFilter _currentScalingFilter;
  33. // private bool _colorSpacePassthroughEnabled;
  34. public Window(MetalRenderer renderer, CAMetalLayer metalLayer)
  35. {
  36. _renderer = renderer;
  37. _metalLayer = metalLayer;
  38. }
  39. private unsafe void ResizeIfNeeded()
  40. {
  41. if (_requestedWidth != 0 && _requestedHeight != 0)
  42. {
  43. // TODO: This is actually a CGSize, but there is no overload for that, so fill the first two fields of rect with the size.
  44. var rect = new NSRect(_requestedWidth, _requestedHeight, 0, 0);
  45. ObjectiveC.objc_msgSend(_metalLayer, "setDrawableSize:", rect);
  46. _requestedWidth = 0;
  47. _requestedHeight = 0;
  48. }
  49. }
  50. public unsafe void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
  51. {
  52. if (_renderer.Pipeline is Pipeline pipeline && texture is Texture tex)
  53. {
  54. ResizeIfNeeded();
  55. var drawable = new CAMetalDrawable(ObjectiveC.IntPtr_objc_msgSend(_metalLayer, "nextDrawable"));
  56. _width = (int)drawable.Texture.Width;
  57. _height = (int)drawable.Texture.Height;
  58. UpdateEffect();
  59. if (_effect != null)
  60. {
  61. // TODO: Run Effects
  62. // view = _effect.Run()
  63. }
  64. int srcX0, srcX1, srcY0, srcY1;
  65. if (crop.Left == 0 && crop.Right == 0)
  66. {
  67. srcX0 = 0;
  68. srcX1 = tex.Width;
  69. }
  70. else
  71. {
  72. srcX0 = crop.Left;
  73. srcX1 = crop.Right;
  74. }
  75. if (crop.Top == 0 && crop.Bottom == 0)
  76. {
  77. srcY0 = 0;
  78. srcY1 = tex.Height;
  79. }
  80. else
  81. {
  82. srcY0 = crop.Top;
  83. srcY1 = crop.Bottom;
  84. }
  85. if (ScreenCaptureRequested)
  86. {
  87. // TODO: Support screen captures
  88. ScreenCaptureRequested = false;
  89. }
  90. float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY));
  91. float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX));
  92. int dstWidth = (int)(_width * ratioX);
  93. int dstHeight = (int)(_height * ratioY);
  94. int dstPaddingX = (_width - dstWidth) / 2;
  95. int dstPaddingY = (_height - dstHeight) / 2;
  96. int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX;
  97. int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX;
  98. int dstY0 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
  99. int dstY1 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
  100. if (_scalingFilter != null)
  101. {
  102. // TODO: Run scaling filter
  103. }
  104. pipeline.Present(
  105. drawable,
  106. tex,
  107. new Extents2D(srcX0, srcY0, srcX1, srcY1),
  108. new Extents2D(dstX0, dstY0, dstX1, dstY1),
  109. _isLinear);
  110. }
  111. }
  112. public void SetSize(int width, int height)
  113. {
  114. _requestedWidth = width;
  115. _requestedHeight = height;
  116. }
  117. public void ChangeVSyncMode(VSyncMode vSyncMode)
  118. {
  119. switch (vSyncMode)
  120. {
  121. case VSyncMode.Unbounded:
  122. _metalLayer.DisplaySyncEnabled = false;
  123. break;
  124. case VSyncMode.Switch:
  125. _metalLayer.DisplaySyncEnabled = true;
  126. break;
  127. }
  128. }
  129. public void SetAntiAliasing(AntiAliasing effect)
  130. {
  131. if (_currentAntiAliasing == effect && _effect != null)
  132. {
  133. return;
  134. }
  135. _currentAntiAliasing = effect;
  136. _updateEffect = true;
  137. }
  138. public void SetScalingFilter(ScalingFilter type)
  139. {
  140. if (_currentScalingFilter == type && _effect != null)
  141. {
  142. return;
  143. }
  144. _currentScalingFilter = type;
  145. _updateScalingFilter = true;
  146. }
  147. public void SetScalingFilterLevel(float level)
  148. {
  149. // _scalingFilterLevel = level;
  150. _updateScalingFilter = true;
  151. }
  152. public void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled)
  153. {
  154. // _colorSpacePassthroughEnabled = colorSpacePassThroughEnabled;
  155. }
  156. private void UpdateEffect()
  157. {
  158. if (_updateEffect)
  159. {
  160. _updateEffect = false;
  161. switch (_currentAntiAliasing)
  162. {
  163. case AntiAliasing.Fxaa:
  164. _effect?.Dispose();
  165. Logger.Warning?.PrintMsg(LogClass.Gpu, "FXAA not implemented for Metal backend!");
  166. break;
  167. case AntiAliasing.None:
  168. _effect?.Dispose();
  169. _effect = null;
  170. break;
  171. case AntiAliasing.SmaaLow:
  172. case AntiAliasing.SmaaMedium:
  173. case AntiAliasing.SmaaHigh:
  174. case AntiAliasing.SmaaUltra:
  175. // var quality = _currentAntiAliasing - AntiAliasing.SmaaLow;
  176. Logger.Warning?.PrintMsg(LogClass.Gpu, "SMAA not implemented for Metal backend!");
  177. break;
  178. }
  179. }
  180. if (_updateScalingFilter)
  181. {
  182. _updateScalingFilter = false;
  183. switch (_currentScalingFilter)
  184. {
  185. case ScalingFilter.Bilinear:
  186. case ScalingFilter.Nearest:
  187. _scalingFilter?.Dispose();
  188. _scalingFilter = null;
  189. _isLinear = _currentScalingFilter == ScalingFilter.Bilinear;
  190. break;
  191. case ScalingFilter.Fsr:
  192. Logger.Warning?.PrintMsg(LogClass.Gpu, "FSR not implemented for Metal backend!");
  193. break;
  194. }
  195. }
  196. }
  197. public void Dispose()
  198. {
  199. _metalLayer.Dispose();
  200. }
  201. }
  202. }