/ src / Ryujinx.Graphics.Vulkan / PipelineConverter.cs
PipelineConverter.cs
  1  using Ryujinx.Common;
  2  using Ryujinx.Graphics.GAL;
  3  using Silk.NET.Vulkan;
  4  using System;
  5  using Format = Silk.NET.Vulkan.Format;
  6  using PolygonMode = Silk.NET.Vulkan.PolygonMode;
  7  
  8  namespace Ryujinx.Graphics.Vulkan
  9  {
 10      static class PipelineConverter
 11      {
 12          public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
 13          {
 14              const int MaxAttachments = Constants.MaxRenderTargets + 1;
 15  
 16              AttachmentDescription[] attachmentDescs = null;
 17  
 18              var subpass = new SubpassDescription
 19              {
 20                  PipelineBindPoint = PipelineBindPoint.Graphics,
 21              };
 22  
 23              AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments];
 24  
 25              Span<int> attachmentIndices = stackalloc int[MaxAttachments];
 26              Span<Format> attachmentFormats = stackalloc Format[MaxAttachments];
 27  
 28              int attachmentCount = 0;
 29              int colorCount = 0;
 30              int maxColorAttachmentIndex = -1;
 31  
 32              for (int i = 0; i < state.AttachmentEnable.Length; i++)
 33              {
 34                  if (state.AttachmentEnable[i])
 35                  {
 36                      attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
 37  
 38                      attachmentIndices[attachmentCount++] = i;
 39                      colorCount++;
 40                      maxColorAttachmentIndex = i;
 41                  }
 42              }
 43  
 44              if (state.DepthStencilEnable)
 45              {
 46                  attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat);
 47              }
 48  
 49              if (attachmentCount != 0)
 50              {
 51                  attachmentDescs = new AttachmentDescription[attachmentCount];
 52  
 53                  for (int i = 0; i < attachmentCount; i++)
 54                  {
 55                      int bindIndex = attachmentIndices[i];
 56  
 57                      attachmentDescs[i] = new AttachmentDescription(
 58                          0,
 59                          attachmentFormats[i],
 60                          TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)state.SamplesCount),
 61                          AttachmentLoadOp.Load,
 62                          AttachmentStoreOp.Store,
 63                          AttachmentLoadOp.Load,
 64                          AttachmentStoreOp.Store,
 65                          ImageLayout.General,
 66                          ImageLayout.General);
 67                  }
 68  
 69                  int colorAttachmentsCount = colorCount;
 70  
 71                  if (colorAttachmentsCount > MaxAttachments - 1)
 72                  {
 73                      colorAttachmentsCount = MaxAttachments - 1;
 74                  }
 75  
 76                  if (colorAttachmentsCount != 0)
 77                  {
 78                      subpass.ColorAttachmentCount = (uint)maxColorAttachmentIndex + 1;
 79                      subpass.PColorAttachments = &attachmentReferences[0];
 80  
 81                      // Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
 82                      for (int i = 0; i <= maxColorAttachmentIndex; i++)
 83                      {
 84                          subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
 85                      }
 86  
 87                      for (int i = 0; i < colorAttachmentsCount; i++)
 88                      {
 89                          int bindIndex = attachmentIndices[i];
 90  
 91                          subpass.PColorAttachments[bindIndex] = new AttachmentReference((uint)i, ImageLayout.General);
 92                      }
 93                  }
 94  
 95                  if (state.DepthStencilEnable)
 96                  {
 97                      uint dsIndex = (uint)attachmentCount - 1;
 98  
 99                      subpass.PDepthStencilAttachment = &attachmentReferences[MaxAttachments - 1];
100                      *subpass.PDepthStencilAttachment = new AttachmentReference(dsIndex, ImageLayout.General);
101                  }
102              }
103  
104              var subpassDependency = CreateSubpassDependency(gd);
105  
106              fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
107              {
108                  var renderPassCreateInfo = new RenderPassCreateInfo
109                  {
110                      SType = StructureType.RenderPassCreateInfo,
111                      PAttachments = pAttachmentDescs,
112                      AttachmentCount = attachmentDescs != null ? (uint)attachmentDescs.Length : 0,
113                      PSubpasses = &subpass,
114                      SubpassCount = 1,
115                      PDependencies = &subpassDependency,
116                      DependencyCount = 1,
117                  };
118  
119                  gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
120  
121                  return new DisposableRenderPass(gd.Api, device, renderPass);
122              }
123          }
124  
125          public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd)
126          {
127              var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
128  
129              return new SubpassDependency(
130                  0,
131                  0,
132                  stages,
133                  stages,
134                  access,
135                  access,
136                  0);
137          }
138  
139          public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd)
140          {
141              var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
142  
143              return new SubpassDependency2(
144                  StructureType.SubpassDependency2,
145                  null,
146                  0,
147                  0,
148                  stages,
149                  stages,
150                  access,
151                  access,
152                  0);
153          }
154  
155          public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd)
156          {
157              PipelineState pipeline = new();
158              pipeline.Initialize();
159  
160              // It is assumed that Dynamic State is enabled when this conversion is used.
161  
162              pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None;
163  
164              pipeline.DepthBoundsTestEnable = false; // Not implemented.
165  
166              pipeline.DepthClampEnable = state.DepthClampEnable;
167  
168              pipeline.DepthTestEnable = state.DepthTest.TestEnable;
169              pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
170              pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
171              pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
172  
173              pipeline.FrontFace = state.FrontFace.Convert();
174  
175              pipeline.HasDepthStencil = state.DepthStencilEnable;
176              pipeline.LineWidth = state.LineWidth;
177              pipeline.LogicOpEnable = state.LogicOpEnable;
178              pipeline.LogicOp = state.LogicOp.Convert();
179  
180              pipeline.PatchControlPoints = state.PatchControlPoints;
181              pipeline.PolygonMode = PolygonMode.Fill; // Not implemented.
182              pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
183              pipeline.RasterizerDiscardEnable = state.RasterizerDiscard;
184              pipeline.SamplesCount = (uint)state.SamplesCount;
185  
186              if (gd.Capabilities.SupportsMultiView)
187              {
188                  pipeline.ScissorsCount = Constants.MaxViewports;
189                  pipeline.ViewportsCount = Constants.MaxViewports;
190              }
191              else
192              {
193                  pipeline.ScissorsCount = 1;
194                  pipeline.ViewportsCount = 1;
195              }
196  
197              pipeline.DepthBiasEnable = state.BiasEnable != 0;
198  
199              // Stencil masks and ref are dynamic, so are 0 in the Vulkan pipeline.
200  
201              pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert();
202              pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
203              pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert();
204              pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert();
205  
206              pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
207              pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
208              pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
209              pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
210  
211              pipeline.StencilTestEnable = state.StencilTest.TestEnable;
212  
213              pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
214  
215              int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
216              int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
217  
218              Span<int> vbScalarSizes = stackalloc int[vbCount];
219  
220              for (int i = 0; i < vaCount; i++)
221              {
222                  var attribute = state.VertexAttribs[i];
223                  var bufferIndex = attribute.IsZero ? 0 : attribute.BufferIndex + 1;
224  
225                  pipeline.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription(
226                      (uint)i,
227                      (uint)bufferIndex,
228                      gd.FormatCapabilities.ConvertToVertexVkFormat(attribute.Format),
229                      (uint)attribute.Offset);
230  
231                  if (!attribute.IsZero && bufferIndex < vbCount)
232                  {
233                      vbScalarSizes[bufferIndex - 1] = Math.Max(attribute.Format.GetScalarSize(), vbScalarSizes[bufferIndex - 1]);
234                  }
235              }
236  
237              int descriptorIndex = 1;
238              pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
239  
240              for (int i = 0; i < vbCount; i++)
241              {
242                  var vertexBuffer = state.VertexBuffers[i];
243  
244                  if (vertexBuffer.Enable)
245                  {
246                      var inputRate = vertexBuffer.Divisor != 0 ? VertexInputRate.Instance : VertexInputRate.Vertex;
247  
248                      int alignedStride = vertexBuffer.Stride;
249  
250                      if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment))
251                      {
252                          alignedStride = BitUtils.AlignUp(vertexBuffer.Stride, alignment);
253                      }
254  
255                      // TODO: Support divisor > 1
256                      pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription(
257                          (uint)i + 1,
258                          (uint)alignedStride,
259                          inputRate);
260                  }
261              }
262  
263              pipeline.VertexBindingDescriptionsCount = (uint)descriptorIndex;
264  
265              // NOTE: Viewports, Scissors are dynamic.
266  
267              for (int i = 0; i < Constants.MaxRenderTargets; i++)
268              {
269                  var blend = state.BlendDescriptors[i];
270  
271                  if (blend.Enable && state.ColorWriteMask[i] != 0)
272                  {
273                      pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState(
274                          blend.Enable,
275                          blend.ColorSrcFactor.Convert(),
276                          blend.ColorDstFactor.Convert(),
277                          blend.ColorOp.Convert(),
278                          blend.AlphaSrcFactor.Convert(),
279                          blend.AlphaDstFactor.Convert(),
280                          blend.AlphaOp.Convert(),
281                          (ColorComponentFlags)state.ColorWriteMask[i]);
282                  }
283                  else
284                  {
285                      pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState(
286                          colorWriteMask: (ColorComponentFlags)state.ColorWriteMask[i]);
287                  }
288              }
289  
290              int attachmentCount = 0;
291              int maxColorAttachmentIndex = -1;
292              uint attachmentIntegerFormatMask = 0;
293              bool allFormatsFloatOrSrgb = true;
294  
295              for (int i = 0; i < Constants.MaxRenderTargets; i++)
296              {
297                  if (state.AttachmentEnable[i])
298                  {
299                      pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
300                      maxColorAttachmentIndex = i;
301  
302                      if (state.AttachmentFormats[i].IsInteger())
303                      {
304                          attachmentIntegerFormatMask |= 1u << i;
305                      }
306  
307                      allFormatsFloatOrSrgb &= state.AttachmentFormats[i].IsFloatOrSrgb();
308                  }
309              }
310  
311              if (state.DepthStencilEnable)
312              {
313                  pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat);
314              }
315  
316              pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);
317              pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
318              pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
319              pipeline.Internal.LogicOpsAllowed = attachmentCount == 0 || !allFormatsFloatOrSrgb;
320  
321              return pipeline;
322          }
323      }
324  }