/ src / Ryujinx.Graphics.OpenGL / Pipeline.cs
Pipeline.cs
   1  using OpenTK.Graphics.OpenGL;
   2  using Ryujinx.Common.Logging;
   3  using Ryujinx.Graphics.GAL;
   4  using Ryujinx.Graphics.OpenGL.Image;
   5  using Ryujinx.Graphics.OpenGL.Queries;
   6  using Ryujinx.Graphics.Shader;
   7  using System;
   8  
   9  namespace Ryujinx.Graphics.OpenGL
  10  {
  11      class Pipeline : IPipeline, IDisposable
  12      {
  13          private const int SavedImages = 2;
  14  
  15          private readonly DrawTextureEmulation _drawTexture;
  16  
  17          internal ulong DrawCount { get; private set; }
  18  
  19          private Program _program;
  20  
  21          private bool _rasterizerDiscard;
  22  
  23          private VertexArray _vertexArray;
  24          private Framebuffer _framebuffer;
  25  
  26          private IntPtr _indexBaseOffset;
  27  
  28          private DrawElementsType _elementsType;
  29  
  30          private PrimitiveType _primitiveType;
  31  
  32          private int _stencilFrontMask;
  33          private bool _depthMask;
  34          private bool _depthTestEnable;
  35          private bool _stencilTestEnable;
  36          private bool _cullEnable;
  37  
  38          private float[] _viewportArray = Array.Empty<float>();
  39          private double[] _depthRangeArray = Array.Empty<double>();
  40  
  41          private int _boundDrawFramebuffer;
  42          private int _boundReadFramebuffer;
  43  
  44          private CounterQueueEvent _activeConditionalRender;
  45  
  46          private readonly Vector4<int>[] _fpIsBgra = new Vector4<int>[SupportBuffer.FragmentIsBgraCount];
  47  
  48          private readonly TextureBase[] _images;
  49          private TextureBase _unit0Texture;
  50          private Sampler _unit0Sampler;
  51  
  52          private FrontFaceDirection _frontFace;
  53          private ClipOrigin _clipOrigin;
  54          private ClipDepthMode _clipDepthMode;
  55  
  56          private uint _fragmentOutputMap;
  57          private uint _componentMasks;
  58          private uint _currentComponentMasks;
  59          private bool _advancedBlendEnable;
  60  
  61          private uint _scissorEnables;
  62  
  63          private bool _tfEnabled;
  64          private TransformFeedbackPrimitiveType _tfTopology;
  65  
  66          private readonly BufferHandle[] _tfbs;
  67          private readonly BufferRange[] _tfbTargets;
  68  
  69          private ColorF _blendConstant;
  70  
  71          internal Pipeline()
  72          {
  73              _drawTexture = new DrawTextureEmulation();
  74              _rasterizerDiscard = false;
  75              _clipOrigin = ClipOrigin.LowerLeft;
  76              _clipDepthMode = ClipDepthMode.NegativeOneToOne;
  77  
  78              _fragmentOutputMap = uint.MaxValue;
  79              _componentMasks = uint.MaxValue;
  80  
  81              _images = new TextureBase[SavedImages];
  82  
  83              _tfbs = new BufferHandle[Constants.MaxTransformFeedbackBuffers];
  84              _tfbTargets = new BufferRange[Constants.MaxTransformFeedbackBuffers];
  85          }
  86  
  87          public void Barrier()
  88          {
  89              GL.MemoryBarrier(MemoryBarrierFlags.AllBarrierBits);
  90          }
  91  
  92          public void BeginTransformFeedback(PrimitiveTopology topology)
  93          {
  94              GL.BeginTransformFeedback(_tfTopology = topology.ConvertToTfType());
  95              _tfEnabled = true;
  96          }
  97  
  98          public void ClearBuffer(BufferHandle destination, int offset, int size, uint value)
  99          {
 100              Buffer.Clear(destination, offset, size, value);
 101          }
 102  
 103          public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
 104          {
 105              EnsureFramebuffer();
 106  
 107              GL.ColorMask(
 108                  index,
 109                  (componentMask & 1) != 0,
 110                  (componentMask & 2) != 0,
 111                  (componentMask & 4) != 0,
 112                  (componentMask & 8) != 0);
 113  
 114              float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha };
 115  
 116              if (layer != 0 || layerCount != _framebuffer.GetColorLayerCount(index))
 117              {
 118                  for (int l = layer; l < layer + layerCount; l++)
 119                  {
 120                      _framebuffer.AttachColorLayerForClear(index, l);
 121  
 122                      GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
 123                  }
 124  
 125                  _framebuffer.DetachColorLayerForClear(index);
 126              }
 127              else
 128              {
 129                  GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
 130              }
 131  
 132              RestoreComponentMask(index);
 133          }
 134  
 135          public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
 136          {
 137              EnsureFramebuffer();
 138  
 139              bool stencilMaskChanged =
 140                  stencilMask != 0 &&
 141                  stencilMask != _stencilFrontMask;
 142  
 143              bool depthMaskChanged = depthMask && depthMask != _depthMask;
 144  
 145              if (stencilMaskChanged)
 146              {
 147                  GL.StencilMaskSeparate(StencilFace.Front, stencilMask);
 148              }
 149  
 150              if (depthMaskChanged)
 151              {
 152                  GL.DepthMask(depthMask);
 153              }
 154  
 155              if (layer != 0 || layerCount != _framebuffer.GetDepthStencilLayerCount())
 156              {
 157                  for (int l = layer; l < layer + layerCount; l++)
 158                  {
 159                      _framebuffer.AttachDepthStencilLayerForClear(l);
 160  
 161                      ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask);
 162                  }
 163  
 164                  _framebuffer.DetachDepthStencilLayerForClear();
 165              }
 166              else
 167              {
 168                  ClearDepthStencil(depthValue, depthMask, stencilValue, stencilMask);
 169              }
 170  
 171              if (stencilMaskChanged)
 172              {
 173                  GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);
 174              }
 175  
 176              if (depthMaskChanged)
 177              {
 178                  GL.DepthMask(_depthMask);
 179              }
 180          }
 181  
 182          private static void ClearDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
 183          {
 184              if (depthMask && stencilMask != 0)
 185              {
 186                  GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue);
 187              }
 188              else if (depthMask)
 189              {
 190                  GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Depth, 0, ref depthValue);
 191              }
 192              else if (stencilMask != 0)
 193              {
 194                  GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
 195              }
 196          }
 197  
 198          public void CommandBufferBarrier()
 199          {
 200              GL.MemoryBarrier(MemoryBarrierFlags.CommandBarrierBit);
 201          }
 202  
 203          public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
 204          {
 205              Buffer.Copy(source, destination, srcOffset, dstOffset, size);
 206          }
 207  
 208          public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
 209          {
 210              if (!_program.IsLinked)
 211              {
 212                  Logger.Debug?.Print(LogClass.Gpu, "Dispatch error, shader not linked.");
 213                  return;
 214              }
 215  
 216              PrepareForDispatch();
 217  
 218              GL.DispatchCompute(groupsX, groupsY, groupsZ);
 219          }
 220  
 221          public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
 222          {
 223              if (!_program.IsLinked)
 224              {
 225                  Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
 226                  return;
 227              }
 228  
 229              PreDraw(vertexCount);
 230  
 231              if (_primitiveType == PrimitiveType.Quads && !HwCapabilities.SupportsQuads)
 232              {
 233                  DrawQuadsImpl(vertexCount, instanceCount, firstVertex, firstInstance);
 234              }
 235              else if (_primitiveType == PrimitiveType.QuadStrip && !HwCapabilities.SupportsQuads)
 236              {
 237                  DrawQuadStripImpl(vertexCount, instanceCount, firstVertex, firstInstance);
 238              }
 239              else
 240              {
 241                  DrawImpl(vertexCount, instanceCount, firstVertex, firstInstance);
 242              }
 243  
 244              PostDraw();
 245          }
 246  
 247          private static void DrawQuadsImpl(
 248              int vertexCount,
 249              int instanceCount,
 250              int firstVertex,
 251              int firstInstance)
 252          {
 253              // TODO: Instanced rendering.
 254              int quadsCount = vertexCount / 4;
 255  
 256              int[] firsts = new int[quadsCount];
 257              int[] counts = new int[quadsCount];
 258  
 259              for (int quadIndex = 0; quadIndex < quadsCount; quadIndex++)
 260              {
 261                  firsts[quadIndex] = firstVertex + quadIndex * 4;
 262                  counts[quadIndex] = 4;
 263              }
 264  
 265              GL.MultiDrawArrays(
 266                  PrimitiveType.TriangleFan,
 267                  firsts,
 268                  counts,
 269                  quadsCount);
 270          }
 271  
 272          private static void DrawQuadStripImpl(
 273              int vertexCount,
 274              int instanceCount,
 275              int firstVertex,
 276              int firstInstance)
 277          {
 278              int quadsCount = (vertexCount - 2) / 2;
 279  
 280              if (firstInstance != 0 || instanceCount != 1)
 281              {
 282                  for (int quadIndex = 0; quadIndex < quadsCount; quadIndex++)
 283                  {
 284                      GL.DrawArraysInstancedBaseInstance(PrimitiveType.TriangleFan, firstVertex + quadIndex * 2, 4, instanceCount, firstInstance);
 285                  }
 286              }
 287              else
 288              {
 289                  int[] firsts = new int[quadsCount];
 290                  int[] counts = new int[quadsCount];
 291  
 292                  firsts[0] = firstVertex;
 293                  counts[0] = 4;
 294  
 295                  for (int quadIndex = 1; quadIndex < quadsCount; quadIndex++)
 296                  {
 297                      firsts[quadIndex] = firstVertex + quadIndex * 2;
 298                      counts[quadIndex] = 4;
 299                  }
 300  
 301                  GL.MultiDrawArrays(
 302                      PrimitiveType.TriangleFan,
 303                      firsts,
 304                      counts,
 305                      quadsCount);
 306              }
 307          }
 308  
 309          private void DrawImpl(
 310              int vertexCount,
 311              int instanceCount,
 312              int firstVertex,
 313              int firstInstance)
 314          {
 315              if (firstInstance == 0 && instanceCount == 1)
 316              {
 317                  GL.DrawArrays(_primitiveType, firstVertex, vertexCount);
 318              }
 319              else if (firstInstance == 0)
 320              {
 321                  GL.DrawArraysInstanced(_primitiveType, firstVertex, vertexCount, instanceCount);
 322              }
 323              else
 324              {
 325                  GL.DrawArraysInstancedBaseInstance(
 326                      _primitiveType,
 327                      firstVertex,
 328                      vertexCount,
 329                      instanceCount,
 330                      firstInstance);
 331              }
 332          }
 333  
 334          public void DrawIndexed(
 335              int indexCount,
 336              int instanceCount,
 337              int firstIndex,
 338              int firstVertex,
 339              int firstInstance)
 340          {
 341              if (!_program.IsLinked)
 342              {
 343                  Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
 344                  return;
 345              }
 346  
 347              PreDrawVbUnbounded();
 348  
 349              int indexElemSize = 1;
 350  
 351              switch (_elementsType)
 352              {
 353                  case DrawElementsType.UnsignedShort:
 354                      indexElemSize = 2;
 355                      break;
 356                  case DrawElementsType.UnsignedInt:
 357                      indexElemSize = 4;
 358                      break;
 359              }
 360  
 361              IntPtr indexBaseOffset = _indexBaseOffset + firstIndex * indexElemSize;
 362  
 363              if (_primitiveType == PrimitiveType.Quads && !HwCapabilities.SupportsQuads)
 364              {
 365                  DrawQuadsIndexedImpl(
 366                      indexCount,
 367                      instanceCount,
 368                      indexBaseOffset,
 369                      indexElemSize,
 370                      firstVertex,
 371                      firstInstance);
 372              }
 373              else if (_primitiveType == PrimitiveType.QuadStrip && !HwCapabilities.SupportsQuads)
 374              {
 375                  DrawQuadStripIndexedImpl(
 376                      indexCount,
 377                      instanceCount,
 378                      indexBaseOffset,
 379                      indexElemSize,
 380                      firstVertex,
 381                      firstInstance);
 382              }
 383              else
 384              {
 385                  DrawIndexedImpl(
 386                      indexCount,
 387                      instanceCount,
 388                      indexBaseOffset,
 389                      firstVertex,
 390                      firstInstance);
 391              }
 392  
 393              PostDraw();
 394          }
 395  
 396          private void DrawQuadsIndexedImpl(
 397              int indexCount,
 398              int instanceCount,
 399              IntPtr indexBaseOffset,
 400              int indexElemSize,
 401              int firstVertex,
 402              int firstInstance)
 403          {
 404              int quadsCount = indexCount / 4;
 405  
 406              if (firstInstance != 0 || instanceCount != 1)
 407              {
 408                  if (firstVertex != 0 && firstInstance != 0)
 409                  {
 410                      for (int quadIndex = 0; quadIndex < quadsCount; quadIndex++)
 411                      {
 412                          GL.DrawElementsInstancedBaseVertexBaseInstance(
 413                              PrimitiveType.TriangleFan,
 414                              4,
 415                              _elementsType,
 416                              indexBaseOffset + quadIndex * 4 * indexElemSize,
 417                              instanceCount,
 418                              firstVertex,
 419                              firstInstance);
 420                      }
 421                  }
 422                  else if (firstInstance != 0)
 423                  {
 424                      for (int quadIndex = 0; quadIndex < quadsCount; quadIndex++)
 425                      {
 426                          GL.DrawElementsInstancedBaseInstance(
 427                              PrimitiveType.TriangleFan,
 428                              4,
 429                              _elementsType,
 430                              indexBaseOffset + quadIndex * 4 * indexElemSize,
 431                              instanceCount,
 432                              firstInstance);
 433                      }
 434                  }
 435                  else
 436                  {
 437                      for (int quadIndex = 0; quadIndex < quadsCount; quadIndex++)
 438                      {
 439                          GL.DrawElementsInstanced(
 440                              PrimitiveType.TriangleFan,
 441                              4,
 442                              _elementsType,
 443                              indexBaseOffset + quadIndex * 4 * indexElemSize,
 444                              instanceCount);
 445                      }
 446                  }
 447              }
 448              else
 449              {
 450                  IntPtr[] indices = new IntPtr[quadsCount];
 451  
 452                  int[] counts = new int[quadsCount];
 453  
 454                  int[] baseVertices = new int[quadsCount];
 455  
 456                  for (int quadIndex = 0; quadIndex < quadsCount; quadIndex++)
 457                  {
 458                      indices[quadIndex] = indexBaseOffset + quadIndex * 4 * indexElemSize;
 459  
 460                      counts[quadIndex] = 4;
 461  
 462                      baseVertices[quadIndex] = firstVertex;
 463                  }
 464  
 465                  GL.MultiDrawElementsBaseVertex(
 466                      PrimitiveType.TriangleFan,
 467                      counts,
 468                      _elementsType,
 469                      indices,
 470                      quadsCount,
 471                      baseVertices);
 472              }
 473          }
 474  
 475          private void DrawQuadStripIndexedImpl(
 476              int indexCount,
 477              int instanceCount,
 478              IntPtr indexBaseOffset,
 479              int indexElemSize,
 480              int firstVertex,
 481              int firstInstance)
 482          {
 483              // TODO: Instanced rendering.
 484              int quadsCount = (indexCount - 2) / 2;
 485  
 486              IntPtr[] indices = new IntPtr[quadsCount];
 487  
 488              int[] counts = new int[quadsCount];
 489  
 490              int[] baseVertices = new int[quadsCount];
 491  
 492              indices[0] = indexBaseOffset;
 493  
 494              counts[0] = 4;
 495  
 496              baseVertices[0] = firstVertex;
 497  
 498              for (int quadIndex = 1; quadIndex < quadsCount; quadIndex++)
 499              {
 500                  indices[quadIndex] = indexBaseOffset + quadIndex * 2 * indexElemSize;
 501  
 502                  counts[quadIndex] = 4;
 503  
 504                  baseVertices[quadIndex] = firstVertex;
 505              }
 506  
 507              GL.MultiDrawElementsBaseVertex(
 508                  PrimitiveType.TriangleFan,
 509                  counts,
 510                  _elementsType,
 511                  indices,
 512                  quadsCount,
 513                  baseVertices);
 514          }
 515  
 516          private void DrawIndexedImpl(
 517              int indexCount,
 518              int instanceCount,
 519              IntPtr indexBaseOffset,
 520              int firstVertex,
 521              int firstInstance)
 522          {
 523              if (firstInstance == 0 && firstVertex == 0 && instanceCount == 1)
 524              {
 525                  GL.DrawElements(_primitiveType, indexCount, _elementsType, indexBaseOffset);
 526              }
 527              else if (firstInstance == 0 && instanceCount == 1)
 528              {
 529                  GL.DrawElementsBaseVertex(
 530                      _primitiveType,
 531                      indexCount,
 532                      _elementsType,
 533                      indexBaseOffset,
 534                      firstVertex);
 535              }
 536              else if (firstInstance == 0 && firstVertex == 0)
 537              {
 538                  GL.DrawElementsInstanced(
 539                      _primitiveType,
 540                      indexCount,
 541                      _elementsType,
 542                      indexBaseOffset,
 543                      instanceCount);
 544              }
 545              else if (firstInstance == 0)
 546              {
 547                  GL.DrawElementsInstancedBaseVertex(
 548                      _primitiveType,
 549                      indexCount,
 550                      _elementsType,
 551                      indexBaseOffset,
 552                      instanceCount,
 553                      firstVertex);
 554              }
 555              else if (firstVertex == 0)
 556              {
 557                  GL.DrawElementsInstancedBaseInstance(
 558                      _primitiveType,
 559                      indexCount,
 560                      _elementsType,
 561                      indexBaseOffset,
 562                      instanceCount,
 563                      firstInstance);
 564              }
 565              else
 566              {
 567                  GL.DrawElementsInstancedBaseVertexBaseInstance(
 568                      _primitiveType,
 569                      indexCount,
 570                      _elementsType,
 571                      indexBaseOffset,
 572                      instanceCount,
 573                      firstVertex,
 574                      firstInstance);
 575              }
 576          }
 577  
 578          public void DrawIndexedIndirect(BufferRange indirectBuffer)
 579          {
 580              if (!_program.IsLinked)
 581              {
 582                  Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
 583                  return;
 584              }
 585  
 586              PreDrawVbUnbounded();
 587  
 588              _vertexArray.SetRangeOfIndexBuffer();
 589  
 590              GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
 591  
 592              GL.DrawElementsIndirect(_primitiveType, _elementsType, (IntPtr)indirectBuffer.Offset);
 593  
 594              _vertexArray.RestoreIndexBuffer();
 595  
 596              PostDraw();
 597          }
 598  
 599          public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
 600          {
 601              if (!_program.IsLinked)
 602              {
 603                  Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
 604                  return;
 605              }
 606  
 607              PreDrawVbUnbounded();
 608  
 609              _vertexArray.SetRangeOfIndexBuffer();
 610  
 611              GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
 612              GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
 613  
 614              GL.MultiDrawElementsIndirectCount(
 615                  _primitiveType,
 616                  (All)_elementsType,
 617                  (IntPtr)indirectBuffer.Offset,
 618                  (IntPtr)parameterBuffer.Offset,
 619                  maxDrawCount,
 620                  stride);
 621  
 622              _vertexArray.RestoreIndexBuffer();
 623  
 624              PostDraw();
 625          }
 626  
 627          public void DrawIndirect(BufferRange indirectBuffer)
 628          {
 629              if (!_program.IsLinked)
 630              {
 631                  Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
 632                  return;
 633              }
 634  
 635              PreDrawVbUnbounded();
 636  
 637              GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
 638  
 639              GL.DrawArraysIndirect(_primitiveType, (IntPtr)indirectBuffer.Offset);
 640  
 641              PostDraw();
 642          }
 643  
 644          public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
 645          {
 646              if (!_program.IsLinked)
 647              {
 648                  Logger.Debug?.Print(LogClass.Gpu, "Draw error, shader not linked.");
 649                  return;
 650              }
 651  
 652              PreDrawVbUnbounded();
 653  
 654              GL.BindBuffer((BufferTarget)All.DrawIndirectBuffer, indirectBuffer.Handle.ToInt32());
 655              GL.BindBuffer((BufferTarget)All.ParameterBuffer, parameterBuffer.Handle.ToInt32());
 656  
 657              GL.MultiDrawArraysIndirectCount(
 658                  _primitiveType,
 659                  (IntPtr)indirectBuffer.Offset,
 660                  (IntPtr)parameterBuffer.Offset,
 661                  maxDrawCount,
 662                  stride);
 663  
 664              PostDraw();
 665          }
 666  
 667          public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion)
 668          {
 669              if (texture is TextureView view && sampler is Sampler samp)
 670              {
 671                  if (HwCapabilities.SupportsDrawTexture)
 672                  {
 673                      GL.NV.DrawTexture(
 674                          view.Handle,
 675                          samp.Handle,
 676                          dstRegion.X1,
 677                          dstRegion.Y1,
 678                          dstRegion.X2,
 679                          dstRegion.Y2,
 680                          0,
 681                          srcRegion.X1 / view.Width,
 682                          srcRegion.Y1 / view.Height,
 683                          srcRegion.X2 / view.Width,
 684                          srcRegion.Y2 / view.Height);
 685                  }
 686                  else
 687                  {
 688                      static void Disable(EnableCap cap, bool enabled)
 689                      {
 690                          if (enabled)
 691                          {
 692                              GL.Disable(cap);
 693                          }
 694                      }
 695  
 696                      static void Enable(EnableCap cap, bool enabled)
 697                      {
 698                          if (enabled)
 699                          {
 700                              GL.Enable(cap);
 701                          }
 702                      }
 703  
 704                      Disable(EnableCap.CullFace, _cullEnable);
 705                      Disable(EnableCap.StencilTest, _stencilTestEnable);
 706                      Disable(EnableCap.DepthTest, _depthTestEnable);
 707  
 708                      if (_depthMask)
 709                      {
 710                          GL.DepthMask(false);
 711                      }
 712  
 713                      if (_tfEnabled)
 714                      {
 715                          GL.EndTransformFeedback();
 716                      }
 717  
 718                      GL.ClipControl(ClipOrigin.UpperLeft, ClipDepthMode.NegativeOneToOne);
 719  
 720                      _drawTexture.Draw(
 721                          view,
 722                          samp,
 723                          dstRegion.X1,
 724                          dstRegion.Y1,
 725                          dstRegion.X2,
 726                          dstRegion.Y2,
 727                          srcRegion.X1 / view.Width,
 728                          srcRegion.Y1 / view.Height,
 729                          srcRegion.X2 / view.Width,
 730                          srcRegion.Y2 / view.Height);
 731  
 732                      _program?.Bind();
 733                      _unit0Sampler?.Bind(0);
 734  
 735                      RestoreViewport0();
 736  
 737                      Enable(EnableCap.CullFace, _cullEnable);
 738                      Enable(EnableCap.StencilTest, _stencilTestEnable);
 739                      Enable(EnableCap.DepthTest, _depthTestEnable);
 740  
 741                      if (_depthMask)
 742                      {
 743                          GL.DepthMask(true);
 744                      }
 745  
 746                      if (_tfEnabled)
 747                      {
 748                          GL.BeginTransformFeedback(_tfTopology);
 749                      }
 750  
 751                      RestoreClipControl();
 752                  }
 753              }
 754          }
 755  
 756          public void EndTransformFeedback()
 757          {
 758              GL.EndTransformFeedback();
 759              _tfEnabled = false;
 760          }
 761  
 762          public void SetAlphaTest(bool enable, float reference, CompareOp op)
 763          {
 764              if (!enable)
 765              {
 766                  GL.Disable(EnableCap.AlphaTest);
 767                  return;
 768              }
 769  
 770              GL.AlphaFunc((AlphaFunction)op.Convert(), reference);
 771              GL.Enable(EnableCap.AlphaTest);
 772          }
 773  
 774          public void SetBlendState(AdvancedBlendDescriptor blend)
 775          {
 776              if (HwCapabilities.SupportsBlendEquationAdvanced)
 777              {
 778                  GL.BlendEquation((BlendEquationMode)blend.Op.Convert());
 779                  GL.NV.BlendParameter(NvBlendEquationAdvanced.BlendOverlapNv, (int)blend.Overlap.Convert());
 780                  GL.NV.BlendParameter(NvBlendEquationAdvanced.BlendPremultipliedSrcNv, blend.SrcPreMultiplied ? 1 : 0);
 781                  GL.Enable(EnableCap.Blend);
 782                  _advancedBlendEnable = true;
 783              }
 784          }
 785  
 786          public void SetBlendState(int index, BlendDescriptor blend)
 787          {
 788              if (_advancedBlendEnable)
 789              {
 790                  GL.Disable(EnableCap.Blend);
 791                  _advancedBlendEnable = false;
 792              }
 793  
 794              if (!blend.Enable)
 795              {
 796                  GL.Disable(IndexedEnableCap.Blend, index);
 797                  return;
 798              }
 799  
 800              GL.BlendEquationSeparate(
 801                  index,
 802                  blend.ColorOp.Convert(),
 803                  blend.AlphaOp.Convert());
 804  
 805              GL.BlendFuncSeparate(
 806                  index,
 807                  (BlendingFactorSrc)blend.ColorSrcFactor.Convert(),
 808                  (BlendingFactorDest)blend.ColorDstFactor.Convert(),
 809                  (BlendingFactorSrc)blend.AlphaSrcFactor.Convert(),
 810                  (BlendingFactorDest)blend.AlphaDstFactor.Convert());
 811  
 812              EnsureFramebuffer();
 813  
 814              _framebuffer.SetDualSourceBlend(
 815                  blend.ColorSrcFactor.IsDualSource() ||
 816                  blend.ColorDstFactor.IsDualSource() ||
 817                  blend.AlphaSrcFactor.IsDualSource() ||
 818                  blend.AlphaDstFactor.IsDualSource());
 819  
 820              if (_blendConstant != blend.BlendConstant)
 821              {
 822                  _blendConstant = blend.BlendConstant;
 823  
 824                  GL.BlendColor(
 825                      blend.BlendConstant.Red,
 826                      blend.BlendConstant.Green,
 827                      blend.BlendConstant.Blue,
 828                      blend.BlendConstant.Alpha);
 829              }
 830  
 831              GL.Enable(IndexedEnableCap.Blend, index);
 832          }
 833  
 834          public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
 835          {
 836              if ((enables & PolygonModeMask.Point) != 0)
 837              {
 838                  GL.Enable(EnableCap.PolygonOffsetPoint);
 839              }
 840              else
 841              {
 842                  GL.Disable(EnableCap.PolygonOffsetPoint);
 843              }
 844  
 845              if ((enables & PolygonModeMask.Line) != 0)
 846              {
 847                  GL.Enable(EnableCap.PolygonOffsetLine);
 848              }
 849              else
 850              {
 851                  GL.Disable(EnableCap.PolygonOffsetLine);
 852              }
 853  
 854              if ((enables & PolygonModeMask.Fill) != 0)
 855              {
 856                  GL.Enable(EnableCap.PolygonOffsetFill);
 857              }
 858              else
 859              {
 860                  GL.Disable(EnableCap.PolygonOffsetFill);
 861              }
 862  
 863              if (enables == 0)
 864              {
 865                  return;
 866              }
 867  
 868              if (HwCapabilities.SupportsPolygonOffsetClamp)
 869              {
 870                  GL.PolygonOffsetClamp(factor, units, clamp);
 871              }
 872              else
 873              {
 874                  GL.PolygonOffset(factor, units);
 875              }
 876          }
 877  
 878          public void SetDepthClamp(bool clamp)
 879          {
 880              if (!clamp)
 881              {
 882                  GL.Disable(EnableCap.DepthClamp);
 883                  return;
 884              }
 885  
 886              GL.Enable(EnableCap.DepthClamp);
 887          }
 888  
 889          public void SetDepthMode(DepthMode mode)
 890          {
 891              ClipDepthMode depthMode = mode.Convert();
 892  
 893              if (_clipDepthMode != depthMode)
 894              {
 895                  _clipDepthMode = depthMode;
 896  
 897                  GL.ClipControl(_clipOrigin, depthMode);
 898              }
 899          }
 900  
 901          public void SetDepthTest(DepthTestDescriptor depthTest)
 902          {
 903              if (depthTest.TestEnable)
 904              {
 905                  GL.Enable(EnableCap.DepthTest);
 906                  GL.DepthFunc((DepthFunction)depthTest.Func.Convert());
 907              }
 908              else
 909              {
 910                  GL.Disable(EnableCap.DepthTest);
 911              }
 912  
 913              GL.DepthMask(depthTest.WriteEnable);
 914              _depthMask = depthTest.WriteEnable;
 915              _depthTestEnable = depthTest.TestEnable;
 916          }
 917  
 918          public void SetFaceCulling(bool enable, Face face)
 919          {
 920              _cullEnable = enable;
 921  
 922              if (!enable)
 923              {
 924                  GL.Disable(EnableCap.CullFace);
 925                  return;
 926              }
 927  
 928              GL.CullFace(face.Convert());
 929  
 930              GL.Enable(EnableCap.CullFace);
 931          }
 932  
 933          public void SetFrontFace(FrontFace frontFace)
 934          {
 935              SetFrontFace(_frontFace = frontFace.Convert());
 936          }
 937  
 938          public void SetImage(ShaderStage stage, int binding, ITexture texture)
 939          {
 940              if ((uint)binding < SavedImages)
 941              {
 942                  _images[binding] = texture as TextureBase;
 943              }
 944  
 945              if (texture == null)
 946              {
 947                  GL.BindImageTexture(binding, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
 948                  return;
 949              }
 950  
 951              TextureBase texBase = (TextureBase)texture;
 952  
 953              SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format);
 954  
 955              if (format != 0)
 956              {
 957                  GL.BindImageTexture(binding, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format);
 958              }
 959          }
 960  
 961          public void SetImageArray(ShaderStage stage, int binding, IImageArray array)
 962          {
 963              (array as ImageArray).Bind(binding);
 964          }
 965  
 966          public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array)
 967          {
 968              throw new NotSupportedException("OpenGL does not support descriptor sets.");
 969          }
 970  
 971          public void SetIndexBuffer(BufferRange buffer, IndexType type)
 972          {
 973              _elementsType = type.Convert();
 974  
 975              _indexBaseOffset = (IntPtr)buffer.Offset;
 976  
 977              EnsureVertexArray();
 978  
 979              _vertexArray.SetIndexBuffer(buffer);
 980          }
 981  
 982          public void SetLogicOpState(bool enable, LogicalOp op)
 983          {
 984              if (enable)
 985              {
 986                  GL.Enable(EnableCap.ColorLogicOp);
 987  
 988                  GL.LogicOp((LogicOp)op.Convert());
 989              }
 990              else
 991              {
 992                  GL.Disable(EnableCap.ColorLogicOp);
 993              }
 994          }
 995  
 996          public void SetMultisampleState(MultisampleDescriptor multisample)
 997          {
 998              if (multisample.AlphaToCoverageEnable)
 999              {
1000                  GL.Enable(EnableCap.SampleAlphaToCoverage);
1001  
1002                  if (multisample.AlphaToOneEnable)
1003                  {
1004                      GL.Enable(EnableCap.SampleAlphaToOne);
1005                  }
1006                  else
1007                  {
1008                      GL.Disable(EnableCap.SampleAlphaToOne);
1009                  }
1010  
1011                  if (HwCapabilities.SupportsAlphaToCoverageDitherControl)
1012                  {
1013                      GL.NV.AlphaToCoverageDitherControl(multisample.AlphaToCoverageDitherEnable
1014                          ? NvAlphaToCoverageDitherControl.AlphaToCoverageDitherEnableNv
1015                          : NvAlphaToCoverageDitherControl.AlphaToCoverageDitherDisableNv);
1016                  }
1017              }
1018              else
1019              {
1020                  GL.Disable(EnableCap.SampleAlphaToCoverage);
1021              }
1022          }
1023  
1024          public void SetLineParameters(float width, bool smooth)
1025          {
1026              if (smooth)
1027              {
1028                  GL.Enable(EnableCap.LineSmooth);
1029              }
1030              else
1031              {
1032                  GL.Disable(EnableCap.LineSmooth);
1033              }
1034  
1035              GL.LineWidth(width);
1036          }
1037  
1038          public unsafe void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
1039          {
1040              GL.PatchParameter(PatchParameterInt.PatchVertices, vertices);
1041  
1042              fixed (float* pOuterLevel = defaultOuterLevel)
1043              {
1044                  GL.PatchParameter(PatchParameterFloat.PatchDefaultOuterLevel, pOuterLevel);
1045              }
1046  
1047              fixed (float* pInnerLevel = defaultInnerLevel)
1048              {
1049                  GL.PatchParameter(PatchParameterFloat.PatchDefaultInnerLevel, pInnerLevel);
1050              }
1051          }
1052  
1053          public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin)
1054          {
1055              // GL_POINT_SPRITE was deprecated in core profile 3.2+ and causes GL_INVALID_ENUM when set.
1056              // As we don't know if the current context is core or compat, it's safer to keep this code.
1057              if (enablePointSprite)
1058              {
1059                  GL.Enable(EnableCap.PointSprite);
1060              }
1061              else
1062              {
1063                  GL.Disable(EnableCap.PointSprite);
1064              }
1065  
1066              if (isProgramPointSize)
1067              {
1068                  GL.Enable(EnableCap.ProgramPointSize);
1069              }
1070              else
1071              {
1072                  GL.Disable(EnableCap.ProgramPointSize);
1073              }
1074  
1075              GL.PointParameter(origin == Origin.LowerLeft
1076                  ? PointSpriteCoordOriginParameter.LowerLeft
1077                  : PointSpriteCoordOriginParameter.UpperLeft);
1078  
1079              // Games seem to set point size to 0 which generates a GL_INVALID_VALUE
1080              // From the spec, GL_INVALID_VALUE is generated if size is less than or equal to 0.
1081              GL.PointSize(Math.Max(float.Epsilon, size));
1082          }
1083  
1084          public void SetPolygonMode(GAL.PolygonMode frontMode, GAL.PolygonMode backMode)
1085          {
1086              if (frontMode == backMode)
1087              {
1088                  GL.PolygonMode(MaterialFace.FrontAndBack, frontMode.Convert());
1089              }
1090              else
1091              {
1092                  GL.PolygonMode(MaterialFace.Front, frontMode.Convert());
1093                  GL.PolygonMode(MaterialFace.Back, backMode.Convert());
1094              }
1095          }
1096  
1097          public void SetPrimitiveRestart(bool enable, int index)
1098          {
1099              if (!enable)
1100              {
1101                  GL.Disable(EnableCap.PrimitiveRestart);
1102                  return;
1103              }
1104  
1105              GL.PrimitiveRestartIndex(index);
1106  
1107              GL.Enable(EnableCap.PrimitiveRestart);
1108          }
1109  
1110          public void SetPrimitiveTopology(PrimitiveTopology topology)
1111          {
1112              _primitiveType = topology.Convert();
1113          }
1114  
1115          public void SetProgram(IProgram program)
1116          {
1117              Program prg = (Program)program;
1118  
1119              if (_tfEnabled)
1120              {
1121                  GL.EndTransformFeedback();
1122                  prg.Bind();
1123                  GL.BeginTransformFeedback(_tfTopology);
1124              }
1125              else
1126              {
1127                  prg.Bind();
1128              }
1129  
1130              if (_fragmentOutputMap != (uint)prg.FragmentOutputMap)
1131              {
1132                  _fragmentOutputMap = (uint)prg.FragmentOutputMap;
1133  
1134                  for (int index = 0; index < Constants.MaxRenderTargets; index++)
1135                  {
1136                      RestoreComponentMask(index, force: false);
1137                  }
1138              }
1139  
1140              _program = prg;
1141          }
1142  
1143          public void SetRasterizerDiscard(bool discard)
1144          {
1145              if (discard)
1146              {
1147                  GL.Enable(EnableCap.RasterizerDiscard);
1148              }
1149              else
1150              {
1151                  GL.Disable(EnableCap.RasterizerDiscard);
1152              }
1153  
1154              _rasterizerDiscard = discard;
1155          }
1156  
1157          public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
1158          {
1159              _componentMasks = 0;
1160  
1161              for (int index = 0; index < componentMasks.Length; index++)
1162              {
1163                  _componentMasks |= componentMasks[index] << (index * 4);
1164  
1165                  RestoreComponentMask(index, force: false);
1166              }
1167          }
1168  
1169          public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
1170          {
1171              EnsureFramebuffer();
1172  
1173              for (int index = 0; index < colors.Length; index++)
1174              {
1175                  TextureView color = (TextureView)colors[index];
1176  
1177                  _framebuffer.AttachColor(index, color);
1178  
1179                  if (color != null)
1180                  {
1181                      int isBgra = color.Format.IsBgr() ? 1 : 0;
1182  
1183                      if (_fpIsBgra[index].X != isBgra)
1184                      {
1185                          _fpIsBgra[index].X = isBgra;
1186  
1187                          RestoreComponentMask(index);
1188                      }
1189                  }
1190              }
1191  
1192              TextureView depthStencilView = (TextureView)depthStencil;
1193  
1194              _framebuffer.AttachDepthStencil(depthStencilView);
1195              _framebuffer.SetDrawBuffers(colors.Length);
1196          }
1197  
1198          public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
1199          {
1200              int count = Math.Min(regions.Length, Constants.MaxViewports);
1201  
1202              Span<int> v = stackalloc int[count * 4];
1203  
1204              for (int index = 0; index < count; index++)
1205              {
1206                  int vIndex = index * 4;
1207  
1208                  var region = regions[index];
1209  
1210                  bool enabled = (region.X | region.Y) != 0 || region.Width != 0xffff || region.Height != 0xffff;
1211                  uint mask = 1u << index;
1212  
1213                  if (enabled)
1214                  {
1215                      v[vIndex] = region.X;
1216                      v[vIndex + 1] = region.Y;
1217                      v[vIndex + 2] = region.Width;
1218                      v[vIndex + 3] = region.Height;
1219  
1220                      if ((_scissorEnables & mask) == 0)
1221                      {
1222                          _scissorEnables |= mask;
1223                          GL.Enable(IndexedEnableCap.ScissorTest, index);
1224                      }
1225                  }
1226                  else
1227                  {
1228                      if ((_scissorEnables & mask) != 0)
1229                      {
1230                          _scissorEnables &= ~mask;
1231                          GL.Disable(IndexedEnableCap.ScissorTest, index);
1232                      }
1233                  }
1234              }
1235  
1236              GL.ScissorArray(0, count, ref v[0]);
1237          }
1238  
1239          public void SetStencilTest(StencilTestDescriptor stencilTest)
1240          {
1241              _stencilTestEnable = stencilTest.TestEnable;
1242  
1243              if (!stencilTest.TestEnable)
1244              {
1245                  GL.Disable(EnableCap.StencilTest);
1246                  return;
1247              }
1248  
1249              GL.StencilOpSeparate(
1250                  StencilFace.Front,
1251                  stencilTest.FrontSFail.Convert(),
1252                  stencilTest.FrontDpFail.Convert(),
1253                  stencilTest.FrontDpPass.Convert());
1254  
1255              GL.StencilFuncSeparate(
1256                  StencilFace.Front,
1257                  (StencilFunction)stencilTest.FrontFunc.Convert(),
1258                  stencilTest.FrontFuncRef,
1259                  stencilTest.FrontFuncMask);
1260  
1261              GL.StencilMaskSeparate(StencilFace.Front, stencilTest.FrontMask);
1262  
1263              GL.StencilOpSeparate(
1264                  StencilFace.Back,
1265                  stencilTest.BackSFail.Convert(),
1266                  stencilTest.BackDpFail.Convert(),
1267                  stencilTest.BackDpPass.Convert());
1268  
1269              GL.StencilFuncSeparate(
1270                  StencilFace.Back,
1271                  (StencilFunction)stencilTest.BackFunc.Convert(),
1272                  stencilTest.BackFuncRef,
1273                  stencilTest.BackFuncMask);
1274  
1275              GL.StencilMaskSeparate(StencilFace.Back, stencilTest.BackMask);
1276  
1277              GL.Enable(EnableCap.StencilTest);
1278  
1279              _stencilFrontMask = stencilTest.FrontMask;
1280          }
1281  
1282          public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
1283          {
1284              SetBuffers(buffers, isStorage: true);
1285          }
1286  
1287          public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
1288          {
1289              if (texture != null)
1290              {
1291                  if (binding == 0)
1292                  {
1293                      _unit0Texture = (TextureBase)texture;
1294                  }
1295                  else
1296                  {
1297                      ((TextureBase)texture).Bind(binding);
1298                  }
1299              }
1300              else
1301              {
1302                  TextureBase.ClearBinding(binding);
1303              }
1304  
1305              Sampler glSampler = (Sampler)sampler;
1306  
1307              glSampler?.Bind(binding);
1308  
1309              if (binding == 0)
1310              {
1311                  _unit0Sampler = glSampler;
1312              }
1313          }
1314  
1315          public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array)
1316          {
1317              (array as TextureArray).Bind(binding);
1318          }
1319  
1320          public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array)
1321          {
1322              throw new NotSupportedException("OpenGL does not support descriptor sets.");
1323          }
1324  
1325          public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
1326          {
1327              if (_tfEnabled)
1328              {
1329                  GL.EndTransformFeedback();
1330              }
1331  
1332              int count = Math.Min(buffers.Length, Constants.MaxTransformFeedbackBuffers);
1333  
1334              for (int i = 0; i < count; i++)
1335              {
1336                  BufferRange buffer = buffers[i];
1337                  _tfbTargets[i] = buffer;
1338  
1339                  if (buffer.Handle == BufferHandle.Null)
1340                  {
1341                      GL.BindBufferBase(BufferRangeTarget.TransformFeedbackBuffer, i, 0);
1342                      continue;
1343                  }
1344  
1345                  if (_tfbs[i] == BufferHandle.Null)
1346                  {
1347                      _tfbs[i] = Buffer.Create();
1348                  }
1349  
1350                  Buffer.Resize(_tfbs[i], buffer.Size);
1351                  Buffer.Copy(buffer.Handle, _tfbs[i], buffer.Offset, 0, buffer.Size);
1352                  GL.BindBufferBase(BufferRangeTarget.TransformFeedbackBuffer, i, _tfbs[i].ToInt32());
1353              }
1354  
1355              if (_tfEnabled)
1356              {
1357                  GL.BeginTransformFeedback(_tfTopology);
1358              }
1359          }
1360  
1361          public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
1362          {
1363              SetBuffers(buffers, isStorage: false);
1364          }
1365  
1366          public void SetUserClipDistance(int index, bool enableClip)
1367          {
1368              if (!enableClip)
1369              {
1370                  GL.Disable(EnableCap.ClipDistance0 + index);
1371                  return;
1372              }
1373  
1374              GL.Enable(EnableCap.ClipDistance0 + index);
1375          }
1376  
1377          public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
1378          {
1379              EnsureVertexArray();
1380  
1381              _vertexArray.SetVertexAttributes(vertexAttribs);
1382          }
1383  
1384          public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
1385          {
1386              EnsureVertexArray();
1387  
1388              _vertexArray.SetVertexBuffers(vertexBuffers);
1389          }
1390  
1391          public void SetViewports(ReadOnlySpan<Viewport> viewports)
1392          {
1393              Array.Resize(ref _viewportArray, viewports.Length * 4);
1394              Array.Resize(ref _depthRangeArray, viewports.Length * 2);
1395  
1396              float[] viewportArray = _viewportArray;
1397              double[] depthRangeArray = _depthRangeArray;
1398  
1399              for (int index = 0; index < viewports.Length; index++)
1400              {
1401                  int viewportElemIndex = index * 4;
1402  
1403                  Viewport viewport = viewports[index];
1404  
1405                  viewportArray[viewportElemIndex + 0] = viewport.Region.X;
1406                  viewportArray[viewportElemIndex + 1] = viewport.Region.Y + (viewport.Region.Height < 0 ? viewport.Region.Height : 0);
1407                  viewportArray[viewportElemIndex + 2] = viewport.Region.Width;
1408                  viewportArray[viewportElemIndex + 3] = MathF.Abs(viewport.Region.Height);
1409  
1410                  if (HwCapabilities.SupportsViewportSwizzle)
1411                  {
1412                      GL.NV.ViewportSwizzle(
1413                          index,
1414                          viewport.SwizzleX.Convert(),
1415                          viewport.SwizzleY.Convert(),
1416                          viewport.SwizzleZ.Convert(),
1417                          viewport.SwizzleW.Convert());
1418                  }
1419  
1420                  depthRangeArray[index * 2 + 0] = viewport.DepthNear;
1421                  depthRangeArray[index * 2 + 1] = viewport.DepthFar;
1422              }
1423  
1424              bool flipY = viewports.Length != 0 && viewports[0].Region.Height < 0;
1425  
1426              SetOrigin(flipY ? ClipOrigin.UpperLeft : ClipOrigin.LowerLeft);
1427  
1428              GL.ViewportArray(0, viewports.Length, viewportArray);
1429              GL.DepthRangeArray(0, viewports.Length, depthRangeArray);
1430          }
1431  
1432          public void TextureBarrier()
1433          {
1434              GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
1435          }
1436  
1437          public void TextureBarrierTiled()
1438          {
1439              GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
1440          }
1441  
1442          private static void SetBuffers(ReadOnlySpan<BufferAssignment> buffers, bool isStorage)
1443          {
1444              BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer;
1445  
1446              for (int index = 0; index < buffers.Length; index++)
1447              {
1448                  BufferAssignment assignment = buffers[index];
1449                  BufferRange buffer = assignment.Range;
1450  
1451                  if (buffer.Handle == BufferHandle.Null)
1452                  {
1453                      GL.BindBufferRange(target, assignment.Binding, 0, IntPtr.Zero, 0);
1454                      continue;
1455                  }
1456  
1457                  GL.BindBufferRange(target, assignment.Binding, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
1458              }
1459          }
1460  
1461          private void SetOrigin(ClipOrigin origin)
1462          {
1463              if (_clipOrigin != origin)
1464              {
1465                  _clipOrigin = origin;
1466  
1467                  GL.ClipControl(origin, _clipDepthMode);
1468  
1469                  SetFrontFace(_frontFace);
1470              }
1471          }
1472  
1473          private void SetFrontFace(FrontFaceDirection frontFace)
1474          {
1475              // Changing clip origin will also change the front face to compensate
1476              // for the flipped viewport, we flip it again here to compensate as
1477              // this effect is undesirable for us.
1478              if (_clipOrigin == ClipOrigin.UpperLeft)
1479              {
1480                  frontFace = frontFace == FrontFaceDirection.Ccw ? FrontFaceDirection.Cw : FrontFaceDirection.Ccw;
1481              }
1482  
1483              GL.FrontFace(frontFace);
1484          }
1485  
1486          private void EnsureVertexArray()
1487          {
1488              if (_vertexArray == null)
1489              {
1490                  _vertexArray = new VertexArray();
1491  
1492                  _vertexArray.Bind();
1493              }
1494          }
1495  
1496          private void EnsureFramebuffer()
1497          {
1498              if (_framebuffer == null)
1499              {
1500                  _framebuffer = new Framebuffer();
1501  
1502                  int boundHandle = _framebuffer.Bind();
1503                  _boundDrawFramebuffer = _boundReadFramebuffer = boundHandle;
1504  
1505                  GL.Enable(EnableCap.FramebufferSrgb);
1506              }
1507          }
1508  
1509          internal (int drawHandle, int readHandle) GetBoundFramebuffers()
1510          {
1511              if (BackgroundContextWorker.InBackground)
1512              {
1513                  return (0, 0);
1514              }
1515  
1516              return (_boundDrawFramebuffer, _boundReadFramebuffer);
1517          }
1518  
1519          private void PrepareForDispatch()
1520          {
1521              _unit0Texture?.Bind(0);
1522          }
1523  
1524          private void PreDraw(int vertexCount)
1525          {
1526              _vertexArray.PreDraw(vertexCount);
1527              PreDraw();
1528          }
1529  
1530          private void PreDrawVbUnbounded()
1531          {
1532              _vertexArray.PreDrawVbUnbounded();
1533              PreDraw();
1534          }
1535  
1536          private void PreDraw()
1537          {
1538              DrawCount++;
1539  
1540              _unit0Texture?.Bind(0);
1541          }
1542  
1543          private void PostDraw()
1544          {
1545              if (_tfEnabled)
1546              {
1547                  for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
1548                  {
1549                      if (_tfbTargets[i].Handle != BufferHandle.Null)
1550                      {
1551                          Buffer.Copy(_tfbs[i], _tfbTargets[i].Handle, 0, _tfbTargets[i].Offset, _tfbTargets[i].Size);
1552                      }
1553                  }
1554              }
1555          }
1556  
1557          public void RestoreComponentMask(int index, bool force = true)
1558          {
1559              // If the bound render target is bgra, swap the red and blue masks.
1560              uint redMask = _fpIsBgra[index].X == 0 ? 1u : 4u;
1561              uint blueMask = _fpIsBgra[index].X == 0 ? 4u : 1u;
1562  
1563              int shift = index * 4;
1564              uint componentMask = _componentMasks & _fragmentOutputMap;
1565              uint checkMask = 0xfu << shift;
1566              uint componentMaskAtIndex = componentMask & checkMask;
1567  
1568              if (!force && componentMaskAtIndex == (_currentComponentMasks & checkMask))
1569              {
1570                  return;
1571              }
1572  
1573              componentMask >>= shift;
1574              componentMask &= 0xfu;
1575  
1576              GL.ColorMask(
1577                  index,
1578                  (componentMask & redMask) != 0,
1579                  (componentMask & 2u) != 0,
1580                  (componentMask & blueMask) != 0,
1581                  (componentMask & 8u) != 0);
1582  
1583              _currentComponentMasks &= ~checkMask;
1584              _currentComponentMasks |= componentMaskAtIndex;
1585          }
1586  
1587          public void RestoreClipControl()
1588          {
1589              GL.ClipControl(_clipOrigin, _clipDepthMode);
1590          }
1591  
1592          public void RestoreScissor0Enable()
1593          {
1594              if ((_scissorEnables & 1u) != 0)
1595              {
1596                  GL.Enable(IndexedEnableCap.ScissorTest, 0);
1597              }
1598          }
1599  
1600          public void RestoreRasterizerDiscard()
1601          {
1602              if (_rasterizerDiscard)
1603              {
1604                  GL.Enable(EnableCap.RasterizerDiscard);
1605              }
1606          }
1607  
1608          public void RestoreViewport0()
1609          {
1610              if (_viewportArray.Length > 0)
1611              {
1612                  GL.ViewportArray(0, 1, _viewportArray);
1613              }
1614          }
1615  
1616          public void RestoreProgram()
1617          {
1618              _program?.Bind();
1619          }
1620  
1621          public void RestoreImages1And2()
1622          {
1623              for (int i = 0; i < SavedImages; i++)
1624              {
1625                  TextureBase texBase = _images[i];
1626  
1627                  if (texBase != null)
1628                  {
1629                      SizedInternalFormat format = FormatTable.GetImageFormat(texBase.Format);
1630  
1631                      if (format != 0)
1632                      {
1633                          GL.BindImageTexture(i, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format);
1634                          continue;
1635                      }
1636                  }
1637  
1638                  GL.BindImageTexture(i, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
1639              }
1640          }
1641  
1642          public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
1643          {
1644              // Compare an event and a constant value.
1645              if (value is CounterQueueEvent evt)
1646              {
1647                  // Easy host conditional rendering when the check matches what GL can do:
1648                  //  - Event is of type samples passed.
1649                  //  - Result is not a combination of multiple queries.
1650                  //  - Comparing against 0.
1651                  //  - Event has not already been flushed.
1652  
1653                  if (compare == 0 && evt.Type == QueryTarget.SamplesPassed && evt.ClearCounter)
1654                  {
1655                      if (!value.ReserveForHostAccess())
1656                      {
1657                          // If the event has been flushed, then just use the values on the CPU.
1658                          // The query object may already be repurposed for another draw (eg. begin + end).
1659                          return false;
1660                      }
1661  
1662                      GL.BeginConditionalRender(evt.Query, isEqual ? ConditionalRenderType.QueryNoWaitInverted : ConditionalRenderType.QueryNoWait);
1663                      _activeConditionalRender = evt;
1664  
1665                      return true;
1666                  }
1667              }
1668  
1669              // The GPU will flush the queries to CPU and evaluate the condition there instead.
1670  
1671              GL.Flush(); // The thread will be stalled manually flushing the counter, so flush GL commands now.
1672              return false;
1673          }
1674  
1675          public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual)
1676          {
1677              GL.Flush(); // The GPU thread will be stalled manually flushing the counter, so flush GL commands now.
1678              return false; // We don't currently have a way to compare two counters for conditional rendering.
1679          }
1680  
1681          public void EndHostConditionalRendering()
1682          {
1683              GL.EndConditionalRender();
1684  
1685              _activeConditionalRender?.ReleaseHostAccess();
1686              _activeConditionalRender = null;
1687          }
1688  
1689          public void Dispose()
1690          {
1691              for (int i = 0; i < Constants.MaxTransformFeedbackBuffers; i++)
1692              {
1693                  if (_tfbs[i] != BufferHandle.Null)
1694                  {
1695                      Buffer.Delete(_tfbs[i]);
1696                      _tfbs[i] = BufferHandle.Null;
1697                  }
1698              }
1699  
1700              _activeConditionalRender?.ReleaseHostAccess();
1701              _framebuffer?.Dispose();
1702              _vertexArray?.Dispose();
1703              _drawTexture.Dispose();
1704          }
1705      }
1706  }