Geometry shader emulation for macOS (#5551)

* Implement vertex and geometry shader conversion to compute

* Call InitializeReservedCounts for compute too

* PR feedback

* Set clip distance mask for geometry and tessellation shaders too

* Transform feedback emulation only for vertex
This commit is contained in:
gdkchan 2023-08-29 21:10:34 -03:00 committed by GitHub
parent 93d78f9ac4
commit f09bba82b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
65 changed files with 3912 additions and 593 deletions

View file

@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory;
using System;
@ -8,7 +9,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <summary>
/// Draw manager.
/// </summary>
class DrawManager
class DrawManager : IDisposable
{
// Since we don't know the index buffer size for indirect draws,
// we must assume a minimum and maximum size and use that for buffer data update purposes.
@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private readonly DeviceStateWithShadow<ThreedClassState> _state;
private readonly DrawState _drawState;
private readonly SpecializationStateUpdater _currentSpecState;
private readonly VtgAsCompute _vtgAsCompute;
private bool _topologySet;
private bool _instancedDrawPending;
@ -53,6 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state = state;
_drawState = drawState;
_currentSpecState = spec;
_vtgAsCompute = new(context, channel, state);
}
/// <summary>
@ -127,7 +130,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
if (renderEnable == ConditionalRenderEnabled.False)
{
PerformDeferredDraws();
PerformDeferredDraws(engine);
}
_drawState.DrawIndexed = false;
@ -190,13 +193,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
_context.Renderer.Pipeline.DrawIndexed(inlineIndexCount, 1, firstIndex, firstVertex, firstInstance);
DrawImpl(engine, inlineIndexCount, 1, firstIndex, firstVertex, firstInstance, indexed: true);
}
else if (_drawState.DrawIndexed)
{
int firstVertex = (int)_state.State.FirstVertex;
_context.Renderer.Pipeline.DrawIndexed(indexCount, 1, firstIndex, firstVertex, firstInstance);
DrawImpl(engine, indexCount, 1, firstIndex, firstVertex, firstInstance, indexed: true);
}
else
{
@ -204,7 +207,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var drawState = _state.State.VertexBufferDrawState;
#pragma warning restore IDE0059
_context.Renderer.Pipeline.Draw(drawVertexCount, 1, drawFirstVertex, firstInstance);
DrawImpl(engine, drawVertexCount, 1, 0, drawFirstVertex, firstInstance, indexed: false);
}
_drawState.DrawIndexed = false;
@ -219,24 +222,26 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Starts draw.
/// This sets primitive type and instanced draw parameters.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawBegin(int argument)
public void DrawBegin(ThreedClass engine, int argument)
{
bool incrementInstance = (argument & (1 << 26)) != 0;
bool resetInstance = (argument & (1 << 27)) == 0;
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
DrawBegin(incrementInstance, resetInstance, type);
DrawBegin(engine, incrementInstance, resetInstance, type);
}
/// <summary>
/// Starts draw.
/// This sets primitive type and instanced draw parameters.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <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="primitiveType">Primitive type</param>
private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveType primitiveType)
private void DrawBegin(ThreedClass engine, bool incrementInstance, bool resetInstance, PrimitiveType primitiveType)
{
if (incrementInstance)
{
@ -244,7 +249,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
else if (resetInstance)
{
PerformDeferredDraws();
PerformDeferredDraws(engine);
_instanceIndex = 0;
}
@ -364,7 +369,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="instanced">True to increment the current instance value, false otherwise</param>
private void DrawIndexBufferBeginEndInstance(ThreedClass engine, int argument, bool instanced)
{
DrawBegin(instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
DrawBegin(engine, instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
int firstIndex = argument & 0xffff;
int indexCount = (argument >> 16) & 0xfff;
@ -409,7 +414,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="instanced">True to increment the current instance value, false otherwise</param>
private void DrawVertexArrayBeginEndInstance(ThreedClass engine, int argument, bool instanced)
{
DrawBegin(instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
DrawBegin(engine, instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
int firstVertex = argument & 0xffff;
int vertexCount = (argument >> 16) & 0xfff;
@ -541,23 +546,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
engine.UpdateState();
if (instanceCount > 1)
{
// Must be called after UpdateState as it assumes the shader state
// has already been set, and that bindings have been updated already.
_channel.BufferManager.SetInstancedDrawVertexCount(count);
}
DrawImpl(engine, count, instanceCount, firstIndex, firstVertex, firstInstance, indexed);
if (indexed)
{
_context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance);
_state.State.FirstVertex = 0;
}
else
{
_context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance);
}
_state.State.FirstInstance = 0;
@ -569,6 +563,67 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
}
/// <summary>
/// Performs a indexed or non-indexed draw.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="count">Index count for indexed draws, vertex count for non-indexed draws</param>
/// <param name="instanceCount">Instance count</param>
/// <param name="firstIndex">First index on the index buffer for indexed draws, ignored for non-indexed draws</param>
/// <param name="firstVertex">First vertex on the vertex buffer</param>
/// <param name="firstInstance">First instance</param>
/// <param name="indexed">True if the draw is indexed, false otherwise</param>
private void DrawImpl(
ThreedClass engine,
int count,
int instanceCount,
int firstIndex,
int firstVertex,
int firstInstance,
bool indexed)
{
if (instanceCount > 1)
{
_channel.BufferManager.SetInstancedDrawVertexCount(count);
}
if (_drawState.VertexAsCompute != null)
{
_vtgAsCompute.DrawAsCompute(
engine,
_drawState.VertexAsCompute,
_drawState.GeometryAsCompute,
_drawState.VertexPassthrough,
_drawState.Topology,
count,
instanceCount,
firstIndex,
firstVertex,
firstInstance,
indexed);
if (_drawState.GeometryAsCompute != null)
{
// Geometry draws need to change the topology, so we need to set it here again
// if we are going to do a regular draw.
// Would have been better to do that on the callee, but doing it here
// avoids having to pass the draw manager instance.
ForceStateDirty();
}
}
else
{
if (indexed)
{
_context.Renderer.Pipeline.DrawIndexed(count, instanceCount, firstIndex, firstVertex, firstInstance);
}
else
{
_context.Renderer.Pipeline.Draw(count, instanceCount, firstVertex, firstInstance);
}
}
}
/// <summary>
/// Performs a indirect draw, with parameters from a GPU buffer.
/// </summary>
@ -667,43 +722,42 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Once we detect the last instanced draw, then we perform the host instanced draw,
/// with the accumulated instance count.
/// </summary>
public void PerformDeferredDraws()
/// <param name="engine">3D engine where this method is being called</param>
public void PerformDeferredDraws(ThreedClass engine)
{
// Perform any pending instanced draw.
if (_instancedDrawPending)
{
_instancedDrawPending = false;
int instanceCount = _instanceIndex + 1;
int firstInstance = _instancedFirstInstance;
bool indexedInline = _instancedIndexedInline;
if (_instancedIndexed || indexedInline)
{
int indexCount = _instancedIndexCount;
if (indexedInline)
{
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount(_context.Renderer);
BufferRange br = new(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
indexCount = inlineIndexCount;
}
_channel.BufferManager.SetInstancedDrawVertexCount(_instancedIndexCount);
int firstIndex = _instancedFirstIndex;
int firstVertex = _instancedFirstVertex;
_context.Renderer.Pipeline.DrawIndexed(
_instancedIndexCount,
_instanceIndex + 1,
_instancedFirstIndex,
_instancedFirstVertex,
_instancedFirstInstance);
DrawImpl(engine, indexCount, instanceCount, firstIndex, firstVertex, firstInstance, indexed: true);
}
else
{
_channel.BufferManager.SetInstancedDrawVertexCount(_instancedDrawStateCount);
int vertexCount = _instancedDrawStateCount;
int firstVertex = _instancedDrawStateFirst;
_context.Renderer.Pipeline.Draw(
_instancedDrawStateCount,
_instanceIndex + 1,
_instancedDrawStateFirst,
_instancedFirstInstance);
DrawImpl(engine, vertexCount, instanceCount, 0, firstVertex, firstInstance, indexed: false);
}
}
}
@ -866,5 +920,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_context.Renderer.Pipeline.EndHostConditionalRendering();
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_vtgAsCompute.Dispose();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}