EmitterContext.cs
1 using Ryujinx.Graphics.Shader.Decoders; 2 using Ryujinx.Graphics.Shader.IntermediateRepresentation; 3 using System.Collections.Generic; 4 using System.Diagnostics; 5 using System.Numerics; 6 using System.Runtime.CompilerServices; 7 using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; 8 9 namespace Ryujinx.Graphics.Shader.Translation 10 { 11 class EmitterContext 12 { 13 public DecodedProgram Program { get; } 14 public TranslatorContext TranslatorContext { get; } 15 public ResourceManager ResourceManager { get; } 16 17 public bool VertexAsCompute { get; } 18 19 public bool IsNonMain { get; } 20 21 public Block CurrBlock { get; set; } 22 public InstOp CurrOp { get; set; } 23 24 public int OperationsCount => _operations.Count; 25 26 private readonly struct BrxTarget 27 { 28 public readonly Operand Selector; 29 public readonly int ExpectedValue; 30 public readonly ulong NextTargetAddress; 31 32 public BrxTarget(Operand selector, int expectedValue, ulong nextTargetAddress) 33 { 34 Selector = selector; 35 ExpectedValue = expectedValue; 36 NextTargetAddress = nextTargetAddress; 37 } 38 } 39 40 private class BlockLabel 41 { 42 public readonly Operand Label; 43 public BrxTarget BrxTarget; 44 45 public BlockLabel(Operand label) 46 { 47 Label = label; 48 } 49 } 50 51 private readonly List<Operation> _operations; 52 private readonly Dictionary<ulong, BlockLabel> _labels; 53 54 public EmitterContext() 55 { 56 _operations = new List<Operation>(); 57 _labels = new Dictionary<ulong, BlockLabel>(); 58 } 59 60 public EmitterContext( 61 TranslatorContext translatorContext, 62 ResourceManager resourceManager, 63 DecodedProgram program, 64 bool vertexAsCompute, 65 bool isNonMain) : this() 66 { 67 TranslatorContext = translatorContext; 68 ResourceManager = resourceManager; 69 Program = program; 70 VertexAsCompute = vertexAsCompute; 71 IsNonMain = isNonMain; 72 73 EmitStart(); 74 } 75 76 private void EmitStart() 77 { 78 if (TranslatorContext.Options.Flags.HasFlag(TranslationFlags.VertexA)) 79 { 80 return; 81 } 82 83 // Vulkan requires the point size to be always written on the shader if the primitive topology is points. 84 // OpenGL requires the point size to be always written on the shader if PROGRAM_POINT_SIZE is set. 85 if (TranslatorContext.Definitions.Stage == ShaderStage.Vertex) 86 { 87 this.Store(StorageKind.Output, IoVariable.PointSize, null, ConstF(TranslatorContext.Definitions.PointSize)); 88 } 89 90 if (VertexAsCompute) 91 { 92 int vertexInfoCbBinding = ResourceManager.Reservations.VertexInfoConstantBufferBinding; 93 int countFieldIndex = TranslatorContext.Stage == ShaderStage.Vertex 94 ? (int)VertexInfoBufferField.VertexCounts 95 : (int)VertexInfoBufferField.GeometryCounts; 96 97 Operand outputVertexOffset = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(0)); 98 Operand vertexCount = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const(countFieldIndex), Const(0)); 99 Operand isVertexOob = this.ICompareGreaterOrEqualUnsigned(outputVertexOffset, vertexCount); 100 101 Operand lblVertexInBounds = Label(); 102 103 this.BranchIfFalse(lblVertexInBounds, isVertexOob); 104 this.Return(); 105 this.MarkLabel(lblVertexInBounds); 106 107 Operand outputInstanceOffset = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(1)); 108 Operand instanceCount = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(1)); 109 Operand firstVertex = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(2)); 110 Operand firstInstance = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(3)); 111 Operand ibBaseOffset = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.GeometryCounts), Const(3)); 112 Operand isInstanceOob = this.ICompareGreaterOrEqualUnsigned(outputInstanceOffset, instanceCount); 113 114 Operand lblInstanceInBounds = Label(); 115 116 this.BranchIfFalse(lblInstanceInBounds, isInstanceOob); 117 this.Return(); 118 this.MarkLabel(lblInstanceInBounds); 119 120 if (TranslatorContext.Stage == ShaderStage.Vertex) 121 { 122 Operand vertexIndexVr = Local(); 123 124 this.TextureSample( 125 SamplerType.TextureBuffer, 126 TextureFlags.IntCoords, 127 ResourceManager.Reservations.GetIndexBufferTextureSetAndBinding(), 128 1, 129 new[] { vertexIndexVr }, 130 new[] { this.IAdd(ibBaseOffset, outputVertexOffset) }); 131 132 this.Store(StorageKind.LocalMemory, ResourceManager.LocalVertexIndexVertexRateMemoryId, this.IAdd(firstVertex, vertexIndexVr)); 133 this.Store(StorageKind.LocalMemory, ResourceManager.LocalVertexIndexInstanceRateMemoryId, this.IAdd(firstInstance, outputInstanceOffset)); 134 } 135 else if (TranslatorContext.Stage == ShaderStage.Geometry) 136 { 137 int inputVertices = TranslatorContext.Definitions.InputTopology.ToInputVertices(); 138 139 Operand baseVertex = this.IMultiply(outputVertexOffset, Const(inputVertices)); 140 141 for (int index = 0; index < inputVertices; index++) 142 { 143 Operand vertexIndex = Local(); 144 145 this.TextureSample( 146 SamplerType.TextureBuffer, 147 TextureFlags.IntCoords, 148 ResourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(), 149 1, 150 new[] { vertexIndex }, 151 new[] { this.IAdd(baseVertex, Const(index)) }); 152 153 this.Store(StorageKind.LocalMemory, ResourceManager.LocalTopologyRemapMemoryId, Const(index), vertexIndex); 154 } 155 156 this.Store(StorageKind.LocalMemory, ResourceManager.LocalGeometryOutputVertexCountMemoryId, Const(0)); 157 this.Store(StorageKind.LocalMemory, ResourceManager.LocalGeometryOutputIndexCountMemoryId, Const(0)); 158 } 159 } 160 } 161 162 public T GetOp<T>() where T : unmanaged 163 { 164 Debug.Assert(Unsafe.SizeOf<T>() == sizeof(ulong)); 165 ulong op = CurrOp.RawOpCode; 166 return Unsafe.As<ulong, T>(ref op); 167 } 168 169 public Operand Add(Instruction inst, Operand dest = null, params Operand[] sources) 170 { 171 Operation operation = new(inst, dest, sources); 172 173 _operations.Add(operation); 174 175 return dest; 176 } 177 178 public Operand Add(Instruction inst, StorageKind storageKind, Operand dest = null, params Operand[] sources) 179 { 180 Operation operation = new(inst, storageKind, dest, sources); 181 182 _operations.Add(operation); 183 184 return dest; 185 } 186 187 public (Operand, Operand) Add(Instruction inst, (Operand, Operand) dest, params Operand[] sources) 188 { 189 Operand[] dests = new[] { dest.Item1, dest.Item2 }; 190 191 Operation operation = new(inst, 0, dests, sources); 192 193 Add(operation); 194 195 return dest; 196 } 197 198 public void Add(Operation operation) 199 { 200 _operations.Add(operation); 201 } 202 203 public void MarkLabel(Operand label) 204 { 205 Add(Instruction.MarkLabel, label); 206 } 207 208 public Operand GetLabel(ulong address) 209 { 210 return EnsureBlockLabel(address).Label; 211 } 212 213 public void SetBrxTarget(ulong address, Operand selector, int targetValue, ulong nextTargetAddress) 214 { 215 BlockLabel blockLabel = EnsureBlockLabel(address); 216 Debug.Assert(blockLabel.BrxTarget.Selector == null); 217 blockLabel.BrxTarget = new BrxTarget(selector, targetValue, nextTargetAddress); 218 } 219 220 public void EnterBlock(ulong address) 221 { 222 BlockLabel blockLabel = EnsureBlockLabel(address); 223 224 MarkLabel(blockLabel.Label); 225 226 BrxTarget brxTarget = blockLabel.BrxTarget; 227 228 if (brxTarget.Selector != null) 229 { 230 this.BranchIfFalse(GetLabel(brxTarget.NextTargetAddress), this.ICompareEqual(brxTarget.Selector, Const(brxTarget.ExpectedValue))); 231 } 232 } 233 234 private BlockLabel EnsureBlockLabel(ulong address) 235 { 236 if (!_labels.TryGetValue(address, out BlockLabel blockLabel)) 237 { 238 blockLabel = new BlockLabel(Label()); 239 240 _labels.Add(address, blockLabel); 241 } 242 243 return blockLabel; 244 } 245 246 public void PrepareForVertexReturn() 247 { 248 // TODO: Support transform feedback emulation on stages other than vertex. 249 // Those stages might produce more primitives, so it needs a way to "compact" the output after it is written. 250 251 if (!TranslatorContext.GpuAccessor.QueryHostSupportsTransformFeedback() && 252 TranslatorContext.GpuAccessor.QueryTransformFeedbackEnabled() && 253 TranslatorContext.Stage == ShaderStage.Vertex) 254 { 255 Operand vertexCount = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.TfeVertexCount)); 256 257 for (int tfbIndex = 0; tfbIndex < ResourceReservations.TfeBuffersCount; tfbIndex++) 258 { 259 var locations = TranslatorContext.GpuAccessor.QueryTransformFeedbackVaryingLocations(tfbIndex); 260 var stride = TranslatorContext.GpuAccessor.QueryTransformFeedbackStride(tfbIndex); 261 262 Operand baseOffset = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.TfeOffset), Const(tfbIndex)); 263 Operand baseVertex = this.Load(StorageKind.Input, IoVariable.BaseVertex); 264 Operand baseInstance = this.Load(StorageKind.Input, IoVariable.BaseInstance); 265 Operand vertexIndex = this.Load(StorageKind.Input, IoVariable.VertexIndex); 266 Operand instanceIndex = this.Load(StorageKind.Input, IoVariable.InstanceIndex); 267 268 Operand outputVertexOffset = this.ISubtract(vertexIndex, baseVertex); 269 Operand outputInstanceOffset = this.ISubtract(instanceIndex, baseInstance); 270 271 Operand outputBaseVertex = this.IMultiply(outputInstanceOffset, vertexCount); 272 273 Operand vertexOffset = this.IMultiply(this.IAdd(outputBaseVertex, outputVertexOffset), Const(stride / 4)); 274 baseOffset = this.IAdd(baseOffset, vertexOffset); 275 276 for (int j = 0; j < locations.Length; j++) 277 { 278 byte location = locations[j]; 279 if (location == 0xff) 280 { 281 continue; 282 } 283 284 Operand offset = this.IAdd(baseOffset, Const(j)); 285 Operand value = Instructions.AttributeMap.GenerateAttributeLoad(this, null, location * 4, isOutput: true, isPerPatch: false); 286 287 int binding = ResourceManager.Reservations.GetTfeBufferStorageBufferBinding(tfbIndex); 288 289 this.Store(StorageKind.StorageBuffer, binding, Const(0), offset, value); 290 } 291 } 292 } 293 294 if (TranslatorContext.Definitions.ViewportTransformDisable) 295 { 296 Operand x = this.Load(StorageKind.Output, IoVariable.Position, null, Const(0)); 297 Operand y = this.Load(StorageKind.Output, IoVariable.Position, null, Const(1)); 298 Operand xScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(0)); 299 Operand yScale = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.ViewportInverse), Const(1)); 300 Operand negativeOne = ConstF(-1.0f); 301 302 this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), this.FPFusedMultiplyAdd(x, xScale, negativeOne)); 303 this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne)); 304 } 305 306 if (TranslatorContext.Definitions.DepthMode && !TranslatorContext.GpuAccessor.QueryHostSupportsDepthClipControl()) 307 { 308 Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)); 309 Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3)); 310 Operand halfW = this.FPMultiply(w, ConstF(0.5f)); 311 312 this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW)); 313 } 314 } 315 316 public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal) 317 { 318 if (TranslatorContext.Definitions.ViewportTransformDisable) 319 { 320 oldXLocal = Local(); 321 this.Copy(oldXLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(0))); 322 oldYLocal = Local(); 323 this.Copy(oldYLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(1))); 324 } 325 else 326 { 327 oldXLocal = null; 328 oldYLocal = null; 329 } 330 331 if (TranslatorContext.Definitions.DepthMode && !TranslatorContext.GpuAccessor.QueryHostSupportsDepthClipControl()) 332 { 333 oldZLocal = Local(); 334 this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2))); 335 } 336 else 337 { 338 oldZLocal = null; 339 } 340 341 PrepareForVertexReturn(); 342 } 343 344 public bool PrepareForReturn() 345 { 346 if (IsNonMain) 347 { 348 return true; 349 } 350 351 if (TranslatorContext.Definitions.LastInVertexPipeline && 352 (TranslatorContext.Definitions.Stage == ShaderStage.Vertex || TranslatorContext.Definitions.Stage == ShaderStage.TessellationEvaluation) && 353 (TranslatorContext.Options.Flags & TranslationFlags.VertexA) == 0) 354 { 355 PrepareForVertexReturn(); 356 } 357 else if (TranslatorContext.Definitions.Stage == ShaderStage.Geometry) 358 { 359 void WritePositionOutput(int primIndex) 360 { 361 Operand x = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(0)); 362 Operand y = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(1)); 363 Operand z = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(2)); 364 Operand w = this.Load(StorageKind.Input, IoVariable.Position, Const(primIndex), Const(3)); 365 366 this.Store(StorageKind.Output, IoVariable.Position, null, Const(0), x); 367 this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), y); 368 this.Store(StorageKind.Output, IoVariable.Position, null, Const(2), z); 369 this.Store(StorageKind.Output, IoVariable.Position, null, Const(3), w); 370 } 371 372 void WriteUserDefinedOutput(int index, int primIndex) 373 { 374 Operand x = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(0)); 375 Operand y = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(1)); 376 Operand z = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(2)); 377 Operand w = this.Load(StorageKind.Input, IoVariable.UserDefined, Const(index), Const(primIndex), Const(3)); 378 379 this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(0), x); 380 this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(1), y); 381 this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(2), z); 382 this.Store(StorageKind.Output, IoVariable.UserDefined, null, Const(index), Const(3), w); 383 } 384 385 if (TranslatorContext.Definitions.GpPassthrough && !TranslatorContext.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) 386 { 387 int inputStart, inputEnd, inputStep; 388 389 InputTopology topology = TranslatorContext.Definitions.InputTopology; 390 391 if (topology == InputTopology.LinesAdjacency) 392 { 393 inputStart = 1; 394 inputEnd = 3; 395 inputStep = 1; 396 } 397 else if (topology == InputTopology.TrianglesAdjacency) 398 { 399 inputStart = 0; 400 inputEnd = 6; 401 inputStep = 2; 402 } 403 else 404 { 405 inputStart = 0; 406 inputEnd = topology.ToInputVerticesNoAdjacency(); 407 inputStep = 1; 408 } 409 410 for (int primIndex = inputStart; primIndex < inputEnd; primIndex += inputStep) 411 { 412 WritePositionOutput(primIndex); 413 414 int passthroughAttributes = TranslatorContext.AttributeUsage.PassthroughAttributes; 415 while (passthroughAttributes != 0) 416 { 417 int index = BitOperations.TrailingZeroCount(passthroughAttributes); 418 WriteUserDefinedOutput(index, primIndex); 419 passthroughAttributes &= ~(1 << index); 420 } 421 422 this.EmitVertex(); 423 } 424 425 this.EndPrimitive(); 426 } 427 } 428 else if (TranslatorContext.Definitions.Stage == ShaderStage.Fragment) 429 { 430 GenerateAlphaToCoverageDitherDiscard(); 431 432 bool supportsBgra = TranslatorContext.GpuAccessor.QueryHostSupportsBgraFormat(); 433 434 if (TranslatorContext.Definitions.OmapDepth) 435 { 436 Operand src = Register(TranslatorContext.GetDepthRegister(), RegisterType.Gpr); 437 438 this.Store(StorageKind.Output, IoVariable.FragmentOutputDepth, null, src); 439 } 440 441 AlphaTestOp alphaTestOp = TranslatorContext.Definitions.AlphaTestCompare; 442 443 if (alphaTestOp != AlphaTestOp.Always) 444 { 445 if (alphaTestOp == AlphaTestOp.Never) 446 { 447 this.Discard(); 448 } 449 else if ((TranslatorContext.Definitions.OmapTargets & 8) != 0) 450 { 451 Instruction comparator = alphaTestOp switch 452 { 453 AlphaTestOp.Equal => Instruction.CompareEqual, 454 AlphaTestOp.Greater => Instruction.CompareGreater, 455 AlphaTestOp.GreaterOrEqual => Instruction.CompareGreaterOrEqual, 456 AlphaTestOp.Less => Instruction.CompareLess, 457 AlphaTestOp.LessOrEqual => Instruction.CompareLessOrEqual, 458 AlphaTestOp.NotEqual => Instruction.CompareNotEqual, 459 _ => 0, 460 }; 461 462 Debug.Assert(comparator != 0, $"Invalid alpha test operation \"{alphaTestOp}\"."); 463 464 Operand alpha = Register(3, RegisterType.Gpr); 465 Operand alphaRef = ConstF(TranslatorContext.Definitions.AlphaTestReference); 466 Operand alphaPass = Add(Instruction.FP32 | comparator, Local(), alpha, alphaRef); 467 Operand alphaPassLabel = Label(); 468 469 this.BranchIfTrue(alphaPassLabel, alphaPass); 470 this.Discard(); 471 this.MarkLabel(alphaPassLabel); 472 } 473 } 474 475 // We don't need to output anything if alpha test always fails. 476 if (alphaTestOp == AlphaTestOp.Never) 477 { 478 return false; 479 } 480 481 int regIndexBase = 0; 482 483 for (int rtIndex = 0; rtIndex < 8; rtIndex++) 484 { 485 for (int component = 0; component < 4; component++) 486 { 487 bool componentEnabled = (TranslatorContext.Definitions.OmapTargets & (1 << (rtIndex * 4 + component))) != 0; 488 if (!componentEnabled) 489 { 490 continue; 491 } 492 493 Operand src = Register(regIndexBase + component, RegisterType.Gpr); 494 495 // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL). 496 if (!supportsBgra && (component == 0 || component == 2)) 497 { 498 Operand isBgra = this.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.FragmentIsBgra), Const(rtIndex)); 499 500 Operand lblIsBgra = Label(); 501 Operand lblEnd = Label(); 502 503 this.BranchIfTrue(lblIsBgra, isBgra); 504 505 this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src); 506 this.Branch(lblEnd); 507 508 MarkLabel(lblIsBgra); 509 510 this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(2 - component), src); 511 512 MarkLabel(lblEnd); 513 } 514 else 515 { 516 this.Store(StorageKind.Output, IoVariable.FragmentOutputColor, null, Const(rtIndex), Const(component), src); 517 } 518 } 519 520 bool targetEnabled = (TranslatorContext.Definitions.OmapTargets & (0xf << (rtIndex * 4))) != 0; 521 if (targetEnabled) 522 { 523 regIndexBase += 4; 524 } 525 } 526 } 527 528 if (VertexAsCompute) 529 { 530 if (TranslatorContext.Stage == ShaderStage.Vertex) 531 { 532 int vertexInfoCbBinding = ResourceManager.Reservations.VertexInfoConstantBufferBinding; 533 int vertexOutputSbBinding = ResourceManager.Reservations.VertexOutputStorageBufferBinding; 534 int stride = ResourceManager.Reservations.OutputSizePerInvocation; 535 536 Operand vertexCount = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(0)); 537 538 Operand outputVertexOffset = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(0)); 539 Operand outputInstanceOffset = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(1)); 540 541 Operand outputBaseVertex = this.IMultiply(outputInstanceOffset, vertexCount); 542 543 Operand baseOffset = this.IMultiply(this.IAdd(outputBaseVertex, outputVertexOffset), Const(stride)); 544 545 for (int offset = 0; offset < stride; offset++) 546 { 547 Operand vertexOffset = this.IAdd(baseOffset, Const(offset)); 548 Operand value = this.Load(StorageKind.LocalMemory, ResourceManager.LocalVertexDataMemoryId, Const(offset)); 549 550 this.Store(StorageKind.StorageBuffer, vertexOutputSbBinding, Const(0), vertexOffset, value); 551 } 552 } 553 else if (TranslatorContext.Stage == ShaderStage.Geometry) 554 { 555 Operand lblLoopHead = Label(); 556 Operand lblExit = Label(); 557 558 this.MarkLabel(lblLoopHead); 559 560 Operand writtenIndices = this.Load(StorageKind.LocalMemory, ResourceManager.LocalGeometryOutputIndexCountMemoryId); 561 562 int maxIndicesPerPrimitiveInvocation = TranslatorContext.Definitions.GetGeometryOutputIndexBufferStridePerInstance(); 563 int maxIndicesPerPrimitive = maxIndicesPerPrimitiveInvocation * TranslatorContext.Definitions.ThreadsPerInputPrimitive; 564 565 this.BranchIfTrue(lblExit, this.ICompareGreaterOrEqualUnsigned(writtenIndices, Const(maxIndicesPerPrimitiveInvocation))); 566 567 int vertexInfoCbBinding = ResourceManager.Reservations.VertexInfoConstantBufferBinding; 568 569 Operand primitiveIndex = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(0)); 570 Operand instanceIndex = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(1)); 571 Operand invocationId = this.Load(StorageKind.Input, IoVariable.GlobalId, Const(2)); 572 Operand vertexCount = this.Load(StorageKind.ConstantBuffer, vertexInfoCbBinding, Const((int)VertexInfoBufferField.VertexCounts), Const(0)); 573 Operand primitiveId = this.IAdd(this.IMultiply(instanceIndex, vertexCount), primitiveIndex); 574 Operand ibOffset = this.IMultiply(primitiveId, Const(maxIndicesPerPrimitive)); 575 ibOffset = this.IAdd(ibOffset, this.IMultiply(invocationId, Const(maxIndicesPerPrimitiveInvocation))); 576 ibOffset = this.IAdd(ibOffset, writtenIndices); 577 578 this.Store(StorageKind.StorageBuffer, ResourceManager.Reservations.GeometryIndexOutputStorageBufferBinding, Const(0), ibOffset, Const(-1)); 579 this.Store(StorageKind.LocalMemory, ResourceManager.LocalGeometryOutputIndexCountMemoryId, this.IAdd(writtenIndices, Const(1))); 580 581 this.Branch(lblLoopHead); 582 583 this.MarkLabel(lblExit); 584 } 585 } 586 587 return true; 588 } 589 590 private void GenerateAlphaToCoverageDitherDiscard() 591 { 592 // If the feature is disabled, or alpha is not written, then we're done. 593 if (!TranslatorContext.Definitions.AlphaToCoverageDitherEnable || (TranslatorContext.Definitions.OmapTargets & 8) == 0) 594 { 595 return; 596 } 597 598 // 11 11 11 10 10 10 10 00 599 // 11 01 01 01 01 00 00 00 600 Operand ditherMask = Const(unchecked((int)0xfbb99110u)); 601 602 Operand fragCoordX = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0)); 603 Operand fragCoordY = this.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1)); 604 605 Operand x = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordX), Const(1)); 606 Operand y = this.BitwiseAnd(this.FP32ConvertToU32(fragCoordY), Const(1)); 607 Operand xy = this.BitwiseOr(x, this.ShiftLeft(y, Const(1))); 608 609 Operand alpha = Register(3, RegisterType.Gpr); 610 Operand scaledAlpha = this.FPMultiply(this.FPSaturate(alpha), ConstF(8)); 611 Operand quantizedAlpha = this.IMinimumU32(this.FP32ConvertToU32(scaledAlpha), Const(7)); 612 Operand shift = this.BitwiseOr(this.ShiftLeft(quantizedAlpha, Const(2)), xy); 613 Operand opaque = this.BitwiseAnd(this.ShiftRightU32(ditherMask, shift), Const(1)); 614 615 Operand a2cDitherEndLabel = Label(); 616 617 this.BranchIfTrue(a2cDitherEndLabel, opaque); 618 this.Discard(); 619 this.MarkLabel(a2cDitherEndLabel); 620 } 621 622 public Operation[] GetOperations() 623 { 624 return _operations.ToArray(); 625 } 626 } 627 }