Implement small indexed draws and other fixes to make guest Vulkan work (#1558)

This commit is contained in:
gdkchan 2020-09-23 20:48:34 -03:00 committed by GitHub
parent e00ca92063
commit bd28ce90e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 357 additions and 133 deletions

View file

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.Gpu.Engine
{
enum ConditionalRenderEnabled
{
False,
True,
Host
}
}

View file

@ -0,0 +1,139 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Engine
{
/// <summary>
/// Holds inline index buffer state.
/// The inline index buffer data is sent to the GPU through the command buffer.
/// </summary>
struct IbStreamer
{
private BufferHandle _inlineIndexBuffer;
private int _inlineIndexBufferSize;
private int _inlineIndexCount;
public bool HasInlineIndexData => _inlineIndexCount != 0;
/// <summary>
/// Gets the handle for the host buffer currently holding the inline index buffer data.
/// </summary>
/// <returns>Host buffer handle</returns>
public BufferHandle GetInlineIndexBuffer()
{
return _inlineIndexBuffer;
}
/// <summary>
/// Gets the number of elements on the current inline index buffer,
/// while also reseting it to zero for the next draw.
/// </summary>
/// <returns>Inline index bufffer count</returns>
public int GetAndResetInlineIndexCount()
{
int temp = _inlineIndexCount;
_inlineIndexCount = 0;
return temp;
}
/// <summary>
/// Pushes four 8-bit index buffer elements.
/// </summary>
/// <param name="renderer">Host renderer</param>
/// <param name="argument">Method call argument</param>
public void VbElementU8(IRenderer renderer, int argument)
{
byte i0 = (byte)argument;
byte i1 = (byte)(argument >> 8);
byte i2 = (byte)(argument >> 16);
byte i3 = (byte)(argument >> 24);
Span<uint> data = stackalloc uint[4];
data[0] = i0;
data[1] = i1;
data[2] = i2;
data[3] = i3;
int offset = _inlineIndexCount * 4;
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
_inlineIndexCount += 4;
}
/// <summary>
/// Pushes two 16-bit index buffer elements.
/// </summary>
/// <param name="renderer">Host renderer</param>
/// <param name="argument">Method call argument</param>
public void VbElementU16(IRenderer renderer, int argument)
{
ushort i0 = (ushort)argument;
ushort i1 = (ushort)(argument >> 16);
Span<uint> data = stackalloc uint[2];
data[0] = i0;
data[1] = i1;
int offset = _inlineIndexCount * 4;
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
_inlineIndexCount += 2;
}
/// <summary>
/// Pushes one 32-bit index buffer element.
/// </summary>
/// <param name="renderer">Host renderer</param>
/// <param name="argument">Method call argument</param>
public void VbElementU32(IRenderer renderer, int argument)
{
uint i0 = (uint)argument;
Span<uint> data = stackalloc uint[1];
data[0] = i0;
int offset = _inlineIndexCount++ * 4;
renderer.SetBufferData(GetInlineIndexBuffer(renderer, offset), offset, MemoryMarshal.Cast<uint, byte>(data));
}
/// <summary>
/// Gets the handle of a buffer large enough to hold the data that will be written to <paramref name="offset"/>.
/// </summary>
/// <param name="renderer">Host renderer</param>
/// <param name="offset">Offset where the data will be written</param>
/// <returns>Buffer handle</returns>
private BufferHandle GetInlineIndexBuffer(IRenderer renderer, int offset)
{
// Calculate a reasonable size for the buffer that can fit all the data,
// and that also won't require frequent resizes if we need to push more data.
int size = BitUtils.AlignUp(offset + 0x10, 0x200);
if (_inlineIndexBuffer == BufferHandle.Null)
{
_inlineIndexBuffer = renderer.CreateBuffer(size);
_inlineIndexBufferSize = size;
}
else if (_inlineIndexBufferSize < size)
{
BufferHandle oldBuffer = _inlineIndexBuffer;
int oldSize = _inlineIndexBufferSize;
_inlineIndexBuffer = renderer.CreateBuffer(size);
_inlineIndexBufferSize = size;
renderer.Pipeline.CopyBuffer(oldBuffer, _inlineIndexBuffer, 0, 0, oldSize);
renderer.DeleteBuffer(oldBuffer);
}
return _inlineIndexBuffer;
}
}
}

View file

@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
UpdateRenderTargetState(state, useControl: false, singleUse: index); UpdateRenderTargetState(state, useControl: false, singleUse: index);
TextureManager.CommitGraphicsBindings(); TextureManager.UpdateRenderTargets();
bool clearDepth = (argument & 1) != 0; bool clearDepth = (argument & 1) != 0;
bool clearStencil = (argument & 2) != 0; bool clearStencil = (argument & 2) != 0;

View file

@ -6,13 +6,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
{ {
partial class Methods partial class Methods
{ {
enum ConditionalRenderEnabled
{
False,
True,
Host
}
/// <summary> /// <summary>
/// Checks if draws and clears should be performed, according /// Checks if draws and clears should be performed, according
/// to currently set conditional rendering conditions. /// to currently set conditional rendering conditions.

View file

@ -1,9 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Gpu.State;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Gpu.Engine namespace Ryujinx.Graphics.Gpu.Engine
{ {
@ -11,9 +8,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
{ {
private bool _drawIndexed; private bool _drawIndexed;
private int _firstIndex;
private int _indexCount;
private bool _instancedDrawPending; private bool _instancedDrawPending;
private bool _instancedIndexed; private bool _instancedIndexed;
@ -26,22 +20,34 @@ namespace Ryujinx.Graphics.Gpu.Engine
private int _instanceIndex; private int _instanceIndex;
private BufferHandle _inlineIndexBuffer = BufferHandle.Null; private IbStreamer _ibStreamer;
private int _inlineIndexBufferSize;
private int _inlineIndexCount;
/// <summary> /// <summary>
/// Primitive type of the current draw. /// Primitive topology of the current draw.
/// </summary> /// </summary>
public PrimitiveType PrimitiveType { get; private set; } public PrimitiveTopology Topology { get; private set; }
/// <summary> /// <summary>
/// Finishes draw call. /// Finishes the draw call.
/// This draws geometry on the bound buffers based on the current GPU state. /// This draws geometry on the bound buffers based on the current GPU state.
/// </summary> /// </summary>
/// <param name="state">Current GPU state</param> /// <param name="state">Current GPU state</param>
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
private void DrawEnd(GpuState state, int argument) private void DrawEnd(GpuState state, int argument)
{
var indexBuffer = state.Get<IndexBufferState>(MethodOffset.IndexBufferState);
DrawEnd(state, indexBuffer.First, indexBuffer.Count);
}
/// <summary>
/// Finishes the draw call.
/// This draws geometry on the bound buffers based on the current GPU state.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
/// <param name="indexCount">Number of index buffer elements used on the draw</param>
private void DrawEnd(GpuState state, int firstIndex, int indexCount)
{ {
ConditionalRenderEnabled renderEnable = GetRenderEnable(state); ConditionalRenderEnabled renderEnable = GetRenderEnable(state);
@ -62,7 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
return; return;
} }
UpdateState(state); UpdateState(state, firstIndex, indexCount);
bool instanced = _vsUsesInstanceId || _isAnyVbInstanced; bool instanced = _vsUsesInstanceId || _isAnyVbInstanced;
@ -72,11 +78,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
_instancedIndexed = _drawIndexed; _instancedIndexed = _drawIndexed;
_instancedFirstIndex = _firstIndex; _instancedFirstIndex = firstIndex;
_instancedFirstVertex = state.Get<int>(MethodOffset.FirstVertex); _instancedFirstVertex = state.Get<int>(MethodOffset.FirstVertex);
_instancedFirstInstance = state.Get<int>(MethodOffset.FirstInstance); _instancedFirstInstance = state.Get<int>(MethodOffset.FirstInstance);
_instancedIndexCount = _indexCount; _instancedIndexCount = indexCount;
var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState); var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
@ -95,31 +101,31 @@ namespace Ryujinx.Graphics.Gpu.Engine
int firstInstance = state.Get<int>(MethodOffset.FirstInstance); int firstInstance = state.Get<int>(MethodOffset.FirstInstance);
if (_inlineIndexCount != 0) int inlineIndexCount = _ibStreamer.GetAndResetInlineIndexCount();
if (inlineIndexCount != 0)
{ {
int firstVertex = state.Get<int>(MethodOffset.FirstVertex); int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
BufferRange br = new BufferRange(_inlineIndexBuffer, 0, _inlineIndexCount * 4); BufferRange br = new BufferRange(_ibStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
_context.Methods.BufferManager.SetIndexBuffer(br, IndexType.UInt); _context.Methods.BufferManager.SetIndexBuffer(br, IndexType.UInt);
_context.Renderer.Pipeline.DrawIndexed( _context.Renderer.Pipeline.DrawIndexed(
_inlineIndexCount, inlineIndexCount,
1, 1,
_firstIndex, firstIndex,
firstVertex, firstVertex,
firstInstance); firstInstance);
_inlineIndexCount = 0;
} }
else if (_drawIndexed) else if (_drawIndexed)
{ {
int firstVertex = state.Get<int>(MethodOffset.FirstVertex); int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
_context.Renderer.Pipeline.DrawIndexed( _context.Renderer.Pipeline.DrawIndexed(
_indexCount, indexCount,
1, 1,
_firstIndex, firstIndex,
firstVertex, firstVertex,
firstInstance); firstInstance);
} }
@ -150,22 +156,46 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
private void DrawBegin(GpuState state, int argument) private void DrawBegin(GpuState state, int argument)
{ {
if ((argument & (1 << 26)) != 0) bool incrementInstance = (argument & (1 << 26)) != 0;
bool resetInstance = (argument & (1 << 27)) == 0;
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
PrimitiveTypeOverride typeOverride = state.Get<PrimitiveTypeOverride>(MethodOffset.PrimitiveTypeOverride);
if (typeOverride != PrimitiveTypeOverride.Invalid)
{
DrawBegin(incrementInstance, resetInstance, typeOverride.Convert());
}
else
{
DrawBegin(incrementInstance, resetInstance, type.Convert());
}
}
/// <summary>
/// Starts draw.
/// This sets primitive type and instanced draw parameters.
/// </summary>
/// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
/// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
/// <param name="topology">Primitive topology</param>
private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology)
{
if (incrementInstance)
{ {
_instanceIndex++; _instanceIndex++;
} }
else if ((argument & (1 << 27)) == 0) else if (resetInstance)
{ {
PerformDeferredDraws(); PerformDeferredDraws();
_instanceIndex = 0; _instanceIndex = 0;
} }
PrimitiveType type = (PrimitiveType)(argument & 0xffff); _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
_context.Renderer.Pipeline.SetPrimitiveTopology(type.Convert()); Topology = topology;
PrimitiveType = type;
} }
/// <summary> /// <summary>
@ -179,6 +209,73 @@ namespace Ryujinx.Graphics.Gpu.Engine
_drawIndexed = true; _drawIndexed = true;
} }
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="argument">Method call argument</param>
private void DrawIndexedSmall(GpuState state, int argument)
{
DrawIndexedSmall(state, argument, false);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="argument">Method call argument</param>
private void DrawIndexedSmall2(GpuState state, int argument)
{
DrawIndexedSmall(state, argument);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements,
/// while also pre-incrementing the current instance value.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="argument">Method call argument</param>
private void DrawIndexedSmallIncInstance(GpuState state, int argument)
{
DrawIndexedSmall(state, argument, true);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements,
/// while also pre-incrementing the current instance value.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="argument">Method call argument</param>
private void DrawIndexedSmallIncInstance2(GpuState state, int argument)
{
DrawIndexedSmallIncInstance(state, argument);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements,
/// while optionally also pre-incrementing the current instance value.
/// </summary>
/// <param name="state">Current GPU state</param>
/// <param name="argument">Method call argument</param>
/// <param name="instanced">True to increment the current instance value, false otherwise</param>
private void DrawIndexedSmall(GpuState state, int argument, bool instanced)
{
PrimitiveTypeOverride typeOverride = state.Get<PrimitiveTypeOverride>(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;
}
/// <summary> /// <summary>
/// Pushes four 8-bit index buffer elements. /// Pushes four 8-bit index buffer elements.
/// </summary> /// </summary>
@ -186,23 +283,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
private void VbElementU8(GpuState state, int argument) private void VbElementU8(GpuState state, int argument)
{ {
byte i0 = (byte)argument; _ibStreamer.VbElementU8(_context.Renderer, argument);
byte i1 = (byte)(argument >> 8);
byte i2 = (byte)(argument >> 16);
byte i3 = (byte)(argument >> 24);
Span<uint> data = stackalloc uint[4];
data[0] = i0;
data[1] = i1;
data[2] = i2;
data[3] = i3;
int offset = _inlineIndexCount * 4;
_context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast<uint, byte>(data));
_inlineIndexCount += 4;
} }
/// <summary> /// <summary>
@ -212,19 +293,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
private void VbElementU16(GpuState state, int argument) private void VbElementU16(GpuState state, int argument)
{ {
ushort i0 = (ushort)argument; _ibStreamer.VbElementU16(_context.Renderer, argument);
ushort i1 = (ushort)(argument >> 16);
Span<uint> data = stackalloc uint[2];
data[0] = i0;
data[1] = i1;
int offset = _inlineIndexCount * 4;
_context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast<uint, byte>(data));
_inlineIndexCount += 2;
} }
/// <summary> /// <summary>
@ -234,46 +303,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
private void VbElementU32(GpuState state, int argument) private void VbElementU32(GpuState state, int argument)
{ {
uint i0 = (uint)argument; _ibStreamer.VbElementU32(_context.Renderer, argument);
Span<uint> data = stackalloc uint[1];
data[0] = i0;
int offset = _inlineIndexCount++ * 4;
_context.Renderer.SetBufferData(GetInlineIndexBuffer(offset), offset, MemoryMarshal.Cast<uint, byte>(data));
}
/// <summary>
/// Gets the handle of a buffer large enough to hold the data that will be written to <paramref name="offset"/>.
/// </summary>
/// <param name="offset">Offset where the data will be written</param>
/// <returns>Buffer handle</returns>
private BufferHandle GetInlineIndexBuffer(int offset)
{
// Calculate a reasonable size for the buffer that can fit all the data,
// and that also won't require frequent resizes if we need to push more data.
int size = BitUtils.AlignUp(offset + 0x10, 0x200);
if (_inlineIndexBuffer == BufferHandle.Null)
{
_inlineIndexBuffer = _context.Renderer.CreateBuffer(size);
_inlineIndexBufferSize = size;
}
else if (_inlineIndexBufferSize < size)
{
BufferHandle oldBuffer = _inlineIndexBuffer;
int oldSize = _inlineIndexBufferSize;
_inlineIndexBuffer = _context.Renderer.CreateBuffer(size);
_inlineIndexBufferSize = size;
_context.Renderer.Pipeline.CopyBuffer(oldBuffer, _inlineIndexBuffer, 0, 0, oldSize);
_context.Renderer.DeleteBuffer(oldBuffer);
}
return _inlineIndexBuffer;
} }
/// <summary> /// <summary>

View file

@ -87,8 +87,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
state.RegisterCallback(MethodOffset.ResetCounter, ResetCounter); state.RegisterCallback(MethodOffset.ResetCounter, ResetCounter);
state.RegisterCallback(MethodOffset.DrawEnd, DrawEnd); state.RegisterCallback(MethodOffset.DrawEnd, DrawEnd);
state.RegisterCallback(MethodOffset.DrawBegin, DrawBegin); state.RegisterCallback(MethodOffset.DrawBegin, DrawBegin);
state.RegisterCallback(MethodOffset.DrawIndexedSmall, DrawIndexedSmall);
state.RegisterCallback(MethodOffset.DrawIndexedSmall2, DrawIndexedSmall2);
state.RegisterCallback(MethodOffset.DrawIndexedSmallIncInstance, DrawIndexedSmallIncInstance);
state.RegisterCallback(MethodOffset.DrawIndexedSmallIncInstance2, DrawIndexedSmallIncInstance2);
state.RegisterCallback(MethodOffset.IndexBufferCount, SetIndexBufferCount); state.RegisterCallback(MethodOffset.IndexBufferCount, SetIndexBufferCount);
@ -111,7 +115,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// Updates host state based on the current guest GPU state. /// Updates host state based on the current guest GPU state.
/// </summary> /// </summary>
/// <param name="state">Guest GPU state</param> /// <param name="state">Guest GPU state</param>
private void UpdateState(GpuState state) /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
/// <param name="indexCount">Number of index buffer elements used on the draw</param>
private void UpdateState(GpuState state, int firstIndex, int indexCount)
{ {
bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable); bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
@ -233,7 +239,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (state.QueryModified(MethodOffset.IndexBufferState)) if (state.QueryModified(MethodOffset.IndexBufferState))
{ {
UpdateIndexBufferState(state); UpdateIndexBufferState(state, firstIndex, indexCount);
} }
if (state.QueryModified(MethodOffset.VertexBufferDrawState, if (state.QueryModified(MethodOffset.VertexBufferDrawState,
@ -273,7 +279,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (tfEnable && !_prevTfEnable) if (tfEnable && !_prevTfEnable)
{ {
_context.Renderer.Pipeline.BeginTransformFeedback(PrimitiveType.Convert()); _context.Renderer.Pipeline.BeginTransformFeedback(Topology);
_prevTfEnable = true; _prevTfEnable = true;
} }
} }
@ -742,14 +748,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// Updates host index buffer binding based on guest GPU state. /// Updates host index buffer binding based on guest GPU state.
/// </summary> /// </summary>
/// <param name="state">Current GPU state</param> /// <param name="state">Current GPU state</param>
private void UpdateIndexBufferState(GpuState state) /// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
/// <param name="indexCount">Number of index buffer elements used on the draw</param>
private void UpdateIndexBufferState(GpuState state, int firstIndex, int indexCount)
{ {
var indexBuffer = state.Get<IndexBufferState>(MethodOffset.IndexBufferState); var indexBuffer = state.Get<IndexBufferState>(MethodOffset.IndexBufferState);
_firstIndex = indexBuffer.First; if (indexCount == 0)
_indexCount = indexBuffer.Count;
if (_indexCount == 0)
{ {
return; return;
} }
@ -758,7 +763,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
// Do not use the end address to calculate the size, because // Do not use the end address to calculate the size, because
// the result may be much larger than the real size of the index buffer. // the result may be much larger than the real size of the index buffer.
ulong size = (ulong)(_firstIndex + _indexCount); ulong size = (ulong)(firstIndex + indexCount);
switch (indexBuffer.Type) switch (indexBuffer.Type)
{ {
@ -806,7 +811,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
ulong size; ulong size;
if (_inlineIndexCount != 0 || _drawIndexed || stride == 0 || instanced) if (_ibStreamer.HasInlineIndexData || _drawIndexed || stride == 0 || instanced)
{ {
// This size may be (much) larger than the real vertex buffer size. // This size may be (much) larger than the real vertex buffer size.
// Avoid calculating it this way, unless we don't have any other option. // Avoid calculating it this way, unless we don't have any other option.

View file

@ -348,7 +348,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Update host framebuffer attachments based on currently bound render target buffers. /// Update host framebuffer attachments based on currently bound render target buffers.
/// </summary> /// </summary>
private void UpdateRenderTargets() public void UpdateRenderTargets()
{ {
bool anyChanged = false; bool anyChanged = false;

View file

@ -147,23 +147,23 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Current primitive topology</returns> /// <returns>Current primitive topology</returns>
public InputTopology QueryPrimitiveTopology() public InputTopology QueryPrimitiveTopology()
{ {
switch (_context.Methods.PrimitiveType) switch (_context.Methods.Topology)
{ {
case PrimitiveType.Points: case PrimitiveTopology.Points:
return InputTopology.Points; return InputTopology.Points;
case PrimitiveType.Lines: case PrimitiveTopology.Lines:
case PrimitiveType.LineLoop: case PrimitiveTopology.LineLoop:
case PrimitiveType.LineStrip: case PrimitiveTopology.LineStrip:
return InputTopology.Lines; return InputTopology.Lines;
case PrimitiveType.LinesAdjacency: case PrimitiveTopology.LinesAdjacency:
case PrimitiveType.LineStripAdjacency: case PrimitiveTopology.LineStripAdjacency:
return InputTopology.LinesAdjacency; return InputTopology.LinesAdjacency;
case PrimitiveType.Triangles: case PrimitiveTopology.Triangles:
case PrimitiveType.TriangleStrip: case PrimitiveTopology.TriangleStrip:
case PrimitiveType.TriangleFan: case PrimitiveTopology.TriangleFan:
return InputTopology.Triangles; return InputTopology.Triangles;
case PrimitiveType.TrianglesAdjacency: case PrimitiveTopology.TrianglesAdjacency:
case PrimitiveType.TriangleStripAdjacency: case PrimitiveTopology.TriangleStripAdjacency:
return InputTopology.TrianglesAdjacency; return InputTopology.TrianglesAdjacency;
} }

View file

@ -86,12 +86,17 @@ namespace Ryujinx.Graphics.Gpu.State
PrimitiveRestartState = 0x591, PrimitiveRestartState = 0x591,
IndexBufferState = 0x5f2, IndexBufferState = 0x5f2,
IndexBufferCount = 0x5f8, IndexBufferCount = 0x5f8,
DrawIndexedSmall = 0x5f9,
DrawIndexedSmall2 = 0x5fa,
DrawIndexedSmallIncInstance = 0x5fc,
DrawIndexedSmallIncInstance2 = 0x5fd,
DepthBiasClamp = 0x61f, DepthBiasClamp = 0x61f,
VertexBufferInstanced = 0x620, VertexBufferInstanced = 0x620,
VertexProgramPointSize = 0x644, VertexProgramPointSize = 0x644,
FaceState = 0x646, FaceState = 0x646,
ViewportTransformEnable = 0x64b, ViewportTransformEnable = 0x64b,
ViewVolumeClipControl = 0x64f, ViewVolumeClipControl = 0x64f,
PrimitiveTypeOverride = 0x65c,
LogicOpState = 0x671, LogicOpState = 0x671,
Clear = 0x674, Clear = 0x674,
RtColorMask = 0x680, RtColorMask = 0x680,

View file

@ -24,6 +24,25 @@ namespace Ryujinx.Graphics.Gpu.State
Patches Patches
} }
/// <summary>
/// Alternative primitive type that might override <see cref="PrimitiveType"/>.
/// </summary>
enum PrimitiveTypeOverride
{
Invalid = 0,
Points = 1,
Lines = 2,
LineStrip = 3,
Triangles = 4,
TriangleStrip = 5,
TriangleFan = 0x1015,
LinesAdjacency = 10,
LineStripAdjacency = 11,
TrianglesAdjacency = 12,
TriangleStripAdjacency = 13,
Patches = 14
}
static class PrimitiveTypeConverter static class PrimitiveTypeConverter
{ {
/// <summary> /// <summary>
@ -53,5 +72,29 @@ namespace Ryujinx.Graphics.Gpu.State
_ => PrimitiveTopology.Triangles _ => PrimitiveTopology.Triangles
}; };
} }
/// <summary>
/// Converts the primitive type into something that can be used with the host API.
/// </summary>
/// <param name="type">The primitive type to convert</param>
/// <returns>A host compatible enum value</returns>
public static PrimitiveTopology Convert(this PrimitiveTypeOverride type)
{
return type switch
{
PrimitiveTypeOverride.Points => PrimitiveTopology.Points,
PrimitiveTypeOverride.Lines => PrimitiveTopology.Lines,
PrimitiveTypeOverride.LineStrip => PrimitiveTopology.LineStrip,
PrimitiveTypeOverride.Triangles => PrimitiveTopology.Triangles,
PrimitiveTypeOverride.TriangleStrip => PrimitiveTopology.TriangleStrip,
PrimitiveTypeOverride.TriangleFan => PrimitiveTopology.TriangleFan,
PrimitiveTypeOverride.LinesAdjacency => PrimitiveTopology.LinesAdjacency,
PrimitiveTypeOverride.LineStripAdjacency => PrimitiveTopology.LineStripAdjacency,
PrimitiveTypeOverride.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency,
PrimitiveTypeOverride.TriangleStripAdjacency => PrimitiveTopology.TriangleStripAdjacency,
PrimitiveTypeOverride.Patches => PrimitiveTopology.Patches,
_ => PrimitiveTopology.Triangles
};
}
} }
} }