forked from Mirror/Ryujinx
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 SetImage(int binding, ITexture texture, Format imageFormat);
|
||||||
|
|
||||||
|
void SetLineParameters(float width, bool smooth);
|
||||||
|
|
||||||
void SetLogicOpState(bool enable, LogicalOp op);
|
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 SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin);
|
||||||
|
|
||||||
|
void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode);
|
||||||
|
|
||||||
void SetPrimitiveRestart(bool enable, int index);
|
void SetPrimitiveRestart(bool enable, int index);
|
||||||
|
|
||||||
void SetPrimitiveTopology(PrimitiveTopology topology);
|
void SetPrimitiveTopology(PrimitiveTopology topology);
|
||||||
|
|
|
@ -181,8 +181,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||||
SetLineParametersCommand.Run(ref GetCommand<SetLineParametersCommand>(memory), threaded, renderer);
|
SetLineParametersCommand.Run(ref GetCommand<SetLineParametersCommand>(memory), threaded, renderer);
|
||||||
_lookup[(int)CommandType.SetLogicOpState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
_lookup[(int)CommandType.SetLogicOpState] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||||
SetLogicOpStateCommand.Run(ref GetCommand<SetLogicOpStateCommand>(memory), threaded, 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) =>
|
_lookup[(int)CommandType.SetPointParameters] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||||
SetPointParametersCommand.Run(ref GetCommand<SetPointParametersCommand>(memory), threaded, 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) =>
|
_lookup[(int)CommandType.SetPrimitiveRestart] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||||
SetPrimitiveRestartCommand.Run(ref GetCommand<SetPrimitiveRestartCommand>(memory), threaded, renderer);
|
SetPrimitiveRestartCommand.Run(ref GetCommand<SetPrimitiveRestartCommand>(memory), threaded, renderer);
|
||||||
_lookup[(int)CommandType.SetPrimitiveTopology] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
_lookup[(int)CommandType.SetPrimitiveTopology] = (Span<byte> memory, ThreadedRenderer threaded, IRenderer renderer) =>
|
||||||
|
|
|
@ -72,7 +72,9 @@
|
||||||
SetIndexBuffer,
|
SetIndexBuffer,
|
||||||
SetLineParameters,
|
SetLineParameters,
|
||||||
SetLogicOpState,
|
SetLogicOpState,
|
||||||
|
SetPatchParameters,
|
||||||
SetPointParameters,
|
SetPointParameters,
|
||||||
|
SetPolygonMode,
|
||||||
SetPrimitiveRestart,
|
SetPrimitiveRestart,
|
||||||
SetPrimitiveTopology,
|
SetPrimitiveTopology,
|
||||||
SetProgram,
|
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();
|
_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)
|
public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin)
|
||||||
{
|
{
|
||||||
_renderer.New<SetPointParametersCommand>().Set(size, isProgramPointSize, enablePointSprite, origin);
|
_renderer.New<SetPointParametersCommand>().Set(size, isProgramPointSize, enablePointSprite, origin);
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode)
|
||||||
|
{
|
||||||
|
_renderer.New<SetPolygonModeCommand>().Set(frontMode, backMode);
|
||||||
|
_renderer.QueueCommand();
|
||||||
|
}
|
||||||
|
|
||||||
public void SetPrimitiveRestart(bool enable, int index)
|
public void SetPrimitiveRestart(bool enable, int index)
|
||||||
{
|
{
|
||||||
_renderer.New<SetPrimitiveRestartCommand>().Set(enable, 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.SetTexHeaderPoolCMaximumIndex,
|
||||||
_state.State.SetBindlessTextureConstantBufferSlotSelect,
|
_state.State.SetBindlessTextureConstantBufferSlotSelect,
|
||||||
false,
|
false,
|
||||||
PrimitiveTopology.Points);
|
PrimitiveTopology.Points,
|
||||||
|
default);
|
||||||
|
|
||||||
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
|
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
|
||||||
_channel,
|
_channel,
|
||||||
|
|
|
@ -72,6 +72,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
nameof(ThreedClassState.VertexBufferState),
|
nameof(ThreedClassState.VertexBufferState),
|
||||||
nameof(ThreedClassState.VertexBufferEndAddress)),
|
nameof(ThreedClassState.VertexBufferEndAddress)),
|
||||||
|
|
||||||
|
new StateUpdateCallbackEntry(UpdateTessellationState,
|
||||||
|
nameof(ThreedClassState.TessOuterLevel),
|
||||||
|
nameof(ThreedClassState.TessInnerLevel),
|
||||||
|
nameof(ThreedClassState.PatchVertices)),
|
||||||
|
|
||||||
new StateUpdateCallbackEntry(UpdateTfBufferState, nameof(ThreedClassState.TfBufferState)),
|
new StateUpdateCallbackEntry(UpdateTfBufferState, nameof(ThreedClassState.TfBufferState)),
|
||||||
new StateUpdateCallbackEntry(UpdateUserClipState, nameof(ThreedClassState.ClipDistanceEnable)),
|
new StateUpdateCallbackEntry(UpdateUserClipState, nameof(ThreedClassState.ClipDistanceEnable)),
|
||||||
|
|
||||||
|
@ -100,6 +105,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
nameof(ThreedClassState.ViewportExtents),
|
nameof(ThreedClassState.ViewportExtents),
|
||||||
nameof(ThreedClassState.YControl)),
|
nameof(ThreedClassState.YControl)),
|
||||||
|
|
||||||
|
new StateUpdateCallbackEntry(UpdatePolygonMode,
|
||||||
|
nameof(ThreedClassState.PolygonModeFront),
|
||||||
|
nameof(ThreedClassState.PolygonModeBack)),
|
||||||
|
|
||||||
new StateUpdateCallbackEntry(UpdateDepthBiasState,
|
new StateUpdateCallbackEntry(UpdateDepthBiasState,
|
||||||
nameof(ThreedClassState.DepthBiasState),
|
nameof(ThreedClassState.DepthBiasState),
|
||||||
nameof(ThreedClassState.DepthBiasFactor),
|
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>
|
/// <summary>
|
||||||
/// Updates transform feedback buffer state based on the guest GPU state.
|
/// Updates transform feedback buffer state based on the guest GPU state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -544,6 +564,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
_context.Renderer.Pipeline.SetViewports(0, viewports);
|
_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>
|
/// <summary>
|
||||||
/// Updates host depth bias (also called polygon offset) state based on current GPU state.
|
/// Updates host depth bias (also called polygon offset) state based on current GPU state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -949,7 +977,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
_state.State.TexturePoolState.MaximumId,
|
_state.State.TexturePoolState.MaximumId,
|
||||||
(int)_state.State.TextureBufferIndex,
|
(int)_state.State.TextureBufferIndex,
|
||||||
_state.State.EarlyZForce,
|
_state.State.EarlyZForce,
|
||||||
_drawState.Topology);
|
_drawState.Topology,
|
||||||
|
_state.State.TessMode);
|
||||||
|
|
||||||
ShaderBundle gs = _channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(ref _state.State, _channel, gas, addresses);
|
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.InlineToMemory;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
|
@ -19,6 +20,43 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
Fragment
|
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>
|
/// <summary>
|
||||||
/// Transform feedback buffer state.
|
/// Transform feedback buffer state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -661,7 +699,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
public Boolean32 EarlyZForce;
|
public Boolean32 EarlyZForce;
|
||||||
public fixed uint Reserved214[45];
|
public fixed uint Reserved214[45];
|
||||||
public uint SyncpointAction;
|
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 Boolean32 RasterizeEnable;
|
||||||
public Array4<TfBufferState> TfBufferState;
|
public Array4<TfBufferState> TfBufferState;
|
||||||
public fixed uint Reserved400[192];
|
public fixed uint Reserved400[192];
|
||||||
|
@ -679,9 +721,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||||
public float ClearDepthValue;
|
public float ClearDepthValue;
|
||||||
public fixed uint ReservedD94[3];
|
public fixed uint ReservedD94[3];
|
||||||
public uint ClearStencilValue;
|
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 DepthBiasState DepthBiasState;
|
||||||
public fixed uint ReservedDCC[5];
|
public int PatchVertices;
|
||||||
|
public fixed uint ReservedDD0[4];
|
||||||
public uint TextureBarrier;
|
public uint TextureBarrier;
|
||||||
public fixed uint ReservedDE4[7];
|
public fixed uint ReservedDE4[7];
|
||||||
public Array16<ScissorState> ScissorState;
|
public Array16<ScissorState> ScissorState;
|
||||||
|
|
|
@ -349,6 +349,26 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
|
||||||
return flags;
|
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>
|
/// <summary>
|
||||||
/// Create a new instance of <see cref="GuestGpuAccessorHeader"/> from an gpu accessor.
|
/// Create a new instance of <see cref="GuestGpuAccessorHeader"/> from an gpu accessor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -364,6 +384,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
|
||||||
ComputeLocalMemorySize = gpuAccessor.QueryComputeLocalMemorySize(),
|
ComputeLocalMemorySize = gpuAccessor.QueryComputeLocalMemorySize(),
|
||||||
ComputeSharedMemorySize = gpuAccessor.QueryComputeSharedMemorySize(),
|
ComputeSharedMemorySize = gpuAccessor.QueryComputeSharedMemorySize(),
|
||||||
PrimitiveTopology = gpuAccessor.QueryPrimitiveTopology(),
|
PrimitiveTopology = gpuAccessor.QueryPrimitiveTopology(),
|
||||||
|
TessellationModePacked = GetTessellationModePacked(gpuAccessor),
|
||||||
StateFlags = GetGpuStateFlags(gpuAccessor)
|
StateFlags = GetGpuStateFlags(gpuAccessor)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public InputTopology PrimitiveTopology;
|
public InputTopology PrimitiveTopology;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tessellation parameters (packed to fit on a byte).
|
||||||
|
/// </summary>
|
||||||
|
public byte TessellationModePacked;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unused/reserved.
|
/// Unused/reserved.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ushort Reserved2;
|
public byte Reserved2;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GPU boolean state that can influence shader compilation.
|
/// GPU boolean state that can influence shader compilation.
|
||||||
|
|
|
@ -134,6 +134,33 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
return _header.PrimitiveTopology;
|
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>
|
/// <summary>
|
||||||
/// Gets the texture descriptor for a given texture on the pool.
|
/// Gets the texture descriptor for a given texture on the pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -168,10 +168,31 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
PrimitiveTopology.TriangleFan => InputTopology.Triangles,
|
PrimitiveTopology.TriangleFan => InputTopology.Triangles,
|
||||||
PrimitiveTopology.TrianglesAdjacency or
|
PrimitiveTopology.TrianglesAdjacency or
|
||||||
PrimitiveTopology.TriangleStripAdjacency => InputTopology.TrianglesAdjacency,
|
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>
|
/// <summary>
|
||||||
/// Gets the texture descriptor for a given texture on the pool.
|
/// Gets the texture descriptor for a given texture on the pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Shader
|
namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
{
|
{
|
||||||
|
@ -32,6 +33,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PrimitiveTopology Topology { get; }
|
public PrimitiveTopology Topology { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tessellation mode.
|
||||||
|
/// </summary>
|
||||||
|
public TessMode TessellationMode { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU accessor state.
|
/// Creates a new instance of the GPU accessor state.
|
||||||
/// </summary>
|
/// </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="textureBufferIndex">Constant buffer slot where the texture handles are located</param>
|
||||||
/// <param name="earlyZForce">Early Z force enable</param>
|
/// <param name="earlyZForce">Early Z force enable</param>
|
||||||
/// <param name="topology">Primitive topology</param>
|
/// <param name="topology">Primitive topology</param>
|
||||||
|
/// <param name="tessellationMode">Tessellation mode</param>
|
||||||
public GpuAccessorState(
|
public GpuAccessorState(
|
||||||
ulong texturePoolGpuVa,
|
ulong texturePoolGpuVa,
|
||||||
int texturePoolMaximumId,
|
int texturePoolMaximumId,
|
||||||
int textureBufferIndex,
|
int textureBufferIndex,
|
||||||
bool earlyZForce,
|
bool earlyZForce,
|
||||||
PrimitiveTopology topology)
|
PrimitiveTopology topology,
|
||||||
|
TessMode tessellationMode)
|
||||||
{
|
{
|
||||||
TexturePoolGpuVa = texturePoolGpuVa;
|
TexturePoolGpuVa = texturePoolGpuVa;
|
||||||
TexturePoolMaximumId = texturePoolMaximumId;
|
TexturePoolMaximumId = texturePoolMaximumId;
|
||||||
TextureBufferIndex = textureBufferIndex;
|
TextureBufferIndex = textureBufferIndex;
|
||||||
EarlyZForce = earlyZForce;
|
EarlyZForce = earlyZForce;
|
||||||
Topology = topology;
|
Topology = topology;
|
||||||
|
TessellationMode = tessellationMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the codegen (to be changed when codegen or guest format change).
|
/// Version of the codegen (to be changed when codegen or guest format change).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const ulong ShaderCodeGenVersion = 2702;
|
private const ulong ShaderCodeGenVersion = 2534;
|
||||||
|
|
||||||
// Progress reporting helpers
|
// Progress reporting helpers
|
||||||
private volatile int _shaderCount;
|
private volatile int _shaderCount;
|
||||||
|
|
|
@ -290,6 +290,23 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
return TextureMinFilter.Nearest;
|
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)
|
public static PrimitiveType Convert(this PrimitiveTopology topology)
|
||||||
{
|
{
|
||||||
switch (topology)
|
switch (topology)
|
||||||
|
|
|
@ -830,6 +830,21 @@ namespace Ryujinx.Graphics.OpenGL
|
||||||
GL.LineWidth(width);
|
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)
|
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.
|
// 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));
|
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)
|
public void SetPrimitiveRestart(bool enable, int index)
|
||||||
{
|
{
|
||||||
if (!enable)
|
if (!enable)
|
||||||
|
|
|
@ -136,6 +136,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
context.AppendLine();
|
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)
|
if (context.Config.UsedInputAttributes != 0 || context.Config.GpPassthrough)
|
||||||
{
|
{
|
||||||
|
@ -150,6 +166,20 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
context.AppendLine();
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -424,17 +454,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
while (usedAttributes != 0)
|
while (usedAttributes != 0)
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
|
|
||||||
DeclareInputAttribute(context, info, index);
|
DeclareInputAttribute(context, info, index);
|
||||||
|
|
||||||
usedAttributes &= ~(1 << 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)
|
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;
|
string iq = string.Empty;
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
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)
|
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
|
||||||
{
|
{
|
||||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing))
|
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing))
|
||||||
|
@ -477,9 +522,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
while (usedAttributes != 0)
|
while (usedAttributes != 0)
|
||||||
{
|
{
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
|
|
||||||
DeclareOutputAttribute(context, index);
|
DeclareOutputAttribute(context, index);
|
||||||
|
|
||||||
usedAttributes &= ~(1 << index);
|
usedAttributes &= ~(1 << index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,7 +530,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
private static void DeclareOutputAttribute(CodeGenContext context, int attr)
|
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)
|
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)
|
private static void DeclareSupportUniformBlock(CodeGenContext context, bool isFragment, int scaleElements)
|
||||||
{
|
{
|
||||||
if (!isFragment && scaleElements == 0)
|
if (!isFragment && scaleElements == 0)
|
||||||
|
|
|
@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
public const string SamplerNamePrefix = "tex";
|
public const string SamplerNamePrefix = "tex";
|
||||||
public const string ImageNamePrefix = "img";
|
public const string ImageNamePrefix = "img";
|
||||||
|
|
||||||
|
public const string PerPatchAttributePrefix = "patch_attr_";
|
||||||
public const string IAttributePrefix = "in_attr";
|
public const string IAttributePrefix = "in_attr";
|
||||||
public const string OAttributePrefix = "out_attr";
|
public const string OAttributePrefix = "out_attr";
|
||||||
|
|
||||||
|
|
|
@ -126,9 +126,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
string dest;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -200,7 +200,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
|
|
||||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -326,7 +327,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
|
|
||||||
if (src2 is AstOperand operand && operand.Type == OperandType.Constant)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,6 +29,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
private static Dictionary<int, BuiltInAttribute> _builtInAttributes =
|
private static Dictionary<int, BuiltInAttribute> _builtInAttributes =
|
||||||
new Dictionary<int, BuiltInAttribute>()
|
new Dictionary<int, BuiltInAttribute>()
|
||||||
{
|
{
|
||||||
|
{ 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.Layer, new BuiltInAttribute("gl_Layer", VariableType.S32) },
|
||||||
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
|
{ AttributeConsts.PointSize, new BuiltInAttribute("gl_PointSize", VariableType.F32) },
|
||||||
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
|
{ AttributeConsts.PositionX, new BuiltInAttribute("gl_Position.x", VariableType.F32) },
|
||||||
|
@ -61,6 +67,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
{ AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) },
|
{ AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) },
|
||||||
{ AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) },
|
{ AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) },
|
||||||
{ AttributeConsts.LaneId, new BuiltInAttribute(null, 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.EqMask, new BuiltInAttribute(null, VariableType.U32) },
|
||||||
{ AttributeConsts.GeMask, new BuiltInAttribute(null, VariableType.U32) },
|
{ AttributeConsts.GeMask, new BuiltInAttribute(null, VariableType.U32) },
|
||||||
{ AttributeConsts.GtMask, 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
|
return operand.Type switch
|
||||||
{
|
{
|
||||||
OperandType.Argument => GetArgumentName(operand.Value),
|
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.Constant => NumberFormatter.FormatInt(operand.Value),
|
||||||
OperandType.ConstantBuffer => GetConstantBufferName(
|
OperandType.ConstantBuffer => GetConstantBufferName(operand, config),
|
||||||
operand.CbufSlot,
|
|
||||||
operand.CbufOffset,
|
|
||||||
config.Stage,
|
|
||||||
config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)),
|
|
||||||
OperandType.LocalVariable => _locals[operand],
|
OperandType.LocalVariable => _locals[operand],
|
||||||
OperandType.Undefined => DefaultNames.UndefinedName,
|
OperandType.Undefined => DefaultNames.UndefinedName,
|
||||||
_ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".")
|
_ => 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)
|
public static string GetConstantBufferName(int slot, int offset, ShaderStage stage, bool cbIndexable)
|
||||||
{
|
{
|
||||||
return $"{GetUbName(stage, slot, cbIndexable)}[{offset >> 2}].{GetSwizzleMask(offset & 3)}";
|
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);
|
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);
|
char swzMask = GetSwizzleMask((value >> 2) & 3);
|
||||||
|
|
||||||
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd)
|
||||||
|
@ -160,7 +176,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
? DefaultNames.OAttributePrefix
|
? DefaultNames.OAttributePrefix
|
||||||
: DefaultNames.IAttributePrefix;
|
: 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;
|
string name = prefix;
|
||||||
|
|
||||||
|
@ -175,9 +198,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
{
|
{
|
||||||
string name = $"{prefix}{(value >> 4)}_{swzMask}";
|
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;
|
return name;
|
||||||
|
@ -186,9 +209,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
{
|
{
|
||||||
string name = $"{prefix}{(value >> 4)}";
|
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;
|
return name + '.' + swzMask;
|
||||||
|
@ -250,9 +273,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
|
|
||||||
string name = builtInAttr.Name;
|
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;
|
return name;
|
||||||
|
@ -278,6 +301,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
return $"{name}[{attrExpr} >> 2][{attrExpr} & 3]";
|
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)
|
public static string GetUbName(ShaderStage stage, int slot, bool cbIndexable)
|
||||||
{
|
{
|
||||||
if (cbIndexable)
|
if (cbIndexable)
|
||||||
|
|
|
@ -262,6 +262,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
int count = 1;
|
int count = 1;
|
||||||
bool isStore = false;
|
bool isStore = false;
|
||||||
bool indexed = false;
|
bool indexed = false;
|
||||||
|
bool perPatch = false;
|
||||||
|
|
||||||
if (name == InstName.Ast)
|
if (name == InstName.Ast)
|
||||||
{
|
{
|
||||||
|
@ -269,14 +270,17 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
count = (int)opAst.AlSize + 1;
|
count = (int)opAst.AlSize + 1;
|
||||||
offset = opAst.Imm11;
|
offset = opAst.Imm11;
|
||||||
indexed = opAst.Phys;
|
indexed = opAst.Phys;
|
||||||
|
perPatch = opAst.P;
|
||||||
isStore = true;
|
isStore = true;
|
||||||
}
|
}
|
||||||
else if (name == InstName.Ald)
|
else if (name == InstName.Ald)
|
||||||
{
|
{
|
||||||
InstAld opAld = new InstAld(opCode);
|
InstAld opAld = new InstAld(opCode);
|
||||||
count = (int)opAld.AlSize + 1;
|
count = (int)opAld.AlSize + 1;
|
||||||
indexed = opAld.Phys;
|
|
||||||
offset = opAld.Imm11;
|
offset = opAld.Imm11;
|
||||||
|
indexed = opAld.Phys;
|
||||||
|
perPatch = opAld.P;
|
||||||
|
isStore = opAld.O;
|
||||||
}
|
}
|
||||||
else /* if (name == InstName.Ipa) */
|
else /* if (name == InstName.Ipa) */
|
||||||
{
|
{
|
||||||
|
@ -307,11 +311,11 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||||
|
|
||||||
if (isStore)
|
if (isStore)
|
||||||
{
|
{
|
||||||
config.SetOutputUserAttribute(index);
|
config.SetOutputUserAttribute(index, perPatch);
|
||||||
}
|
}
|
||||||
else
|
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 SrcB => (int)((_opcode >> 20) & 0xFF);
|
||||||
public int SrcC => (int)((_opcode >> 39) & 0xFF);
|
public int SrcC => (int)((_opcode >> 39) & 0xFF);
|
||||||
public int Pred => (int)((_opcode >> 16) & 0x7);
|
public int Pred => (int)((_opcode >> 16) & 0x7);
|
||||||
public int Imm16 => (int)((_opcode >> 20) & 0xFFFF);
|
|
||||||
public bool PredInv => (_opcode & 0x80000) != 0;
|
public bool PredInv => (_opcode & 0x80000) != 0;
|
||||||
|
public int Imm16 => (int)((_opcode >> 20) & 0xFFFF);
|
||||||
public bool WriteCC => (_opcode & 0x800000000000) != 0;
|
public bool WriteCC => (_opcode & 0x800000000000) != 0;
|
||||||
public bool DFormat => (_opcode & 0x40000000000000) != 0;
|
public bool DFormat => (_opcode & 0x40000000000000) != 0;
|
||||||
public VectorSelect ASelect => (VectorSelect)((int)((_opcode >> 45) & 0x8) | (int)((_opcode >> 36) & 0x7));
|
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 SrcB => (int)((_opcode >> 20) & 0xFF);
|
||||||
public int Pred => (int)((_opcode >> 16) & 0x7);
|
public int Pred => (int)((_opcode >> 16) & 0x7);
|
||||||
public bool PredInv => (_opcode & 0x80000) != 0;
|
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 ASelect => (VectorSelect)((int)((_opcode >> 45) & 0x8) | (int)((_opcode >> 36) & 0x7));
|
||||||
public VectorSelect BSelect => (VectorSelect)((int)((_opcode >> 46) & 0x8) | (int)((_opcode >> 28) & 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));
|
public IComp VComp => (IComp)((int)((_opcode >> 45) & 0x4) | (int)((_opcode >> 43) & 0x3));
|
||||||
|
|
|
@ -96,6 +96,21 @@ namespace Ryujinx.Graphics.Shader
|
||||||
return InputTopology.Points;
|
return InputTopology.Points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QueryTessCw()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TessPatchType QueryTessPatchType()
|
||||||
|
{
|
||||||
|
return TessPatchType.Triangles;
|
||||||
|
}
|
||||||
|
|
||||||
|
TessSpacing QueryTessSpacing()
|
||||||
|
{
|
||||||
|
return TessSpacing.EqualSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1)
|
TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1)
|
||||||
{
|
{
|
||||||
return TextureFormat.R8G8B8A8Unorm;
|
return TextureFormat.R8G8B8A8Unorm;
|
||||||
|
|
|
@ -474,13 +474,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.Config.GpuAccessor.Log("Shader instruction Vset is not implemented.");
|
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)
|
public static void Vshl(EmitterContext context)
|
||||||
{
|
{
|
||||||
InstVshl op = context.GetOp<InstVshl>();
|
InstVshl op = context.GetOp<InstVshl>();
|
||||||
|
|
|
@ -40,19 +40,33 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
|
|
||||||
context.Config.SetUsedFeature(FeatureFlags.IaIndexing);
|
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);
|
context.Copy(Register(rd), src);
|
||||||
}
|
}
|
||||||
else
|
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));
|
context.Copy(Register(rd), context.LoadAttribute(src, Const(0), primVertex));
|
||||||
}
|
}
|
||||||
|
@ -83,9 +97,13 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
}
|
}
|
||||||
else
|
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));
|
context.Copy(dest, Register(rd));
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
src = Attribute(AttributeConsts.LaneId);
|
src = Attribute(AttributeConsts.LaneId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SReg.InvocationId:
|
||||||
|
src = Attribute(AttributeConsts.InvocationId);
|
||||||
|
break;
|
||||||
|
|
||||||
case SReg.YDirection:
|
case SReg.YDirection:
|
||||||
src = ConstF(1); // TODO: Use value from Y direction GPU register.
|
src = ConstF(1); // TODO: Use value from Y direction GPU register.
|
||||||
break;
|
break;
|
||||||
|
@ -87,6 +91,22 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
src = context.Config.Stage == ShaderStage.Fragment ? Attribute(AttributeConsts.ThreadKill) : Const(0);
|
src = context.Config.Stage == ShaderStage.Fragment ? Attribute(AttributeConsts.ThreadKill) : Const(0);
|
||||||
break;
|
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:
|
case SReg.TId:
|
||||||
Operand tidX = Attribute(AttributeConsts.ThreadIdX);
|
Operand tidX = Attribute(AttributeConsts.ThreadIdX);
|
||||||
Operand tidY = Attribute(AttributeConsts.ThreadIdY);
|
Operand tidY = Attribute(AttributeConsts.ThreadIdY);
|
||||||
|
|
|
@ -120,6 +120,68 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
context.Copy(GetDest(op.Dest), res);
|
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)
|
private static Operand Extend(EmitterContext context, Operand src, VectorSelect type)
|
||||||
{
|
{
|
||||||
return type switch
|
return type switch
|
||||||
|
|
|
@ -161,5 +161,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
|
|
||||||
return false;
|
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);
|
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)
|
public static Operand Cbuf(int slot, int offset)
|
||||||
{
|
{
|
||||||
return new Operand(slot, offset);
|
return new Operand(slot, offset);
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
{
|
{
|
||||||
Argument,
|
Argument,
|
||||||
Attribute,
|
Attribute,
|
||||||
|
AttributePerPatch,
|
||||||
Constant,
|
Constant,
|
||||||
ConstantBuffer,
|
ConstantBuffer,
|
||||||
Label,
|
Label,
|
||||||
|
@ -11,4 +12,12 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
Register,
|
Register,
|
||||||
Undefined
|
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)
|
public static VariableType GetVarType(OperandType type)
|
||||||
{
|
{
|
||||||
switch (type)
|
return type switch
|
||||||
{
|
{
|
||||||
case OperandType.Attribute: return VariableType.F32;
|
OperandType.Attribute => VariableType.F32,
|
||||||
case OperandType.Constant: return VariableType.S32;
|
OperandType.AttributePerPatch => VariableType.F32,
|
||||||
case OperandType.ConstantBuffer: return VariableType.F32;
|
OperandType.Constant => VariableType.S32,
|
||||||
case OperandType.Undefined: return VariableType.S32;
|
OperandType.ConstantBuffer => VariableType.F32,
|
||||||
}
|
OperandType.Undefined => VariableType.S32,
|
||||||
|
_ => throw new ArgumentException($"Invalid operand type \"{type}\".")
|
||||||
throw new ArgumentException($"Invalid operand type \"{type}\".");
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -282,6 +282,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
|
|
||||||
public AstOperand GetOperandUse(Operand operand)
|
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);
|
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,6 +2,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
static class AttributeConsts
|
static class AttributeConsts
|
||||||
{
|
{
|
||||||
|
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 Layer = 0x064;
|
||||||
public const int PointSize = 0x06c;
|
public const int PointSize = 0x06c;
|
||||||
public const int PositionX = 0x070;
|
public const int PositionX = 0x070;
|
||||||
|
@ -28,10 +34,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
public const int UserAttributeBase = 0x80;
|
public const int UserAttributeBase = 0x80;
|
||||||
public const int UserAttributeEnd = UserAttributeBase + UserAttributesCount * 16;
|
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
|
// Note: Those attributes are used internally by the translator
|
||||||
// only, they don't exist on Maxwell.
|
// 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 FragmentOutputDepth = 0x1000000;
|
||||||
public const int FragmentOutputColorBase = 0x1000010;
|
public const int FragmentOutputColorBase = 0x1000010;
|
||||||
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16;
|
||||||
|
@ -49,12 +58,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
public const int LaneId = 0x2000020;
|
public const int LaneId = 0x2000020;
|
||||||
|
|
||||||
public const int EqMask = 0x2000024;
|
public const int InvocationId = 0x2000024;
|
||||||
public const int GeMask = 0x2000028;
|
public const int PrimitiveId = 0x2000028;
|
||||||
public const int GtMask = 0x200002c;
|
public const int PatchVerticesIn = 0x200002c;
|
||||||
public const int LeMask = 0x2000030;
|
|
||||||
public const int LtMask = 0x2000034;
|
|
||||||
|
|
||||||
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)
|
if (target.Enabled)
|
||||||
{
|
{
|
||||||
Config.SetOutputUserAttribute(rtIndex);
|
Config.SetOutputUserAttribute(rtIndex, perPatch: false);
|
||||||
regIndexBase += 4;
|
regIndexBase += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
public bool GpPassthrough { get; }
|
public bool GpPassthrough { get; }
|
||||||
|
|
||||||
|
public int ThreadsPerInputPrimitive { get; }
|
||||||
|
|
||||||
public OutputTopology OutputTopology { get; }
|
public OutputTopology OutputTopology { get; }
|
||||||
|
|
||||||
public int MaxOutputVertices { get; }
|
public int MaxOutputVertices { get; }
|
||||||
|
@ -42,7 +44,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
private readonly TranslationCounts _counts;
|
private readonly TranslationCounts _counts;
|
||||||
|
|
||||||
public int UsedInputAttributes { get; private set; }
|
public int UsedInputAttributes { get; private set; }
|
||||||
|
public int UsedInputAttributesPerPatch { get; private set; }
|
||||||
public int UsedOutputAttributes { get; private set; }
|
public int UsedOutputAttributes { get; private set; }
|
||||||
|
public int UsedOutputAttributesPerPatch { get; private set; }
|
||||||
public int PassthroughAttributes { get; private set; }
|
public int PassthroughAttributes { get; private set; }
|
||||||
|
|
||||||
private int _usedConstantBuffers;
|
private int _usedConstantBuffers;
|
||||||
|
@ -113,6 +117,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
Stage = header.Stage;
|
Stage = header.Stage;
|
||||||
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
|
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
|
||||||
|
ThreadsPerInputPrimitive = header.ThreadsPerInputPrimitive;
|
||||||
OutputTopology = header.OutputTopology;
|
OutputTopology = header.OutputTopology;
|
||||||
MaxOutputVertices = header.MaxOutputVertexCount;
|
MaxOutputVertices = header.MaxOutputVertexCount;
|
||||||
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
|
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
|
||||||
|
@ -219,17 +224,31 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetInputUserAttribute(int index)
|
public void SetInputUserAttribute(int index, bool perPatch)
|
||||||
|
{
|
||||||
|
if (perPatch)
|
||||||
|
{
|
||||||
|
UsedInputAttributesPerPatch |= 1 << index;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
UsedInputAttributes |= 1 << index;
|
UsedInputAttributes |= 1 << index;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void SetOutputUserAttribute(int index)
|
public void SetOutputUserAttribute(int index, bool perPatch)
|
||||||
|
{
|
||||||
|
if (perPatch)
|
||||||
|
{
|
||||||
|
UsedOutputAttributesPerPatch |= 1 << index;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
UsedOutputAttributes |= 1 << index;
|
UsedOutputAttributes |= 1 << index;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void MergeOutputUserAttributes(int mask)
|
public void MergeOutputUserAttributes(int mask, int maskPerPatch)
|
||||||
{
|
{
|
||||||
if (GpPassthrough)
|
if (GpPassthrough)
|
||||||
{
|
{
|
||||||
|
@ -238,6 +257,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UsedOutputAttributes |= mask;
|
UsedOutputAttributes |= mask;
|
||||||
|
UsedOutputAttributesPerPatch |= maskPerPatch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -216,27 +216,38 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeOutput(int baseAttr)
|
if (config.Stage == ShaderStage.Vertex)
|
||||||
|
{
|
||||||
|
InitializeOutput(context, AttributeConsts.PositionX, perPatch: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int usedAttributes = context.Config.UsedOutputAttributes;
|
||||||
|
while (usedAttributes != 0)
|
||||||
|
{
|
||||||
|
int index = BitOperations.TrailingZeroCount(usedAttributes);
|
||||||
|
|
||||||
|
InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false);
|
||||||
|
|
||||||
|
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++)
|
for (int c = 0; c < 4; c++)
|
||||||
{
|
{
|
||||||
context.Copy(Attribute(baseAttr + c * 4), ConstF(c == 3 ? 1f : 0f));
|
int attrOffset = baseAttr + c * 4;
|
||||||
}
|
context.Copy(perPatch ? AttributePerPatch(attrOffset) : Attribute(attrOffset), ConstF(c == 3 ? 1f : 0f));
|
||||||
}
|
|
||||||
|
|
||||||
if (config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
InitializeOutput(AttributeConsts.PositionX);
|
|
||||||
}
|
|
||||||
|
|
||||||
int usedAttribtes = context.Config.UsedOutputAttributes;
|
|
||||||
while (usedAttribtes != 0)
|
|
||||||
{
|
|
||||||
int index = BitOperations.TrailingZeroCount(usedAttribtes);
|
|
||||||
|
|
||||||
InitializeOutput(AttributeConsts.UserAttributeBase + index * 16);
|
|
||||||
|
|
||||||
usedAttribtes &= ~(1 << index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
private static bool IsUserAttribute(Operand operand)
|
private static bool IsUserAttribute(Operand operand)
|
||||||
{
|
{
|
||||||
return operand != null &&
|
if (operand != null && operand.Type.IsAttribute())
|
||||||
operand.Type == OperandType.Attribute &&
|
{
|
||||||
operand.Value >= AttributeConsts.UserAttributeBase &&
|
int value = operand.Value & AttributeConsts.Mask;
|
||||||
operand.Value < AttributeConsts.UserAttributeEnd;
|
return value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FunctionCode[] Combine(FunctionCode[] a, FunctionCode[] b, int aStart)
|
private static FunctionCode[] Combine(FunctionCode[] a, FunctionCode[] b, int aStart)
|
||||||
|
@ -133,14 +136,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
if (nextStage != null)
|
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 _);
|
FunctionCode[] code = EmitShader(_cfg, _config, initializeOutputs: other == null, out _);
|
||||||
|
|
||||||
if (other != null)
|
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);
|
FunctionCode[] otherCode = EmitShader(other._cfg, other._config, initializeOutputs: true, out int aStart);
|
||||||
|
|
||||||
|
|
Reference in a new issue