Initial tessellation shader support (#2534)
* Initial tessellation shader support * Nits * Re-arrange built-in table * This is not needed anymore * PR feedback
This commit is contained in:
parent
7603dbe3c8
commit
d512ce122c
42 changed files with 775 additions and 148 deletions
|
@ -55,11 +55,15 @@ namespace Ryujinx.Graphics.GAL
|
|||
|
||||
void SetImage(int binding, ITexture texture, Format imageFormat);
|
||||
|
||||
void SetLineParameters(float width, bool smooth);
|
||||
|
||||
void SetLogicOpState(bool enable, LogicalOp op);
|
||||
|
||||
void SetLineParameters(float width, bool smooth);
|
||||
void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel);
|
||||
void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin);
|
||||
|
||||
void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode);
|
||||
|
||||
void SetPrimitiveRestart(bool enable, int index);
|
||||
|
||||
void SetPrimitiveTopology(PrimitiveTopology topology);
|
||||
|
|
|
@ -181,8 +181,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||
SetLineParametersCommand.Run(ref GetCommand<SetLineParametersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetLogicOpState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetLogicOpStateCommand.Run(ref GetCommand<SetLogicOpStateCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPatchParameters] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetPatchParametersCommand.Run(ref GetCommand<SetPatchParametersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPointParameters] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetPointParametersCommand.Run(ref GetCommand<SetPointParametersCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPolygonMode] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetPolygonModeCommand.Run(ref GetCommand<SetPolygonModeCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPrimitiveRestart] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
SetPrimitiveRestartCommand.Run(ref GetCommand<SetPrimitiveRestartCommand>(memory), threaded, renderer);
|
||||
_lookup[(int)CommandType.SetPrimitiveTopology] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||
|
|
|
@ -72,7 +72,9 @@
|
|||
SetIndexBuffer,
|
||||
SetLineParameters,
|
||||
SetLogicOpState,
|
||||
SetPatchParameters,
|
||||
SetPointParameters,
|
||||
SetPolygonMode,
|
||||
SetPrimitiveRestart,
|
||||
SetPrimitiveTopology,
|
||||
SetProgram,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetPatchParametersCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.SetPatchParameters;
|
||||
private int _vertices;
|
||||
private Array4<float> _defaultOuterLevel;
|
||||
private Array2<float> _defaultInnerLevel;
|
||||
|
||||
public void Set(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
|
||||
{
|
||||
_vertices = vertices;
|
||||
defaultOuterLevel.CopyTo(_defaultOuterLevel.ToSpan());
|
||||
defaultInnerLevel.CopyTo(_defaultInnerLevel.ToSpan());
|
||||
}
|
||||
|
||||
public static void Run(ref SetPatchParametersCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetPatchParameters(command._vertices, command._defaultOuterLevel.ToSpan(), command._defaultInnerLevel.ToSpan());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetPolygonModeCommand : IGALCommand
|
||||
{
|
||||
public CommandType CommandType => CommandType.SetPolygonMode;
|
||||
private PolygonMode _frontMode;
|
||||
private PolygonMode _backMode;
|
||||
|
||||
public void Set(PolygonMode frontMode, PolygonMode backMode)
|
||||
{
|
||||
_frontMode = frontMode;
|
||||
_backMode = backMode;
|
||||
}
|
||||
|
||||
public static void Run(ref SetPolygonModeCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetPolygonMode(command._frontMode, command._backMode);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -179,12 +179,24 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
|
||||
{
|
||||
_renderer.New<SetPatchParametersCommand>().Set(vertices, defaultOuterLevel, defaultInnerLevel);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin)
|
||||
{
|
||||
_renderer.New<SetPointParametersCommand>().Set(size, isProgramPointSize, enablePointSprite, origin);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode)
|
||||
{
|
||||
_renderer.New<SetPolygonModeCommand>().Set(frontMode, backMode);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetPrimitiveRestart(bool enable, int index)
|
||||
{
|
||||
_renderer.New<SetPrimitiveRestartCommand>().Set(enable, index);
|
||||
|
|
9
Ryujinx.Graphics.GAL/PolygonMode.cs
Normal file
9
Ryujinx.Graphics.GAL/PolygonMode.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum PolygonMode
|
||||
{
|
||||
Point = 0x1b00,
|
||||
Line = 0x1b01,
|
||||
Fill = 0x1b02
|
||||
}
|
||||
}
|
|
@ -129,7 +129,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||
_state.State.SetTexHeaderPoolCMaximumIndex,
|
||||
_state.State.SetBindlessTextureConstantBufferSlotSelect,
|
||||
false,
|
||||
PrimitiveTopology.Points);
|
||||
PrimitiveTopology.Points,
|
||||
default);
|
||||
|
||||
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
|
||||
_channel,
|
||||
|
|
|
@ -72,6 +72,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
nameof(ThreedClassState.VertexBufferState),
|
||||
nameof(ThreedClassState.VertexBufferEndAddress)),
|
||||
|
||||
new StateUpdateCallbackEntry(UpdateTessellationState,
|
||||
nameof(ThreedClassState.TessOuterLevel),
|
||||
nameof(ThreedClassState.TessInnerLevel),
|
||||
nameof(ThreedClassState.PatchVertices)),
|
||||
|
||||
new StateUpdateCallbackEntry(UpdateTfBufferState, nameof(ThreedClassState.TfBufferState)),
|
||||
new StateUpdateCallbackEntry(UpdateUserClipState, nameof(ThreedClassState.ClipDistanceEnable)),
|
||||
|
||||
|
@ -100,6 +105,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
nameof(ThreedClassState.ViewportExtents),
|
||||
nameof(ThreedClassState.YControl)),
|
||||
|
||||
new StateUpdateCallbackEntry(UpdatePolygonMode,
|
||||
nameof(ThreedClassState.PolygonModeFront),
|
||||
nameof(ThreedClassState.PolygonModeBack)),
|
||||
|
||||
new StateUpdateCallbackEntry(UpdateDepthBiasState,
|
||||
nameof(ThreedClassState.DepthBiasState),
|
||||
nameof(ThreedClassState.DepthBiasFactor),
|
||||
|
@ -259,6 +268,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates tessellation state based on the guest GPU state.
|
||||
/// </summary>
|
||||
private void UpdateTessellationState()
|
||||
{
|
||||
_context.Renderer.Pipeline.SetPatchParameters(
|
||||
_state.State.PatchVertices,
|
||||
_state.State.TessOuterLevel.ToSpan(),
|
||||
_state.State.TessInnerLevel.ToSpan());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates transform feedback buffer state based on the guest GPU state.
|
||||
/// </summary>
|
||||
|
@ -544,6 +564,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
_context.Renderer.Pipeline.SetViewports(0, viewports);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates polygon mode state based on current GPU state.
|
||||
/// </summary>
|
||||
private void UpdatePolygonMode()
|
||||
{
|
||||
_context.Renderer.Pipeline.SetPolygonMode(_state.State.PolygonModeFront, _state.State.PolygonModeBack);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host depth bias (also called polygon offset) state based on current GPU state.
|
||||
/// </summary>
|
||||
|
@ -949,7 +977,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
_state.State.TexturePoolState.MaximumId,
|
||||
(int)_state.State.TextureBufferIndex,
|
||||
_state.State.EarlyZForce,
|
||||
_drawState.Topology);
|
||||
_drawState.Topology,
|
||||
_state.State.TessMode);
|
||||
|
||||
ShaderBundle gs = _channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(ref _state.State, _channel, gas, addresses);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
|
|||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
@ -19,6 +20,43 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
Fragment
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tessellation mode.
|
||||
/// </summary>
|
||||
struct TessMode
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public uint Packed;
|
||||
#pragma warning restore CS0649
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the tessellation abstract patch type.
|
||||
/// </summary>
|
||||
/// <returns>Abtract patch type</returns>
|
||||
public TessPatchType UnpackPatchType()
|
||||
{
|
||||
return (TessPatchType)(Packed & 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the spacing between tessellated vertices of the patch.
|
||||
/// </summary>
|
||||
/// <returns>Spacing between tessellated vertices</returns>
|
||||
public TessSpacing UnpackSpacing()
|
||||
{
|
||||
return (TessSpacing)((Packed >> 4) & 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the primitive winding order.
|
||||
/// </summary>
|
||||
/// <returns>True if clockwise, false if counter-clockwise</returns>
|
||||
public bool UnpackCw()
|
||||
{
|
||||
return (Packed & (1 << 8)) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform feedback buffer state.
|
||||
/// </summary>
|
||||
|
@ -661,7 +699,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
public Boolean32 EarlyZForce;
|
||||
public fixed uint Reserved214[45];
|
||||
public uint SyncpointAction;
|
||||
public fixed uint Reserved2CC[44];
|
||||
public fixed uint Reserved2CC[21];
|
||||
public TessMode TessMode;
|
||||
public Array4<float> TessOuterLevel;
|
||||
public Array2<float> TessInnerLevel;
|
||||
public fixed uint Reserved33C[16];
|
||||
public Boolean32 RasterizeEnable;
|
||||
public Array4<TfBufferState> TfBufferState;
|
||||
public fixed uint Reserved400[192];
|
||||
|
@ -679,9 +721,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||
public float ClearDepthValue;
|
||||
public fixed uint ReservedD94[3];
|
||||
public uint ClearStencilValue;
|
||||
public fixed uint ReservedDA4[7];
|
||||
public fixed uint ReservedDA4[2];
|
||||
public PolygonMode PolygonModeFront;
|
||||
public PolygonMode PolygonModeBack;
|
||||
public Boolean32 PolygonSmoothEnable;
|
||||
public fixed uint ReservedDB8[2];
|
||||
public DepthBiasState DepthBiasState;
|
||||
public fixed uint ReservedDCC[5];
|
||||
public int PatchVertices;
|
||||
public fixed uint ReservedDD0[4];
|
||||
public uint TextureBarrier;
|
||||
public fixed uint ReservedDE4[7];
|
||||
public Array16<ScissorState> ScissorState;
|
||||
|
|
|
@ -349,6 +349,26 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
|
|||
return flags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Packs the tessellation parameters from the gpu accessor.
|
||||
/// </summary>
|
||||
/// <param name="gpuAccessor">The gpu accessor</param>
|
||||
/// <returns>The packed tessellation parameters</returns>
|
||||
private static byte GetTessellationModePacked(IGpuAccessor gpuAccessor)
|
||||
{
|
||||
byte value;
|
||||
|
||||
value = (byte)((int)gpuAccessor.QueryTessPatchType() & 3);
|
||||
value |= (byte)(((int)gpuAccessor.QueryTessSpacing() & 3) << 2);
|
||||
|
||||
if (gpuAccessor.QueryTessCw())
|
||||
{
|
||||
value |= 0x10;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new instance of <see cref="GuestGpuAccessorHeader"/> from an gpu accessor.
|
||||
/// </summary>
|
||||
|
@ -364,6 +384,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
|
|||
ComputeLocalMemorySize = gpuAccessor.QueryComputeLocalMemorySize(),
|
||||
ComputeSharedMemorySize = gpuAccessor.QueryComputeSharedMemorySize(),
|
||||
PrimitiveTopology = gpuAccessor.QueryPrimitiveTopology(),
|
||||
TessellationModePacked = GetTessellationModePacked(gpuAccessor),
|
||||
StateFlags = GetGpuStateFlags(gpuAccessor)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -49,10 +49,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
|
|||
/// </summary>
|
||||
public InputTopology PrimitiveTopology;
|
||||
|
||||
/// <summary>
|
||||
/// Tessellation parameters (packed to fit on a byte).
|
||||
/// </summary>
|
||||
public byte TessellationModePacked;
|
||||
|
||||
/// <summary>
|
||||
/// Unused/reserved.
|
||||
/// </summary>
|
||||
public ushort Reserved2;
|
||||
public byte Reserved2;
|
||||
|
||||
/// <summary>
|
||||
/// GPU boolean state that can influence shader compilation.
|
||||
|
|
|
@ -134,6 +134,33 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
return _header.PrimitiveTopology;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader primitive winding order.
|
||||
/// </summary>
|
||||
/// <returns>True if the primitive winding order is clockwise, false if counter-clockwise</returns>
|
||||
public bool QueryTessCw()
|
||||
{
|
||||
return (_header.TessellationModePacked & 0x10) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader abstract patch type.
|
||||
/// </summary>
|
||||
/// <returns>Abstract patch type</returns>
|
||||
public TessPatchType QueryTessPatchType()
|
||||
{
|
||||
return (TessPatchType)(_header.TessellationModePacked & 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader spacing between tessellated vertices of the patch.
|
||||
/// </summary>
|
||||
/// <returns>Spacing between tessellated vertices of the patch</returns>
|
||||
public TessSpacing QueryTessSpacing()
|
||||
{
|
||||
return (TessSpacing)((_header.TessellationModePacked >> 2) & 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture descriptor for a given texture on the pool.
|
||||
/// </summary>
|
||||
|
|
|
@ -168,10 +168,31 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
PrimitiveTopology.TriangleFan => InputTopology.Triangles,
|
||||
PrimitiveTopology.TrianglesAdjacency or
|
||||
PrimitiveTopology.TriangleStripAdjacency => InputTopology.TrianglesAdjacency,
|
||||
_ => InputTopology.Points,
|
||||
PrimitiveTopology.Patches => _state.TessellationMode.UnpackPatchType() == TessPatchType.Isolines
|
||||
? InputTopology.Lines
|
||||
: InputTopology.Triangles,
|
||||
_ => InputTopology.Points
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader primitive winding order.
|
||||
/// </summary>
|
||||
/// <returns>True if the primitive winding order is clockwise, false if counter-clockwise</returns>
|
||||
public bool QueryTessCw() => _state.TessellationMode.UnpackCw();
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader abstract patch type.
|
||||
/// </summary>
|
||||
/// <returns>Abstract patch type</returns>
|
||||
public TessPatchType QueryTessPatchType() => _state.TessellationMode.UnpackPatchType();
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader spacing between tessellated vertices of the patch.
|
||||
/// </summary>
|
||||
/// <returns>Spacing between tessellated vertices of the patch</returns>
|
||||
public TessSpacing QueryTessSpacing() => _state.TessellationMode.UnpackSpacing();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture descriptor for a given texture on the pool.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
|
@ -32,6 +33,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// </summary>
|
||||
public PrimitiveTopology Topology { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Tessellation mode.
|
||||
/// </summary>
|
||||
public TessMode TessellationMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the GPU accessor state.
|
||||
/// </summary>
|
||||
|
@ -40,18 +46,21 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="textureBufferIndex">Constant buffer slot where the texture handles are located</param>
|
||||
/// <param name="earlyZForce">Early Z force enable</param>
|
||||
/// <param name="topology">Primitive topology</param>
|
||||
/// <param name="tessellationMode">Tessellation mode</param>
|
||||
public GpuAccessorState(
|
||||
ulong texturePoolGpuVa,
|
||||
int texturePoolMaximumId,
|
||||
int textureBufferIndex,
|
||||
bool earlyZForce,
|
||||
PrimitiveTopology topology)
|
||||
PrimitiveTopology topology,
|
||||
TessMode tessellationMode)
|
||||
{
|
||||
TexturePoolGpuVa = texturePoolGpuVa;
|
||||
TexturePoolMaximumId = texturePoolMaximumId;
|
||||
TextureBufferIndex = textureBufferIndex;
|
||||
EarlyZForce = earlyZForce;
|
||||
Topology = topology;
|
||||
TessellationMode = tessellationMode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <summary>
|
||||
/// Version of the codegen (to be changed when codegen or guest format change).
|
||||
/// </summary>
|
||||
private const ulong ShaderCodeGenVersion = 2702;
|
||||
private const ulong ShaderCodeGenVersion = 2534;
|
||||
|
||||
// Progress reporting helpers
|
||||
private volatile int _shaderCount;
|
||||
|
|
|
@ -290,6 +290,23 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
return TextureMinFilter.Nearest;
|
||||
}
|
||||
|
||||
public static OpenTK.Graphics.OpenGL.PolygonMode Convert(this GAL.PolygonMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case GAL.PolygonMode.Point:
|
||||
return OpenTK.Graphics.OpenGL.PolygonMode.Point;
|
||||
case GAL.PolygonMode.Line:
|
||||
return OpenTK.Graphics.OpenGL.PolygonMode.Line;
|
||||
case GAL.PolygonMode.Fill:
|
||||
return OpenTK.Graphics.OpenGL.PolygonMode.Fill;
|
||||
}
|
||||
|
||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid {nameof(GAL.PolygonMode)} enum value: {mode}.");
|
||||
|
||||
return OpenTK.Graphics.OpenGL.PolygonMode.Fill;
|
||||
}
|
||||
|
||||
public static PrimitiveType Convert(this PrimitiveTopology topology)
|
||||
{
|
||||
switch (topology)
|
||||
|
|
|
@ -830,6 +830,21 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
GL.LineWidth(width);
|
||||
}
|
||||
|
||||
public unsafe void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
|
||||
{
|
||||
GL.PatchParameter(PatchParameterInt.PatchVertices, vertices);
|
||||
|
||||
fixed (float* pOuterLevel = defaultOuterLevel)
|
||||
{
|
||||
GL.PatchParameter(PatchParameterFloat.PatchDefaultOuterLevel, pOuterLevel);
|
||||
}
|
||||
|
||||
fixed (float* pInnerLevel = defaultInnerLevel)
|
||||
{
|
||||
GL.PatchParameter(PatchParameterFloat.PatchDefaultInnerLevel, pInnerLevel);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin)
|
||||
{
|
||||
// GL_POINT_SPRITE was deprecated in core profile 3.2+ and causes GL_INVALID_ENUM when set.
|
||||
|
@ -861,6 +876,19 @@ namespace Ryujinx.Graphics.OpenGL
|
|||
GL.PointSize(Math.Max(float.Epsilon, size));
|
||||
}
|
||||
|
||||
public void SetPolygonMode(GAL.PolygonMode frontMode, GAL.PolygonMode backMode)
|
||||
{
|
||||
if (frontMode == backMode)
|
||||
{
|
||||
GL.PolygonMode(MaterialFace.FrontAndBack, frontMode.Convert());
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.PolygonMode(MaterialFace.Front, frontMode.Convert());
|
||||
GL.PolygonMode(MaterialFace.Back, backMode.Convert());
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPrimitiveRestart(bool enable, int index)
|
||||
{
|
||||
if (!enable)
|
||||
|
|
|
@ -136,6 +136,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
context.AppendLine();
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.TessellationControl)
|
||||
{
|
||||
int threadsPerInputPrimitive = context.Config.ThreadsPerInputPrimitive;
|
||||
|
||||
context.AppendLine($"layout (vertices = {threadsPerInputPrimitive}) out;");
|
||||
context.AppendLine();
|
||||
}
|
||||
else if (context.Config.Stage == ShaderStage.TessellationEvaluation)
|
||||
{
|
||||
string patchType = context.Config.GpuAccessor.QueryTessPatchType().ToGlsl();
|
||||
string spacing = context.Config.GpuAccessor.QueryTessSpacing().ToGlsl();
|
||||
string windingOrder = context.Config.GpuAccessor.QueryTessCw() ? "cw" : "ccw";
|
||||
|
||||
context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;");
|
||||
context.AppendLine();
|
||||
}
|
||||
|
||||
if (context.Config.UsedInputAttributes != 0 || context.Config.GpPassthrough)
|
||||
{
|
||||
|
@ -150,6 +166,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
context.AppendLine();
|
||||
}
|
||||
|
||||
if (context.Config.UsedInputAttributesPerPatch != 0)
|
||||
{
|
||||
DeclareInputAttributesPerPatch(context, context.Config.UsedInputAttributesPerPatch);
|
||||
|
||||
context.AppendLine();
|
||||
}
|
||||
|
||||
if (context.Config.UsedOutputAttributesPerPatch != 0)
|
||||
{
|
||||
DeclareUsedOutputAttributesPerPatch(context, context.Config.UsedOutputAttributesPerPatch);
|
||||
|
||||
context.AppendLine();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -424,17 +454,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
while (usedAttributes != 0)
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||
|
||||
DeclareInputAttribute(context, info, index);
|
||||
|
||||
usedAttributes &= ~(1 << index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareInputAttributesPerPatch(CodeGenContext context, int usedAttributes)
|
||||
{
|
||||
while (usedAttributes != 0)
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||
DeclareInputAttributePerPatch(context, index);
|
||||
usedAttributes &= ~(1 << index);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareInputAttribute(CodeGenContext context, StructuredProgramInfo info, int attr)
|
||||
{
|
||||
string suffix = context.Config.Stage == ShaderStage.Geometry ? "[]" : string.Empty;
|
||||
string suffix = OperandManager.IsArrayAttribute(context.Config.Stage, isOutAttr: false) ? "[]" : string.Empty;
|
||||
string iq = string.Empty;
|
||||
|
||||
if (context.Config.Stage == ShaderStage.Fragment)
|
||||
|
@ -465,6 +503,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
}
|
||||
}
|
||||
|
||||
private static void DeclareInputAttributePerPatch(CodeGenContext context, int attr)
|
||||
{
|
||||
string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}";
|
||||
|
||||
context.AppendLine($"patch in vec4 {name};");
|
||||
}
|
||||
|
||||
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
||||
{
|
||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing))
|
||||
|
@ -477,9 +522,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
while (usedAttributes != 0)
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||
|
||||
DeclareOutputAttribute(context, index);
|
||||
|
||||
usedAttributes &= ~(1 << index);
|
||||
}
|
||||
}
|
||||
|
@ -487,7 +530,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
private static void DeclareOutputAttribute(CodeGenContext context, int attr)
|
||||
{
|
||||
string name = $"{DefaultNames.OAttributePrefix}{attr}";
|
||||
string suffix = OperandManager.IsArrayAttribute(context.Config.Stage, isOutAttr: true) ? "[]" : string.Empty;
|
||||
string name = $"{DefaultNames.OAttributePrefix}{attr}{suffix}";
|
||||
|
||||
if ((context.Config.Options.Flags & TranslationFlags.Feedback) != 0)
|
||||
{
|
||||
|
@ -504,6 +548,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
}
|
||||
}
|
||||
|
||||
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, int usedAttributes)
|
||||
{
|
||||
while (usedAttributes != 0)
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||
DeclareOutputAttributePerPatch(context, index);
|
||||
usedAttributes &= ~(1 << index);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareOutputAttributePerPatch(CodeGenContext context, int attr)
|
||||
{
|
||||
string name = $"{DefaultNames.PerPatchAttributePrefix}{attr}";
|
||||
|
||||
context.AppendLine($"patch out vec4 {name};");
|
||||
}
|
||||
|
||||
private static void DeclareSupportUniformBlock(CodeGenContext context, bool isFragment, int scaleElements)
|
||||
{
|
||||
if (!isFragment && scaleElements == 0)
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
public const string SamplerNamePrefix = "tex";
|
||||
public const string ImageNamePrefix = "img";
|
||||
|
||||
public const string PerPatchAttributePrefix = "patch_attr_";
|
||||
public const string IAttributePrefix = "in_attr";
|
||||
public const string OAttributePrefix = "out_attr";
|
||||
|
||||
|
|
|
@ -126,9 +126,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
string dest;
|
||||
|
||||
if (assignment.Destination is AstOperand operand && operand.Type == OperandType.Attribute)
|
||||
if (assignment.Destination is AstOperand operand && operand.Type.IsAttribute())
|
||||
{
|
||||
dest = OperandManager.GetOutAttributeName(operand.Value, context.Config);
|
||||
bool perPatch = operand.Type == OperandType.AttributePerPatch;
|
||||
dest = OperandManager.GetOutAttributeName(operand.Value, context.Config, perPatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -200,7 +200,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
|
||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||
{
|
||||
return OperandManager.GetAttributeName(baseAttr.Value + (operand.Value << 2), context.Config, isOutAttr: false, indexExpr);
|
||||
int attrOffset = baseAttr.Value + (operand.Value << 2);
|
||||
return OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: false, indexExpr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -326,7 +327,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||
|
||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
||||
{
|
||||
attrName = OperandManager.GetAttributeName(baseAttr.Value + (operand.Value << 2), context.Config, isOutAttr: true);
|
||||
int attrOffset = baseAttr.Value + (operand.Value << 2);
|
||||
attrName = OperandManager.GetAttributeName(attrOffset, context.Config, perPatch: false, isOutAttr: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -29,27 +29,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
private static Dictionary<int, BuiltInAttribute> _builtInAttributes =
|
||||
new Dictionary<int, BuiltInAttribute>()
|
||||
{
|
||||
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) },
|
||||
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
|
||||
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
|
||||
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) },
|
||||
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) },
|
||||
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) },
|
||||
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) },
|
||||
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) },
|
||||
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) },
|
||||
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) },
|
||||
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) },
|
||||
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) },
|
||||
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) },
|
||||
{ AttributeConsts.TessLevelOuter0, new BuiltInAttribute("gl_TessLevelOuter[0]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelOuter1, new BuiltInAttribute("gl_TessLevelOuter[1]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelOuter2, new BuiltInAttribute("gl_TessLevelOuter[2]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelOuter3, new BuiltInAttribute("gl_TessLevelOuter[3]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelInner0, new BuiltInAttribute("gl_TessLevelInner[0]", VariableType.F32) },
|
||||
{ AttributeConsts.TessLevelInner1, new BuiltInAttribute("gl_TessLevelInner[1]", VariableType.F32) },
|
||||
{ AttributeConsts.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) },
|
||||
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
|
||||
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
|
||||
{ AttributeConsts.PositionY, new BuiltInAttribute("gl_Position.y", VariableType.F32) },
|
||||
{ AttributeConsts.PositionZ, new BuiltInAttribute("gl_Position.z", VariableType.F32) },
|
||||
{ AttributeConsts.PositionW, new BuiltInAttribute("gl_Position.w", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance0, new BuiltInAttribute("gl_ClipDistance[0]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance1, new BuiltInAttribute("gl_ClipDistance[1]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance2, new BuiltInAttribute("gl_ClipDistance[2]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance3, new BuiltInAttribute("gl_ClipDistance[3]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance4, new BuiltInAttribute("gl_ClipDistance[4]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance5, new BuiltInAttribute("gl_ClipDistance[5]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance6, new BuiltInAttribute("gl_ClipDistance[6]", VariableType.F32) },
|
||||
{ AttributeConsts.ClipDistance7, new BuiltInAttribute("gl_ClipDistance[7]", VariableType.F32) },
|
||||
{ AttributeConsts.PointCoordX, new BuiltInAttribute("gl_PointCoord.x", VariableType.F32) },
|
||||
{ AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) },
|
||||
{ AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) },
|
||||
{ AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) },
|
||||
{ AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) },
|
||||
{ AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) },
|
||||
{ AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) },
|
||||
|
||||
// Special.
|
||||
{ AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) },
|
||||
|
@ -61,6 +67,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{ AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) },
|
||||
{ AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) },
|
||||
{ AttributeConsts.LaneId, new BuiltInAttribute(null, VariableType.U32) },
|
||||
{ AttributeConsts.InvocationId, new BuiltInAttribute("gl_InvocationID", VariableType.S32) },
|
||||
{ AttributeConsts.PrimitiveId, new BuiltInAttribute("gl_PrimitiveID", VariableType.S32) },
|
||||
{ AttributeConsts.PatchVerticesIn, new BuiltInAttribute("gl_PatchVerticesIn", VariableType.S32) },
|
||||
{ AttributeConsts.EqMask, new BuiltInAttribute(null, VariableType.U32) },
|
||||
{ AttributeConsts.GeMask, new BuiltInAttribute(null, VariableType.U32) },
|
||||
{ AttributeConsts.GtMask, new BuiltInAttribute(null, VariableType.U32) },
|
||||
|
@ -99,19 +108,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
return operand.Type switch
|
||||
{
|
||||
OperandType.Argument => GetArgumentName(operand.Value),
|
||||
OperandType.Attribute => GetAttributeName(operand.Value, config),
|
||||
OperandType.Attribute => GetAttributeName(operand.Value, config, perPatch: false),
|
||||
OperandType.AttributePerPatch => GetAttributeName(operand.Value, config, perPatch: true),
|
||||
OperandType.Constant => NumberFormatter.FormatInt(operand.Value),
|
||||
OperandType.ConstantBuffer => GetConstantBufferName(
|
||||
operand.CbufSlot,
|
||||
operand.CbufOffset,
|
||||
config.Stage,
|
||||
config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)),
|
||||
OperandType.ConstantBuffer => GetConstantBufferName(operand, config),
|
||||
OperandType.LocalVariable => _locals[operand],
|
||||
OperandType.Undefined => DefaultNames.UndefinedName,
|
||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetConstantBufferName(AstOperand operand, ShaderConfig config)
|
||||
{
|
||||
return GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing));
|
||||
}
|
||||
|
||||
public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable)
|
||||
{
|
||||
return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}";
|
||||
|
@ -142,14 +153,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
return GetVec4Indexed(GetUbName(stage, slotExpr) + $"[{offsetExpr} >> 2]", offsetExpr + " & 3", indexElement);
|
||||
}
|
||||
|
||||
public static string GetOutAttributeName(int value, ShaderConfig config)
|
||||
public static string GetOutAttributeName(int value, ShaderConfig config, bool perPatch)
|
||||
{
|
||||
return GetAttributeName(value, config, isOutAttr: true);
|
||||
return GetAttributeName(value, config, perPatch, isOutAttr: true);
|
||||
}
|
||||
|
||||
public static string GetAttributeName(int value, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0")
|
||||
public static string GetAttributeName(int value, ShaderConfig config, bool perPatch, bool isOutAttr = false, string indexExpr = "0")
|
||||
{
|
||||
value &= ~3;
|
||||
if ((value & AttributeConsts.LoadOutputMask) != 0)
|
||||
{
|
||||
isOutAttr = true;
|
||||
}
|
||||
|
||||
value &= AttributeConsts.Mask & ~3;
|
||||
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
||||
|
||||
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
||||
|
@ -160,7 +176,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
? DefaultNames.OAttributePrefix
|
||||
: DefaultNames.IAttributePrefix;
|
||||
|
||||
if (config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing))
|
||||
bool indexable = config.UsedFeatures.HasFlag(isOutAttr ? FeatureFlags.OaIndexing : FeatureFlags.IaIndexing);
|
||||
|
||||
if (!indexable && perPatch)
|
||||
{
|
||||
prefix = DefaultNames.PerPatchAttributePrefix;
|
||||
}
|
||||
|
||||
if (indexable)
|
||||
{
|
||||
string name = prefix;
|
||||
|
||||
|
@ -175,9 +198,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
string name = $"{prefix}{(value >> 4)}_{swzMask}";
|
||||
|
||||
if (config.Stage == ShaderStage.Geometry && !isOutAttr)
|
||||
if (!perPatch && IsArrayAttribute(config.Stage, isOutAttr))
|
||||
{
|
||||
name += $"[{indexExpr}]";
|
||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
||||
}
|
||||
|
||||
return name;
|
||||
|
@ -186,9 +209,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
{
|
||||
string name = $"{prefix}{(value >> 4)}";
|
||||
|
||||
if (config.Stage == ShaderStage.Geometry && !isOutAttr)
|
||||
if (!perPatch && IsArrayAttribute(config.Stage, isOutAttr))
|
||||
{
|
||||
name += $"[{indexExpr}]";
|
||||
name += isOutAttr ? "[gl_InvocationID]" : $"[{indexExpr}]";
|
||||
}
|
||||
|
||||
return name + '.' + swzMask;
|
||||
|
@ -250,9 +273,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
|
||||
string name = builtInAttr.Name;
|
||||
|
||||
if (config.Stage == ShaderStage.Geometry && (value & AttributeConsts.SpecialMask) == 0 && !isOutAttr)
|
||||
if (!perPatch && IsArrayAttribute(config.Stage, isOutAttr) && IsArrayBuiltIn(value))
|
||||
{
|
||||
name = $"gl_in[{indexExpr}].{name}";
|
||||
name = isOutAttr ? $"gl_out[gl_InvocationID].{name}" : $"gl_in[{indexExpr}].{name}";
|
||||
}
|
||||
|
||||
return name;
|
||||
|
@ -278,6 +301,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||
return $"{name}[{attrExpr} >> 2][{attrExpr} & 3]";
|
||||
}
|
||||
|
||||
public static bool IsArrayAttribute(ShaderStage stage, bool isOutAttr)
|
||||
{
|
||||
if (isOutAttr)
|
||||
{
|
||||
return stage == ShaderStage.TessellationControl;
|
||||
}
|
||||
else
|
||||
{
|
||||
return stage == ShaderStage.TessellationControl ||
|
||||
stage == ShaderStage.TessellationEvaluation ||
|
||||
stage == ShaderStage.Geometry;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsArrayBuiltIn(int attr)
|
||||
{
|
||||
if (attr <= AttributeConsts.TessLevelInner1 ||
|
||||
attr == AttributeConsts.TessCoordX ||
|
||||
attr == AttributeConsts.TessCoordY)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (attr & AttributeConsts.SpecialMask) == 0;
|
||||
}
|
||||
|
||||
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
|
||||
{
|
||||
if (cbIndexable)
|
||||
|
|
|
@ -262,6 +262,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
int count = 1;
|
||||
bool isStore = false;
|
||||
bool indexed = false;
|
||||
bool perPatch = false;
|
||||
|
||||
if (name == InstName.Ast)
|
||||
{
|
||||
|
@ -269,14 +270,17 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
count = (int)opAst.AlSize + 1;
|
||||
offset = opAst.Imm11;
|
||||
indexed = opAst.Phys;
|
||||
perPatch = opAst.P;
|
||||
isStore = true;
|
||||
}
|
||||
else if (name == InstName.Ald)
|
||||
{
|
||||
InstAld opAld = new InstAld(opCode);
|
||||
count = (int)opAld.AlSize + 1;
|
||||
indexed = opAld.Phys;
|
||||
offset = opAld.Imm11;
|
||||
indexed = opAld.Phys;
|
||||
perPatch = opAld.P;
|
||||
isStore = opAld.O;
|
||||
}
|
||||
else /* if (name == InstName.Ipa) */
|
||||
{
|
||||
|
@ -307,11 +311,11 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
|
||||
if (isStore)
|
||||
{
|
||||
config.SetOutputUserAttribute(index);
|
||||
config.SetOutputUserAttribute(index, perPatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
config.SetInputUserAttribute(index);
|
||||
config.SetInputUserAttribute(index, perPatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5175,8 +5175,8 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
public int SrcB => (int)((_opcode >> 20) & 0xFF);
|
||||
public int SrcC => (int)((_opcode >> 39) & 0xFF);
|
||||
public int Pred => (int)((_opcode >> 16) & 0x7);
|
||||
public int Imm16 => (int)((_opcode >> 20) & 0xFFFF);
|
||||
public bool PredInv => (_opcode & 0x80000) != 0;
|
||||
public int Imm16 => (int)((_opcode >> 20) & 0xFFFF);
|
||||
public bool WriteCC => (_opcode & 0x800000000000) != 0;
|
||||
public bool DFormat => (_opcode & 0x40000000000000) != 0;
|
||||
public VectorSelect ASelect => (VectorSelect)((int)((_opcode >> 45) & 0x8) | (int)((_opcode >> 36) & 0x7));
|
||||
|
@ -5236,6 +5236,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
public int SrcB => (int)((_opcode >> 20) & 0xFF);
|
||||
public int Pred => (int)((_opcode >> 16) & 0x7);
|
||||
public bool PredInv => (_opcode & 0x80000) != 0;
|
||||
public int Imm16 => (int)((_opcode >> 20) & 0xFFFF);
|
||||
public VectorSelect ASelect => (VectorSelect)((int)((_opcode >> 45) & 0x8) | (int)((_opcode >> 36) & 0x7));
|
||||
public VectorSelect BSelect => (VectorSelect)((int)((_opcode >> 46) & 0x8) | (int)((_opcode >> 28) & 0x7));
|
||||
public IComp VComp => (IComp)((int)((_opcode >> 45) & 0x4) | (int)((_opcode >> 43) & 0x3));
|
||||
|
|
|
@ -96,6 +96,21 @@ namespace Ryujinx.Graphics.Shader
|
|||
return InputTopology.Points;
|
||||
}
|
||||
|
||||
bool QueryTessCw()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
TessPatchType QueryTessPatchType()
|
||||
{
|
||||
return TessPatchType.Triangles;
|
||||
}
|
||||
|
||||
TessSpacing QueryTessSpacing()
|
||||
{
|
||||
return TessSpacing.EqualSpacing;
|
||||
}
|
||||
|
||||
TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1)
|
||||
{
|
||||
return TextureFormat.R8G8B8A8Unorm;
|
||||
|
|
|
@ -474,13 +474,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
context.Config.GpuAccessor.Log("Shader instruction Vset is not implemented.");
|
||||
}
|
||||
|
||||
public static void Vsetp(EmitterContext context)
|
||||
{
|
||||
InstVsetp op = context.GetOp<InstVsetp>();
|
||||
|
||||
context.Config.GpuAccessor.Log("Shader instruction Vsetp is not implemented.");
|
||||
}
|
||||
|
||||
public static void Vshl(EmitterContext context)
|
||||
{
|
||||
InstVshl op = context.GetOp<InstVshl>();
|
||||
|
|
|
@ -40,19 +40,33 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
|
||||
context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
|
||||
}
|
||||
else if (op.SrcB == RegisterConsts.RegisterZeroIndex)
|
||||
else if (op.SrcB == RegisterConsts.RegisterZeroIndex || op.P)
|
||||
{
|
||||
Operand src = Attribute(op.Imm11 + index * 4);
|
||||
int offset = op.Imm11 + index * 4;
|
||||
|
||||
context.FlagAttributeRead(src.Value);
|
||||
context.FlagAttributeRead(offset);
|
||||
|
||||
if (op.O)
|
||||
{
|
||||
offset |= AttributeConsts.LoadOutputMask;
|
||||
}
|
||||
|
||||
Operand src = op.P ? AttributePerPatch(offset) : Attribute(offset);
|
||||
|
||||
context.Copy(Register(rd), src);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand src = Const(op.Imm11 + index * 4);
|
||||
int offset = op.Imm11 + index * 4;
|
||||
|
||||
context.FlagAttributeRead(src.Value);
|
||||
context.FlagAttributeRead(offset);
|
||||
|
||||
if (op.O)
|
||||
{
|
||||
offset |= AttributeConsts.LoadOutputMask;
|
||||
}
|
||||
|
||||
Operand src = Const(offset);
|
||||
|
||||
context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex));
|
||||
}
|
||||
|
@ -83,9 +97,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
}
|
||||
else
|
||||
{
|
||||
Operand dest = Attribute(op.Imm11 + index * 4);
|
||||
// TODO: Support indirect stores using Ra.
|
||||
|
||||
context.FlagAttributeWritten(dest.Value);
|
||||
int offset = op.Imm11 + index * 4;
|
||||
|
||||
context.FlagAttributeWritten(offset);
|
||||
|
||||
Operand dest = op.P ? AttributePerPatch(offset) : Attribute(offset);
|
||||
|
||||
context.Copy(dest, Register(rd));
|
||||
}
|
||||
|
|
|
@ -79,6 +79,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
src = Attribute(AttributeConsts.LaneId);
|
||||
break;
|
||||
|
||||
case SReg.InvocationId:
|
||||
src = Attribute(AttributeConsts.InvocationId);
|
||||
break;
|
||||
|
||||
case SReg.YDirection:
|
||||
src = ConstF(1); // TODO: Use value from Y direction GPU register.
|
||||
break;
|
||||
|
@ -87,6 +91,22 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
src = context.Config.Stage == ShaderStage.Fragment ? Attribute(AttributeConsts.ThreadKill) : Const(0);
|
||||
break;
|
||||
|
||||
case SReg.InvocationInfo:
|
||||
if (context.Config.Stage != ShaderStage.Compute && context.Config.Stage != ShaderStage.Fragment)
|
||||
{
|
||||
Operand primitiveId = Attribute(AttributeConsts.PrimitiveId);
|
||||
Operand patchVerticesIn = Attribute(AttributeConsts.PatchVerticesIn);
|
||||
|
||||
patchVerticesIn = context.ShiftLeft(patchVerticesIn, Const(16));
|
||||
|
||||
src = context.BitwiseOr(primitiveId, patchVerticesIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
src = Const(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case SReg.TId:
|
||||
Operand tidX = Attribute(AttributeConsts.ThreadIdX);
|
||||
Operand tidY = Attribute(AttributeConsts.ThreadIdY);
|
||||
|
|
|
@ -120,6 +120,68 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||
context.Copy(GetDest(op.Dest), res);
|
||||
}
|
||||
|
||||
public static void Vsetp(EmitterContext context)
|
||||
{
|
||||
InstVsetp op = context.GetOp<InstVsetp>();
|
||||
|
||||
Operand srcA = Extend(context, GetSrcReg(context, op.SrcA), op.ASelect);
|
||||
|
||||
Operand srcB;
|
||||
|
||||
if (op.BVideo)
|
||||
{
|
||||
srcB = Extend(context, GetSrcReg(context, op.SrcB), op.BSelect);
|
||||
}
|
||||
else
|
||||
{
|
||||
int imm = op.Imm16;
|
||||
|
||||
if ((op.BSelect & VectorSelect.S8B0) != 0)
|
||||
{
|
||||
imm = (imm << 16) >> 16;
|
||||
}
|
||||
|
||||
srcB = Const(imm);
|
||||
}
|
||||
|
||||
Operand p0Res;
|
||||
|
||||
bool signedA = (op.ASelect & VectorSelect.S8B0) != 0;
|
||||
bool signedB = (op.BSelect & VectorSelect.S8B0) != 0;
|
||||
|
||||
if (signedA != signedB)
|
||||
{
|
||||
bool a32 = (op.ASelect & ~VectorSelect.S8B0) == VectorSelect.U32;
|
||||
bool b32 = (op.BSelect & ~VectorSelect.S8B0) == VectorSelect.U32;
|
||||
|
||||
if (!a32 && !b32)
|
||||
{
|
||||
// Both values are extended small integer and can always fit in a S32, just do a signed comparison.
|
||||
p0Res = GetIntComparison(context, op.VComp, srcA, srcB, isSigned: true, extended: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Mismatching sign case.
|
||||
p0Res = Const(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sign matches, just do a regular comparison.
|
||||
p0Res = GetIntComparison(context, op.VComp, srcA, srcB, signedA, extended: false);
|
||||
}
|
||||
|
||||
Operand p1Res = context.BitwiseNot(p0Res);
|
||||
|
||||
Operand pred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
|
||||
|
||||
p0Res = InstEmitAluHelper.GetPredLogicalOp(context, op.BoolOp, p0Res, pred);
|
||||
p1Res = InstEmitAluHelper.GetPredLogicalOp(context, op.BoolOp, p1Res, pred);
|
||||
|
||||
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
|
||||
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
|
||||
}
|
||||
|
||||
private static Operand Extend(EmitterContext context, Operand src, VectorSelect type)
|
||||
{
|
||||
return type switch
|
||||
|
|
|
@ -161,5 +161,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsTextureQuery(this Instruction inst)
|
||||
{
|
||||
inst &= Instruction.Mask;
|
||||
return inst == Instruction.Lod || inst == Instruction.TextureSize;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
return new Operand(OperandType.Attribute, value);
|
||||
}
|
||||
|
||||
public static Operand AttributePerPatch(int value)
|
||||
{
|
||||
return new Operand(OperandType.AttributePerPatch, value);
|
||||
}
|
||||
|
||||
public static Operand Cbuf(int slot, int offset)
|
||||
{
|
||||
return new Operand(slot, offset);
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
{
|
||||
Argument,
|
||||
Attribute,
|
||||
AttributePerPatch,
|
||||
Constant,
|
||||
ConstantBuffer,
|
||||
Label,
|
||||
|
@ -11,4 +12,12 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||
Register,
|
||||
Undefined
|
||||
}
|
||||
|
||||
static class OperandTypeExtensions
|
||||
{
|
||||
public static bool IsAttribute(this OperandType type)
|
||||
{
|
||||
return type == OperandType.Attribute || type == OperandType.AttributePerPatch;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,15 +19,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
public static VariableType GetVarType(OperandType type)
|
||||
{
|
||||
switch (type)
|
||||
return type switch
|
||||
{
|
||||
case OperandType.Attribute: return VariableType.F32;
|
||||
case OperandType.Constant: return VariableType.S32;
|
||||
case OperandType.ConstantBuffer: return VariableType.F32;
|
||||
case OperandType.Undefined: return VariableType.S32;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid operand type \"{type}\".");
|
||||
OperandType.Attribute => VariableType.F32,
|
||||
OperandType.AttributePerPatch => VariableType.F32,
|
||||
OperandType.Constant => VariableType.S32,
|
||||
OperandType.ConstantBuffer => VariableType.F32,
|
||||
OperandType.Undefined => VariableType.S32,
|
||||
_ => throw new ArgumentException($"Invalid operand type \"{type}\".")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -282,6 +282,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
|
||||
public AstOperand GetOperandUse(Operand operand)
|
||||
{
|
||||
// If this flag is set, we're reading from an output attribute instead.
|
||||
if (operand.Type.IsAttribute() && (operand.Value & AttributeConsts.LoadOutputMask) != 0)
|
||||
{
|
||||
return GetOperandDef(operand);
|
||||
}
|
||||
|
||||
return GetOperand(operand);
|
||||
}
|
||||
|
||||
|
|
22
Ryujinx.Graphics.Shader/TessPatchType.cs
Normal file
22
Ryujinx.Graphics.Shader/TessPatchType.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
public enum TessPatchType
|
||||
{
|
||||
Isolines = 0,
|
||||
Triangles = 1,
|
||||
Quads = 2
|
||||
}
|
||||
|
||||
static class TessPatchTypeExtensions
|
||||
{
|
||||
public static string ToGlsl(this TessPatchType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
TessPatchType.Isolines => "isolines",
|
||||
TessPatchType.Quads => "quads",
|
||||
_ => "triangles"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
22
Ryujinx.Graphics.Shader/TessSpacing.cs
Normal file
22
Ryujinx.Graphics.Shader/TessSpacing.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
public enum TessSpacing
|
||||
{
|
||||
EqualSpacing = 0,
|
||||
FractionalEventSpacing = 1,
|
||||
FractionalOddSpacing = 2
|
||||
}
|
||||
|
||||
static class TessSpacingExtensions
|
||||
{
|
||||
public static string ToGlsl(this TessSpacing spacing)
|
||||
{
|
||||
return spacing switch
|
||||
{
|
||||
TessSpacing.FractionalEventSpacing => "fractional_even_spacing",
|
||||
TessSpacing.FractionalOddSpacing => "fractional_odd_spacing",
|
||||
_ => "equal_spacing"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,36 +2,45 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
static class AttributeConsts
|
||||
{
|
||||
public const int Layer = 0x064;
|
||||
public const int PointSize = 0x06c;
|
||||
public const int PositionX = 0x070;
|
||||
public const int PositionY = 0x074;
|
||||
public const int PositionZ = 0x078;
|
||||
public const int PositionW = 0x07c;
|
||||
public const int ClipDistance0 = 0x2c0;
|
||||
public const int ClipDistance1 = 0x2c4;
|
||||
public const int ClipDistance2 = 0x2c8;
|
||||
public const int ClipDistance3 = 0x2cc;
|
||||
public const int ClipDistance4 = 0x2d0;
|
||||
public const int ClipDistance5 = 0x2d4;
|
||||
public const int ClipDistance6 = 0x2d8;
|
||||
public const int ClipDistance7 = 0x2dc;
|
||||
public const int PointCoordX = 0x2e0;
|
||||
public const int PointCoordY = 0x2e4;
|
||||
public const int TessCoordX = 0x2f0;
|
||||
public const int TessCoordY = 0x2f4;
|
||||
public const int InstanceId = 0x2f8;
|
||||
public const int VertexId = 0x2fc;
|
||||
public const int FrontFacing = 0x3fc;
|
||||
public const int TessLevelOuter0 = 0x000;
|
||||
public const int TessLevelOuter1 = 0x004;
|
||||
public const int TessLevelOuter2 = 0x008;
|
||||
public const int TessLevelOuter3 = 0x00c;
|
||||
public const int TessLevelInner0 = 0x010;
|
||||
public const int TessLevelInner1 = 0x014;
|
||||
public const int Layer = 0x064;
|
||||
public const int PointSize = 0x06c;
|
||||
public const int PositionX = 0x070;
|
||||
public const int PositionY = 0x074;
|
||||
public const int PositionZ = 0x078;
|
||||
public const int PositionW = 0x07c;
|
||||
public const int ClipDistance0 = 0x2c0;
|
||||
public const int ClipDistance1 = 0x2c4;
|
||||
public const int ClipDistance2 = 0x2c8;
|
||||
public const int ClipDistance3 = 0x2cc;
|
||||
public const int ClipDistance4 = 0x2d0;
|
||||
public const int ClipDistance5 = 0x2d4;
|
||||
public const int ClipDistance6 = 0x2d8;
|
||||
public const int ClipDistance7 = 0x2dc;
|
||||
public const int PointCoordX = 0x2e0;
|
||||
public const int PointCoordY = 0x2e4;
|
||||
public const int TessCoordX = 0x2f0;
|
||||
public const int TessCoordY = 0x2f4;
|
||||
public const int InstanceId = 0x2f8;
|
||||
public const int VertexId = 0x2fc;
|
||||
public const int FrontFacing = 0x3fc;
|
||||
|
||||
public const int UserAttributesCount = 32;
|
||||
public const int UserAttributeBase = 0x80;
|
||||
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
|
||||
|
||||
public const int LoadOutputMask = 1 << 30;
|
||||
public const int Mask = 0x3fffffff;
|
||||
|
||||
|
||||
// Note: Those attributes are used internally by the translator
|
||||
// only, they don't exist on Maxwell.
|
||||
public const int SpecialMask = 0xff << 24;
|
||||
public const int SpecialMask = 0xf << 24;
|
||||
public const int FragmentOutputDepth = 0x1000000;
|
||||
public const int FragmentOutputColorBase = 0x1000010;
|
||||
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
||||
|
@ -49,12 +58,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public const int LaneId = 0x2000020;
|
||||
|
||||
public const int EqMask = 0x2000024;
|
||||
public const int GeMask = 0x2000028;
|
||||
public const int GtMask = 0x200002c;
|
||||
public const int LeMask = 0x2000030;
|
||||
public const int LtMask = 0x2000034;
|
||||
public const int InvocationId = 0x2000024;
|
||||
public const int PrimitiveId = 0x2000028;
|
||||
public const int PatchVerticesIn = 0x200002c;
|
||||
|
||||
public const int ThreadKill = 0x2000038;
|
||||
public const int EqMask = 0x2000030;
|
||||
public const int GeMask = 0x2000034;
|
||||
public const int GtMask = 0x2000038;
|
||||
public const int LeMask = 0x200003c;
|
||||
public const int LtMask = 0x2000040;
|
||||
|
||||
public const int ThreadKill = 0x2000044;
|
||||
}
|
||||
}
|
|
@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
if (target.Enabled)
|
||||
{
|
||||
Config.SetOutputUserAttribute(rtIndex);
|
||||
Config.SetOutputUserAttribute(rtIndex, perPatch: false);
|
||||
regIndexBase += 4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public bool GpPassthrough { get; }
|
||||
|
||||
public int ThreadsPerInputPrimitive { get; }
|
||||
|
||||
public OutputTopology OutputTopology { get; }
|
||||
|
||||
public int MaxOutputVertices { get; }
|
||||
|
@ -42,7 +44,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
private readonly TranslationCounts _counts;
|
||||
|
||||
public int UsedInputAttributes { get; private set; }
|
||||
public int UsedInputAttributesPerPatch { get; private set; }
|
||||
public int UsedOutputAttributes { get; private set; }
|
||||
public int UsedOutputAttributesPerPatch { get; private set; }
|
||||
public int PassthroughAttributes { get; private set; }
|
||||
|
||||
private int _usedConstantBuffers;
|
||||
|
@ -111,15 +115,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options, TranslationCounts counts) : this(gpuAccessor, options, counts)
|
||||
{
|
||||
Stage = header.Stage;
|
||||
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
|
||||
OutputTopology = header.OutputTopology;
|
||||
MaxOutputVertices = header.MaxOutputVertexCount;
|
||||
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
|
||||
ImapTypes = header.ImapTypes;
|
||||
OmapTargets = header.OmapTargets;
|
||||
OmapSampleMask = header.OmapSampleMask;
|
||||
OmapDepth = header.OmapDepth;
|
||||
Stage = header.Stage;
|
||||
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
|
||||
ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive;
|
||||
OutputTopology = header.OutputTopology;
|
||||
MaxOutputVertices = header.MaxOutputVertexCount;
|
||||
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
|
||||
ImapTypes = header.ImapTypes;
|
||||
OmapTargets = header.OmapTargets;
|
||||
OmapSampleMask = header.OmapSampleMask;
|
||||
OmapDepth = header.OmapDepth;
|
||||
}
|
||||
|
||||
public int GetDepthRegister()
|
||||
|
@ -169,7 +174,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public TextureFormat GetTextureFormatAtomic(int handle, int cbufSlot = -1)
|
||||
{
|
||||
// Atomic image instructions do not support GL_EXT_shader_image_load_formatted,
|
||||
// Atomic image instructions do not support GL_EXT_shader_image_load_formatted,
|
||||
// and must have a type specified. Default to R32Sint if not available.
|
||||
|
||||
var format = GpuAccessor.QueryTextureFormat(handle, cbufSlot);
|
||||
|
@ -219,17 +224,31 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
}
|
||||
|
||||
public void SetInputUserAttribute(int index)
|
||||
public void SetInputUserAttribute(int index, bool perPatch)
|
||||
{
|
||||
UsedInputAttributes |= 1 << index;
|
||||
if (perPatch)
|
||||
{
|
||||
UsedInputAttributesPerPatch |= 1 << index;
|
||||
}
|
||||
else
|
||||
{
|
||||
UsedInputAttributes |= 1 << index;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOutputUserAttribute(int index)
|
||||
public void SetOutputUserAttribute(int index, bool perPatch)
|
||||
{
|
||||
UsedOutputAttributes |= 1 << index;
|
||||
if (perPatch)
|
||||
{
|
||||
UsedOutputAttributesPerPatch |= 1 << index;
|
||||
}
|
||||
else
|
||||
{
|
||||
UsedOutputAttributes |= 1 << index;
|
||||
}
|
||||
}
|
||||
|
||||
public void MergeOutputUserAttributes(int mask)
|
||||
public void MergeOutputUserAttributes(int mask, int maskPerPatch)
|
||||
{
|
||||
if (GpPassthrough)
|
||||
{
|
||||
|
@ -238,6 +257,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
else
|
||||
{
|
||||
UsedOutputAttributes |= mask;
|
||||
UsedOutputAttributesPerPatch |= maskPerPatch;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -216,27 +216,38 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return;
|
||||
}
|
||||
|
||||
void InitializeOutput(int baseAttr)
|
||||
{
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
context.Copy(Attribute(baseAttr + c * 4), ConstF(c == 3 ? 1f : 0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
InitializeOutput(AttributeConsts.PositionX);
|
||||
InitializeOutput(context, AttributeConsts.PositionX, perPatch: false);
|
||||
}
|
||||
|
||||
int usedAttribtes = context.Config.UsedOutputAttributes;
|
||||
while (usedAttribtes != 0)
|
||||
int usedAttributes = context.Config.UsedOutputAttributes;
|
||||
while (usedAttributes != 0)
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(usedAttribtes);
|
||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||
|
||||
InitializeOutput(AttributeConsts.UserAttributeBase + index * 16);
|
||||
InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false);
|
||||
|
||||
usedAttribtes &= ~(1 << index);
|
||||
usedAttributes &= ~(1 << index);
|
||||
}
|
||||
|
||||
int usedAttributesPerPatch = context.Config.UsedOutputAttributesPerPatch;
|
||||
while (usedAttributesPerPatch != 0)
|
||||
{
|
||||
int index = BitOperations.TrailingZeroCount(usedAttributesPerPatch);
|
||||
|
||||
InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: true);
|
||||
|
||||
usedAttributesPerPatch &= ~(1 << index);
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitializeOutput(EmitterContext context, int baseAttr, bool perPatch)
|
||||
{
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
int attrOffset = baseAttr + c * 4;
|
||||
context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,10 +32,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
private static bool IsUserAttribute(Operand operand)
|
||||
{
|
||||
return operand != null &&
|
||||
operand.Type == OperandType.Attribute &&
|
||||
operand.Value >= AttributeConsts.UserAttributeBase &&
|
||||
operand.Value < AttributeConsts.UserAttributeEnd;
|
||||
if (operand != null && operand.Type.IsAttribute())
|
||||
{
|
||||
int value = operand.Value & AttributeConsts.Mask;
|
||||
return value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static FunctionCode[] Combine(FunctionCode[] a, FunctionCode[] b, int aStart)
|
||||
|
@ -133,14 +136,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
if (nextStage != null)
|
||||
{
|
||||
_config.MergeOutputUserAttributes(nextStage._config.UsedInputAttributes);
|
||||
_config.MergeOutputUserAttributes(
|
||||
nextStage._config.UsedInputAttributes,
|
||||
nextStage._config.UsedInputAttributesPerPatch);
|
||||
}
|
||||
|
||||
FunctionCode[] code = EmitShader(_cfg, _config, initializeOutputs: other == null, out _);
|
||||
|
||||
if (other != null)
|
||||
{
|
||||
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes);
|
||||
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, 0);
|
||||
|
||||
FunctionCode[] otherCode = EmitShader(other._cfg, other._config, initializeOutputs: true, out int aStart);
|
||||
|
||||
|
|
Loading…
Reference in a new issue