PipelineState.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.GAL;
  3. using SharpMetal.Foundation;
  4. using SharpMetal.Metal;
  5. using System;
  6. using System.Runtime.Versioning;
  7. namespace Ryujinx.Graphics.Metal
  8. {
  9. [SupportedOSPlatform("macos")]
  10. struct PipelineState
  11. {
  12. public PipelineUid Internal;
  13. public uint StagesCount
  14. {
  15. readonly get => (byte)((Internal.Id0 >> 0) & 0xFF);
  16. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
  17. }
  18. public uint VertexAttributeDescriptionsCount
  19. {
  20. readonly get => (byte)((Internal.Id0 >> 8) & 0xFF);
  21. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
  22. }
  23. public uint VertexBindingDescriptionsCount
  24. {
  25. readonly get => (byte)((Internal.Id0 >> 16) & 0xFF);
  26. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFFF00FFFF) | ((ulong)value << 16);
  27. }
  28. public uint ColorBlendAttachmentStateCount
  29. {
  30. readonly get => (byte)((Internal.Id0 >> 24) & 0xFF);
  31. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00FFFFFF) | ((ulong)value << 24);
  32. }
  33. /*
  34. * Can be an input to a pipeline, but not sure what the situation for that is.
  35. public PrimitiveTopology Topology
  36. {
  37. readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF);
  38. set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
  39. }
  40. */
  41. public MTLLogicOperation LogicOp
  42. {
  43. readonly get => (MTLLogicOperation)((Internal.Id0 >> 32) & 0xF);
  44. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFF0FFFFFFFF) | ((ulong)value << 32);
  45. }
  46. //?
  47. public bool PrimitiveRestartEnable
  48. {
  49. readonly get => ((Internal.Id0 >> 36) & 0x1) != 0UL;
  50. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFEFFFFFFFFF) | ((value ? 1UL : 0UL) << 36);
  51. }
  52. public bool RasterizerDiscardEnable
  53. {
  54. readonly get => ((Internal.Id0 >> 37) & 0x1) != 0UL;
  55. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFDFFFFFFFFF) | ((value ? 1UL : 0UL) << 37);
  56. }
  57. public bool LogicOpEnable
  58. {
  59. readonly get => ((Internal.Id0 >> 38) & 0x1) != 0UL;
  60. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFBFFFFFFFFF) | ((value ? 1UL : 0UL) << 38);
  61. }
  62. public bool AlphaToCoverageEnable
  63. {
  64. readonly get => ((Internal.Id0 >> 40) & 0x1) != 0UL;
  65. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFEFFFFFFFFFF) | ((value ? 1UL : 0UL) << 40);
  66. }
  67. public bool AlphaToOneEnable
  68. {
  69. readonly get => ((Internal.Id0 >> 41) & 0x1) != 0UL;
  70. set => Internal.Id0 = (Internal.Id0 & 0xFFFFFDFFFFFFFFFF) | ((value ? 1UL : 0UL) << 41);
  71. }
  72. public MTLPixelFormat DepthStencilFormat
  73. {
  74. readonly get => (MTLPixelFormat)(Internal.Id0 >> 48);
  75. set => Internal.Id0 = (Internal.Id0 & 0x0000FFFFFFFFFFFF) | ((ulong)value << 48);
  76. }
  77. // Not sure how to appropriately use this, but it does need to be passed for tess.
  78. public uint PatchControlPoints
  79. {
  80. readonly get => (uint)((Internal.Id1 >> 0) & 0xFFFFFFFF);
  81. set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
  82. }
  83. public uint SamplesCount
  84. {
  85. readonly get => (uint)((Internal.Id1 >> 32) & 0xFFFFFFFF);
  86. set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)value << 32);
  87. }
  88. // Advanced blend not supported
  89. private readonly void BuildColorAttachment(MTLRenderPipelineColorAttachmentDescriptor descriptor, ColorBlendStateUid blendState)
  90. {
  91. descriptor.PixelFormat = blendState.PixelFormat;
  92. descriptor.SetBlendingEnabled(blendState.Enable);
  93. descriptor.AlphaBlendOperation = blendState.AlphaBlendOperation;
  94. descriptor.RgbBlendOperation = blendState.RgbBlendOperation;
  95. descriptor.SourceAlphaBlendFactor = blendState.SourceAlphaBlendFactor;
  96. descriptor.DestinationAlphaBlendFactor = blendState.DestinationAlphaBlendFactor;
  97. descriptor.SourceRGBBlendFactor = blendState.SourceRGBBlendFactor;
  98. descriptor.DestinationRGBBlendFactor = blendState.DestinationRGBBlendFactor;
  99. descriptor.WriteMask = blendState.WriteMask;
  100. }
  101. private readonly MTLVertexDescriptor BuildVertexDescriptor()
  102. {
  103. MTLVertexDescriptor vertexDescriptor = new();
  104. for (int i = 0; i < VertexAttributeDescriptionsCount; i++)
  105. {
  106. VertexInputAttributeUid uid = Internal.VertexAttributes[i];
  107. MTLVertexAttributeDescriptor attrib = vertexDescriptor.Attributes.Object((ulong)i);
  108. attrib.Format = uid.Format;
  109. attrib.Offset = uid.Offset;
  110. attrib.BufferIndex = uid.BufferIndex;
  111. }
  112. for (int i = 0; i < VertexBindingDescriptionsCount; i++)
  113. {
  114. VertexInputLayoutUid uid = Internal.VertexBindings[i];
  115. MTLVertexBufferLayoutDescriptor layout = vertexDescriptor.Layouts.Object((ulong)i);
  116. layout.StepFunction = uid.StepFunction;
  117. layout.StepRate = uid.StepRate;
  118. layout.Stride = uid.Stride;
  119. }
  120. return vertexDescriptor;
  121. }
  122. private MTLRenderPipelineDescriptor CreateRenderDescriptor(Program program)
  123. {
  124. MTLRenderPipelineDescriptor renderPipelineDescriptor = new();
  125. for (int i = 0; i < Constants.MaxColorAttachments; i++)
  126. {
  127. ColorBlendStateUid blendState = Internal.ColorBlendState[i];
  128. if (blendState.PixelFormat != MTLPixelFormat.Invalid)
  129. {
  130. MTLRenderPipelineColorAttachmentDescriptor pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i);
  131. BuildColorAttachment(pipelineAttachment, blendState);
  132. }
  133. }
  134. MTLPixelFormat dsFormat = DepthStencilFormat;
  135. if (dsFormat != MTLPixelFormat.Invalid)
  136. {
  137. switch (dsFormat)
  138. {
  139. // Depth Only Attachment
  140. case MTLPixelFormat.Depth16Unorm:
  141. case MTLPixelFormat.Depth32Float:
  142. renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat;
  143. break;
  144. // Stencil Only Attachment
  145. case MTLPixelFormat.Stencil8:
  146. renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat;
  147. break;
  148. // Combined Attachment
  149. case MTLPixelFormat.Depth24UnormStencil8:
  150. case MTLPixelFormat.Depth32FloatStencil8:
  151. renderPipelineDescriptor.DepthAttachmentPixelFormat = dsFormat;
  152. renderPipelineDescriptor.StencilAttachmentPixelFormat = dsFormat;
  153. break;
  154. default:
  155. Logger.Error?.PrintMsg(LogClass.Gpu, $"Unsupported Depth/Stencil Format: {dsFormat}!");
  156. break;
  157. }
  158. }
  159. renderPipelineDescriptor.LogicOperationEnabled = LogicOpEnable;
  160. renderPipelineDescriptor.LogicOperation = LogicOp;
  161. renderPipelineDescriptor.AlphaToCoverageEnabled = AlphaToCoverageEnable;
  162. renderPipelineDescriptor.AlphaToOneEnabled = AlphaToOneEnable;
  163. renderPipelineDescriptor.RasterizationEnabled = !RasterizerDiscardEnable;
  164. renderPipelineDescriptor.SampleCount = Math.Max(1, SamplesCount);
  165. MTLVertexDescriptor vertexDescriptor = BuildVertexDescriptor();
  166. renderPipelineDescriptor.VertexDescriptor = vertexDescriptor;
  167. renderPipelineDescriptor.VertexFunction = program.VertexFunction;
  168. if (program.FragmentFunction.NativePtr != 0)
  169. {
  170. renderPipelineDescriptor.FragmentFunction = program.FragmentFunction;
  171. }
  172. return renderPipelineDescriptor;
  173. }
  174. public MTLRenderPipelineState CreateRenderPipeline(MTLDevice device, Program program)
  175. {
  176. if (program.TryGetGraphicsPipeline(ref Internal, out MTLRenderPipelineState pipelineState))
  177. {
  178. return pipelineState;
  179. }
  180. using MTLRenderPipelineDescriptor descriptor = CreateRenderDescriptor(program);
  181. NSError error = new(IntPtr.Zero);
  182. pipelineState = device.NewRenderPipelineState(descriptor, ref error);
  183. if (error != IntPtr.Zero)
  184. {
  185. Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Render Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
  186. }
  187. program.AddGraphicsPipeline(ref Internal, pipelineState);
  188. return pipelineState;
  189. }
  190. public static MTLComputePipelineDescriptor CreateComputeDescriptor(Program program)
  191. {
  192. ComputeSize localSize = program.ComputeLocalSize;
  193. uint maxThreads = (uint)(localSize.X * localSize.Y * localSize.Z);
  194. if (maxThreads == 0)
  195. {
  196. throw new InvalidOperationException($"Local thread size for compute cannot be 0 in any dimension.");
  197. }
  198. MTLComputePipelineDescriptor descriptor = new()
  199. {
  200. ComputeFunction = program.ComputeFunction,
  201. MaxTotalThreadsPerThreadgroup = maxThreads,
  202. ThreadGroupSizeIsMultipleOfThreadExecutionWidth = true,
  203. };
  204. return descriptor;
  205. }
  206. public static MTLComputePipelineState CreateComputePipeline(MTLDevice device, Program program)
  207. {
  208. if (program.TryGetComputePipeline(out MTLComputePipelineState pipelineState))
  209. {
  210. return pipelineState;
  211. }
  212. using MTLComputePipelineDescriptor descriptor = CreateComputeDescriptor(program);
  213. NSError error = new(IntPtr.Zero);
  214. pipelineState = device.NewComputePipelineState(descriptor, MTLPipelineOption.None, 0, ref error);
  215. if (error != IntPtr.Zero)
  216. {
  217. Logger.Error?.PrintMsg(LogClass.Gpu, $"Failed to create Compute Pipeline State: {StringHelper.String(error.LocalizedDescription)}");
  218. }
  219. program.AddComputePipeline(pipelineState);
  220. return pipelineState;
  221. }
  222. public void Initialize()
  223. {
  224. SamplesCount = 1;
  225. Internal.ResetColorState();
  226. }
  227. /*
  228. * TODO, this is from vulkan.
  229. private void UpdateVertexAttributeDescriptions(VulkanRenderer gd)
  230. {
  231. // Vertex attributes exceeding the stride are invalid.
  232. // In metal, they cause glitches with the vertex shader fetching incorrect values.
  233. // To work around this, we reduce the format to something that doesn't exceed the stride if possible.
  234. // The assumption is that the exceeding components are not actually accessed on the shader.
  235. for (int index = 0; index < VertexAttributeDescriptionsCount; index++)
  236. {
  237. var attribute = Internal.VertexAttributeDescriptions[index];
  238. int vbIndex = GetVertexBufferIndex(attribute.Binding);
  239. if (vbIndex >= 0)
  240. {
  241. ref var vb = ref Internal.VertexBindingDescriptions[vbIndex];
  242. Format format = attribute.Format;
  243. while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride)
  244. {
  245. Format newFormat = FormatTable.DropLastComponent(format);
  246. if (newFormat == format)
  247. {
  248. // That case means we failed to find a format that fits within the stride,
  249. // so just restore the original format and give up.
  250. format = attribute.Format;
  251. break;
  252. }
  253. format = newFormat;
  254. }
  255. if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format))
  256. {
  257. attribute.Format = format;
  258. }
  259. }
  260. _vertexAttributeDescriptions2[index] = attribute;
  261. }
  262. }
  263. private int GetVertexBufferIndex(uint binding)
  264. {
  265. for (int index = 0; index < VertexBindingDescriptionsCount; index++)
  266. {
  267. if (Internal.VertexBindingDescriptions[index].Binding == binding)
  268. {
  269. return index;
  270. }
  271. }
  272. return -1;
  273. }
  274. */
  275. }
  276. }