diff --git a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
index f18b91cfc..deaa6f5ac 100644
--- a/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
+++ b/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
@@ -1475,9 +1475,11 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1;
- if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0)
+ if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
{
- EmitScalarUnaryOpF(context, Intrinsic.X86Rcpss, 0);
+ Operand res = EmitSse41FP32RoundExp8(context, context.AddIntrinsic(Intrinsic.X86Rcpss, GetVec(op.Rn)), scalar: true);
+
+ context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
}
else
{
@@ -1494,9 +1496,16 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1;
- if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0)
+ if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
{
- EmitVectorUnaryOpF(context, Intrinsic.X86Rcpps, 0);
+ Operand res = EmitSse41FP32RoundExp8(context, context.AddIntrinsic(Intrinsic.X86Rcpps, GetVec(op.Rn)), scalar: false);
+
+ if (op.RegisterSize == RegisterSize.Simd64)
+ {
+ res = context.VectorZeroUpper64(res);
+ }
+
+ context.Copy(GetVec(op.Rd), res);
}
else
{
@@ -1652,7 +1661,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse41)
{
- EmitScalarRoundOpF(context, FPRoundingMode.TowardsMinusInfinity);
+ EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsMinusInfinity);
}
else
{
@@ -1667,7 +1676,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse41)
{
- EmitVectorRoundOpF(context, FPRoundingMode.TowardsMinusInfinity);
+ EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsMinusInfinity);
}
else
{
@@ -1682,7 +1691,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse41)
{
- EmitScalarRoundOpF(context, FPRoundingMode.ToNearest);
+ EmitSse41ScalarRoundOpF(context, FPRoundingMode.ToNearest);
}
else
{
@@ -1697,7 +1706,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse41)
{
- EmitVectorRoundOpF(context, FPRoundingMode.ToNearest);
+ EmitSse41VectorRoundOpF(context, FPRoundingMode.ToNearest);
}
else
{
@@ -1712,7 +1721,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse41)
{
- EmitScalarRoundOpF(context, FPRoundingMode.TowardsPlusInfinity);
+ EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsPlusInfinity);
}
else
{
@@ -1727,7 +1736,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse41)
{
- EmitVectorRoundOpF(context, FPRoundingMode.TowardsPlusInfinity);
+ EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsPlusInfinity);
}
else
{
@@ -1778,7 +1787,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse41)
{
- EmitScalarRoundOpF(context, FPRoundingMode.TowardsZero);
+ EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsZero);
}
else
{
@@ -1793,7 +1802,7 @@ namespace ARMeilleure.Instructions
{
if (Optimizations.UseSse41)
{
- EmitVectorRoundOpF(context, FPRoundingMode.TowardsZero);
+ EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsZero);
}
else
{
@@ -1810,9 +1819,11 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1;
- if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0)
+ if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
{
- EmitScalarUnaryOpF(context, Intrinsic.X86Rsqrtss, 0);
+ Operand res = EmitSse41FP32RoundExp8(context, context.AddIntrinsic(Intrinsic.X86Rsqrtss, GetVec(op.Rn)), scalar: true);
+
+ context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
}
else
{
@@ -1829,9 +1840,16 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1;
- if (Optimizations.FastFP && Optimizations.UseSse && sizeF == 0)
+ if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
{
- EmitVectorUnaryOpF(context, Intrinsic.X86Rsqrtps, 0);
+ Operand res = EmitSse41FP32RoundExp8(context, context.AddIntrinsic(Intrinsic.X86Rsqrtps, GetVec(op.Rn)), scalar: false);
+
+ if (op.RegisterSize == RegisterSize.Simd64)
+ {
+ res = context.VectorZeroUpper64(res);
+ }
+
+ context.Copy(GetVec(op.Rd), res);
}
else
{
@@ -3498,7 +3516,7 @@ namespace ARMeilleure.Instructions
return context.ConditionalSelect(cmp, op1, op2);
}
- private static void EmitScalarRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode)
+ private static void EmitSse41ScalarRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
@@ -3520,7 +3538,7 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res);
}
- private static void EmitVectorRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode)
+ private static void EmitSse41VectorRoundOpF(ArmEmitterContext context, FPRoundingMode roundMode)
{
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
@@ -3538,6 +3556,35 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(op.Rd), res);
}
+ private static Operand EmitSse41FP32RoundExp8(ArmEmitterContext context, Operand value, bool scalar)
+ {
+ Operand roundMask;
+ Operand truncMask;
+ Operand expMask;
+
+ if (scalar)
+ {
+ roundMask = X86GetScalar(context, 0x4000);
+ truncMask = X86GetScalar(context, unchecked((int)0xFFFF8000));
+ expMask = X86GetScalar(context, 0x7F800000);
+ }
+ else
+ {
+ roundMask = X86GetAllElements(context, 0x4000);
+ truncMask = X86GetAllElements(context, unchecked((int)0xFFFF8000));
+ expMask = X86GetAllElements(context, 0x7F800000);
+ }
+
+ Operand oValue = value;
+ Operand masked = context.AddIntrinsic(Intrinsic.X86Pand, value, expMask);
+ Operand isNaNInf = context.AddIntrinsic(Intrinsic.X86Pcmpeqw, masked, expMask);
+
+ value = context.AddIntrinsic(Intrinsic.X86Paddw, value, roundMask);
+ value = context.AddIntrinsic(Intrinsic.X86Pand, value, truncMask);
+
+ return context.AddIntrinsic(Intrinsic.X86Blendvps, value, oValue, isNaNInf);
+ }
+
public static void EmitSse2VectorIsNaNOpF(
ArmEmitterContext context,
Operand opF,
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
index a0fef1b22..61cc8f899 100644
--- a/Ryujinx.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -479,7 +479,7 @@ namespace Ryujinx.Configuration
System.Region.Value = Region.USA;
System.TimeZone.Value = "UTC";
System.SystemTimeOffset.Value = 0;
- System.EnableDockedMode.Value = false;
+ System.EnableDockedMode.Value = true;
EnableDiscordIntegration.Value = true;
CheckUpdatesOnStart.Value = true;
ShowConfirmExit.Value = true;
diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs
index c2c1a9a19..c5aef77f6 100644
--- a/Ryujinx.Graphics.Gpu/Image/Pool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs
@@ -8,14 +8,16 @@ namespace Ryujinx.Graphics.Gpu.Image
///
/// Represents a pool of GPU resources, such as samplers or textures.
///
- /// Type of the GPU resource
- abstract class Pool : IDisposable
+ /// Type of the GPU resource
+ /// Type of the descriptor
+ abstract class Pool : IDisposable where T2 : unmanaged
{
protected const int DescriptorSize = 0x20;
protected GpuContext Context;
- protected T[] Items;
+ protected T1[] Items;
+ protected T2[] DescriptorCache;
///
/// The maximum ID value of resources on the pool (inclusive).
@@ -47,7 +49,8 @@ namespace Ryujinx.Graphics.Gpu.Image
ulong size = (ulong)(uint)count * DescriptorSize;
- Items = new T[count];
+ Items = new T1[count];
+ DescriptorCache = new T2[count];
Address = address;
Size = size;
@@ -56,12 +59,23 @@ namespace Ryujinx.Graphics.Gpu.Image
_modifiedDelegate = RegionModified;
}
+
+ ///
+ /// Gets the descriptor for a given ID.
+ ///
+ /// ID of the descriptor. This is effectively a zero-based index
+ /// The descriptor
+ public T2 GetDescriptor(int id)
+ {
+ return Context.PhysicalMemory.Read(Address + (ulong)id * DescriptorSize);
+ }
+
///
/// Gets the GPU resource with the given ID.
///
/// ID of the resource. This is effectively a zero-based index
/// The GPU resource with the given ID
- public abstract T Get(int id);
+ public abstract T1 Get(int id);
///
/// Synchronizes host memory with guest memory.
@@ -97,7 +111,7 @@ namespace Ryujinx.Graphics.Gpu.Image
protected abstract void InvalidateRangeImpl(ulong address, ulong size);
- protected abstract void Delete(T item);
+ protected abstract void Delete(T1 item);
///
/// Performs the disposal of all resources stored on the pool.
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
index 2c28b743f..64a146fb3 100644
--- a/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
+++ b/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
@@ -1,4 +1,6 @@
using Ryujinx.Graphics.GAL;
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
namespace Ryujinx.Graphics.Gpu.Image
{
@@ -244,5 +246,15 @@ namespace Ryujinx.Graphics.Gpu.Image
{
return ((Word2 >> 12) & 0xfff) * Frac8ToF32;
}
+
+ ///
+ /// Check if two descriptors are equal.
+ ///
+ /// The descriptor to compare against
+ /// True if they are equal, false otherwise
+ public bool Equals(ref SamplerDescriptor other)
+ {
+ return Unsafe.As>(ref this).Equals(Unsafe.As>(ref other));
+ }
}
}
diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
index ca13a7d6f..1395aea22 100644
--- a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
@@ -3,7 +3,7 @@ namespace Ryujinx.Graphics.Gpu.Image
///
/// Sampler pool.
///
- class SamplerPool : Pool
+ class SamplerPool : Pool
{
private int _sequenceNumber;
@@ -38,11 +38,13 @@ namespace Ryujinx.Graphics.Gpu.Image
if (sampler == null)
{
- SamplerDescriptor descriptor = Context.PhysicalMemory.Read(Address + (ulong)id * DescriptorSize);
+ SamplerDescriptor descriptor = GetDescriptor(id);
sampler = new Sampler(Context, descriptor);
Items[id] = sampler;
+
+ DescriptorCache[id] = descriptor;
}
return sampler;
@@ -65,6 +67,14 @@ namespace Ryujinx.Graphics.Gpu.Image
if (sampler != null)
{
+ SamplerDescriptor descriptor = GetDescriptor(id);
+
+ // If the descriptors are the same, the sampler is still valid.
+ if (descriptor.Equals(ref DescriptorCache[id]))
+ {
+ continue;
+ }
+
sampler.Dispose();
Items[id] = null;
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
index 76d97bf8c..e85df136b 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
@@ -1,4 +1,6 @@
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
namespace Ryujinx.Graphics.Gpu.Image
{
@@ -248,5 +250,15 @@ namespace Ryujinx.Graphics.Gpu.Image
return result;
}
+
+ ///
+ /// Check if two descriptors are equal.
+ ///
+ /// The descriptor to compare against
+ /// True if they are equal, false otherwise
+ public bool Equals(ref TextureDescriptor other)
+ {
+ return Unsafe.As>(ref this).Equals(Unsafe.As>(ref other));
+ }
}
}
diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index dfcd8a528..58e881ca7 100644
--- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -1,6 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
-using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
using System;
using System.Collections.Generic;
@@ -10,7 +9,7 @@ namespace Ryujinx.Graphics.Gpu.Image
///
/// Texture pool.
///
- class TexturePool : Pool
+ class TexturePool : Pool
{
private int _sequenceNumber;
@@ -65,6 +64,8 @@ namespace Ryujinx.Graphics.Gpu.Image
texture.IncrementReferenceCount();
Items[id] = texture;
+
+ DescriptorCache[id] = descriptor;
}
else
{
@@ -91,16 +92,6 @@ namespace Ryujinx.Graphics.Gpu.Image
return texture;
}
- ///
- /// Gets the texture descriptor from a given texture ID.
- ///
- /// ID of the texture. This is effectively a zero-based index
- /// The texture descriptor
- public TextureDescriptor GetDescriptor(int id)
- {
- return Context.PhysicalMemory.Read(Address + (ulong)id * DescriptorSize);
- }
-
///
/// Implementation of the texture pool range invalidation.
///
@@ -122,8 +113,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// If the descriptors are the same, the texture is the same,
// we don't need to remove as it was not modified. Just continue.
- if (texture.Info.GpuAddress == descriptor.UnpackAddress() &&
- texture.IsExactMatch(GetInfo(descriptor, out _), TextureSearchFlags.Strict) != TextureMatchQuality.NoMatch)
+ if (descriptor.Equals(ref DescriptorCache[id]))
{
continue;
}
diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index f3e4679b8..f266bbb44 100644
--- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
///
/// Version of the codegen (to be changed when codegen or guest format change).
///
- private const ulong ShaderCodeGenVersion = 1790;
+ private const ulong ShaderCodeGenVersion = 1961;
///
/// Creates a new instance of the shader cache.
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
index a6109a959..dd3c8196b 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs
@@ -26,6 +26,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine("#extension GL_ARB_compute_shader : enable");
}
+ if (context.Config.GpPassthrough)
+ {
+ context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable");
+ }
+
context.AppendLine("#pragma optionNV(fastmath off)");
context.AppendLine();
@@ -33,20 +38,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;");
context.AppendLine();
- if (context.Config.Stage == ShaderStage.Geometry)
- {
- string inPrimitive = context.Config.GpuAccessor.QueryPrimitiveTopology().ToGlslString();
-
- context.AppendLine($"layout ({inPrimitive}) in;");
-
- string outPrimitive = context.Config.OutputTopology.ToGlslString();
-
- int maxOutputVertices = context.Config.MaxOutputVertices;
-
- context.AppendLine($"layout ({outPrimitive}, max_vertices = {maxOutputVertices}) out;");
- context.AppendLine();
- }
-
if (context.Config.Stage == ShaderStage.Compute)
{
int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
@@ -109,6 +100,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
if (context.Config.Stage != ShaderStage.Compute)
{
+ if (context.Config.Stage == ShaderStage.Geometry)
+ {
+ string inPrimitive = context.Config.GpuAccessor.QueryPrimitiveTopology().ToGlslString();
+
+ context.AppendLine($"layout ({inPrimitive}) in;");
+
+ if (context.Config.GpPassthrough)
+ {
+ context.AppendLine($"layout (passthrough) in gl_PerVertex");
+ context.EnterScope();
+ context.AppendLine("vec4 gl_Position;");
+ context.AppendLine("float gl_PointSize;");
+ context.AppendLine("float gl_ClipDistance[];");
+ context.LeaveScope(";");
+ }
+ else
+ {
+ string outPrimitive = context.Config.OutputTopology.ToGlslString();
+
+ int maxOutputVertices = context.Config.MaxOutputVertices;
+
+ context.AppendLine($"layout ({outPrimitive}, max_vertices = {maxOutputVertices}) out;");
+ }
+
+ context.AppendLine();
+ }
+
if (info.IAttributes.Count != 0)
{
DeclareInputAttributes(context, info);
@@ -432,6 +450,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
};
}
+ string pass = context.Config.GpPassthrough ? "passthrough, " : string.Empty;
+
string name = $"{DefaultNames.IAttributePrefix}{attr}";
if ((context.Config.Flags & TranslationFlags.Feedback) != 0)
@@ -440,12 +460,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
char swzMask = "xyzw"[c];
- context.AppendLine($"layout (location = {attr}, component = {c}) {iq}in float {name}_{swzMask}{suffix};");
+ context.AppendLine($"layout ({pass}location = {attr}, component = {c}) {iq}in float {name}_{swzMask}{suffix};");
}
}
else
{
- context.AppendLine($"layout (location = {attr}) {iq}in vec4 {name}{suffix};");
+ context.AppendLine($"layout ({pass}location = {attr}) {iq}in vec4 {name}{suffix};");
}
}
}
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
index 276544fca..37a1cd89c 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs
@@ -69,7 +69,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
// compiler may eliminate them.
// (Not needed for fragment shader as it is the last stage).
if (context.Config.Stage != ShaderStage.Compute &&
- context.Config.Stage != ShaderStage.Fragment)
+ context.Config.Stage != ShaderStage.Fragment &&
+ !context.Config.GpPassthrough)
{
for (int attr = 0; attr < Declarations.MaxAttributes; attr++)
{
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl
index 380bc581f..356bdd793 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl
@@ -1,9 +1,10 @@
-float Helper_Shuffle(float x, uint index, uint mask)
+float Helper_Shuffle(float x, uint index, uint mask, out bool valid)
{
uint clamp = mask & 0x1fu;
uint segMask = (mask >> 8) & 0x1fu;
uint minThreadId = gl_SubGroupInvocationARB & segMask;
uint maxThreadId = minThreadId | (clamp & ~segMask);
uint srcThreadId = (index & ~segMask) | minThreadId;
- return (srcThreadId <= maxThreadId) ? readInvocationARB(x, srcThreadId) : x;
+ valid = srcThreadId <= maxThreadId;
+ return valid ? readInvocationARB(x, srcThreadId) : x;
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl
index 46750f20d..a79b90e20 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl
@@ -1,9 +1,10 @@
-float Helper_ShuffleDown(float x, uint index, uint mask)
+float Helper_ShuffleDown(float x, uint index, uint mask, out bool valid)
{
uint clamp = mask & 0x1fu;
uint segMask = (mask >> 8) & 0x1fu;
uint minThreadId = gl_SubGroupInvocationARB & segMask;
uint maxThreadId = minThreadId | (clamp & ~segMask);
uint srcThreadId = gl_SubGroupInvocationARB + index;
- return (srcThreadId <= maxThreadId) ? readInvocationARB(x, srcThreadId) : x;
+ valid = srcThreadId <= maxThreadId;
+ return valid ? readInvocationARB(x, srcThreadId) : x;
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl
index 2bc834697..4e74f217f 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl
@@ -1,8 +1,9 @@
-float Helper_ShuffleUp(float x, uint index, uint mask)
+float Helper_ShuffleUp(float x, uint index, uint mask, out bool valid)
{
uint clamp = mask & 0x1fu;
uint segMask = (mask >> 8) & 0x1fu;
uint minThreadId = gl_SubGroupInvocationARB & segMask;
uint srcThreadId = gl_SubGroupInvocationARB - index;
- return (srcThreadId >= minThreadId) ? readInvocationARB(x, srcThreadId) : x;
+ valid = srcThreadId >= minThreadId;
+ return valid ? readInvocationARB(x, srcThreadId) : x;
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl
index 1049e181f..0631472be 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl
@@ -1,9 +1,10 @@
-float Helper_ShuffleXor(float x, uint index, uint mask)
+float Helper_ShuffleXor(float x, uint index, uint mask, out bool valid)
{
uint clamp = mask & 0x1fu;
uint segMask = (mask >> 8) & 0x1fu;
uint minThreadId = gl_SubGroupInvocationARB & segMask;
uint maxThreadId = minThreadId | (clamp & ~segMask);
uint srcThreadId = gl_SubGroupInvocationARB ^ index;
- return (srcThreadId <= maxThreadId) ? readInvocationARB(x, srcThreadId) : x;
+ valid = srcThreadId <= maxThreadId;
+ return valid ? readInvocationARB(x, srcThreadId) : x;
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
index 5f5574c31..f3774a60d 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs
@@ -88,13 +88,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.LoopContinue, InstType.OpNullary, "continue");
Add(Instruction.PackDouble2x32, InstType.Special);
Add(Instruction.PackHalf2x16, InstType.Special);
- Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3);
- Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3);
- Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3);
- Add(Instruction.Shuffle, InstType.CallTernary, HelperFunctionNames.Shuffle);
- Add(Instruction.ShuffleDown, InstType.CallTernary, HelperFunctionNames.ShuffleDown);
- Add(Instruction.ShuffleUp, InstType.CallTernary, HelperFunctionNames.ShuffleUp);
- Add(Instruction.ShuffleXor, InstType.CallTernary, HelperFunctionNames.ShuffleXor);
Add(Instruction.Maximum, InstType.CallBinary, "max");
Add(Instruction.MaximumU32, InstType.CallBinary, "max");
Add(Instruction.MemoryBarrier, InstType.CallNullary, "memoryBarrier");
@@ -107,6 +100,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.ReciprocalSquareRoot, InstType.CallUnary, "inversesqrt");
Add(Instruction.Return, InstType.OpNullary, "return");
Add(Instruction.Round, InstType.CallUnary, "roundEven");
+ Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3);
+ Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3);
+ Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3);
+ Add(Instruction.Shuffle, InstType.CallQuaternary, HelperFunctionNames.Shuffle);
+ Add(Instruction.ShuffleDown, InstType.CallQuaternary, HelperFunctionNames.ShuffleDown);
+ Add(Instruction.ShuffleUp, InstType.CallQuaternary, HelperFunctionNames.ShuffleUp);
+ Add(Instruction.ShuffleXor, InstType.CallQuaternary, HelperFunctionNames.ShuffleXor);
Add(Instruction.Sine, InstType.CallUnary, "sin");
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
Add(Instruction.StoreLocal, InstType.Special);
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs
index 7adc5ad33..b13a74f45 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs
@@ -37,8 +37,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{
switch (dstType)
{
- case VariableType.S32: return $"floatBitsToInt({expr})";
- case VariableType.U32: return $"floatBitsToUint({expr})";
+ case VariableType.Bool: return $"(floatBitsToInt({expr}) != 0)";
+ case VariableType.S32: return $"floatBitsToInt({expr})";
+ case VariableType.U32: return $"floatBitsToUint({expr})";
}
}
else if (dstType == VariableType.F32)
diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
index 264c732df..085325eef 100644
--- a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
+++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs
@@ -118,25 +118,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
Operand srcB = op.IsBImmediate ? Const(op.ImmediateB) : Register(op.Rb);
Operand srcC = op.IsCImmediate ? Const(op.ImmediateC) : Register(op.Rc);
- Operand res = null;
-
- switch (op.ShuffleType)
+ (Operand res, Operand valid) = op.ShuffleType switch
{
- case ShuffleType.Indexed:
- res = context.Shuffle(srcA, srcB, srcC);
- break;
- case ShuffleType.Up:
- res = context.ShuffleUp(srcA, srcB, srcC);
- break;
- case ShuffleType.Down:
- res = context.ShuffleDown(srcA, srcB, srcC);
- break;
- case ShuffleType.Butterfly:
- res = context.ShuffleXor(srcA, srcB, srcC);
- break;
- }
+ ShuffleType.Indexed => context.Shuffle(srcA, srcB, srcC),
+ ShuffleType.Up => context.ShuffleUp(srcA, srcB, srcC),
+ ShuffleType.Down => context.ShuffleDown(srcA, srcB, srcC),
+ ShuffleType.Butterfly => context.ShuffleXor(srcA, srcB, srcC),
+ _ => (null, null)
+ };
context.Copy(GetDest(context), res);
+ context.Copy(pred, valid);
}
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs
index 48dda24b1..0f545e56f 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs
@@ -4,8 +4,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
{
Operand Dest { get; set; }
+ int DestsCount { get; }
int SourcesCount { get; }
+ Operand GetDest(int index);
Operand GetSource(int index);
void SetSource(int index, Operand operand);
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
index a86a278a7..4f0801b7b 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs
@@ -6,25 +6,42 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
{
public Instruction Inst { get; private set; }
- private Operand _dest;
+ private Operand[] _dests;
public Operand Dest
{
- get => _dest;
- set => _dest = AssignDest(value);
+ get
+ {
+ return _dests.Length != 0 ? _dests[0] : null;
+ }
+ set
+ {
+ if (value != null && value.Type == OperandType.LocalVariable)
+ {
+ value.AsgOp = this;
+ }
+
+ if (value != null)
+ {
+ _dests = new[] { value };
+ }
+ else
+ {
+ _dests = Array.Empty();
+ }
+ }
}
+ public int DestsCount => _dests.Length;
+
private Operand[] _sources;
public int SourcesCount => _sources.Length;
public int Index { get; }
- public Operation(Instruction inst, Operand dest, params Operand[] sources)
+ private Operation(Operand[] sources)
{
- Inst = inst;
- Dest = dest;
-
// The array may be modified externally, so we store a copy.
_sources = (Operand[])sources.Clone();
@@ -39,11 +56,42 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
}
}
- public Operation(
- Instruction inst,
- int index,
- Operand dest,
- params Operand[] sources) : this(inst, dest, sources)
+ public Operation(Instruction inst, int index, Operand[] dests, Operand[] sources) : this(sources)
+ {
+ Inst = inst;
+ Index = index;
+
+ // The array may be modified externally, so we store a copy.
+ _dests = (Operand[])dests.Clone();
+
+ for (int dstIndex = 0; dstIndex < dests.Length; dstIndex++)
+ {
+ Operand dest = dests[dstIndex];
+
+ if (dest != null && dest.Type == OperandType.LocalVariable)
+ {
+ dest.AsgOp = this;
+ }
+ }
+ }
+
+ public Operation(Instruction inst, Operand dest, params Operand[] sources) : this(sources)
+ {
+ Inst = inst;
+
+ if (dest != null)
+ {
+ dest.AsgOp = this;
+
+ _dests = new[] { dest };
+ }
+ else
+ {
+ _dests = Array.Empty();
+ }
+ }
+
+ public Operation(Instruction inst, int index, Operand dest, params Operand[] sources) : this(inst, dest, sources)
{
Index = index;
}
@@ -67,14 +115,9 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
}
}
- private Operand AssignDest(Operand dest)
+ public Operand GetDest(int index)
{
- if (dest != null && dest.Type == OperandType.LocalVariable)
- {
- dest.AsgOp = this;
- }
-
- return dest;
+ return _dests[index];
}
public Operand GetSource(int index)
@@ -82,6 +125,23 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
return _sources[index];
}
+ public void SetDest(int index, Operand dest)
+ {
+ Operand oldDest = _dests[index];
+
+ if (oldDest != null && oldDest.Type == OperandType.LocalVariable)
+ {
+ oldDest.AsgOp = null;
+ }
+
+ if (dest != null && dest.Type == OperandType.LocalVariable)
+ {
+ dest.AsgOp = this;
+ }
+
+ _dests[index] = dest;
+ }
+
public void SetSource(int index, Operand source)
{
Operand oldSrc = _sources[index];
diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs
index 13ff41bd1..8fa25ae9b 100644
--- a/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs
+++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
@@ -12,6 +13,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
set => _dest = AssignDest(value);
}
+ public int DestsCount => _dest != null ? 1 : 0;
+
private HashSet _blocks;
private class PhiSource
@@ -64,6 +67,16 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
}
}
+ public Operand GetDest(int index)
+ {
+ if (index != 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ return _dest;
+ }
+
public Operand GetSource(int index)
{
return _sources[index].Operand;
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
index fcf39cc05..88cfe7299 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs
@@ -94,13 +94,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool);
Add(Instruction.LogicalNot, VariableType.Bool, VariableType.Bool);
Add(Instruction.LogicalOr, VariableType.Bool, VariableType.Bool, VariableType.Bool);
- Add(Instruction.ShiftLeft, VariableType.Int, VariableType.Int, VariableType.Int);
- Add(Instruction.ShiftRightS32, VariableType.S32, VariableType.S32, VariableType.Int);
- Add(Instruction.ShiftRightU32, VariableType.U32, VariableType.U32, VariableType.Int);
- Add(Instruction.Shuffle, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32);
- Add(Instruction.ShuffleDown, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32);
- Add(Instruction.ShuffleUp, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32);
- Add(Instruction.ShuffleXor, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32);
Add(Instruction.Maximum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.MaximumU32, VariableType.U32, VariableType.U32, VariableType.U32);
Add(Instruction.Minimum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
@@ -113,6 +106,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.PackHalf2x16, VariableType.U32, VariableType.F32, VariableType.F32);
Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.Round, VariableType.Scalar, VariableType.Scalar);
+ Add(Instruction.ShiftLeft, VariableType.Int, VariableType.Int, VariableType.Int);
+ Add(Instruction.ShiftRightS32, VariableType.S32, VariableType.S32, VariableType.Int);
+ Add(Instruction.ShiftRightU32, VariableType.U32, VariableType.U32, VariableType.Int);
+ Add(Instruction.Shuffle, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
+ Add(Instruction.ShuffleDown, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
+ Add(Instruction.ShuffleUp, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
+ Add(Instruction.ShuffleXor, VariableType.F32, VariableType.F32, VariableType.U32, VariableType.U32, VariableType.Bool);
Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
index 8c73e698e..497cffc88 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs
@@ -77,6 +77,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
bool isCall = inst == Instruction.Call;
int sourcesCount = operation.SourcesCount;
+ int outDestsCount = operation.DestsCount != 0 ? operation.DestsCount - 1 : 0;
List callOutOperands = new List();
@@ -93,7 +94,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
sourcesCount += callOutOperands.Count;
}
- IAstNode[] sources = new IAstNode[sourcesCount];
+ IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
for (int index = 0; index < operation.SourcesCount; index++)
{
@@ -110,6 +111,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
callOutOperands.Clear();
}
+ for (int index = 0; index < outDestsCount; index++)
+ {
+ AstOperand oper = context.GetOperandDef(operation.GetDest(1 + index));
+
+ oper.VarType = InstructionInfo.GetSrcVarType(inst, sourcesCount + index);
+
+ sources[sourcesCount + index] = oper;
+ }
+
AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
{
return new AstTextureOperation(
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
index d5d30f12e..df6b0839a 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs
@@ -37,6 +37,17 @@ namespace Ryujinx.Graphics.Shader.Translation
return dest;
}
+ public (Operand, Operand) Add(Instruction inst, (Operand, Operand) dest, params Operand[] sources)
+ {
+ Operand[] dests = new[] { dest.Item1, dest.Item2 };
+
+ Operation operation = new Operation(inst, 0, dests, sources);
+
+ Add(operation);
+
+ return dest;
+ }
+
public void Add(Operation operation)
{
_operations.Add(operation);
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
index b2418c2e1..dcefb5913 100644
--- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
+++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs
@@ -588,24 +588,24 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.ShiftRightU32, Local(), a, b);
}
- public static Operand Shuffle(this EmitterContext context, Operand a, Operand b, Operand c)
+ public static (Operand, Operand) Shuffle(this EmitterContext context, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.Shuffle, Local(), a, b, c);
+ return context.Add(Instruction.Shuffle, (Local(), Local()), a, b, c);
}
- public static Operand ShuffleDown(this EmitterContext context, Operand a, Operand b, Operand c)
+ public static (Operand, Operand) ShuffleDown(this EmitterContext context, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.ShuffleDown, Local(), a, b, c);
+ return context.Add(Instruction.ShuffleDown, (Local(), Local()), a, b, c);
}
- public static Operand ShuffleUp(this EmitterContext context, Operand a, Operand b, Operand c)
+ public static (Operand, Operand) ShuffleUp(this EmitterContext context, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.ShuffleUp, Local(), a, b, c);
+ return context.Add(Instruction.ShuffleUp, (Local(), Local()), a, b, c);
}
- public static Operand ShuffleXor(this EmitterContext context, Operand a, Operand b, Operand c)
+ public static (Operand, Operand) ShuffleXor(this EmitterContext context, Operand a, Operand b, Operand c)
{
- return context.Add(Instruction.ShuffleXor, Local(), a, b, c);
+ return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
}
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c)
diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
index 32c7d2f0d..9a0815c3a 100644
--- a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs
@@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
else if ((operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation)) ||
(operation.Inst == Instruction.ShuffleXor && MatchDdxOrDdy(operation)))
{
- if (operation.Dest.UseOps.Count == 0)
+ if (DestHasNoUses(operation))
{
RemoveNode(block, node);
}
@@ -260,6 +260,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (src.UseOps.Remove(node) && src.UseOps.Count == 0)
{
+ Debug.Assert(src.AsgOp != null);
nodes.Enqueue(src.AsgOp);
}
}
@@ -268,7 +269,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
private static bool IsUnused(INode node)
{
- return !HasSideEffects(node) && DestIsLocalVar(node) && node.Dest.UseOps.Count == 0;
+ return !HasSideEffects(node) && DestIsLocalVar(node) && DestHasNoUses(node);
}
private static bool HasSideEffects(INode node)
@@ -298,7 +299,33 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
private static bool DestIsLocalVar(INode node)
{
- return node.Dest != null && node.Dest.Type == OperandType.LocalVariable;
+ if (node.DestsCount == 0)
+ {
+ return false;
+ }
+
+ for (int index = 0; index < node.DestsCount; index++)
+ {
+ if (node.GetDest(index).Type != OperandType.LocalVariable)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool DestHasNoUses(INode node)
+ {
+ for (int index = 0; index < node.DestsCount; index++)
+ {
+ if (node.GetDest(index).UseOps.Count != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
}
}
}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
index 637ce8fe0..b1fd6470e 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs
@@ -6,6 +6,8 @@ namespace Ryujinx.Graphics.Shader.Translation
{
public ShaderStage Stage { get; }
+ public bool GpPassthrough { get; }
+
public OutputTopology OutputTopology { get; }
public int MaxOutputVertices { get; }
@@ -33,6 +35,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
{
Stage = ShaderStage.Compute;
+ GpPassthrough = false;
OutputTopology = OutputTopology.PointList;
MaxOutputVertices = 0;
LocalMemorySize = 0;
@@ -51,6 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
{
Stage = header.Stage;
+ GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
OutputTopology = header.OutputTopology;
MaxOutputVertices = header.MaxOutputVertexCount;
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs b/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs
index 1218d591b..ff5932e15 100644
--- a/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs
+++ b/Ryujinx.Graphics.Shader/Translation/ShaderHeader.cs
@@ -81,6 +81,8 @@ namespace Ryujinx.Graphics.Shader.Translation
public int SassVersion { get; }
+ public bool GpPassthrough { get; }
+
public bool DoesLoadOrStore { get; }
public bool DoesFp64 { get; }
@@ -136,6 +138,8 @@ namespace Ryujinx.Graphics.Shader.Translation
SassVersion = commonWord0.Extract(17, 4);
+ GpPassthrough = commonWord0.Extract(24);
+
DoesLoadOrStore = commonWord0.Extract(26);
DoesFp64 = commonWord0.Extract(27);
diff --git a/Ryujinx.Graphics.Shader/Translation/Ssa.cs b/Ryujinx.Graphics.Shader/Translation/Ssa.cs
index a4d763be7..ff812e646 100644
--- a/Ryujinx.Graphics.Shader/Translation/Ssa.cs
+++ b/Ryujinx.Graphics.Shader/Translation/Ssa.cs
@@ -116,13 +116,18 @@ namespace Ryujinx.Graphics.Shader.Translation
operation.SetSource(index, RenameLocal(operation.GetSource(index)));
}
- if (operation.Dest != null && operation.Dest.Type == OperandType.Register)
+ for (int index = 0; index < operation.DestsCount; index++)
{
- Operand local = Local();
+ Operand dest = operation.GetDest(index);
- localDefs[GetKeyFromRegister(operation.Dest.GetRegister())] = local;
+ if (dest.Type == OperandType.Register)
+ {
+ Operand local = Local();
- operation.Dest = local;
+ localDefs[GetKeyFromRegister(dest.GetRegister())] = local;
+
+ operation.SetDest(index, local);
+ }
}
}
@@ -185,9 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation
return operand;
}
- LinkedListNode node = block.Operations.First;
-
- while (node != null)
+ for (LinkedListNode node = block.Operations.First; node != null; node = node.Next)
{
if (node.Value is Operation operation)
{
@@ -196,8 +199,6 @@ namespace Ryujinx.Graphics.Shader.Translation
operation.SetSource(index, RenameGlobal(operation.GetSource(index)));
}
}
-
- node = node.Next;
}
}
}
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index dac28ef3c..76c0a139a 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -18,7 +18,7 @@
"system_region": "USA",
"system_time_zone": "UTC",
"system_time_offset": 0,
- "docked_mode": false,
+ "docked_mode": true,
"enable_discord_integration": true,
"check_updates_on_start": true,
"show_confirm_exit": true,
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade
index 6457ecfeb..5ead8dd82 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.glade
+++ b/Ryujinx/Ui/Windows/SettingsWindow.glade
@@ -1458,7 +1458,7 @@