/ src / Ryujinx.Graphics.Vulkan / PipelineState.cs
PipelineState.cs
  1  using Ryujinx.Common.Memory;
  2  using Silk.NET.Vulkan;
  3  using System;
  4  using System.Numerics;
  5  
  6  namespace Ryujinx.Graphics.Vulkan
  7  {
  8      struct PipelineState : IDisposable
  9      {
 10          private const int RequiredSubgroupSize = 32;
 11          private const int MaxDynamicStatesCount = 9;
 12  
 13          public PipelineUid Internal;
 14  
 15          public float LineWidth
 16          {
 17              readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 0) & 0xFFFFFFFF));
 18              set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0);
 19          }
 20  
 21          public float DepthBiasClamp
 22          {
 23              readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 32) & 0xFFFFFFFF));
 24              set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32);
 25          }
 26  
 27          public float DepthBiasConstantFactor
 28          {
 29              readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 0) & 0xFFFFFFFF));
 30              set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0);
 31          }
 32  
 33          public float DepthBiasSlopeFactor
 34          {
 35              readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 32) & 0xFFFFFFFF));
 36              set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32);
 37          }
 38  
 39          public uint StencilFrontCompareMask
 40          {
 41              readonly get => (uint)((Internal.Id2 >> 0) & 0xFFFFFFFF);
 42              set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
 43          }
 44  
 45          public uint StencilFrontWriteMask
 46          {
 47              readonly get => (uint)((Internal.Id2 >> 32) & 0xFFFFFFFF);
 48              set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF) | ((ulong)value << 32);
 49          }
 50  
 51          public uint StencilFrontReference
 52          {
 53              readonly get => (uint)((Internal.Id3 >> 0) & 0xFFFFFFFF);
 54              set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
 55          }
 56  
 57          public uint StencilBackCompareMask
 58          {
 59              readonly get => (uint)((Internal.Id3 >> 32) & 0xFFFFFFFF);
 60              set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFF) | ((ulong)value << 32);
 61          }
 62  
 63          public uint StencilBackWriteMask
 64          {
 65              readonly get => (uint)((Internal.Id4 >> 0) & 0xFFFFFFFF);
 66              set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
 67          }
 68  
 69          public uint StencilBackReference
 70          {
 71              readonly get => (uint)((Internal.Id4 >> 32) & 0xFFFFFFFF);
 72              set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF) | ((ulong)value << 32);
 73          }
 74  
 75          public PolygonMode PolygonMode
 76          {
 77              readonly get => (PolygonMode)((Internal.Id5 >> 0) & 0x3FFFFFFF);
 78              set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFFC0000000) | ((ulong)value << 0);
 79          }
 80  
 81          public uint StagesCount
 82          {
 83              readonly get => (byte)((Internal.Id5 >> 30) & 0xFF);
 84              set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30);
 85          }
 86  
 87          public uint VertexAttributeDescriptionsCount
 88          {
 89              readonly get => (byte)((Internal.Id5 >> 38) & 0xFF);
 90              set => Internal.Id5 = (Internal.Id5 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38);
 91          }
 92  
 93          public uint VertexBindingDescriptionsCount
 94          {
 95              readonly get => (byte)((Internal.Id5 >> 46) & 0xFF);
 96              set => Internal.Id5 = (Internal.Id5 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46);
 97          }
 98  
 99          public uint ViewportsCount
100          {
101              readonly get => (byte)((Internal.Id5 >> 54) & 0xFF);
102              set => Internal.Id5 = (Internal.Id5 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54);
103          }
104  
105          public uint ScissorsCount
106          {
107              readonly get => (byte)((Internal.Id6 >> 0) & 0xFF);
108              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
109          }
110  
111          public uint ColorBlendAttachmentStateCount
112          {
113              readonly get => (byte)((Internal.Id6 >> 8) & 0xFF);
114              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
115          }
116  
117          public PrimitiveTopology Topology
118          {
119              readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF);
120              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
121          }
122  
123          public LogicOp LogicOp
124          {
125              readonly get => (LogicOp)((Internal.Id6 >> 20) & 0xF);
126              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20);
127          }
128  
129          public CompareOp DepthCompareOp
130          {
131              readonly get => (CompareOp)((Internal.Id6 >> 24) & 0x7);
132              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24);
133          }
134  
135          public StencilOp StencilFrontFailOp
136          {
137              readonly get => (StencilOp)((Internal.Id6 >> 27) & 0x7);
138              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27);
139          }
140  
141          public StencilOp StencilFrontPassOp
142          {
143              readonly get => (StencilOp)((Internal.Id6 >> 30) & 0x7);
144              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30);
145          }
146  
147          public StencilOp StencilFrontDepthFailOp
148          {
149              readonly get => (StencilOp)((Internal.Id6 >> 33) & 0x7);
150              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33);
151          }
152  
153          public CompareOp StencilFrontCompareOp
154          {
155              readonly get => (CompareOp)((Internal.Id6 >> 36) & 0x7);
156              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36);
157          }
158  
159          public StencilOp StencilBackFailOp
160          {
161              readonly get => (StencilOp)((Internal.Id6 >> 39) & 0x7);
162              set => Internal.Id6 = (Internal.Id6 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39);
163          }
164  
165          public StencilOp StencilBackPassOp
166          {
167              readonly get => (StencilOp)((Internal.Id6 >> 42) & 0x7);
168              set => Internal.Id6 = (Internal.Id6 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42);
169          }
170  
171          public StencilOp StencilBackDepthFailOp
172          {
173              readonly get => (StencilOp)((Internal.Id6 >> 45) & 0x7);
174              set => Internal.Id6 = (Internal.Id6 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45);
175          }
176  
177          public CompareOp StencilBackCompareOp
178          {
179              readonly get => (CompareOp)((Internal.Id6 >> 48) & 0x7);
180              set => Internal.Id6 = (Internal.Id6 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48);
181          }
182  
183          public CullModeFlags CullMode
184          {
185              readonly get => (CullModeFlags)((Internal.Id6 >> 51) & 0x3);
186              set => Internal.Id6 = (Internal.Id6 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51);
187          }
188  
189          public bool PrimitiveRestartEnable
190          {
191              readonly get => ((Internal.Id6 >> 53) & 0x1) != 0UL;
192              set => Internal.Id6 = (Internal.Id6 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53);
193          }
194  
195          public bool DepthClampEnable
196          {
197              readonly get => ((Internal.Id6 >> 54) & 0x1) != 0UL;
198              set => Internal.Id6 = (Internal.Id6 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54);
199          }
200  
201          public bool RasterizerDiscardEnable
202          {
203              readonly get => ((Internal.Id6 >> 55) & 0x1) != 0UL;
204              set => Internal.Id6 = (Internal.Id6 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55);
205          }
206  
207          public FrontFace FrontFace
208          {
209              readonly get => (FrontFace)((Internal.Id6 >> 56) & 0x1);
210              set => Internal.Id6 = (Internal.Id6 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56);
211          }
212  
213          public bool DepthBiasEnable
214          {
215              readonly get => ((Internal.Id6 >> 57) & 0x1) != 0UL;
216              set => Internal.Id6 = (Internal.Id6 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57);
217          }
218  
219          public bool DepthTestEnable
220          {
221              readonly get => ((Internal.Id6 >> 58) & 0x1) != 0UL;
222              set => Internal.Id6 = (Internal.Id6 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58);
223          }
224  
225          public bool DepthWriteEnable
226          {
227              readonly get => ((Internal.Id6 >> 59) & 0x1) != 0UL;
228              set => Internal.Id6 = (Internal.Id6 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59);
229          }
230  
231          public bool DepthBoundsTestEnable
232          {
233              readonly get => ((Internal.Id6 >> 60) & 0x1) != 0UL;
234              set => Internal.Id6 = (Internal.Id6 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60);
235          }
236  
237          public bool StencilTestEnable
238          {
239              readonly get => ((Internal.Id6 >> 61) & 0x1) != 0UL;
240              set => Internal.Id6 = (Internal.Id6 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61);
241          }
242  
243          public bool LogicOpEnable
244          {
245              readonly get => ((Internal.Id6 >> 62) & 0x1) != 0UL;
246              set => Internal.Id6 = (Internal.Id6 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62);
247          }
248  
249          public bool HasDepthStencil
250          {
251              readonly get => ((Internal.Id6 >> 63) & 0x1) != 0UL;
252              set => Internal.Id6 = (Internal.Id6 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
253          }
254  
255          public uint PatchControlPoints
256          {
257              readonly get => (uint)((Internal.Id7 >> 0) & 0xFFFFFFFF);
258              set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
259          }
260  
261          public uint SamplesCount
262          {
263              readonly get => (uint)((Internal.Id7 >> 32) & 0xFFFFFFFF);
264              set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF) | ((ulong)value << 32);
265          }
266  
267          public bool AlphaToCoverageEnable
268          {
269              readonly get => ((Internal.Id8 >> 0) & 0x1) != 0UL;
270              set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0);
271          }
272  
273          public bool AlphaToOneEnable
274          {
275              readonly get => ((Internal.Id8 >> 1) & 0x1) != 0UL;
276              set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1);
277          }
278  
279          public bool AdvancedBlendSrcPreMultiplied
280          {
281              readonly get => ((Internal.Id8 >> 2) & 0x1) != 0UL;
282              set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2);
283          }
284  
285          public bool AdvancedBlendDstPreMultiplied
286          {
287              readonly get => ((Internal.Id8 >> 3) & 0x1) != 0UL;
288              set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3);
289          }
290  
291          public BlendOverlapEXT AdvancedBlendOverlap
292          {
293              readonly get => (BlendOverlapEXT)((Internal.Id8 >> 4) & 0x3);
294              set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4);
295          }
296  
297          public bool DepthMode
298          {
299              readonly get => ((Internal.Id8 >> 6) & 0x1) != 0UL;
300              set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
301          }
302  
303          public FeedbackLoopAspects FeedbackLoopAspects
304          {
305              readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3);
306              set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
307          }
308  
309          public bool HasTessellationControlShader;
310          public NativeArray<PipelineShaderStageCreateInfo> Stages;
311          public PipelineLayout PipelineLayout;
312          public SpecData SpecializationData;
313  
314          private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2;
315  
316          public void Initialize()
317          {
318              HasTessellationControlShader = false;
319              Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
320  
321              AdvancedBlendSrcPreMultiplied = true;
322              AdvancedBlendDstPreMultiplied = true;
323              AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt;
324  
325              LineWidth = 1f;
326              SamplesCount = 1;
327              DepthMode = true;
328          }
329  
330          public unsafe Auto<DisposablePipeline> CreateComputePipeline(
331              VulkanRenderer gd,
332              Device device,
333              ShaderCollection program,
334              PipelineCache cache)
335          {
336              if (program.TryGetComputePipeline(ref SpecializationData, out var pipeline))
337              {
338                  return pipeline;
339              }
340  
341              var pipelineCreateInfo = new ComputePipelineCreateInfo
342              {
343                  SType = StructureType.ComputePipelineCreateInfo,
344                  Stage = Stages[0],
345                  BasePipelineIndex = -1,
346                  Layout = PipelineLayout,
347              };
348  
349              Pipeline pipelineHandle = default;
350  
351              bool hasSpec = program.SpecDescriptions != null;
352  
353              var desc = hasSpec ? program.SpecDescriptions[0] : SpecDescription.Empty;
354  
355              if (hasSpec && SpecializationData.Length < (int)desc.Info.DataSize)
356              {
357                  throw new InvalidOperationException("Specialization data size does not match description");
358              }
359  
360              fixed (SpecializationInfo* info = &desc.Info)
361              fixed (SpecializationMapEntry* map = desc.Map)
362              fixed (byte* data = SpecializationData.Span)
363              {
364                  if (hasSpec)
365                  {
366                      info->PMapEntries = map;
367                      info->PData = data;
368                      pipelineCreateInfo.Stage.PSpecializationInfo = info;
369                  }
370  
371                  gd.Api.CreateComputePipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError();
372              }
373  
374              pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle));
375  
376              program.AddComputePipeline(ref SpecializationData, pipeline);
377  
378              return pipeline;
379          }
380  
381          public unsafe Auto<DisposablePipeline> CreateGraphicsPipeline(
382              VulkanRenderer gd,
383              Device device,
384              ShaderCollection program,
385              PipelineCache cache,
386              RenderPass renderPass,
387              bool throwOnError = false)
388          {
389              if (program.TryGetGraphicsPipeline(ref Internal, out var pipeline))
390              {
391                  return pipeline;
392              }
393  
394              Pipeline pipelineHandle = default;
395  
396              bool isMoltenVk = gd.IsMoltenVk;
397  
398              if (isMoltenVk)
399              {
400                  UpdateVertexAttributeDescriptions(gd);
401              }
402  
403              fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
404              fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0])
405              fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
406              fixed (PipelineColorBlendAttachmentState* pColorBlendAttachmentState = &Internal.ColorBlendAttachmentState[0])
407              {
408                  var vertexInputState = new PipelineVertexInputStateCreateInfo
409                  {
410                      SType = StructureType.PipelineVertexInputStateCreateInfo,
411                      VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
412                      PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions,
413                      VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
414                      PVertexBindingDescriptions = pVertexBindingDescriptions,
415                  };
416  
417                  // Using patches topology without a tessellation shader is invalid.
418                  // If we find such a case, return null pipeline to skip the draw.
419                  if (Topology == PrimitiveTopology.PatchList && !HasTessellationControlShader)
420                  {
421                      program.AddGraphicsPipeline(ref Internal, null);
422  
423                      return null;
424                  }
425  
426                  bool primitiveRestartEnable = PrimitiveRestartEnable;
427  
428                  bool topologySupportsRestart;
429  
430                  if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
431                  {
432                      topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart || Topology != PrimitiveTopology.PatchList;
433                  }
434                  else
435                  {
436                      topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
437                                                Topology == PrimitiveTopology.TriangleStrip ||
438                                                Topology == PrimitiveTopology.TriangleFan ||
439                                                Topology == PrimitiveTopology.LineStripWithAdjacency ||
440                                                Topology == PrimitiveTopology.TriangleStripWithAdjacency;
441                  }
442  
443                  primitiveRestartEnable &= topologySupportsRestart;
444  
445                  var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo
446                  {
447                      SType = StructureType.PipelineInputAssemblyStateCreateInfo,
448                      PrimitiveRestartEnable = primitiveRestartEnable,
449                      Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology,
450                  };
451  
452                  var tessellationState = new PipelineTessellationStateCreateInfo
453                  {
454                      SType = StructureType.PipelineTessellationStateCreateInfo,
455                      PatchControlPoints = PatchControlPoints,
456                  };
457  
458                  var rasterizationState = new PipelineRasterizationStateCreateInfo
459                  {
460                      SType = StructureType.PipelineRasterizationStateCreateInfo,
461                      DepthClampEnable = DepthClampEnable,
462                      RasterizerDiscardEnable = RasterizerDiscardEnable,
463                      PolygonMode = PolygonMode,
464                      LineWidth = LineWidth,
465                      CullMode = CullMode,
466                      FrontFace = FrontFace,
467                      DepthBiasEnable = DepthBiasEnable,
468                  };
469  
470                  var viewportState = new PipelineViewportStateCreateInfo
471                  {
472                      SType = StructureType.PipelineViewportStateCreateInfo,
473                      ViewportCount = ViewportsCount,
474                      ScissorCount = ScissorsCount,
475                  };
476  
477                  if (gd.Capabilities.SupportsDepthClipControl)
478                  {
479                      var viewportDepthClipControlState = new PipelineViewportDepthClipControlCreateInfoEXT
480                      {
481                          SType = StructureType.PipelineViewportDepthClipControlCreateInfoExt,
482                          NegativeOneToOne = DepthMode,
483                      };
484  
485                      viewportState.PNext = &viewportDepthClipControlState;
486                  }
487  
488                  var multisampleState = new PipelineMultisampleStateCreateInfo
489                  {
490                      SType = StructureType.PipelineMultisampleStateCreateInfo,
491                      SampleShadingEnable = false,
492                      RasterizationSamples = TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, SamplesCount),
493                      MinSampleShading = 1,
494                      AlphaToCoverageEnable = AlphaToCoverageEnable,
495                      AlphaToOneEnable = AlphaToOneEnable,
496                  };
497  
498                  var stencilFront = new StencilOpState(
499                      StencilFrontFailOp,
500                      StencilFrontPassOp,
501                      StencilFrontDepthFailOp,
502                      StencilFrontCompareOp);
503  
504                  var stencilBack = new StencilOpState(
505                      StencilBackFailOp,
506                      StencilBackPassOp,
507                      StencilBackDepthFailOp,
508                      StencilBackCompareOp);
509  
510                  var depthStencilState = new PipelineDepthStencilStateCreateInfo
511                  {
512                      SType = StructureType.PipelineDepthStencilStateCreateInfo,
513                      DepthTestEnable = DepthTestEnable,
514                      DepthWriteEnable = DepthWriteEnable,
515                      DepthCompareOp = DepthCompareOp,
516                      DepthBoundsTestEnable = false,
517                      StencilTestEnable = StencilTestEnable,
518                      Front = stencilFront,
519                      Back = stencilBack,
520                  };
521  
522                  uint blendEnables = 0;
523  
524                  if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
525                  {
526                      // Blend can't be enabled for integer formats, so let's make sure it is disabled.
527                      uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask;
528  
529                      while (attachmentIntegerFormatMask != 0)
530                      {
531                          int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask);
532  
533                          if (Internal.ColorBlendAttachmentState[i].BlendEnable)
534                          {
535                              blendEnables |= 1u << i;
536                          }
537  
538                          Internal.ColorBlendAttachmentState[i].BlendEnable = false;
539                          attachmentIntegerFormatMask &= ~(1u << i);
540                      }
541                  }
542  
543                  // Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
544                  // so we need to force disable them here.
545                  bool logicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed);
546  
547                  var colorBlendState = new PipelineColorBlendStateCreateInfo
548                  {
549                      SType = StructureType.PipelineColorBlendStateCreateInfo,
550                      LogicOpEnable = logicOpEnable,
551                      LogicOp = LogicOp,
552                      AttachmentCount = ColorBlendAttachmentStateCount,
553                      PAttachments = pColorBlendAttachmentState,
554                  };
555  
556                  PipelineColorBlendAdvancedStateCreateInfoEXT colorBlendAdvancedState;
557  
558                  if (!AdvancedBlendSrcPreMultiplied ||
559                      !AdvancedBlendDstPreMultiplied ||
560                      AdvancedBlendOverlap != BlendOverlapEXT.UncorrelatedExt)
561                  {
562                      colorBlendAdvancedState = new PipelineColorBlendAdvancedStateCreateInfoEXT
563                      {
564                          SType = StructureType.PipelineColorBlendAdvancedStateCreateInfoExt,
565                          SrcPremultiplied = AdvancedBlendSrcPreMultiplied,
566                          DstPremultiplied = AdvancedBlendDstPreMultiplied,
567                          BlendOverlap = AdvancedBlendOverlap,
568                      };
569  
570                      colorBlendState.PNext = &colorBlendAdvancedState;
571                  }
572  
573                  bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
574                  bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
575  
576                  DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
577  
578                  int dynamicStatesCount = 7;
579  
580                  dynamicStates[0] = DynamicState.Viewport;
581                  dynamicStates[1] = DynamicState.Scissor;
582                  dynamicStates[2] = DynamicState.DepthBias;
583                  dynamicStates[3] = DynamicState.StencilCompareMask;
584                  dynamicStates[4] = DynamicState.StencilWriteMask;
585                  dynamicStates[5] = DynamicState.StencilReference;
586                  dynamicStates[6] = DynamicState.BlendConstants;
587  
588                  if (supportsExtDynamicState)
589                  {
590                      dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
591                  }
592  
593                  if (supportsFeedbackLoopDynamicState)
594                  {
595                      dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
596                  }
597  
598                  var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
599                  {
600                      SType = StructureType.PipelineDynamicStateCreateInfo,
601                      DynamicStateCount = (uint)dynamicStatesCount,
602                      PDynamicStates = dynamicStates,
603                  };
604  
605                  PipelineCreateFlags flags = 0;
606  
607                  if (gd.Capabilities.SupportsAttachmentFeedbackLoop)
608                  {
609                      FeedbackLoopAspects aspects = FeedbackLoopAspects;
610  
611                      if ((aspects & FeedbackLoopAspects.Color) != 0)
612                      {
613                          flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
614                      }
615  
616                      if ((aspects & FeedbackLoopAspects.Depth) != 0)
617                      {
618                          flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
619                      }
620                  }
621  
622                  var pipelineCreateInfo = new GraphicsPipelineCreateInfo
623                  {
624                      SType = StructureType.GraphicsPipelineCreateInfo,
625                      Flags = flags,
626                      StageCount = StagesCount,
627                      PStages = Stages.Pointer,
628                      PVertexInputState = &vertexInputState,
629                      PInputAssemblyState = &inputAssemblyState,
630                      PTessellationState = &tessellationState,
631                      PViewportState = &viewportState,
632                      PRasterizationState = &rasterizationState,
633                      PMultisampleState = &multisampleState,
634                      PDepthStencilState = &depthStencilState,
635                      PColorBlendState = &colorBlendState,
636                      PDynamicState = &pipelineDynamicStateCreateInfo,
637                      Layout = PipelineLayout,
638                      RenderPass = renderPass,
639                  };
640  
641                  Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle);
642  
643                  if (throwOnError)
644                  {
645                      result.ThrowOnError();
646                  }
647                  else if (result.IsError())
648                  {
649                      program.AddGraphicsPipeline(ref Internal, null);
650  
651                      return null;
652                  }
653  
654                  // Restore previous blend enable values if we changed it.
655                  while (blendEnables != 0)
656                  {
657                      int i = BitOperations.TrailingZeroCount(blendEnables);
658  
659                      Internal.ColorBlendAttachmentState[i].BlendEnable = true;
660                      blendEnables &= ~(1u << i);
661                  }
662              }
663  
664              pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle));
665  
666              program.AddGraphicsPipeline(ref Internal, pipeline);
667  
668              return pipeline;
669          }
670  
671          private void UpdateVertexAttributeDescriptions(VulkanRenderer gd)
672          {
673              // Vertex attributes exceeding the stride are invalid.
674              // In metal, they cause glitches with the vertex shader fetching incorrect values.
675              // To work around this, we reduce the format to something that doesn't exceed the stride if possible.
676              // The assumption is that the exceeding components are not actually accessed on the shader.
677  
678              for (int index = 0; index < VertexAttributeDescriptionsCount; index++)
679              {
680                  var attribute = Internal.VertexAttributeDescriptions[index];
681                  int vbIndex = GetVertexBufferIndex(attribute.Binding);
682  
683                  if (vbIndex >= 0)
684                  {
685                      ref var vb = ref Internal.VertexBindingDescriptions[vbIndex];
686  
687                      Format format = attribute.Format;
688  
689                      while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride)
690                      {
691                          Format newFormat = FormatTable.DropLastComponent(format);
692  
693                          if (newFormat == format)
694                          {
695                              // That case means we failed to find a format that fits within the stride,
696                              // so just restore the original format and give up.
697                              format = attribute.Format;
698                              break;
699                          }
700  
701                          format = newFormat;
702                      }
703  
704                      if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format))
705                      {
706                          attribute.Format = format;
707                      }
708                  }
709  
710                  _vertexAttributeDescriptions2[index] = attribute;
711              }
712          }
713  
714          private int GetVertexBufferIndex(uint binding)
715          {
716              for (int index = 0; index < VertexBindingDescriptionsCount; index++)
717              {
718                  if (Internal.VertexBindingDescriptions[index].Binding == binding)
719                  {
720                      return index;
721                  }
722              }
723  
724              return -1;
725          }
726  
727          public readonly void Dispose()
728          {
729              Stages.Dispose();
730          }
731      }
732  }