using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.State; namespace Ryujinx.Graphics.Gpu.Engine { partial class Methods { private bool _drawIndexed; private bool _instancedDrawPending; private bool _instancedIndexed; private int _instancedFirstIndex; private int _instancedFirstVertex; private int _instancedFirstInstance; private int _instancedIndexCount; private int _instancedDrawStateFirst; private int _instancedDrawStateCount; private int _instanceIndex; private IbStreamer _ibStreamer; /// /// Primitive topology of the current draw. /// public PrimitiveTopology Topology { get; private set; } /// /// Finishes the draw call. /// This draws geometry on the bound buffers based on the current GPU state. /// /// Current GPU state /// Method call argument private void DrawEnd(GpuState state, int argument) { var indexBuffer = state.Get(MethodOffset.IndexBufferState); DrawEnd(state, indexBuffer.First, indexBuffer.Count); } /// /// Finishes the draw call. /// This draws geometry on the bound buffers based on the current GPU state. /// /// Current GPU state /// Index of the first index buffer element used on the draw /// Number of index buffer elements used on the draw private void DrawEnd(GpuState state, int firstIndex, int indexCount) { ConditionalRenderEnabled renderEnable = GetRenderEnable(state); if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending) { if (renderEnable == ConditionalRenderEnabled.False) { PerformDeferredDraws(); } _drawIndexed = false; if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } return; } UpdateState(state, firstIndex, indexCount); bool instanced = _vsUsesInstanceId || _isAnyVbInstanced; if (instanced) { _instancedDrawPending = true; _instancedIndexed = _drawIndexed; _instancedFirstIndex = firstIndex; _instancedFirstVertex = state.Get(MethodOffset.FirstVertex); _instancedFirstInstance = state.Get(MethodOffset.FirstInstance); _instancedIndexCount = indexCount; var drawState = state.Get(MethodOffset.VertexBufferDrawState); _instancedDrawStateFirst = drawState.First; _instancedDrawStateCount = drawState.Count; _drawIndexed = false; if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } return; } int firstInstance = state.Get(MethodOffset.FirstInstance); int inlineIndexCount = _ibStreamer.GetAndResetInlineIndexCount(); if (inlineIndexCount != 0) { int firstVertex = state.Get(MethodOffset.FirstVertex); BufferRange br = new BufferRange(_ibStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); _context.Methods.BufferManager.SetIndexBuffer(br, IndexType.UInt); _context.Renderer.Pipeline.DrawIndexed( inlineIndexCount, 1, firstIndex, firstVertex, firstInstance); } else if (_drawIndexed) { int firstVertex = state.Get(MethodOffset.FirstVertex); _context.Renderer.Pipeline.DrawIndexed( indexCount, 1, firstIndex, firstVertex, firstInstance); } else { var drawState = state.Get(MethodOffset.VertexBufferDrawState); _context.Renderer.Pipeline.Draw( drawState.Count, 1, drawState.First, firstInstance); } _drawIndexed = false; if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } } /// /// Starts draw. /// This sets primitive type and instanced draw parameters. /// /// Current GPU state /// Method call argument private void DrawBegin(GpuState state, int argument) { bool incrementInstance = (argument & (1 << 26)) != 0; bool resetInstance = (argument & (1 << 27)) == 0; PrimitiveType type = (PrimitiveType)(argument & 0xffff); PrimitiveTypeOverride typeOverride = state.Get(MethodOffset.PrimitiveTypeOverride); if (typeOverride != PrimitiveTypeOverride.Invalid) { DrawBegin(incrementInstance, resetInstance, typeOverride.Convert()); } else { DrawBegin(incrementInstance, resetInstance, type.Convert()); } } /// /// Starts draw. /// This sets primitive type and instanced draw parameters. /// /// Indicates if the current instance should be incremented /// Indicates if the current instance should be set to zero /// Primitive topology private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology) { if (incrementInstance) { _instanceIndex++; } else if (resetInstance) { PerformDeferredDraws(); _instanceIndex = 0; } _context.Renderer.Pipeline.SetPrimitiveTopology(topology); Topology = topology; } /// /// Sets the index buffer count. /// This also sets internal state that indicates that the next draw is an indexed draw. /// /// Current GPU state /// Method call argument private void SetIndexBufferCount(GpuState state, int argument) { _drawIndexed = true; } /// /// Performs a indexed draw with a low number of index buffer elements. /// /// Current GPU state /// Method call argument private void DrawIndexedSmall(GpuState state, int argument) { DrawIndexedSmall(state, argument, false); } /// /// Performs a indexed draw with a low number of index buffer elements. /// /// Current GPU state /// Method call argument private void DrawIndexedSmall2(GpuState state, int argument) { DrawIndexedSmall(state, argument); } /// /// Performs a indexed draw with a low number of index buffer elements, /// while also pre-incrementing the current instance value. /// /// Current GPU state /// Method call argument private void DrawIndexedSmallIncInstance(GpuState state, int argument) { DrawIndexedSmall(state, argument, true); } /// /// Performs a indexed draw with a low number of index buffer elements, /// while also pre-incrementing the current instance value. /// /// Current GPU state /// Method call argument private void DrawIndexedSmallIncInstance2(GpuState state, int argument) { DrawIndexedSmallIncInstance(state, argument); } /// /// Performs a indexed draw with a low number of index buffer elements, /// while optionally also pre-incrementing the current instance value. /// /// Current GPU state /// Method call argument /// True to increment the current instance value, false otherwise private void DrawIndexedSmall(GpuState state, int argument, bool instanced) { PrimitiveTypeOverride typeOverride = state.Get(MethodOffset.PrimitiveTypeOverride); DrawBegin(instanced, !instanced, typeOverride.Convert()); int firstIndex = argument & 0xffff; int indexCount = (argument >> 16) & 0xfff; bool oldDrawIndexed = _drawIndexed; _drawIndexed = true; DrawEnd(state, firstIndex, indexCount); _drawIndexed = oldDrawIndexed; } /// /// Pushes four 8-bit index buffer elements. /// /// Current GPU state /// Method call argument private void VbElementU8(GpuState state, int argument) { _ibStreamer.VbElementU8(_context.Renderer, argument); } /// /// Pushes two 16-bit index buffer elements. /// /// Current GPU state /// Method call argument private void VbElementU16(GpuState state, int argument) { _ibStreamer.VbElementU16(_context.Renderer, argument); } /// /// Pushes one 32-bit index buffer element. /// /// Current GPU state /// Method call argument private void VbElementU32(GpuState state, int argument) { _ibStreamer.VbElementU32(_context.Renderer, argument); } /// /// Perform any deferred draws. /// This is used for instanced draws. /// Since each instance is a separate draw, we defer the draw and accumulate the instance count. /// Once we detect the last instanced draw, then we perform the host instanced draw, /// with the accumulated instance count. /// public void PerformDeferredDraws() { // Perform any pending instanced draw. if (_instancedDrawPending) { _instancedDrawPending = false; if (_instancedIndexed) { _context.Renderer.Pipeline.DrawIndexed( _instancedIndexCount, _instanceIndex + 1, _instancedFirstIndex, _instancedFirstVertex, _instancedFirstInstance); } else { _context.Renderer.Pipeline.Draw( _instancedDrawStateCount, _instanceIndex + 1, _instancedDrawStateFirst, _instancedFirstInstance); } } } } }