Merge branch 'master' into pptc_and_pool_enhancements
This commit is contained in:
commit
da236ad750
31 changed files with 377 additions and 142 deletions
|
@ -1475,9 +1475,11 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
int sizeF = op.Size & 1;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -1494,9 +1496,16 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
int sizeF = op.Size & 1;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -1652,7 +1661,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitScalarRoundOpF(context, FPRoundingMode.TowardsMinusInfinity);
|
EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsMinusInfinity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1667,7 +1676,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitVectorRoundOpF(context, FPRoundingMode.TowardsMinusInfinity);
|
EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsMinusInfinity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1682,7 +1691,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitScalarRoundOpF(context, FPRoundingMode.ToNearest);
|
EmitSse41ScalarRoundOpF(context, FPRoundingMode.ToNearest);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1697,7 +1706,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitVectorRoundOpF(context, FPRoundingMode.ToNearest);
|
EmitSse41VectorRoundOpF(context, FPRoundingMode.ToNearest);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1712,7 +1721,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitScalarRoundOpF(context, FPRoundingMode.TowardsPlusInfinity);
|
EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsPlusInfinity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1727,7 +1736,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitVectorRoundOpF(context, FPRoundingMode.TowardsPlusInfinity);
|
EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsPlusInfinity);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1778,7 +1787,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitScalarRoundOpF(context, FPRoundingMode.TowardsZero);
|
EmitSse41ScalarRoundOpF(context, FPRoundingMode.TowardsZero);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1793,7 +1802,7 @@ namespace ARMeilleure.Instructions
|
||||||
{
|
{
|
||||||
if (Optimizations.UseSse41)
|
if (Optimizations.UseSse41)
|
||||||
{
|
{
|
||||||
EmitVectorRoundOpF(context, FPRoundingMode.TowardsZero);
|
EmitSse41VectorRoundOpF(context, FPRoundingMode.TowardsZero);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1810,9 +1819,11 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
int sizeF = op.Size & 1;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -1829,9 +1840,16 @@ namespace ARMeilleure.Instructions
|
||||||
|
|
||||||
int sizeF = op.Size & 1;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -3498,7 +3516,7 @@ namespace ARMeilleure.Instructions
|
||||||
return context.ConditionalSelect(cmp, op1, op2);
|
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;
|
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
|
@ -3520,7 +3538,7 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVec(op.Rd), res);
|
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;
|
OpCodeSimd op = (OpCodeSimd)context.CurrOp;
|
||||||
|
|
||||||
|
@ -3538,6 +3556,35 @@ namespace ARMeilleure.Instructions
|
||||||
context.Copy(GetVec(op.Rd), res);
|
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(
|
public static void EmitSse2VectorIsNaNOpF(
|
||||||
ArmEmitterContext context,
|
ArmEmitterContext context,
|
||||||
Operand opF,
|
Operand opF,
|
||||||
|
|
|
@ -479,7 +479,7 @@ namespace Ryujinx.Configuration
|
||||||
System.Region.Value = Region.USA;
|
System.Region.Value = Region.USA;
|
||||||
System.TimeZone.Value = "UTC";
|
System.TimeZone.Value = "UTC";
|
||||||
System.SystemTimeOffset.Value = 0;
|
System.SystemTimeOffset.Value = 0;
|
||||||
System.EnableDockedMode.Value = false;
|
System.EnableDockedMode.Value = true;
|
||||||
EnableDiscordIntegration.Value = true;
|
EnableDiscordIntegration.Value = true;
|
||||||
CheckUpdatesOnStart.Value = true;
|
CheckUpdatesOnStart.Value = true;
|
||||||
ShowConfirmExit.Value = true;
|
ShowConfirmExit.Value = true;
|
||||||
|
|
|
@ -8,14 +8,16 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a pool of GPU resources, such as samplers or textures.
|
/// Represents a pool of GPU resources, such as samplers or textures.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of the GPU resource</typeparam>
|
/// <typeparam name="T1">Type of the GPU resource</typeparam>
|
||||||
abstract class Pool<T> : IDisposable
|
/// <typeparam name="T2">Type of the descriptor</typeparam>
|
||||||
|
abstract class Pool<T1, T2> : IDisposable where T2 : unmanaged
|
||||||
{
|
{
|
||||||
protected const int DescriptorSize = 0x20;
|
protected const int DescriptorSize = 0x20;
|
||||||
|
|
||||||
protected GpuContext Context;
|
protected GpuContext Context;
|
||||||
|
|
||||||
protected T[] Items;
|
protected T1[] Items;
|
||||||
|
protected T2[] DescriptorCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum ID value of resources on the pool (inclusive).
|
/// 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;
|
ulong size = (ulong)(uint)count * DescriptorSize;
|
||||||
|
|
||||||
Items = new T[count];
|
Items = new T1[count];
|
||||||
|
DescriptorCache = new T2[count];
|
||||||
|
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
|
@ -56,12 +59,23 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
_modifiedDelegate = RegionModified;
|
_modifiedDelegate = RegionModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the descriptor for a given ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">ID of the descriptor. This is effectively a zero-based index</param>
|
||||||
|
/// <returns>The descriptor</returns>
|
||||||
|
public T2 GetDescriptor(int id)
|
||||||
|
{
|
||||||
|
return Context.PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the GPU resource with the given ID.
|
/// Gets the GPU resource with the given ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">ID of the resource. This is effectively a zero-based index</param>
|
/// <param name="id">ID of the resource. This is effectively a zero-based index</param>
|
||||||
/// <returns>The GPU resource with the given ID</returns>
|
/// <returns>The GPU resource with the given ID</returns>
|
||||||
public abstract T Get(int id);
|
public abstract T1 Get(int id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronizes host memory with guest memory.
|
/// 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 InvalidateRangeImpl(ulong address, ulong size);
|
||||||
|
|
||||||
protected abstract void Delete(T item);
|
protected abstract void Delete(T1 item);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs the disposal of all resources stored on the pool.
|
/// Performs the disposal of all resources stored on the pool.
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
|
@ -244,5 +246,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
return ((Word2 >> 12) & 0xfff) * Frac8ToF32;
|
return ((Word2 >> 12) & 0xfff) * Frac8ToF32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if two descriptors are equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The descriptor to compare against</param>
|
||||||
|
/// <returns>True if they are equal, false otherwise</returns>
|
||||||
|
public bool Equals(ref SamplerDescriptor other)
|
||||||
|
{
|
||||||
|
return Unsafe.As<SamplerDescriptor, Vector256<byte>>(ref this).Equals(Unsafe.As<SamplerDescriptor, Vector256<byte>>(ref other));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sampler pool.
|
/// Sampler pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class SamplerPool : Pool<Sampler>
|
class SamplerPool : Pool<Sampler, SamplerDescriptor>
|
||||||
{
|
{
|
||||||
private int _sequenceNumber;
|
private int _sequenceNumber;
|
||||||
|
|
||||||
|
@ -38,11 +38,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (sampler == null)
|
if (sampler == null)
|
||||||
{
|
{
|
||||||
SamplerDescriptor descriptor = Context.PhysicalMemory.Read<SamplerDescriptor>(Address + (ulong)id * DescriptorSize);
|
SamplerDescriptor descriptor = GetDescriptor(id);
|
||||||
|
|
||||||
sampler = new Sampler(Context, descriptor);
|
sampler = new Sampler(Context, descriptor);
|
||||||
|
|
||||||
Items[id] = sampler;
|
Items[id] = sampler;
|
||||||
|
|
||||||
|
DescriptorCache[id] = descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sampler;
|
return sampler;
|
||||||
|
@ -65,6 +67,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
if (sampler != null)
|
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();
|
sampler.Dispose();
|
||||||
|
|
||||||
Items[id] = null;
|
Items[id] = null;
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
|
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
|
@ -248,5 +250,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if two descriptors are equal.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">The descriptor to compare against</param>
|
||||||
|
/// <returns>True if they are equal, false otherwise</returns>
|
||||||
|
public bool Equals(ref TextureDescriptor other)
|
||||||
|
{
|
||||||
|
return Unsafe.As<TextureDescriptor, Vector256<byte>>(ref this).Equals(Unsafe.As<TextureDescriptor, Vector256<byte>>(ref other));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -10,7 +9,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Texture pool.
|
/// Texture pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class TexturePool : Pool<Texture>
|
class TexturePool : Pool<Texture, TextureDescriptor>
|
||||||
{
|
{
|
||||||
private int _sequenceNumber;
|
private int _sequenceNumber;
|
||||||
|
|
||||||
|
@ -65,6 +64,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
texture.IncrementReferenceCount();
|
texture.IncrementReferenceCount();
|
||||||
|
|
||||||
Items[id] = texture;
|
Items[id] = texture;
|
||||||
|
|
||||||
|
DescriptorCache[id] = descriptor;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -91,16 +92,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the texture descriptor from a given texture ID.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
|
||||||
/// <returns>The texture descriptor</returns>
|
|
||||||
public TextureDescriptor GetDescriptor(int id)
|
|
||||||
{
|
|
||||||
return Context.PhysicalMemory.Read<TextureDescriptor>(Address + (ulong)id * DescriptorSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implementation of the texture pool range invalidation.
|
/// Implementation of the texture pool range invalidation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -122,8 +113,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
|
||||||
// If the descriptors are the same, the texture is the same,
|
// If the descriptors are the same, the texture is the same,
|
||||||
// we don't need to remove as it was not modified. Just continue.
|
// we don't need to remove as it was not modified. Just continue.
|
||||||
if (texture.Info.GpuAddress == descriptor.UnpackAddress() &&
|
if (descriptor.Equals(ref DescriptorCache[id]))
|
||||||
texture.IsExactMatch(GetInfo(descriptor, out _), TextureSearchFlags.Strict) != TextureMatchQuality.NoMatch)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the codegen (to be changed when codegen or guest format change).
|
/// Version of the codegen (to be changed when codegen or guest format change).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const ulong ShaderCodeGenVersion = 1790;
|
private const ulong ShaderCodeGenVersion = 1961;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the shader cache.
|
/// Creates a new instance of the shader cache.
|
||||||
|
|
|
@ -26,6 +26,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
context.AppendLine("#extension GL_ARB_compute_shader : enable");
|
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("#pragma optionNV(fastmath off)");
|
||||||
|
|
||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
|
@ -33,20 +38,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;");
|
context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;");
|
||||||
context.AppendLine();
|
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)
|
if (context.Config.Stage == ShaderStage.Compute)
|
||||||
{
|
{
|
||||||
int localMemorySize = BitUtils.DivRoundUp(context.Config.GpuAccessor.QueryComputeLocalMemorySize(), 4);
|
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.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)
|
if (info.IAttributes.Count != 0)
|
||||||
{
|
{
|
||||||
DeclareInputAttributes(context, info);
|
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}";
|
string name = $"{DefaultNames.IAttributePrefix}{attr}";
|
||||||
|
|
||||||
if ((context.Config.Flags & TranslationFlags.Feedback) != 0)
|
if ((context.Config.Flags & TranslationFlags.Feedback) != 0)
|
||||||
|
@ -440,12 +460,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
{
|
{
|
||||||
char swzMask = "xyzw"[c];
|
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
|
else
|
||||||
{
|
{
|
||||||
context.AppendLine($"layout (location = {attr}) {iq}in vec4 {name}{suffix};");
|
context.AppendLine($"layout ({pass}location = {attr}) {iq}in vec4 {name}{suffix};");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
// compiler may eliminate them.
|
// compiler may eliminate them.
|
||||||
// (Not needed for fragment shader as it is the last stage).
|
// (Not needed for fragment shader as it is the last stage).
|
||||||
if (context.Config.Stage != ShaderStage.Compute &&
|
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++)
|
for (int attr = 0; attr < Declarations.MaxAttributes; attr++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 clamp = mask & 0x1fu;
|
||||||
uint segMask = (mask >> 8) & 0x1fu;
|
uint segMask = (mask >> 8) & 0x1fu;
|
||||||
uint minThreadId = gl_SubGroupInvocationARB & segMask;
|
uint minThreadId = gl_SubGroupInvocationARB & segMask;
|
||||||
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
||||||
uint srcThreadId = (index & ~segMask) | minThreadId;
|
uint srcThreadId = (index & ~segMask) | minThreadId;
|
||||||
return (srcThreadId <= maxThreadId) ? readInvocationARB(x, srcThreadId) : x;
|
valid = srcThreadId <= maxThreadId;
|
||||||
|
return valid ? readInvocationARB(x, srcThreadId) : x;
|
||||||
}
|
}
|
|
@ -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 clamp = mask & 0x1fu;
|
||||||
uint segMask = (mask >> 8) & 0x1fu;
|
uint segMask = (mask >> 8) & 0x1fu;
|
||||||
uint minThreadId = gl_SubGroupInvocationARB & segMask;
|
uint minThreadId = gl_SubGroupInvocationARB & segMask;
|
||||||
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
||||||
uint srcThreadId = gl_SubGroupInvocationARB + index;
|
uint srcThreadId = gl_SubGroupInvocationARB + index;
|
||||||
return (srcThreadId <= maxThreadId) ? readInvocationARB(x, srcThreadId) : x;
|
valid = srcThreadId <= maxThreadId;
|
||||||
|
return valid ? readInvocationARB(x, srcThreadId) : x;
|
||||||
}
|
}
|
|
@ -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 clamp = mask & 0x1fu;
|
||||||
uint segMask = (mask >> 8) & 0x1fu;
|
uint segMask = (mask >> 8) & 0x1fu;
|
||||||
uint minThreadId = gl_SubGroupInvocationARB & segMask;
|
uint minThreadId = gl_SubGroupInvocationARB & segMask;
|
||||||
uint srcThreadId = gl_SubGroupInvocationARB - index;
|
uint srcThreadId = gl_SubGroupInvocationARB - index;
|
||||||
return (srcThreadId >= minThreadId) ? readInvocationARB(x, srcThreadId) : x;
|
valid = srcThreadId >= minThreadId;
|
||||||
|
return valid ? readInvocationARB(x, srcThreadId) : x;
|
||||||
}
|
}
|
|
@ -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 clamp = mask & 0x1fu;
|
||||||
uint segMask = (mask >> 8) & 0x1fu;
|
uint segMask = (mask >> 8) & 0x1fu;
|
||||||
uint minThreadId = gl_SubGroupInvocationARB & segMask;
|
uint minThreadId = gl_SubGroupInvocationARB & segMask;
|
||||||
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
||||||
uint srcThreadId = gl_SubGroupInvocationARB ^ index;
|
uint srcThreadId = gl_SubGroupInvocationARB ^ index;
|
||||||
return (srcThreadId <= maxThreadId) ? readInvocationARB(x, srcThreadId) : x;
|
valid = srcThreadId <= maxThreadId;
|
||||||
|
return valid ? readInvocationARB(x, srcThreadId) : x;
|
||||||
}
|
}
|
|
@ -88,13 +88,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
Add(Instruction.LoopContinue, InstType.OpNullary, "continue");
|
Add(Instruction.LoopContinue, InstType.OpNullary, "continue");
|
||||||
Add(Instruction.PackDouble2x32, InstType.Special);
|
Add(Instruction.PackDouble2x32, InstType.Special);
|
||||||
Add(Instruction.PackHalf2x16, 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.Maximum, InstType.CallBinary, "max");
|
||||||
Add(Instruction.MaximumU32, InstType.CallBinary, "max");
|
Add(Instruction.MaximumU32, InstType.CallBinary, "max");
|
||||||
Add(Instruction.MemoryBarrier, InstType.CallNullary, "memoryBarrier");
|
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.ReciprocalSquareRoot, InstType.CallUnary, "inversesqrt");
|
||||||
Add(Instruction.Return, InstType.OpNullary, "return");
|
Add(Instruction.Return, InstType.OpNullary, "return");
|
||||||
Add(Instruction.Round, InstType.CallUnary, "roundEven");
|
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.Sine, InstType.CallUnary, "sin");
|
||||||
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
|
Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt");
|
||||||
Add(Instruction.StoreLocal, InstType.Special);
|
Add(Instruction.StoreLocal, InstType.Special);
|
||||||
|
|
|
@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||||
{
|
{
|
||||||
switch (dstType)
|
switch (dstType)
|
||||||
{
|
{
|
||||||
|
case VariableType.Bool: return $"(floatBitsToInt({expr}) != 0)";
|
||||||
case VariableType.S32: return $"floatBitsToInt({expr})";
|
case VariableType.S32: return $"floatBitsToInt({expr})";
|
||||||
case VariableType.U32: return $"floatBitsToUint({expr})";
|
case VariableType.U32: return $"floatBitsToUint({expr})";
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,25 +118,17 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||||
Operand srcB = op.IsBImmediate ? Const(op.ImmediateB) : Register(op.Rb);
|
Operand srcB = op.IsBImmediate ? Const(op.ImmediateB) : Register(op.Rb);
|
||||||
Operand srcC = op.IsCImmediate ? Const(op.ImmediateC) : Register(op.Rc);
|
Operand srcC = op.IsCImmediate ? Const(op.ImmediateC) : Register(op.Rc);
|
||||||
|
|
||||||
Operand res = null;
|
(Operand res, Operand valid) = op.ShuffleType switch
|
||||||
|
|
||||||
switch (op.ShuffleType)
|
|
||||||
{
|
{
|
||||||
case ShuffleType.Indexed:
|
ShuffleType.Indexed => context.Shuffle(srcA, srcB, srcC),
|
||||||
res = context.Shuffle(srcA, srcB, srcC);
|
ShuffleType.Up => context.ShuffleUp(srcA, srcB, srcC),
|
||||||
break;
|
ShuffleType.Down => context.ShuffleDown(srcA, srcB, srcC),
|
||||||
case ShuffleType.Up:
|
ShuffleType.Butterfly => context.ShuffleXor(srcA, srcB, srcC),
|
||||||
res = context.ShuffleUp(srcA, srcB, srcC);
|
_ => (null, null)
|
||||||
break;
|
};
|
||||||
case ShuffleType.Down:
|
|
||||||
res = context.ShuffleDown(srcA, srcB, srcC);
|
|
||||||
break;
|
|
||||||
case ShuffleType.Butterfly:
|
|
||||||
res = context.ShuffleXor(srcA, srcB, srcC);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(GetDest(context), res);
|
context.Copy(GetDest(context), res);
|
||||||
|
context.Copy(pred, valid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,8 +4,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
{
|
{
|
||||||
Operand Dest { get; set; }
|
Operand Dest { get; set; }
|
||||||
|
|
||||||
|
int DestsCount { get; }
|
||||||
int SourcesCount { get; }
|
int SourcesCount { get; }
|
||||||
|
|
||||||
|
Operand GetDest(int index);
|
||||||
Operand GetSource(int index);
|
Operand GetSource(int index);
|
||||||
|
|
||||||
void SetSource(int index, Operand operand);
|
void SetSource(int index, Operand operand);
|
||||||
|
|
|
@ -6,13 +6,33 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
{
|
{
|
||||||
public Instruction Inst { get; private set; }
|
public Instruction Inst { get; private set; }
|
||||||
|
|
||||||
private Operand _dest;
|
private Operand[] _dests;
|
||||||
|
|
||||||
public Operand Dest
|
public Operand Dest
|
||||||
{
|
{
|
||||||
get => _dest;
|
get
|
||||||
set => _dest = AssignDest(value);
|
{
|
||||||
|
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<Operand>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int DestsCount => _dests.Length;
|
||||||
|
|
||||||
private Operand[] _sources;
|
private Operand[] _sources;
|
||||||
|
|
||||||
|
@ -20,11 +40,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
|
|
||||||
public int Index { get; }
|
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.
|
// The array may be modified externally, so we store a copy.
|
||||||
_sources = (Operand[])sources.Clone();
|
_sources = (Operand[])sources.Clone();
|
||||||
|
|
||||||
|
@ -39,11 +56,42 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operation(
|
public Operation(Instruction inst, int index, Operand[] dests, Operand[] sources) : this(sources)
|
||||||
Instruction inst,
|
{
|
||||||
int index,
|
Inst = inst;
|
||||||
Operand dest,
|
Index = index;
|
||||||
params Operand[] sources) : this(inst, dest, sources)
|
|
||||||
|
// 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<Operand>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operation(Instruction inst, int index, Operand dest, params Operand[] sources) : this(inst, dest, sources)
|
||||||
{
|
{
|
||||||
Index = index;
|
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)
|
return _dests[index];
|
||||||
{
|
|
||||||
dest.AsgOp = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Operand GetSource(int index)
|
public Operand GetSource(int index)
|
||||||
|
@ -82,6 +125,23 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
return _sources[index];
|
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)
|
public void SetSource(int index, Operand source)
|
||||||
{
|
{
|
||||||
Operand oldSrc = _sources[index];
|
Operand oldSrc = _sources[index];
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
|
@ -12,6 +13,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||||
set => _dest = AssignDest(value);
|
set => _dest = AssignDest(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int DestsCount => _dest != null ? 1 : 0;
|
||||||
|
|
||||||
private HashSet<BasicBlock> _blocks;
|
private HashSet<BasicBlock> _blocks;
|
||||||
|
|
||||||
private class PhiSource
|
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)
|
public Operand GetSource(int index)
|
||||||
{
|
{
|
||||||
return _sources[index].Operand;
|
return _sources[index].Operand;
|
||||||
|
|
|
@ -94,13 +94,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool);
|
Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool);
|
||||||
Add(Instruction.LogicalNot, VariableType.Bool, VariableType.Bool);
|
Add(Instruction.LogicalNot, VariableType.Bool, VariableType.Bool);
|
||||||
Add(Instruction.LogicalOr, VariableType.Bool, 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.Maximum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
|
||||||
Add(Instruction.MaximumU32, VariableType.U32, VariableType.U32, VariableType.U32);
|
Add(Instruction.MaximumU32, VariableType.U32, VariableType.U32, VariableType.U32);
|
||||||
Add(Instruction.Minimum, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar);
|
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.PackHalf2x16, VariableType.U32, VariableType.F32, VariableType.F32);
|
||||||
Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar);
|
Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar);
|
||||||
Add(Instruction.Round, 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.Sine, VariableType.Scalar, VariableType.Scalar);
|
||||||
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
|
Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar);
|
||||||
Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
|
Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.U32);
|
||||||
|
|
|
@ -77,6 +77,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
bool isCall = inst == Instruction.Call;
|
bool isCall = inst == Instruction.Call;
|
||||||
|
|
||||||
int sourcesCount = operation.SourcesCount;
|
int sourcesCount = operation.SourcesCount;
|
||||||
|
int outDestsCount = operation.DestsCount != 0 ? operation.DestsCount - 1 : 0;
|
||||||
|
|
||||||
List<Operand> callOutOperands = new List<Operand>();
|
List<Operand> callOutOperands = new List<Operand>();
|
||||||
|
|
||||||
|
@ -93,7 +94,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
sourcesCount += callOutOperands.Count;
|
sourcesCount += callOutOperands.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
IAstNode[] sources = new IAstNode[sourcesCount];
|
IAstNode[] sources = new IAstNode[sourcesCount + outDestsCount];
|
||||||
|
|
||||||
for (int index = 0; index < operation.SourcesCount; index++)
|
for (int index = 0; index < operation.SourcesCount; index++)
|
||||||
{
|
{
|
||||||
|
@ -110,6 +111,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||||
callOutOperands.Clear();
|
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)
|
AstTextureOperation GetAstTextureOperation(TextureOperation texOp)
|
||||||
{
|
{
|
||||||
return new AstTextureOperation(
|
return new AstTextureOperation(
|
||||||
|
|
|
@ -37,6 +37,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return dest;
|
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)
|
public void Add(Operation operation)
|
||||||
{
|
{
|
||||||
_operations.Add(operation);
|
_operations.Add(operation);
|
||||||
|
|
|
@ -588,24 +588,24 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return context.Add(Instruction.ShiftRightU32, Local(), a, b);
|
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)
|
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
else if ((operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation)) ||
|
else if ((operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation)) ||
|
||||||
(operation.Inst == Instruction.ShuffleXor && MatchDdxOrDdy(operation)))
|
(operation.Inst == Instruction.ShuffleXor && MatchDdxOrDdy(operation)))
|
||||||
{
|
{
|
||||||
if (operation.Dest.UseOps.Count == 0)
|
if (DestHasNoUses(operation))
|
||||||
{
|
{
|
||||||
RemoveNode(block, node);
|
RemoveNode(block, node);
|
||||||
}
|
}
|
||||||
|
@ -260,6 +260,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
if (src.UseOps.Remove(node) && src.UseOps.Count == 0)
|
if (src.UseOps.Remove(node) && src.UseOps.Count == 0)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(src.AsgOp != null);
|
||||||
nodes.Enqueue(src.AsgOp);
|
nodes.Enqueue(src.AsgOp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -268,7 +269,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
private static bool IsUnused(INode node)
|
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)
|
private static bool HasSideEffects(INode node)
|
||||||
|
@ -298,7 +299,33 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||||
|
|
||||||
private static bool DestIsLocalVar(INode node)
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,6 +6,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
public ShaderStage Stage { get; }
|
public ShaderStage Stage { get; }
|
||||||
|
|
||||||
|
public bool GpPassthrough { get; }
|
||||||
|
|
||||||
public OutputTopology OutputTopology { get; }
|
public OutputTopology OutputTopology { get; }
|
||||||
|
|
||||||
public int MaxOutputVertices { get; }
|
public int MaxOutputVertices { get; }
|
||||||
|
@ -33,6 +35,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
|
public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
|
||||||
{
|
{
|
||||||
Stage = ShaderStage.Compute;
|
Stage = ShaderStage.Compute;
|
||||||
|
GpPassthrough = false;
|
||||||
OutputTopology = OutputTopology.PointList;
|
OutputTopology = OutputTopology.PointList;
|
||||||
MaxOutputVertices = 0;
|
MaxOutputVertices = 0;
|
||||||
LocalMemorySize = 0;
|
LocalMemorySize = 0;
|
||||||
|
@ -51,6 +54,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
|
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts)
|
||||||
{
|
{
|
||||||
Stage = header.Stage;
|
Stage = header.Stage;
|
||||||
|
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
|
||||||
OutputTopology = header.OutputTopology;
|
OutputTopology = header.OutputTopology;
|
||||||
MaxOutputVertices = header.MaxOutputVertexCount;
|
MaxOutputVertices = header.MaxOutputVertexCount;
|
||||||
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
|
LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize;
|
||||||
|
|
|
@ -81,6 +81,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
public int SassVersion { get; }
|
public int SassVersion { get; }
|
||||||
|
|
||||||
|
public bool GpPassthrough { get; }
|
||||||
|
|
||||||
public bool DoesLoadOrStore { get; }
|
public bool DoesLoadOrStore { get; }
|
||||||
public bool DoesFp64 { get; }
|
public bool DoesFp64 { get; }
|
||||||
|
|
||||||
|
@ -136,6 +138,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
|
||||||
SassVersion = commonWord0.Extract(17, 4);
|
SassVersion = commonWord0.Extract(17, 4);
|
||||||
|
|
||||||
|
GpPassthrough = commonWord0.Extract(24);
|
||||||
|
|
||||||
DoesLoadOrStore = commonWord0.Extract(26);
|
DoesLoadOrStore = commonWord0.Extract(26);
|
||||||
DoesFp64 = commonWord0.Extract(27);
|
DoesFp64 = commonWord0.Extract(27);
|
||||||
|
|
||||||
|
|
|
@ -116,13 +116,18 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
operation.SetSource(index, RenameLocal(operation.GetSource(index)));
|
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 dest = operation.GetDest(index);
|
||||||
|
|
||||||
|
if (dest.Type == OperandType.Register)
|
||||||
{
|
{
|
||||||
Operand local = Local();
|
Operand local = Local();
|
||||||
|
|
||||||
localDefs[GetKeyFromRegister(operation.Dest.GetRegister())] = local;
|
localDefs[GetKeyFromRegister(dest.GetRegister())] = local;
|
||||||
|
|
||||||
operation.Dest = local;
|
operation.SetDest(index, local);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,9 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
return operand;
|
return operand;
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkedListNode<INode> node = block.Operations.First;
|
for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
|
||||||
|
|
||||||
while (node != null)
|
|
||||||
{
|
{
|
||||||
if (node.Value is Operation operation)
|
if (node.Value is Operation operation)
|
||||||
{
|
{
|
||||||
|
@ -196,8 +199,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||||
operation.SetSource(index, RenameGlobal(operation.GetSource(index)));
|
operation.SetSource(index, RenameGlobal(operation.GetSource(index)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
node = node.Next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
"system_region": "USA",
|
"system_region": "USA",
|
||||||
"system_time_zone": "UTC",
|
"system_time_zone": "UTC",
|
||||||
"system_time_offset": 0,
|
"system_time_offset": 0,
|
||||||
"docked_mode": false,
|
"docked_mode": true,
|
||||||
"enable_discord_integration": true,
|
"enable_discord_integration": true,
|
||||||
"check_updates_on_start": true,
|
"check_updates_on_start": true,
|
||||||
"show_confirm_exit": true,
|
"show_confirm_exit": true,
|
||||||
|
|
|
@ -1458,7 +1458,7 @@
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkCheckButton" id="_ptcToggle">
|
<object class="GtkCheckButton" id="_ptcToggle">
|
||||||
<property name="label" translatable="yes">Enable Profiled Persistent Translation Cache</property>
|
<property name="label" translatable="yes">Enable PPTC (Profiled Persistent Translation Cache)</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">True</property>
|
<property name="can-focus">True</property>
|
||||||
<property name="receives-default">False</property>
|
<property name="receives-default">False</property>
|
||||||
|
|
|
@ -1171,7 +1171,7 @@
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"title": "Enable Docked Mode",
|
"title": "Enable Docked Mode",
|
||||||
"description": "Enables or disables Docked Mode",
|
"description": "Enables or disables Docked Mode",
|
||||||
"default": false,
|
"default": true,
|
||||||
"examples": [
|
"examples": [
|
||||||
true,
|
true,
|
||||||
false
|
false
|
||||||
|
|
Loading…
Reference in a new issue