/ src / Ryujinx.Graphics.Gpu / Engine / Threed / DrawManager.cs
DrawManager.cs
  1  using Ryujinx.Graphics.GAL;
  2  using Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw;
  3  using Ryujinx.Graphics.Gpu.Engine.Types;
  4  using Ryujinx.Graphics.Gpu.Image;
  5  using Ryujinx.Graphics.Gpu.Memory;
  6  using Ryujinx.Memory.Range;
  7  using System;
  8  
  9  namespace Ryujinx.Graphics.Gpu.Engine.Threed
 10  {
 11      /// <summary>
 12      /// Draw manager.
 13      /// </summary>
 14      class DrawManager : IDisposable
 15      {
 16          // Since we don't know the index buffer size for indirect draws,
 17          // we must assume a minimum and maximum size and use that for buffer data update purposes.
 18          private const int MinIndirectIndexCount = 0x10000;
 19          private const int MaxIndirectIndexCount = 0x4000000;
 20  
 21          private readonly GpuContext _context;
 22          private readonly GpuChannel _channel;
 23          private readonly DeviceStateWithShadow<ThreedClassState> _state;
 24          private readonly DrawState _drawState;
 25          private readonly SpecializationStateUpdater _currentSpecState;
 26          private readonly VtgAsCompute _vtgAsCompute;
 27          private bool _topologySet;
 28  
 29          private bool _instancedDrawPending;
 30          private bool _instancedIndexed;
 31          private bool _instancedIndexedInline;
 32  
 33          private int _instancedFirstIndex;
 34          private int _instancedFirstVertex;
 35          private int _instancedFirstInstance;
 36          private int _instancedIndexCount;
 37          private int _instancedDrawStateFirst;
 38          private int _instancedDrawStateCount;
 39  
 40          private int _instanceIndex;
 41  
 42          private const int VertexBufferFirstMethodOffset = 0x35d;
 43          private const int IndexBufferCountMethodOffset = 0x5f8;
 44  
 45          /// <summary>
 46          /// Creates a new instance of the draw manager.
 47          /// </summary>
 48          /// <param name="context">GPU context</param>
 49          /// <param name="channel">GPU channel</param>
 50          /// <param name="state">Channel state</param>
 51          /// <param name="drawState">Draw state</param>
 52          /// <param name="spec">Specialization state updater</param>
 53          public DrawManager(GpuContext context, GpuChannel channel, DeviceStateWithShadow<ThreedClassState> state, DrawState drawState, SpecializationStateUpdater spec)
 54          {
 55              _context = context;
 56              _channel = channel;
 57              _state = state;
 58              _drawState = drawState;
 59              _currentSpecState = spec;
 60              _vtgAsCompute = new(context, channel, state);
 61          }
 62  
 63          /// <summary>
 64          /// Marks the entire state as dirty, forcing a full host state update before the next draw.
 65          /// </summary>
 66          public void ForceStateDirty()
 67          {
 68              _topologySet = false;
 69          }
 70  
 71          /// <summary>
 72          /// Pushes four 8-bit index buffer elements.
 73          /// </summary>
 74          /// <param name="argument">Method call argument</param>
 75          public void VbElementU8(int argument)
 76          {
 77              _drawState.IbStreamer.VbElementU8(_context.Renderer, argument);
 78          }
 79  
 80          /// <summary>
 81          /// Pushes two 16-bit index buffer elements.
 82          /// </summary>
 83          /// <param name="argument">Method call argument</param>
 84          public void VbElementU16(int argument)
 85          {
 86              _drawState.IbStreamer.VbElementU16(_context.Renderer, argument);
 87          }
 88  
 89          /// <summary>
 90          /// Pushes one 32-bit index buffer element.
 91          /// </summary>
 92          /// <param name="argument">Method call argument</param>
 93          public void VbElementU32(int argument)
 94          {
 95              _drawState.IbStreamer.VbElementU32(_context.Renderer, argument);
 96          }
 97  
 98          /// <summary>
 99          /// Finishes the draw call.
100          /// This draws geometry on the bound buffers based on the current GPU state.
101          /// </summary>
102          /// <param name="engine">3D engine where this method is being called</param>
103          /// <param name="argument">Method call argument</param>
104          public void DrawEnd(ThreedClass engine, int argument)
105          {
106              _drawState.DrawUsesEngineState = true;
107  
108              DrawEnd(
109                  engine,
110                  _state.State.IndexBufferState.First,
111                  (int)_state.State.IndexBufferCount,
112                  _state.State.VertexBufferDrawState.First,
113                  _state.State.VertexBufferDrawState.Count);
114          }
115  
116          /// <summary>
117          /// Finishes the draw call.
118          /// This draws geometry on the bound buffers based on the current GPU state.
119          /// </summary>
120          /// <param name="engine">3D engine where this method is being called</param>
121          /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
122          /// <param name="indexCount">Number of index buffer elements used on the draw</param>
123          /// <param name="drawFirstVertex">Index of the first vertex used on the draw</param>
124          /// <param name="drawVertexCount">Number of vertices used on the draw</param>
125          private void DrawEnd(ThreedClass engine, int firstIndex, int indexCount, int drawFirstVertex, int drawVertexCount)
126          {
127              ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
128                  _context,
129                  _channel.MemoryManager,
130                  _state.State.RenderEnableAddress,
131                  _state.State.RenderEnableCondition);
132  
133              if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending)
134              {
135                  if (renderEnable == ConditionalRenderEnabled.False)
136                  {
137                      PerformDeferredDraws(engine);
138                  }
139  
140                  _drawState.DrawIndexed = false;
141  
142                  if (renderEnable == ConditionalRenderEnabled.Host)
143                  {
144                      _context.Renderer.Pipeline.EndHostConditionalRendering();
145                  }
146  
147                  return;
148              }
149  
150              _drawState.FirstIndex = firstIndex;
151              _drawState.IndexCount = indexCount;
152              _drawState.DrawFirstVertex = drawFirstVertex;
153              _drawState.DrawVertexCount = drawVertexCount;
154              _currentSpecState.SetHasConstantBufferDrawParameters(false);
155  
156              engine.UpdateState();
157  
158              bool instanced = _drawState.VsUsesInstanceId || _drawState.IsAnyVbInstanced;
159  
160              if (instanced)
161              {
162                  _instancedDrawPending = true;
163  
164                  int ibCount = _drawState.IbStreamer.InlineIndexCount;
165  
166                  _instancedIndexed = _drawState.DrawIndexed;
167                  _instancedIndexedInline = ibCount != 0;
168  
169                  _instancedFirstIndex = firstIndex;
170                  _instancedFirstVertex = (int)_state.State.FirstVertex;
171                  _instancedFirstInstance = (int)_state.State.FirstInstance;
172  
173                  _instancedIndexCount = ibCount != 0 ? ibCount : indexCount;
174  
175                  _instancedDrawStateFirst = drawFirstVertex;
176                  _instancedDrawStateCount = drawVertexCount;
177  
178                  _drawState.DrawIndexed = false;
179  
180                  if (renderEnable == ConditionalRenderEnabled.Host)
181                  {
182                      _context.Renderer.Pipeline.EndHostConditionalRendering();
183                  }
184  
185                  return;
186              }
187  
188              int firstInstance = (int)_state.State.FirstInstance;
189  
190              int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
191  
192              if (inlineIndexCount != 0)
193              {
194                  int firstVertex = (int)_state.State.FirstVertex;
195  
196                  BufferRange br = new(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
197  
198                  _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
199  
200                  DrawImpl(engine, inlineIndexCount, 1, firstIndex, firstVertex, firstInstance, indexed: true);
201              }
202              else if (_drawState.DrawIndexed)
203              {
204                  int firstVertex = (int)_state.State.FirstVertex;
205  
206                  DrawImpl(engine, indexCount, 1, firstIndex, firstVertex, firstInstance, indexed: true);
207              }
208              else
209              {
210                  DrawImpl(engine, drawVertexCount, 1, 0, drawFirstVertex, firstInstance, indexed: false);
211              }
212  
213              _drawState.DrawIndexed = false;
214  
215              if (renderEnable == ConditionalRenderEnabled.Host)
216              {
217                  _context.Renderer.Pipeline.EndHostConditionalRendering();
218              }
219          }
220  
221          /// <summary>
222          /// Starts draw.
223          /// This sets primitive type and instanced draw parameters.
224          /// </summary>
225          /// <param name="engine">3D engine where this method is being called</param>
226          /// <param name="argument">Method call argument</param>
227          public void DrawBegin(ThreedClass engine, int argument)
228          {
229              bool incrementInstance = (argument & (1 << 26)) != 0;
230              bool resetInstance = (argument & (1 << 27)) == 0;
231  
232              PrimitiveType type = (PrimitiveType)(argument & 0xffff);
233              DrawBegin(engine, incrementInstance, resetInstance, type);
234          }
235  
236          /// <summary>
237          /// Starts draw.
238          /// This sets primitive type and instanced draw parameters.
239          /// </summary>
240          /// <param name="engine">3D engine where this method is being called</param>
241          /// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
242          /// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
243          /// <param name="primitiveType">Primitive type</param>
244          private void DrawBegin(ThreedClass engine, bool incrementInstance, bool resetInstance, PrimitiveType primitiveType)
245          {
246              if (incrementInstance)
247              {
248                  _instanceIndex++;
249              }
250              else if (resetInstance)
251              {
252                  PerformDeferredDraws(engine);
253  
254                  _instanceIndex = 0;
255              }
256  
257              PrimitiveTopology topology;
258  
259              if (_state.State.PrimitiveTypeOverrideEnable)
260              {
261                  PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
262                  topology = typeOverride.Convert();
263              }
264              else
265              {
266                  topology = primitiveType.Convert();
267              }
268  
269              UpdateTopology(topology);
270          }
271  
272          /// <summary>
273          /// Updates the current primitive topology if needed.
274          /// </summary>
275          /// <param name="topology">New primitive topology</param>
276          private void UpdateTopology(PrimitiveTopology topology)
277          {
278              if (_drawState.Topology != topology || !_topologySet)
279              {
280                  _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
281                  _currentSpecState.SetTopology(topology);
282                  _drawState.Topology = topology;
283                  _topologySet = true;
284              }
285          }
286  
287          /// <summary>
288          /// Sets the index buffer count.
289          /// This also sets internal state that indicates that the next draw is an indexed draw.
290          /// </summary>
291          /// <param name="argument">Method call argument</param>
292          public void SetIndexBufferCount(int argument)
293          {
294              _drawState.DrawIndexed = true;
295          }
296  
297          // TODO: Verify if the index type is implied from the method that is called,
298          // or if it uses the state index type on hardware.
299  
300          /// <summary>
301          /// Performs a indexed draw with 8-bit index buffer elements.
302          /// </summary>
303          /// <param name="engine">3D engine where this method is being called</param>
304          /// <param name="argument">Method call argument</param>
305          public void DrawIndexBuffer8BeginEndInstanceFirst(ThreedClass engine, int argument)
306          {
307              DrawIndexBufferBeginEndInstance(engine, argument, false);
308          }
309  
310          /// <summary>
311          /// Performs a indexed draw with 16-bit index buffer elements.
312          /// </summary>
313          /// <param name="engine">3D engine where this method is being called</param>
314          /// <param name="argument">Method call argument</param>
315          public void DrawIndexBuffer16BeginEndInstanceFirst(ThreedClass engine, int argument)
316          {
317              DrawIndexBufferBeginEndInstance(engine, argument, false);
318          }
319  
320          /// <summary>
321          /// Performs a indexed draw with 32-bit index buffer elements.
322          /// </summary>
323          /// <param name="engine">3D engine where this method is being called</param>
324          /// <param name="argument">Method call argument</param>
325          public void DrawIndexBuffer32BeginEndInstanceFirst(ThreedClass engine, int argument)
326          {
327              DrawIndexBufferBeginEndInstance(engine, argument, false);
328          }
329  
330          /// <summary>
331          /// Performs a indexed draw with 8-bit index buffer elements,
332          /// while also pre-incrementing the current instance value.
333          /// </summary>
334          /// <param name="engine">3D engine where this method is being called</param>
335          /// <param name="argument">Method call argument</param>
336          public void DrawIndexBuffer8BeginEndInstanceSubsequent(ThreedClass engine, int argument)
337          {
338              DrawIndexBufferBeginEndInstance(engine, argument, true);
339          }
340  
341          /// <summary>
342          /// Performs a indexed draw with 16-bit index buffer elements,
343          /// while also pre-incrementing the current instance value.
344          /// </summary>
345          /// <param name="engine">3D engine where this method is being called</param>
346          /// <param name="argument">Method call argument</param>
347          public void DrawIndexBuffer16BeginEndInstanceSubsequent(ThreedClass engine, int argument)
348          {
349              DrawIndexBufferBeginEndInstance(engine, argument, true);
350          }
351  
352          /// <summary>
353          /// Performs a indexed draw with 32-bit index buffer elements,
354          /// while also pre-incrementing the current instance value.
355          /// </summary>
356          /// <param name="engine">3D engine where this method is being called</param>
357          /// <param name="argument">Method call argument</param>
358          public void DrawIndexBuffer32BeginEndInstanceSubsequent(ThreedClass engine, int argument)
359          {
360              DrawIndexBufferBeginEndInstance(engine, argument, true);
361          }
362  
363          /// <summary>
364          /// Performs a indexed draw with a low number of index buffer elements,
365          /// while optionally also pre-incrementing the current instance value.
366          /// </summary>
367          /// <param name="engine">3D engine where this method is being called</param>
368          /// <param name="argument">Method call argument</param>
369          /// <param name="instanced">True to increment the current instance value, false otherwise</param>
370          private void DrawIndexBufferBeginEndInstance(ThreedClass engine, int argument, bool instanced)
371          {
372              DrawBegin(engine, instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
373  
374              int firstIndex = argument & 0xffff;
375              int indexCount = (argument >> 16) & 0xfff;
376  
377              bool oldDrawIndexed = _drawState.DrawIndexed;
378  
379              _drawState.DrawIndexed = true;
380              _drawState.DrawUsesEngineState = false;
381              engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
382  
383              DrawEnd(engine, firstIndex, indexCount, 0, 0);
384  
385              _drawState.DrawIndexed = oldDrawIndexed;
386          }
387  
388          /// <summary>
389          /// Performs a non-indexed draw with the specified topology, index and count.
390          /// </summary>
391          /// <param name="engine">3D engine where this method is being called</param>
392          /// <param name="argument">Method call argument</param>
393          public void DrawVertexArrayBeginEndInstanceFirst(ThreedClass engine, int argument)
394          {
395              DrawVertexArrayBeginEndInstance(engine, argument, false);
396          }
397  
398          /// <summary>
399          /// Performs a non-indexed draw with the specified topology, index and count,
400          /// while incrementing the current instance.
401          /// </summary>
402          /// <param name="engine">3D engine where this method is being called</param>
403          /// <param name="argument">Method call argument</param>
404          public void DrawVertexArrayBeginEndInstanceSubsequent(ThreedClass engine, int argument)
405          {
406              DrawVertexArrayBeginEndInstance(engine, argument, true);
407          }
408  
409          /// <summary>
410          /// Performs a indexed draw with a low number of index buffer elements,
411          /// while optionally also pre-incrementing the current instance value.
412          /// </summary>
413          /// <param name="engine">3D engine where this method is being called</param>
414          /// <param name="argument">Method call argument</param>
415          /// <param name="instanced">True to increment the current instance value, false otherwise</param>
416          private void DrawVertexArrayBeginEndInstance(ThreedClass engine, int argument, bool instanced)
417          {
418              DrawBegin(engine, instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
419  
420              int firstVertex = argument & 0xffff;
421              int vertexCount = (argument >> 16) & 0xfff;
422  
423              bool oldDrawIndexed = _drawState.DrawIndexed;
424  
425              _drawState.DrawIndexed = false;
426              _drawState.DrawUsesEngineState = false;
427              engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
428  
429              DrawEnd(engine, 0, 0, firstVertex, vertexCount);
430  
431              _drawState.DrawIndexed = oldDrawIndexed;
432          }
433  
434          /// <summary>
435          /// Performs a texture draw with a source texture and sampler ID, along with source
436          /// and destination coordinates and sizes.
437          /// </summary>
438          /// <param name="engine">3D engine where this method is being called</param>
439          /// <param name="argument">Method call argument</param>
440          public void DrawTexture(ThreedClass engine, int argument)
441          {
442              static float FixedToFloat(int fixedValue)
443              {
444                  return fixedValue * (1f / 4096);
445              }
446  
447              float dstX0 = FixedToFloat(_state.State.DrawTextureDstX);
448              float dstY0 = FixedToFloat(_state.State.DrawTextureDstY);
449              float dstWidth = FixedToFloat(_state.State.DrawTextureDstWidth);
450              float dstHeight = FixedToFloat(_state.State.DrawTextureDstHeight);
451  
452              // TODO: Confirm behaviour on hardware.
453              // When this is active, the origin appears to be on the bottom.
454              if (_state.State.YControl.HasFlag(YControl.NegateY))
455              {
456                  dstY0 -= dstHeight;
457              }
458  
459              float dstX1 = dstX0 + dstWidth;
460              float dstY1 = dstY0 + dstHeight;
461  
462              float srcX0 = FixedToFloat(_state.State.DrawTextureSrcX);
463              float srcY0 = FixedToFloat(_state.State.DrawTextureSrcY);
464              float srcX1 = ((float)_state.State.DrawTextureDuDx / (1UL << 32)) * dstWidth + srcX0;
465              float srcY1 = ((float)_state.State.DrawTextureDvDy / (1UL << 32)) * dstHeight + srcY0;
466  
467              engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex));
468  
469              _channel.TextureManager.UpdateRenderTargets();
470  
471              int textureId = _state.State.DrawTextureTextureId;
472              int samplerId = _state.State.DrawTextureSamplerId;
473  
474              (var texture, var sampler) = _channel.TextureManager.GetGraphicsTextureAndSampler(textureId, samplerId);
475  
476              srcX0 *= texture.ScaleFactor;
477              srcY0 *= texture.ScaleFactor;
478              srcX1 *= texture.ScaleFactor;
479              srcY1 *= texture.ScaleFactor;
480  
481              float dstScale = _channel.TextureManager.RenderTargetScale;
482  
483              dstX0 *= dstScale;
484              dstY0 *= dstScale;
485              dstX1 *= dstScale;
486              dstY1 *= dstScale;
487  
488              _context.Renderer.Pipeline.DrawTexture(
489                  texture?.HostTexture,
490                  sampler?.GetHostSampler(texture),
491                  new Extents2DF(srcX0, srcY0, srcX1, srcY1),
492                  new Extents2DF(dstX0, dstY0, dstX1, dstY1));
493          }
494  
495          /// <summary>
496          /// Performs a indexed or non-indexed draw.
497          /// </summary>
498          /// <param name="engine">3D engine where this method is being called</param>
499          /// <param name="topology">Primitive topology</param>
500          /// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
501          /// <param name="instanceCount">Instance count</param>
502          /// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
503          /// <param name="firstVertex">First vertex on the vertex buffer</param>
504          /// <param name="firstInstance">First instance</param>
505          /// <param name="indexed">True if the draw is indexed, false otherwise</param>
506          public void Draw(
507              ThreedClass engine,
508              PrimitiveTopology topology,
509              int count,
510              int instanceCount,
511              int firstIndex,
512              int firstVertex,
513              int firstInstance,
514              bool indexed)
515          {
516              UpdateTopology(topology);
517  
518              ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
519                  _context,
520                  _channel.MemoryManager,
521                  _state.State.RenderEnableAddress,
522                  _state.State.RenderEnableCondition);
523  
524              if (renderEnable == ConditionalRenderEnabled.False)
525              {
526                  _drawState.DrawIndexed = false;
527                  return;
528              }
529  
530              if (indexed)
531              {
532                  _drawState.FirstIndex = firstIndex;
533                  _drawState.IndexCount = count;
534                  _state.State.FirstVertex = (uint)firstVertex;
535                  engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
536              }
537              else
538              {
539                  _drawState.DrawFirstVertex = firstVertex;
540                  _drawState.DrawVertexCount = count;
541                  engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
542              }
543  
544              _state.State.FirstInstance = (uint)firstInstance;
545  
546              _drawState.DrawIndexed = indexed;
547              _drawState.DrawUsesEngineState = true;
548              _currentSpecState.SetHasConstantBufferDrawParameters(true);
549  
550              engine.UpdateState();
551  
552              DrawImpl(engine, count, instanceCount, firstIndex, firstVertex, firstInstance, indexed);
553  
554              if (indexed)
555              {
556                  _state.State.FirstVertex = 0;
557              }
558  
559              _state.State.FirstInstance = 0;
560  
561              _drawState.DrawIndexed = false;
562  
563              if (renderEnable == ConditionalRenderEnabled.Host)
564              {
565                  _context.Renderer.Pipeline.EndHostConditionalRendering();
566              }
567          }
568  
569          /// <summary>
570          /// Performs a indexed or non-indexed draw.
571          /// </summary>
572          /// <param name="engine">3D engine where this method is being called</param>
573          /// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
574          /// <param name="instanceCount">Instance count</param>
575          /// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
576          /// <param name="firstVertex">First vertex on the vertex buffer</param>
577          /// <param name="firstInstance">First instance</param>
578          /// <param name="indexed">True if the draw is indexed, false otherwise</param>
579          private void DrawImpl(
580              ThreedClass engine,
581              int count,
582              int instanceCount,
583              int firstIndex,
584              int firstVertex,
585              int firstInstance,
586              bool indexed)
587          {
588              if (instanceCount > 1)
589              {
590                  _channel.BufferManager.SetInstancedDrawVertexCount(count);
591              }
592  
593              if (_drawState.VertexAsCompute != null)
594              {
595                  _vtgAsCompute.DrawAsCompute(
596                      engine,
597                      _drawState.VertexAsCompute,
598                      _drawState.GeometryAsCompute,
599                      _drawState.VertexPassthrough,
600                      _drawState.Topology,
601                      count,
602                      instanceCount,
603                      firstIndex,
604                      firstVertex,
605                      firstInstance,
606                      indexed);
607  
608                  if (_drawState.GeometryAsCompute != null)
609                  {
610                      // Geometry draws need to change the topology, so we need to set it here again
611                      // if we are going to do a regular draw.
612                      // Would have been better to do that on the callee, but doing it here
613                      // avoids having to pass the draw manager instance.
614                      ForceStateDirty();
615                  }
616              }
617              else
618              {
619                  if (indexed)
620                  {
621                      _context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance);
622                  }
623                  else
624                  {
625                      _context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance);
626                  }
627              }
628          }
629  
630          /// <summary>
631          /// Performs a indirect draw, with parameters from a GPU buffer.
632          /// </summary>
633          /// <param name="engine">3D engine where this method is being called</param>
634          /// <param name="topology">Primitive topology</param>
635          /// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
636          /// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
637          /// <param name="maxDrawCount">Maximum number of draws that can be made</param>
638          /// <param name="stride">Distance in bytes between each entry on the data pointed to by <paramref name="indirectBufferAddress"/></param>
639          /// <param name="indexCount">Maximum number of indices that the draw can consume</param>
640          /// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
641          public void DrawIndirect(
642              ThreedClass engine,
643              PrimitiveTopology topology,
644              MultiRange indirectBufferRange,
645              MultiRange parameterBufferRange,
646              int maxDrawCount,
647              int stride,
648              int indexCount,
649              IndirectDrawType drawType)
650          {
651              UpdateTopology(topology);
652  
653              ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
654                  _context,
655                  _channel.MemoryManager,
656                  _state.State.RenderEnableAddress,
657                  _state.State.RenderEnableCondition);
658  
659              if (renderEnable == ConditionalRenderEnabled.False)
660              {
661                  _drawState.DrawIndexed = false;
662                  return;
663              }
664  
665              PhysicalMemory memory = _channel.MemoryManager.Physical;
666  
667              bool hasCount = (drawType & IndirectDrawType.Count) != 0;
668              bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
669  
670              if (indexed)
671              {
672                  indexCount = Math.Clamp(indexCount, MinIndirectIndexCount, MaxIndirectIndexCount);
673                  _drawState.FirstIndex = 0;
674                  _drawState.IndexCount = indexCount;
675                  engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
676              }
677  
678              _drawState.DrawIndexed = indexed;
679              _drawState.DrawIndirect = true;
680              _drawState.DrawUsesEngineState = true;
681              _currentSpecState.SetHasConstantBufferDrawParameters(true);
682  
683              engine.UpdateState();
684  
685              if (hasCount)
686              {
687                  var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
688                  var parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
689  
690                  if (indexed)
691                  {
692                      _context.Renderer.Pipeline.DrawIndexedIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
693                  }
694                  else
695                  {
696                      _context.Renderer.Pipeline.DrawIndirectCount(indirectBuffer, parameterBuffer, maxDrawCount, stride);
697                  }
698              }
699              else
700              {
701                  var indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
702  
703                  if (indexed)
704                  {
705                      _context.Renderer.Pipeline.DrawIndexedIndirect(indirectBuffer);
706                  }
707                  else
708                  {
709                      _context.Renderer.Pipeline.DrawIndirect(indirectBuffer);
710                  }
711              }
712  
713              _drawState.DrawIndexed = false;
714              _drawState.DrawIndirect = false;
715  
716              if (renderEnable == ConditionalRenderEnabled.Host)
717              {
718                  _context.Renderer.Pipeline.EndHostConditionalRendering();
719              }
720          }
721  
722          /// <summary>
723          /// Perform any deferred draws.
724          /// This is used for instanced draws.
725          /// Since each instance is a separate draw, we defer the draw and accumulate the instance count.
726          /// Once we detect the last instanced draw, then we perform the host instanced draw,
727          /// with the accumulated instance count.
728          /// </summary>
729          /// <param name="engine">3D engine where this method is being called</param>
730          public void PerformDeferredDraws(ThreedClass engine)
731          {
732              // Perform any pending instanced draw.
733              if (_instancedDrawPending)
734              {
735                  _instancedDrawPending = false;
736  
737                  int instanceCount = _instanceIndex + 1;
738                  int firstInstance = _instancedFirstInstance;
739                  bool indexedInline = _instancedIndexedInline;
740  
741                  if (_instancedIndexed || indexedInline)
742                  {
743                      int indexCount = _instancedIndexCount;
744  
745                      if (indexedInline)
746                      {
747                          int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
748                          BufferRange br = new(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
749  
750                          _channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
751                          indexCount = inlineIndexCount;
752                      }
753  
754                      int firstIndex = _instancedFirstIndex;
755                      int firstVertex = _instancedFirstVertex;
756  
757                      DrawImpl(engine, indexCount, instanceCount, firstIndex, firstVertex, firstInstance, indexed: true);
758                  }
759                  else
760                  {
761                      int vertexCount = _instancedDrawStateCount;
762                      int firstVertex = _instancedDrawStateFirst;
763  
764                      DrawImpl(engine, vertexCount, instanceCount, 0, firstVertex, firstInstance, indexed: false);
765                  }
766              }
767          }
768  
769          /// <summary>
770          /// Clears the current color and depth-stencil buffers.
771          /// Which buffers should be cleared can also be specified with the argument.
772          /// </summary>
773          /// <param name="engine">3D engine where this method is being called</param>
774          /// <param name="argument">Method call argument</param>
775          public void Clear(ThreedClass engine, int argument)
776          {
777              Clear(engine, argument, 1);
778          }
779  
780          /// <summary>
781          /// Clears the current color and depth-stencil buffers.
782          /// Which buffers should be cleared can also specified with the arguments.
783          /// </summary>
784          /// <param name="engine">3D engine where this method is being called</param>
785          /// <param name="argument">Method call argument</param>
786          /// <param name="layerCount">For array and 3D textures, indicates how many layers should be cleared</param>
787          public void Clear(ThreedClass engine, int argument, int layerCount)
788          {
789              ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
790                  _context,
791                  _channel.MemoryManager,
792                  _state.State.RenderEnableAddress,
793                  _state.State.RenderEnableCondition);
794  
795              if (renderEnable == ConditionalRenderEnabled.False)
796              {
797                  return;
798              }
799  
800              bool clearDepth = (argument & 1) != 0;
801              bool clearStencil = (argument & 2) != 0;
802              uint componentMask = (uint)((argument >> 2) & 0xf);
803              int index = (argument >> 6) & 0xf;
804              int layer = (argument >> 10) & 0x3ff;
805  
806              RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor;
807  
808              if (layer != 0 || layerCount > 1)
809              {
810                  updateFlags |= RenderTargetUpdateFlags.Layered;
811              }
812  
813              bool clearDS = clearDepth || clearStencil;
814  
815              if (clearDS)
816              {
817                  updateFlags |= RenderTargetUpdateFlags.UpdateDepthStencil;
818              }
819  
820              // If there is a mismatch on the host clip region and the one explicitly defined by the guest
821              // on the screen scissor state, then we need to force only one texture to be bound to avoid
822              // host clipping.
823              var screenScissorState = _state.State.ScreenScissorState;
824  
825              bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 0;
826              bool clearAffectedByScissor = (_state.State.ClearFlags & 0x100) != 0;
827  
828              if (clearDS || componentMask == 15)
829              {
830                  // A full clear if scissor is disabled, or it matches the screen scissor state.
831  
832                  bool fullClear = screenScissorState.X == 0 && screenScissorState.Y == 0;
833  
834                  if (fullClear && clearAffectedByScissor && _state.State.ScissorState[0].Enable)
835                  {
836                      ref var scissorState = ref _state.State.ScissorState[0];
837  
838                      fullClear = scissorState.X1 == screenScissorState.X &&
839                          scissorState.Y1 == screenScissorState.Y &&
840                          scissorState.X2 >= screenScissorState.X + screenScissorState.Width &&
841                          scissorState.Y2 >= screenScissorState.Y + screenScissorState.Height;
842                  }
843  
844                  if (fullClear && clearDS)
845                  {
846                      // Must clear all aspects of the depth-stencil format.
847  
848                      FormatInfo dsFormat = _state.State.RtDepthStencilState.Format.Convert();
849  
850                      bool hasDepth = dsFormat.Format.HasDepth();
851                      bool hasStencil = dsFormat.Format.HasStencil();
852  
853                      if (hasStencil && (!clearStencil || (clearAffectedByStencilMask && _state.State.StencilTestState.FrontMask != 0xff)))
854                      {
855                          fullClear = false;
856                      }
857                      else if (hasDepth && !clearDepth)
858                      {
859                          fullClear = false;
860                      }
861                  }
862  
863                  if (fullClear)
864                  {
865                      updateFlags |= RenderTargetUpdateFlags.DiscardClip;
866                  }
867              }
868  
869              engine.UpdateRenderTargetState(updateFlags, singleUse: componentMask != 0 ? index : -1);
870  
871              // Must happen after UpdateRenderTargetState to have up-to-date clip region values.
872              bool clipMismatch = (screenScissorState.X | screenScissorState.Y) != 0 ||
873                                  screenScissorState.Width != _channel.TextureManager.ClipRegionWidth ||
874                                  screenScissorState.Height != _channel.TextureManager.ClipRegionHeight;
875  
876              bool needsCustomScissor = !clearAffectedByScissor || clipMismatch;
877  
878              // Scissor and rasterizer discard also affect clears.
879              ulong updateMask = 1UL << StateUpdater.RasterizerStateIndex;
880  
881              if (!needsCustomScissor)
882              {
883                  updateMask |= 1UL << StateUpdater.ScissorStateIndex;
884              }
885  
886              engine.UpdateState(updateMask);
887  
888              if (needsCustomScissor)
889              {
890                  int scissorX = screenScissorState.X;
891                  int scissorY = screenScissorState.Y;
892                  int scissorW = screenScissorState.Width;
893                  int scissorH = screenScissorState.Height;
894  
895                  if (clearAffectedByScissor && _state.State.ScissorState[0].Enable)
896                  {
897                      ref var scissorState = ref _state.State.ScissorState[0];
898  
899                      scissorX = Math.Max(scissorX, scissorState.X1);
900                      scissorY = Math.Max(scissorY, scissorState.Y1);
901                      scissorW = Math.Min(scissorW, scissorState.X2 - scissorState.X1);
902                      scissorH = Math.Min(scissorH, scissorState.Y2 - scissorState.Y1);
903                  }
904  
905                  float scale = _channel.TextureManager.RenderTargetScale;
906                  if (scale != 1f)
907                  {
908                      scissorX = (int)(scissorX * scale);
909                      scissorY = (int)(scissorY * scale);
910                      scissorW = (int)MathF.Ceiling(scissorW * scale);
911                      scissorH = (int)MathF.Ceiling(scissorH * scale);
912                  }
913  
914                  Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[]
915                  {
916                      new Rectangle<int>(scissorX, scissorY, scissorW, scissorH),
917                  };
918  
919                  _context.Renderer.Pipeline.SetScissors(scissors);
920              }
921  
922              _channel.TextureManager.UpdateRenderTargets();
923  
924              if (componentMask != 0)
925              {
926                  var clearColor = _state.State.ClearColors;
927  
928                  ColorF color = new(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
929  
930                  _context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, layerCount, componentMask, color);
931              }
932  
933              if (clearDepth || clearStencil)
934              {
935                  float depthValue = _state.State.ClearDepthValue;
936                  int stencilValue = (int)_state.State.ClearStencilValue;
937  
938                  int stencilMask = 0;
939  
940                  if (clearStencil)
941                  {
942                      stencilMask = clearAffectedByStencilMask ? _state.State.StencilTestState.FrontMask : 0xff;
943                  }
944  
945                  if (clipMismatch)
946                  {
947                      _channel.TextureManager.UpdateRenderTargetDepthStencil();
948                  }
949  
950                  _context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
951                      layer,
952                      layerCount,
953                      depthValue,
954                      clearDepth,
955                      stencilValue,
956                      stencilMask);
957              }
958  
959              if (needsCustomScissor)
960              {
961                  engine.UpdateScissorState();
962              }
963  
964              engine.UpdateRenderTargetState(RenderTargetUpdateFlags.UpdateAll);
965  
966              if (renderEnable == ConditionalRenderEnabled.Host)
967              {
968                  _context.Renderer.Pipeline.EndHostConditionalRendering();
969              }
970          }
971  
972          protected virtual void Dispose(bool disposing)
973          {
974              if (disposing)
975              {
976                  _vtgAsCompute.Dispose();
977              }
978          }
979  
980          public void Dispose()
981          {
982              Dispose(true);
983              GC.SuppressFinalize(this);
984          }
985      }
986  }