MetalRenderer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. using Ryujinx.Common.Configuration;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Shader.Translation;
  4. using SharpMetal.Metal;
  5. using SharpMetal.QuartzCore;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Runtime.Versioning;
  9. namespace Ryujinx.Graphics.Metal
  10. {
  11. [SupportedOSPlatform("macos")]
  12. public sealed class MetalRenderer : IRenderer
  13. {
  14. public const int TotalSets = 4;
  15. private readonly MTLDevice _device;
  16. private readonly MTLCommandQueue _queue;
  17. private readonly Func<CAMetalLayer> _getMetalLayer;
  18. private Pipeline _pipeline;
  19. private Window _window;
  20. public uint ProgramCount { get; set; }
  21. #pragma warning disable CS0067 // The event is never used
  22. public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured;
  23. #pragma warning restore CS0067
  24. public bool PreferThreading => true;
  25. public IPipeline Pipeline => _pipeline;
  26. public IWindow Window => _window;
  27. internal MTLCommandQueue BackgroundQueue { get; private set; }
  28. internal HelperShader HelperShader { get; private set; }
  29. internal BufferManager BufferManager { get; private set; }
  30. internal CommandBufferPool CommandBufferPool { get; private set; }
  31. internal BackgroundResources BackgroundResources { get; private set; }
  32. internal Action<Action> InterruptAction { get; private set; }
  33. internal SyncManager SyncManager { get; private set; }
  34. internal HashSet<Program> Programs { get; }
  35. internal HashSet<SamplerHolder> Samplers { get; }
  36. public MetalRenderer(Func<CAMetalLayer> metalLayer)
  37. {
  38. _device = MTLDevice.CreateSystemDefaultDevice();
  39. Programs = [];
  40. Samplers = [];
  41. if (_device.ArgumentBuffersSupport != MTLArgumentBuffersTier.Tier2)
  42. {
  43. throw new NotSupportedException("Metal backend requires Tier 2 Argument Buffer support.");
  44. }
  45. _queue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers + 1);
  46. BackgroundQueue = _device.NewCommandQueue(CommandBufferPool.MaxCommandBuffers);
  47. _getMetalLayer = metalLayer;
  48. }
  49. public void Initialize(GraphicsDebugLevel logLevel)
  50. {
  51. CAMetalLayer layer = _getMetalLayer();
  52. layer.Device = _device;
  53. layer.FramebufferOnly = false;
  54. CommandBufferPool = new CommandBufferPool(_queue);
  55. _window = new Window(this, layer);
  56. _pipeline = new Pipeline(_device, this);
  57. BufferManager = new BufferManager(_device, this, _pipeline);
  58. _pipeline.InitEncoderStateManager(BufferManager);
  59. BackgroundResources = new BackgroundResources(this);
  60. HelperShader = new HelperShader(_device, this, _pipeline);
  61. SyncManager = new SyncManager(this);
  62. }
  63. public void BackgroundContextAction(Action action, bool alwaysBackground = false)
  64. {
  65. // GetData methods should be thread safe, so we can call this directly.
  66. // Texture copy (scaled) may also happen in here, so that should also be thread safe.
  67. action();
  68. }
  69. public BufferHandle CreateBuffer(int size, BufferAccess access)
  70. {
  71. return BufferManager.CreateWithHandle(size);
  72. }
  73. public BufferHandle CreateBuffer(IntPtr pointer, int size)
  74. {
  75. return BufferManager.Create(pointer, size);
  76. }
  77. public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers)
  78. {
  79. throw new NotImplementedException();
  80. }
  81. public IImageArray CreateImageArray(int size, bool isBuffer)
  82. {
  83. return new ImageArray(size, isBuffer, _pipeline);
  84. }
  85. public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
  86. {
  87. ProgramCount++;
  88. return new Program(this, _device, shaders, info.ResourceLayout, info.ComputeLocalSize);
  89. }
  90. public ISampler CreateSampler(SamplerCreateInfo info)
  91. {
  92. return new SamplerHolder(this, _device, info);
  93. }
  94. public ITexture CreateTexture(TextureCreateInfo info)
  95. {
  96. if (info.Target == Target.TextureBuffer)
  97. {
  98. return new TextureBuffer(_device, this, _pipeline, info);
  99. }
  100. return new Texture(_device, this, _pipeline, info);
  101. }
  102. public ITextureArray CreateTextureArray(int size, bool isBuffer)
  103. {
  104. return new TextureArray(size, isBuffer, _pipeline);
  105. }
  106. public bool PrepareHostMapping(IntPtr address, ulong size)
  107. {
  108. // TODO: Metal Host Mapping
  109. return false;
  110. }
  111. public void CreateSync(ulong id, bool strict)
  112. {
  113. SyncManager.Create(id, strict);
  114. }
  115. public void DeleteBuffer(BufferHandle buffer)
  116. {
  117. BufferManager.Delete(buffer);
  118. }
  119. public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
  120. {
  121. return BufferManager.GetData(buffer, offset, size);
  122. }
  123. public Capabilities GetCapabilities()
  124. {
  125. // TODO: Finalize these values
  126. return new Capabilities(
  127. api: TargetApi.Metal,
  128. vendorName: HardwareInfoTools.GetVendor(),
  129. SystemMemoryType.UnifiedMemory,
  130. hasFrontFacingBug: false,
  131. hasVectorIndexingBug: false,
  132. needsFragmentOutputSpecialization: true,
  133. reduceShaderPrecision: true,
  134. supportsAstcCompression: true,
  135. supportsBc123Compression: true,
  136. supportsBc45Compression: true,
  137. supportsBc67Compression: true,
  138. supportsEtc2Compression: true,
  139. supports3DTextureCompression: true,
  140. supportsBgraFormat: true,
  141. supportsR4G4Format: false,
  142. supportsR4G4B4A4Format: true,
  143. supportsScaledVertexFormats: false,
  144. supportsSnormBufferTextureFormat: true,
  145. supportsSparseBuffer: false,
  146. supports5BitComponentFormat: true,
  147. supportsBlendEquationAdvanced: false,
  148. supportsFragmentShaderInterlock: true,
  149. supportsFragmentShaderOrderingIntel: false,
  150. supportsGeometryShader: false,
  151. supportsGeometryShaderPassthrough: false,
  152. supportsTransformFeedback: false,
  153. supportsImageLoadFormatted: false,
  154. supportsLayerVertexTessellation: false,
  155. supportsMismatchingViewFormat: true,
  156. supportsCubemapView: true,
  157. supportsNonConstantTextureOffset: false,
  158. supportsQuads: false,
  159. supportsSeparateSampler: true,
  160. supportsShaderBallot: false,
  161. supportsShaderBarrierDivergence: false,
  162. supportsShaderFloat64: false,
  163. supportsTextureGatherOffsets: false,
  164. supportsTextureShadowLod: false,
  165. supportsVertexStoreAndAtomics: false,
  166. supportsViewportIndexVertexTessellation: false,
  167. supportsViewportMask: false,
  168. supportsViewportSwizzle: false,
  169. supportsIndirectParameters: true,
  170. supportsDepthClipControl: false,
  171. uniformBufferSetIndex: (int)Constants.ConstantBuffersSetIndex,
  172. storageBufferSetIndex: (int)Constants.StorageBuffersSetIndex,
  173. textureSetIndex: (int)Constants.TexturesSetIndex,
  174. imageSetIndex: (int)Constants.ImagesSetIndex,
  175. extraSetBaseIndex: TotalSets,
  176. maximumExtraSets: (int)Constants.MaximumExtraSets,
  177. maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
  178. maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
  179. maximumTexturesPerStage: Constants.MaxTexturesPerStage,
  180. maximumImagesPerStage: Constants.MaxImagesPerStage,
  181. maximumComputeSharedMemorySize: (int)_device.MaxThreadgroupMemoryLength,
  182. maximumSupportedAnisotropy: 16,
  183. shaderSubgroupSize: 256,
  184. storageBufferOffsetAlignment: 16,
  185. textureBufferOffsetAlignment: 16,
  186. gatherBiasPrecision: 0,
  187. maximumGpuMemory: 0
  188. );
  189. }
  190. public ulong GetCurrentSync()
  191. {
  192. return SyncManager.GetCurrent();
  193. }
  194. public HardwareInfo GetHardwareInfo()
  195. {
  196. return new HardwareInfo(HardwareInfoTools.GetVendor(), HardwareInfoTools.GetModel(), "Apple");
  197. }
  198. public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info)
  199. {
  200. throw new NotImplementedException();
  201. }
  202. public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
  203. {
  204. BufferManager.SetData(buffer, offset, data, _pipeline.Cbs);
  205. }
  206. public void UpdateCounters()
  207. {
  208. // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
  209. }
  210. public void PreFrame()
  211. {
  212. SyncManager.Cleanup();
  213. }
  214. public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved)
  215. {
  216. // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
  217. CounterEvent counterEvent = new();
  218. resultHandler?.Invoke(counterEvent, type == CounterType.SamplesPassed ? (ulong)1 : 0);
  219. return counterEvent;
  220. }
  221. public void ResetCounter(CounterType type)
  222. {
  223. // https://developer.apple.com/documentation/metal/gpu_counters_and_counter_sample_buffers/creating_a_counter_sample_buffer_to_store_a_gpu_s_counter_data_during_a_pass?language=objc
  224. }
  225. public void WaitSync(ulong id)
  226. {
  227. SyncManager.Wait(id);
  228. }
  229. public void FlushAllCommands()
  230. {
  231. _pipeline.FlushCommandsImpl();
  232. }
  233. public void RegisterFlush()
  234. {
  235. SyncManager.RegisterFlush();
  236. // Periodically free unused regions of the staging buffer to avoid doing it all at once.
  237. BufferManager.StagingBuffer.FreeCompleted();
  238. }
  239. public void SetInterruptAction(Action<Action> interruptAction)
  240. {
  241. InterruptAction = interruptAction;
  242. }
  243. public void Screenshot()
  244. {
  245. // TODO: Screenshots
  246. }
  247. public void Dispose()
  248. {
  249. BackgroundResources.Dispose();
  250. foreach (Program program in Programs)
  251. {
  252. program.Dispose();
  253. }
  254. foreach (SamplerHolder sampler in Samplers)
  255. {
  256. sampler.Dispose();
  257. }
  258. _pipeline.Dispose();
  259. _window.Dispose();
  260. }
  261. }
  262. }