aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Ryujinx.Graphics.GAL/IPipeline.cs6
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs4
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/CommandType.cs2
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs25
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs20
-rw-r--r--Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs12
-rw-r--r--Ryujinx.Graphics.GAL/PolygonMode.cs9
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs3
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs31
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs53
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs21
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs7
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs27
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs23
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs11
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs2
-rw-r--r--Ryujinx.Graphics.OpenGL/EnumConversion.cs17
-rw-r--r--Ryujinx.Graphics.OpenGL/Pipeline.cs28
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs73
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs1
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs5
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs6
-rw-r--r--Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs125
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/Decoder.cs10
-rw-r--r--Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs3
-rw-r--r--Ryujinx.Graphics.Shader/IGpuAccessor.cs15
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmit.cs7
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs32
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs20
-rw-r--r--Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs62
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs6
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs5
-rw-r--r--Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs9
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs16
-rw-r--r--Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs6
-rw-r--r--Ryujinx.Graphics.Shader/TessPatchType.cs22
-rw-r--r--Ryujinx.Graphics.Shader/TessSpacing.cs22
-rw-r--r--Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs69
-rw-r--r--Ryujinx.Graphics.Shader/Translation/EmitterContext.cs2
-rw-r--r--Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs50
-rw-r--r--Ryujinx.Graphics.Shader/Translation/Translator.cs35
-rw-r--r--Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs17
42 files changed, 773 insertions, 146 deletions
diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs
index 7a7d83cc..bfc432b1 100644
--- a/Ryujinx.Graphics.GAL/IPipeline.cs
+++ b/Ryujinx.Graphics.GAL/IPipeline.cs
@@ -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);
diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs
index 82a75ea7..47ceeb7d 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs
@@ -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) =>
diff --git a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
index 0761a7f0..ac73a3fe 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
@@ -72,7 +72,9 @@
SetIndexBuffer,
SetLineParameters,
SetLogicOpState,
+ SetPatchParameters,
SetPointParameters,
+ SetPolygonMode,
SetPrimitiveRestart,
SetPrimitiveTopology,
SetProgram,
diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs
new file mode 100644
index 00000000..7847e8d0
--- /dev/null
+++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPatchParametersCommand.cs
@@ -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());
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs
new file mode 100644
index 00000000..6de78f04
--- /dev/null
+++ b/Ryujinx.Graphics.GAL/Multithreading/Commands/SetPolygonModeCommand.cs
@@ -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);
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
index 0f523481..3c39a77f 100644
--- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
+++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs
@@ -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);
diff --git a/Ryujinx.Graphics.GAL/PolygonMode.cs b/Ryujinx.Graphics.GAL/PolygonMode.cs
new file mode 100644
index 00000000..d6110c1b
--- /dev/null
+++ b/Ryujinx.Graphics.GAL/PolygonMode.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.Graphics.GAL
+{
+ public enum PolygonMode
+ {
+ Point = 0x1b00,
+ Line = 0x1b01,
+ Fill = 0x1b02
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
index 8469f1ae..00015c40 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs
@@ -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,
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index f9d16803..4a5633c9 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -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),
@@ -260,6 +269,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>
private void UpdateTfBufferState()
@@ -545,6 +565,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
/// <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>
private void UpdateDepthBiasState()
@@ -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);
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
index a6392e3d..58bc0957 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
@@ -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
@@ -20,6 +21,43 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
/// <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>
struct TfBufferState
@@ -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;
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
index 33da42db..09107346 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/CacheHelper.cs
@@ -350,6 +350,26 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
}
/// <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>
/// <param name="gpuAccessor">The gpu accessor</param>
@@ -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)
};
}
diff --git a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs
index 610b2da1..2e044750 100644
--- a/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/Cache/Definition/GuestGpuAccessorHeader.cs
@@ -50,9 +50,14 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache.Definition
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.
diff --git a/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs
index 3a52b2fe..21d08823 100644
--- a/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/CachedGpuAccessor.cs
@@ -135,6 +135,33 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
/// <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>
/// <param name="handle">Index of the texture (this is the word offset of the handle in the constant buffer)</param>
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
index 50e24b97..64604a99 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
@@ -168,11 +168,32 @@ 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>
/// <param name="handle">Index of the texture (this is the word offset of the handle in the constant buffer)</param>
diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
index 8d817113..ebbf3b69 100644
--- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessorState.cs
@@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Gpu.Engine.Threed;
namespace Ryujinx.Graphics.Gpu.Shader
{
@@ -33,6 +34,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
public PrimitiveTopology Topology { get; }
/// <summary>
+ /// Tessellation mode.
+ /// </summary>
+ public TessMode TessellationMode { get; }
+
+ /// <summary>
/// Creates a new instance of the GPU accessor state.
/// </summary>
/// <param name="texturePoolGpuVa">GPU virtual address of the texture pool</param>
@@ -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;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index e69e7dcb..f2180820 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -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;
diff --git a/Ryujinx.Graphics.OpenGL/EnumConversion.cs b/Ryujinx.Graphics.OpenGL/EnumConversion.cs
index cc3db003..ccdbcfec 100644
--- a/Ryujinx.Graphics.OpenGL/EnumConversion.cs
+++ b/Ryujinx.Graphics.OpenGL/EnumConversion.cs
@@ -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)
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index dd07afcf..d0a509b4 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -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)
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index e29ff486..3e2e51b0 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -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)
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
index eaf1050c..47350408 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs
@@ -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";
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
index 2d6ede0a..077737c8 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
@@ -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
{
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
index edaacd3e..5e46bb46 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs
@@ -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
{
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
index d35525f9..9680df27 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs
@@ -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)
diff --git a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
index 656e9c44..b446e650 100644
--- a/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs
@@ -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);
}
}
}
diff --git a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs b/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs
index ca4ff12a..b61412c6 100644
--- a/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs
+++ b/Ryujinx.Graphics.Shader/Decoders/InstDecoders.cs
@@ -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));
diff --git a/Ryujinx.Graphics.Shader/IGpuAccessor.cs b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
index 6af42cf2..3fdce8ea 100644
--- a/Ryujinx.Graphics.Shader/IGpuAccessor.cs
+++ b/Ryujinx.Graphics.Shader/IGpuAccessor.cs
@@ -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;
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs
index 9cc591ca..33c1065a 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmit.cs
@@ -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>();
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
index e865caf2..f82b835c 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAttribute.cs
@@ -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(offset);
- context.FlagAttributeRead(src.Value);
+ 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.
+
+ int offset = op.Imm11 + index * 4;
+
+ context.FlagAttributeWritten(offset);
- context.FlagAttributeWritten(dest.Value);
+ Operand dest = op.P ? AttributePerPatch(offset) : Attribute(offset);
context.Copy(dest, Register(rd));
}
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
index 245b2253..240fd6b1 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
@@ -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);
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs
index 890b31d6..120d6f22 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideoMinMax.cs
@@ -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
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
index b0db56f0..03badec9 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs
@@ -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;
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs
index 221e278f..7fed861e 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs
@@ -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);
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs
index 3427b103..7566a03f 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs
@@ -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;
+ }
+ }
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
index 95c5731a..e56008f0 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs
@@ -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}\".")
+ };
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
index a9e44175..2a39d021 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
@@ -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);
}
diff --git a/Ryujinx.Graphics.Shader/TessPatchType.cs b/Ryujinx.Graphics.Shader/TessPatchType.cs
new file mode 100644
index 00000000..2361b69f
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/TessPatchType.cs
@@ -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"
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/TessSpacing.cs b/Ryujinx.Graphics.Shader/TessSpacing.cs
new file mode 100644
index 00000000..35c44190
--- /dev/null
+++ b/Ryujinx.Graphics.Shader/TessSpacing.cs
@@ -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"
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
index 3d0d216e..128013d8 100644
--- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
+++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs
@@ -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 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 = 0x2000038;
+ public const int ThreadKill = 0x2000044;
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
index bdfd9626..6f2a6c3b 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
@@ -216,7 +216,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (target.Enabled)
{
- Config.SetOutputUserAttribute(rtIndex);
+ Config.SetOutputUserAttribute(rtIndex, perPatch: false);
regIndexBase += 4;
}
}
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index 72fa7733..ec7e8982 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -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;
}
}
diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs
index 0a0ee4a7..0243eba1 100644
--- a/Ryujinx.Graphics.Shader/Translation/Translator.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs
@@ -216,27 +216,38 @@ namespace Ryujinx.Graphics.Shader.Translation
return;
}
- void InitializeOutput(int baseAttr)
+ if (config.Stage == ShaderStage.Vertex)
{
- for (int c = 0; c < 4; c++)
- {
- context.Copy(Attribute(baseAttr + c * 4), ConstF(c == 3 ? 1f : 0f));
- }
+ InitializeOutput(context, AttributeConsts.PositionX, perPatch: false);
}
- if (config.Stage == ShaderStage.Vertex)
+ int usedAttributes = context.Config.UsedOutputAttributes;
+ while (usedAttributes != 0)
{
- InitializeOutput(AttributeConsts.PositionX);
+ int index = BitOperations.TrailingZeroCount(usedAttributes);
+
+ InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: false);
+
+ usedAttributes &= ~(1 << index);
}
- int usedAttribtes = context.Config.UsedOutputAttributes;
- while (usedAttribtes != 0)
+ int usedAttributesPerPatch = context.Config.UsedOutputAttributesPerPatch;
+ while (usedAttributesPerPatch != 0)
{
- int index = BitOperations.TrailingZeroCount(usedAttribtes);
+ int index = BitOperations.TrailingZeroCount(usedAttributesPerPatch);
- InitializeOutput(AttributeConsts.UserAttributeBase + index * 16);
+ InitializeOutput(context, AttributeConsts.UserAttributeBase + index * 16, perPatch: true);
- usedAttribtes &= ~(1 << index);
+ 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));
}
}
diff --git a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
index 3c7b3c2b..34b116d6 100644
--- a/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
+++ b/Ryujinx.Graphics.Shader/Translation/TranslatorContext.cs
@@ -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);