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 }