/ src / Ryujinx.Graphics.Shader / Translation / EmitterContext.cs
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  }