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);
}
}
}
}
}