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 }