Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3e5c211394 | ||
|
153b8bfc7c | ||
|
c6a699414a | ||
|
2563f88de0 | ||
|
b0b7843d5c | ||
|
6ed613a6e6 | ||
|
64079c034c |
2
.github/reviewers.yml
vendored
2
.github/reviewers.yml
vendored
@@ -29,4 +29,4 @@ infra:
|
|||||||
- TSRBerry
|
- TSRBerry
|
||||||
|
|
||||||
default:
|
default:
|
||||||
- @developers
|
- '@developers'
|
||||||
|
4
.github/update_reviewers.py
vendored
4
.github/update_reviewers.py
vendored
@@ -62,9 +62,9 @@ if __name__ == "__main__":
|
|||||||
sys.stderr.write("usage: <app_id> <private_key_env_name> <installation_id> <repo_path> <pr_id> <config_path>\n")
|
sys.stderr.write("usage: <app_id> <private_key_env_name> <installation_id> <repo_path> <pr_id> <config_path>\n")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
app_id = sys.argv[1]
|
app_id = int(sys.argv[1])
|
||||||
private_key = os.environ[sys.argv[2]]
|
private_key = os.environ[sys.argv[2]]
|
||||||
installation_id = sys.argv[3]
|
installation_id = int(sys.argv[3])
|
||||||
repo_path = sys.argv[4]
|
repo_path = sys.argv[4]
|
||||||
pr_id = int(sys.argv[5])
|
pr_id = int(sys.argv[5])
|
||||||
config_path = Path(sys.argv[6])
|
config_path = Path(sys.argv[6])
|
||||||
|
@@ -52,6 +52,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
public readonly int MaximumComputeSharedMemorySize;
|
public readonly int MaximumComputeSharedMemorySize;
|
||||||
public readonly float MaximumSupportedAnisotropy;
|
public readonly float MaximumSupportedAnisotropy;
|
||||||
|
public readonly int ShaderSubgroupSize;
|
||||||
public readonly int StorageBufferOffsetAlignment;
|
public readonly int StorageBufferOffsetAlignment;
|
||||||
|
|
||||||
public readonly int GatherBiasPrecision;
|
public readonly int GatherBiasPrecision;
|
||||||
@@ -101,6 +102,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
uint maximumImagesPerStage,
|
uint maximumImagesPerStage,
|
||||||
int maximumComputeSharedMemorySize,
|
int maximumComputeSharedMemorySize,
|
||||||
float maximumSupportedAnisotropy,
|
float maximumSupportedAnisotropy,
|
||||||
|
int shaderSubgroupSize,
|
||||||
int storageBufferOffsetAlignment,
|
int storageBufferOffsetAlignment,
|
||||||
int gatherBiasPrecision)
|
int gatherBiasPrecision)
|
||||||
{
|
{
|
||||||
@@ -148,6 +150,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
MaximumImagesPerStage = maximumImagesPerStage;
|
MaximumImagesPerStage = maximumImagesPerStage;
|
||||||
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
|
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
|
||||||
MaximumSupportedAnisotropy = maximumSupportedAnisotropy;
|
MaximumSupportedAnisotropy = maximumSupportedAnisotropy;
|
||||||
|
ShaderSubgroupSize = shaderSubgroupSize;
|
||||||
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
||||||
GatherBiasPrecision = gatherBiasPrecision;
|
GatherBiasPrecision = gatherBiasPrecision;
|
||||||
}
|
}
|
||||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 5576;
|
private const uint CodeGenVersion = 5540;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -137,6 +137,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
|
public int QueryHostStorageBufferOffsetAlignment() => _context.Capabilities.StorageBufferOffsetAlignment;
|
||||||
|
|
||||||
|
public int QueryHostSubgroupSize() => _context.Capabilities.ShaderSubgroupSize;
|
||||||
|
|
||||||
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
|
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
|
||||||
|
|
||||||
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
|
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
|
||||||
|
@@ -7,5 +7,6 @@
|
|||||||
public const int MaxVertexAttribs = 16;
|
public const int MaxVertexAttribs = 16;
|
||||||
public const int MaxVertexBuffers = 16;
|
public const int MaxVertexBuffers = 16;
|
||||||
public const int MaxTransformFeedbackBuffers = 4;
|
public const int MaxTransformFeedbackBuffers = 4;
|
||||||
|
public const int MaxSubgroupSize = 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -175,6 +175,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
maximumImagesPerStage: 8,
|
maximumImagesPerStage: 8,
|
||||||
maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize,
|
maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize,
|
||||||
maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy,
|
maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy,
|
||||||
|
shaderSubgroupSize: Constants.MaxSubgroupSize,
|
||||||
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
|
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
|
||||||
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan.
|
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan.
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
{
|
{
|
||||||
context.AppendLine("#extension GL_KHR_shader_subgroup_basic : enable");
|
context.AppendLine("#extension GL_KHR_shader_subgroup_basic : enable");
|
||||||
context.AppendLine("#extension GL_KHR_shader_subgroup_ballot : enable");
|
context.AppendLine("#extension GL_KHR_shader_subgroup_ballot : enable");
|
||||||
|
context.AppendLine("#extension GL_KHR_shader_subgroup_shuffle : enable");
|
||||||
}
|
}
|
||||||
|
|
||||||
context.AppendLine("#extension GL_ARB_shader_group_vote : enable");
|
context.AppendLine("#extension GL_ARB_shader_group_vote : enable");
|
||||||
@@ -201,26 +202,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl");
|
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.Shuffle) != 0)
|
|
||||||
{
|
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/Shuffle.glsl");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.ShuffleDown) != 0)
|
|
||||||
{
|
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleDown.glsl");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.ShuffleUp) != 0)
|
|
||||||
{
|
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleUp.glsl");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.ShuffleXor) != 0)
|
|
||||||
{
|
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/ShuffleXor.glsl");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0)
|
if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0)
|
||||||
{
|
{
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl");
|
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl");
|
||||||
|
@@ -5,10 +5,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
public static string MultiplyHighS32 = "Helper_MultiplyHighS32";
|
public static string MultiplyHighS32 = "Helper_MultiplyHighS32";
|
||||||
public static string MultiplyHighU32 = "Helper_MultiplyHighU32";
|
public static string MultiplyHighU32 = "Helper_MultiplyHighU32";
|
||||||
|
|
||||||
public static string Shuffle = "Helper_Shuffle";
|
|
||||||
public static string ShuffleDown = "Helper_ShuffleDown";
|
|
||||||
public static string ShuffleUp = "Helper_ShuffleUp";
|
|
||||||
public static string ShuffleXor = "Helper_ShuffleXor";
|
|
||||||
public static string SwizzleAdd = "Helper_SwizzleAdd";
|
public static string SwizzleAdd = "Helper_SwizzleAdd";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
float Helper_Shuffle(float x, uint index, uint mask, out bool valid)
|
|
||||||
{
|
|
||||||
uint clamp = mask & 0x1fu;
|
|
||||||
uint segMask = (mask >> 8) & 0x1fu;
|
|
||||||
uint minThreadId = $SUBGROUP_INVOCATION$ & segMask;
|
|
||||||
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
|
||||||
uint srcThreadId = (index & ~segMask) | minThreadId;
|
|
||||||
valid = srcThreadId <= maxThreadId;
|
|
||||||
float v = $SUBGROUP_BROADCAST$(x, srcThreadId);
|
|
||||||
return valid ? v : x;
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
float Helper_ShuffleDown(float x, uint index, uint mask, out bool valid)
|
|
||||||
{
|
|
||||||
uint clamp = mask & 0x1fu;
|
|
||||||
uint segMask = (mask >> 8) & 0x1fu;
|
|
||||||
uint minThreadId = $SUBGROUP_INVOCATION$ & segMask;
|
|
||||||
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
|
||||||
uint srcThreadId = $SUBGROUP_INVOCATION$ + index;
|
|
||||||
valid = srcThreadId <= maxThreadId;
|
|
||||||
float v = $SUBGROUP_BROADCAST$(x, srcThreadId);
|
|
||||||
return valid ? v : x;
|
|
||||||
}
|
|
@@ -1,9 +0,0 @@
|
|||||||
float Helper_ShuffleUp(float x, uint index, uint mask, out bool valid)
|
|
||||||
{
|
|
||||||
uint segMask = (mask >> 8) & 0x1fu;
|
|
||||||
uint minThreadId = $SUBGROUP_INVOCATION$ & segMask;
|
|
||||||
uint srcThreadId = $SUBGROUP_INVOCATION$ - index;
|
|
||||||
valid = int(srcThreadId) >= int(minThreadId);
|
|
||||||
float v = $SUBGROUP_BROADCAST$(x, srcThreadId);
|
|
||||||
return valid ? v : x;
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
float Helper_ShuffleXor(float x, uint index, uint mask, out bool valid)
|
|
||||||
{
|
|
||||||
uint clamp = mask & 0x1fu;
|
|
||||||
uint segMask = (mask >> 8) & 0x1fu;
|
|
||||||
uint minThreadId = $SUBGROUP_INVOCATION$ & segMask;
|
|
||||||
uint maxThreadId = minThreadId | (clamp & ~segMask);
|
|
||||||
uint srcThreadId = $SUBGROUP_INVOCATION$ ^ index;
|
|
||||||
valid = srcThreadId <= maxThreadId;
|
|
||||||
float v = $SUBGROUP_BROADCAST$(x, srcThreadId);
|
|
||||||
return valid ? v : x;
|
|
||||||
}
|
|
@@ -9,6 +9,7 @@ using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenFSI;
|
|||||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
|
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
|
||||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenMemory;
|
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenMemory;
|
||||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenPacking;
|
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenPacking;
|
||||||
|
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenShuffle;
|
||||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenVector;
|
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenVector;
|
||||||
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
||||||
|
|
||||||
@@ -174,6 +175,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
case Instruction.PackHalf2x16:
|
case Instruction.PackHalf2x16:
|
||||||
return PackHalf2x16(context, operation);
|
return PackHalf2x16(context, operation);
|
||||||
|
|
||||||
|
case Instruction.Shuffle:
|
||||||
|
return Shuffle(context, operation);
|
||||||
|
|
||||||
case Instruction.Store:
|
case Instruction.Store:
|
||||||
return Store(context, operation);
|
return Store(context, operation);
|
||||||
|
|
||||||
|
@@ -13,14 +13,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
AggregateType dstType = GetSrcVarType(operation.Inst, 0);
|
AggregateType dstType = GetSrcVarType(operation.Inst, 0);
|
||||||
|
|
||||||
string arg = GetSoureExpr(context, operation.GetSource(0), dstType);
|
string arg = GetSoureExpr(context, operation.GetSource(0), dstType);
|
||||||
|
char component = "xyzw"[operation.Index];
|
||||||
|
|
||||||
if (context.HostCapabilities.SupportsShaderBallot)
|
if (context.HostCapabilities.SupportsShaderBallot)
|
||||||
{
|
{
|
||||||
return $"unpackUint2x32(ballotARB({arg})).x";
|
return $"unpackUint2x32(ballotARB({arg})).{component}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return $"subgroupBallot({arg}).x";
|
return $"subgroupBallot({arg}).{component}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -108,10 +108,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3);
|
Add(Instruction.ShiftLeft, InstType.OpBinary, "<<", 3);
|
||||||
Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3);
|
Add(Instruction.ShiftRightS32, InstType.OpBinary, ">>", 3);
|
||||||
Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3);
|
Add(Instruction.ShiftRightU32, InstType.OpBinary, ">>", 3);
|
||||||
Add(Instruction.Shuffle, InstType.CallQuaternary, HelperFunctionNames.Shuffle);
|
Add(Instruction.Shuffle, InstType.Special);
|
||||||
Add(Instruction.ShuffleDown, InstType.CallQuaternary, HelperFunctionNames.ShuffleDown);
|
Add(Instruction.ShuffleDown, InstType.CallBinary, "subgroupShuffleDown");
|
||||||
Add(Instruction.ShuffleUp, InstType.CallQuaternary, HelperFunctionNames.ShuffleUp);
|
Add(Instruction.ShuffleUp, InstType.CallBinary, "subgroupShuffleUp");
|
||||||
Add(Instruction.ShuffleXor, InstType.CallQuaternary, HelperFunctionNames.ShuffleXor);
|
Add(Instruction.ShuffleXor, InstType.CallBinary, "subgroupShuffleXor");
|
||||||
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.Store, InstType.Special);
|
Add(Instruction.Store, InstType.Special);
|
||||||
|
@@ -0,0 +1,25 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
|
||||||
|
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||||
|
{
|
||||||
|
static class InstGenShuffle
|
||||||
|
{
|
||||||
|
public static string Shuffle(CodeGenContext context, AstOperation operation)
|
||||||
|
{
|
||||||
|
string value = GetSoureExpr(context, operation.GetSource(0), AggregateType.FP32);
|
||||||
|
string index = GetSoureExpr(context, operation.GetSource(1), AggregateType.U32);
|
||||||
|
|
||||||
|
if (context.HostCapabilities.SupportsShaderBallot)
|
||||||
|
{
|
||||||
|
return $"readInvocationARB({value}, {index})";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $"subgroupShuffle({value}, {index})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -231,7 +231,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
|
||||||
var maskVector = context.GroupNonUniformBallot(uvec4Type, execution, context.Get(AggregateType.Bool, source));
|
var maskVector = context.GroupNonUniformBallot(uvec4Type, execution, context.Get(AggregateType.Bool, source));
|
||||||
var mask = context.CompositeExtract(context.TypeU32(), maskVector, (SpvLiteralInteger)0);
|
var mask = context.CompositeExtract(context.TypeU32(), maskVector, (SpvLiteralInteger)operation.Index);
|
||||||
|
|
||||||
return new OperationResult(AggregateType.U32, mask);
|
return new OperationResult(AggregateType.U32, mask);
|
||||||
}
|
}
|
||||||
@@ -1100,117 +1100,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static OperationResult GenerateShuffle(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateShuffle(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var x = context.GetFP32(operation.GetSource(0));
|
var value = context.GetFP32(operation.GetSource(0));
|
||||||
var index = context.GetU32(operation.GetSource(1));
|
var index = context.GetU32(operation.GetSource(1));
|
||||||
var mask = context.GetU32(operation.GetSource(2));
|
|
||||||
|
|
||||||
var const31 = context.Constant(context.TypeU32(), 31);
|
var result = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), value, index);
|
||||||
var const8 = context.Constant(context.TypeU32(), 8);
|
|
||||||
|
|
||||||
var clamp = context.BitwiseAnd(context.TypeU32(), mask, const31);
|
|
||||||
var segMask = context.BitwiseAnd(context.TypeU32(), context.ShiftRightLogical(context.TypeU32(), mask, const8), const31);
|
|
||||||
var notSegMask = context.Not(context.TypeU32(), segMask);
|
|
||||||
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
|
||||||
var indexNotSegMask = context.BitwiseAnd(context.TypeU32(), index, notSegMask);
|
|
||||||
|
|
||||||
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
|
||||||
|
|
||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
|
||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
|
||||||
var srcThreadId = context.BitwiseOr(context.TypeU32(), indexNotSegMask, minThreadId);
|
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
|
||||||
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
|
||||||
|
|
||||||
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid));
|
|
||||||
|
|
||||||
return new OperationResult(AggregateType.FP32, result);
|
return new OperationResult(AggregateType.FP32, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateShuffleDown(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateShuffleDown(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var x = context.GetFP32(operation.GetSource(0));
|
var value = context.GetFP32(operation.GetSource(0));
|
||||||
var index = context.GetU32(operation.GetSource(1));
|
var index = context.GetU32(operation.GetSource(1));
|
||||||
var mask = context.GetU32(operation.GetSource(2));
|
|
||||||
|
|
||||||
var const31 = context.Constant(context.TypeU32(), 31);
|
var result = context.GroupNonUniformShuffleDown(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), value, index);
|
||||||
var const8 = context.Constant(context.TypeU32(), 8);
|
|
||||||
|
|
||||||
var clamp = context.BitwiseAnd(context.TypeU32(), mask, const31);
|
|
||||||
var segMask = context.BitwiseAnd(context.TypeU32(), context.ShiftRightLogical(context.TypeU32(), mask, const8), const31);
|
|
||||||
var notSegMask = context.Not(context.TypeU32(), segMask);
|
|
||||||
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
|
||||||
|
|
||||||
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
|
||||||
|
|
||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
|
||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
|
||||||
var srcThreadId = context.IAdd(context.TypeU32(), threadId, index);
|
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
|
||||||
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
|
||||||
|
|
||||||
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid));
|
|
||||||
|
|
||||||
return new OperationResult(AggregateType.FP32, result);
|
return new OperationResult(AggregateType.FP32, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateShuffleUp(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateShuffleUp(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var x = context.GetFP32(operation.GetSource(0));
|
var value = context.GetFP32(operation.GetSource(0));
|
||||||
var index = context.GetU32(operation.GetSource(1));
|
var index = context.GetU32(operation.GetSource(1));
|
||||||
var mask = context.GetU32(operation.GetSource(2));
|
|
||||||
|
|
||||||
var const31 = context.Constant(context.TypeU32(), 31);
|
var result = context.GroupNonUniformShuffleUp(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), value, index);
|
||||||
var const8 = context.Constant(context.TypeU32(), 8);
|
|
||||||
|
|
||||||
var segMask = context.BitwiseAnd(context.TypeU32(), context.ShiftRightLogical(context.TypeU32(), mask, const8), const31);
|
|
||||||
|
|
||||||
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
|
||||||
|
|
||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
|
||||||
var srcThreadId = context.ISub(context.TypeU32(), threadId, index);
|
|
||||||
var valid = context.SGreaterThanEqual(context.TypeBool(), srcThreadId, minThreadId);
|
|
||||||
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
|
||||||
|
|
||||||
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid));
|
|
||||||
|
|
||||||
return new OperationResult(AggregateType.FP32, result);
|
return new OperationResult(AggregateType.FP32, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateShuffleXor(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateShuffleXor(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var x = context.GetFP32(operation.GetSource(0));
|
var value = context.GetFP32(operation.GetSource(0));
|
||||||
var index = context.GetU32(operation.GetSource(1));
|
var index = context.GetU32(operation.GetSource(1));
|
||||||
var mask = context.GetU32(operation.GetSource(2));
|
|
||||||
|
|
||||||
var const31 = context.Constant(context.TypeU32(), 31);
|
var result = context.GroupNonUniformShuffleXor(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), value, index);
|
||||||
var const8 = context.Constant(context.TypeU32(), 8);
|
|
||||||
|
|
||||||
var clamp = context.BitwiseAnd(context.TypeU32(), mask, const31);
|
|
||||||
var segMask = context.BitwiseAnd(context.TypeU32(), context.ShiftRightLogical(context.TypeU32(), mask, const8), const31);
|
|
||||||
var notSegMask = context.Not(context.TypeU32(), segMask);
|
|
||||||
var clampNotSegMask = context.BitwiseAnd(context.TypeU32(), clamp, notSegMask);
|
|
||||||
|
|
||||||
var threadId = GetScalarInput(context, IoVariable.SubgroupLaneId);
|
|
||||||
|
|
||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
|
||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
|
||||||
var srcThreadId = context.BitwiseXor(context.TypeU32(), threadId, index);
|
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
|
||||||
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
|
||||||
|
|
||||||
context.Store(context.GetLocalPointer(validLocal), context.BitcastIfNeeded(validLocal.VarType, AggregateType.Bool, valid));
|
|
||||||
|
|
||||||
return new OperationResult(AggregateType.FP32, result);
|
return new OperationResult(AggregateType.FP32, result);
|
||||||
}
|
}
|
||||||
|
@@ -28,12 +28,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
_poolLock = new object();
|
_poolLock = new object();
|
||||||
}
|
}
|
||||||
|
|
||||||
private const HelperFunctionsMask NeedsInvocationIdMask =
|
private const HelperFunctionsMask NeedsInvocationIdMask = HelperFunctionsMask.SwizzleAdd;
|
||||||
HelperFunctionsMask.Shuffle |
|
|
||||||
HelperFunctionsMask.ShuffleDown |
|
|
||||||
HelperFunctionsMask.ShuffleUp |
|
|
||||||
HelperFunctionsMask.ShuffleXor |
|
|
||||||
HelperFunctionsMask.SwizzleAdd;
|
|
||||||
|
|
||||||
public static byte[] Generate(StructuredProgramInfo info, CodeGenParameters parameters)
|
public static byte[] Generate(StructuredProgramInfo info, CodeGenParameters parameters)
|
||||||
{
|
{
|
||||||
|
@@ -307,6 +307,9 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||||||
case InstName.Sts:
|
case InstName.Sts:
|
||||||
context.SetUsedFeature(FeatureFlags.SharedMemory);
|
context.SetUsedFeature(FeatureFlags.SharedMemory);
|
||||||
break;
|
break;
|
||||||
|
case InstName.Shfl:
|
||||||
|
context.SetUsedFeature(FeatureFlags.Shuffle);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
block.OpCodes.Add(op);
|
block.OpCodes.Add(op);
|
||||||
|
@@ -194,6 +194,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries host shader subgroup size.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Host shader subgroup size in invocations</returns>
|
||||||
|
int QueryHostSubgroupSize()
|
||||||
|
{
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host support for texture formats with BGRA component order (such as BGRA8).
|
/// Queries host support for texture formats with BGRA component order (such as BGRA8).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -76,7 +76,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
switch (op.SReg)
|
switch (op.SReg)
|
||||||
{
|
{
|
||||||
case SReg.LaneId:
|
case SReg.LaneId:
|
||||||
src = context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
|
src = EmitLoadSubgroupLaneId(context);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.InvocationId:
|
case SReg.InvocationId:
|
||||||
@@ -146,19 +146,19 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SReg.EqMask:
|
case SReg.EqMask:
|
||||||
src = context.Load(StorageKind.Input, IoVariable.SubgroupEqMask, null, Const(0));
|
src = EmitLoadSubgroupMask(context, IoVariable.SubgroupEqMask);
|
||||||
break;
|
break;
|
||||||
case SReg.LtMask:
|
case SReg.LtMask:
|
||||||
src = context.Load(StorageKind.Input, IoVariable.SubgroupLtMask, null, Const(0));
|
src = EmitLoadSubgroupMask(context, IoVariable.SubgroupLtMask);
|
||||||
break;
|
break;
|
||||||
case SReg.LeMask:
|
case SReg.LeMask:
|
||||||
src = context.Load(StorageKind.Input, IoVariable.SubgroupLeMask, null, Const(0));
|
src = EmitLoadSubgroupMask(context, IoVariable.SubgroupLeMask);
|
||||||
break;
|
break;
|
||||||
case SReg.GtMask:
|
case SReg.GtMask:
|
||||||
src = context.Load(StorageKind.Input, IoVariable.SubgroupGtMask, null, Const(0));
|
src = EmitLoadSubgroupMask(context, IoVariable.SubgroupGtMask);
|
||||||
break;
|
break;
|
||||||
case SReg.GeMask:
|
case SReg.GeMask:
|
||||||
src = context.Load(StorageKind.Input, IoVariable.SubgroupGeMask, null, Const(0));
|
src = EmitLoadSubgroupMask(context, IoVariable.SubgroupGeMask);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -169,6 +169,52 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
context.Copy(GetDest(op.Dest), src);
|
context.Copy(GetDest(op.Dest), src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand EmitLoadSubgroupLaneId(EmitterContext context)
|
||||||
|
{
|
||||||
|
if (context.TranslatorContext.GpuAccessor.QueryHostSubgroupSize() <= 32)
|
||||||
|
{
|
||||||
|
return context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.BitwiseAnd(context.Load(StorageKind.Input, IoVariable.SubgroupLaneId), Const(0x1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitLoadSubgroupMask(EmitterContext context, IoVariable ioVariable)
|
||||||
|
{
|
||||||
|
int subgroupSize = context.TranslatorContext.GpuAccessor.QueryHostSubgroupSize();
|
||||||
|
|
||||||
|
if (subgroupSize <= 32)
|
||||||
|
{
|
||||||
|
return context.Load(StorageKind.Input, ioVariable, null, Const(0));
|
||||||
|
}
|
||||||
|
else if (subgroupSize == 64)
|
||||||
|
{
|
||||||
|
Operand laneId = context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
|
||||||
|
Operand low = context.Load(StorageKind.Input, ioVariable, null, Const(0));
|
||||||
|
Operand high = context.Load(StorageKind.Input, ioVariable, null, Const(1));
|
||||||
|
|
||||||
|
return context.ConditionalSelect(context.BitwiseAnd(laneId, Const(32)), high, low);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Operand laneId = context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
|
||||||
|
Operand element = context.ShiftRightU32(laneId, Const(5));
|
||||||
|
|
||||||
|
Operand res = context.Load(StorageKind.Input, ioVariable, null, Const(0));
|
||||||
|
res = context.ConditionalSelect(
|
||||||
|
context.ICompareEqual(element, Const(1)),
|
||||||
|
context.Load(StorageKind.Input, ioVariable, null, Const(1)), res);
|
||||||
|
res = context.ConditionalSelect(
|
||||||
|
context.ICompareEqual(element, Const(2)),
|
||||||
|
context.Load(StorageKind.Input, ioVariable, null, Const(2)), res);
|
||||||
|
res = context.ConditionalSelect(
|
||||||
|
context.ICompareEqual(element, Const(3)),
|
||||||
|
context.Load(StorageKind.Input, ioVariable, null, Const(3)), res);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void SelR(EmitterContext context)
|
public static void SelR(EmitterContext context)
|
||||||
{
|
{
|
||||||
InstSelR op = context.GetOp<InstSelR>();
|
InstSelR op = context.GetOp<InstSelR>();
|
||||||
|
@@ -10,10 +10,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
{
|
{
|
||||||
static partial class InstEmit
|
static partial class InstEmit
|
||||||
{
|
{
|
||||||
private static readonly int[,] _maskLut = new int[,]
|
private static readonly int[][] _maskLut = new int[][]
|
||||||
{
|
{
|
||||||
{ 0b0001, 0b0010, 0b0100, 0b1000, 0b0011, 0b1001, 0b1010, 0b1100 },
|
new int[] { 0b0001, 0b0010, 0b0100, 0b1000, 0b0011, 0b1001, 0b1010, 0b1100 },
|
||||||
{ 0b0111, 0b1011, 0b1101, 0b1110, 0b1111, 0b0000, 0b0000, 0b0000 },
|
new int[] { 0b0111, 0b1011, 0b1101, 0b1110, 0b1111, 0b0000, 0b0000, 0b0000 },
|
||||||
};
|
};
|
||||||
|
|
||||||
public const bool Sample1DAs2D = true;
|
public const bool Sample1DAs2D = true;
|
||||||
@@ -605,7 +605,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) };
|
Operand[] rd1 = new Operand[2] { ConstF(0), ConstF(0) };
|
||||||
|
|
||||||
int handle = imm;
|
int handle = imm;
|
||||||
int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1, writeMask];
|
int componentMask = _maskLut[dest2 == RegisterConsts.RegisterZeroIndex ? 0 : 1][writeMask];
|
||||||
|
|
||||||
int componentsCount = BitOperations.PopCount((uint)componentMask);
|
int componentsCount = BitOperations.PopCount((uint)componentMask);
|
||||||
|
|
||||||
|
@@ -50,20 +50,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
InstVote op = context.GetOp<InstVote>();
|
InstVote op = context.GetOp<InstVote>();
|
||||||
|
|
||||||
Operand pred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
|
Operand pred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
|
||||||
Operand res = null;
|
Operand res = EmitVote(context, op.VoteMode, pred);
|
||||||
|
|
||||||
switch (op.VoteMode)
|
|
||||||
{
|
|
||||||
case VoteMode.All:
|
|
||||||
res = context.VoteAll(pred);
|
|
||||||
break;
|
|
||||||
case VoteMode.Any:
|
|
||||||
res = context.VoteAny(pred);
|
|
||||||
break;
|
|
||||||
case VoteMode.Eq:
|
|
||||||
res = context.VoteAllEqual(pred);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res != null)
|
if (res != null)
|
||||||
{
|
{
|
||||||
@@ -76,7 +63,81 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (op.Dest != RegisterConsts.RegisterZeroIndex)
|
if (op.Dest != RegisterConsts.RegisterZeroIndex)
|
||||||
{
|
{
|
||||||
context.Copy(GetDest(op.Dest), context.Ballot(pred));
|
context.Copy(GetDest(op.Dest), EmitBallot(context, pred));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitVote(EmitterContext context, VoteMode voteMode, Operand pred)
|
||||||
|
{
|
||||||
|
int subgroupSize = context.TranslatorContext.GpuAccessor.QueryHostSubgroupSize();
|
||||||
|
|
||||||
|
if (subgroupSize <= 32)
|
||||||
|
{
|
||||||
|
return voteMode switch
|
||||||
|
{
|
||||||
|
VoteMode.All => context.VoteAll(pred),
|
||||||
|
VoteMode.Any => context.VoteAny(pred),
|
||||||
|
VoteMode.Eq => context.VoteAllEqual(pred),
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emulate vote with ballot masks.
|
||||||
|
// We do that when the GPU thread count is not 32,
|
||||||
|
// since the shader code assumes it is 32.
|
||||||
|
// allInvocations => ballot(pred) == ballot(true),
|
||||||
|
// anyInvocation => ballot(pred) != 0,
|
||||||
|
// allInvocationsEqual => ballot(pred) == balot(true) || ballot(pred) == 0
|
||||||
|
Operand ballotMask = EmitBallot(context, pred);
|
||||||
|
|
||||||
|
Operand AllTrue() => context.ICompareEqual(ballotMask, EmitBallot(context, Const(IrConsts.True)));
|
||||||
|
|
||||||
|
return voteMode switch
|
||||||
|
{
|
||||||
|
VoteMode.All => AllTrue(),
|
||||||
|
VoteMode.Any => context.ICompareNotEqual(ballotMask, Const(0)),
|
||||||
|
VoteMode.Eq => context.BitwiseOr(AllTrue(), context.ICompareEqual(ballotMask, Const(0))),
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand EmitBallot(EmitterContext context, Operand pred)
|
||||||
|
{
|
||||||
|
int subgroupSize = context.TranslatorContext.GpuAccessor.QueryHostSubgroupSize();
|
||||||
|
|
||||||
|
if (subgroupSize <= 32)
|
||||||
|
{
|
||||||
|
return context.Ballot(pred, 0);
|
||||||
|
}
|
||||||
|
else if (subgroupSize == 64)
|
||||||
|
{
|
||||||
|
// TODO: Add support for vector destination and do that with a single operation.
|
||||||
|
|
||||||
|
Operand laneId = context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
|
||||||
|
Operand low = context.Ballot(pred, 0);
|
||||||
|
Operand high = context.Ballot(pred, 1);
|
||||||
|
|
||||||
|
return context.ConditionalSelect(context.BitwiseAnd(laneId, Const(32)), high, low);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Add support for vector destination and do that with a single operation.
|
||||||
|
|
||||||
|
Operand laneId = context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
|
||||||
|
Operand element = context.ShiftRightU32(laneId, Const(5));
|
||||||
|
|
||||||
|
Operand res = context.Ballot(pred, 0);
|
||||||
|
res = context.ConditionalSelect(
|
||||||
|
context.ICompareEqual(element, Const(1)),
|
||||||
|
context.Ballot(pred, 1), res);
|
||||||
|
res = context.ConditionalSelect(
|
||||||
|
context.ICompareEqual(element, Const(2)),
|
||||||
|
context.Ballot(pred, 2), res);
|
||||||
|
res = context.ConditionalSelect(
|
||||||
|
context.ICompareEqual(element, Const(3)),
|
||||||
|
context.Ballot(pred, 3), res);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,10 +12,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleDown.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@@ -7,10 +7,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
{
|
{
|
||||||
MultiplyHighS32 = 1 << 2,
|
MultiplyHighS32 = 1 << 2,
|
||||||
MultiplyHighU32 = 1 << 3,
|
MultiplyHighU32 = 1 << 3,
|
||||||
Shuffle = 1 << 4,
|
|
||||||
ShuffleDown = 1 << 5,
|
|
||||||
ShuffleUp = 1 << 6,
|
|
||||||
ShuffleXor = 1 << 7,
|
|
||||||
SwizzleAdd = 1 << 10,
|
SwizzleAdd = 1 << 10,
|
||||||
FSI = 1 << 11,
|
FSI = 1 << 11,
|
||||||
}
|
}
|
||||||
|
@@ -109,14 +109,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Add(Instruction.PackDouble2x32, AggregateType.FP64, AggregateType.U32, AggregateType.U32);
|
Add(Instruction.PackDouble2x32, AggregateType.FP64, AggregateType.U32, AggregateType.U32);
|
||||||
Add(Instruction.PackHalf2x16, AggregateType.U32, AggregateType.FP32, AggregateType.FP32);
|
Add(Instruction.PackHalf2x16, AggregateType.U32, AggregateType.FP32, AggregateType.FP32);
|
||||||
Add(Instruction.ReciprocalSquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.ReciprocalSquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
|
Add(Instruction.Return, AggregateType.Void, AggregateType.U32);
|
||||||
Add(Instruction.Round, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Round, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.ShiftLeft, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.ShiftLeft, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.ShiftRightS32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.ShiftRightS32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.ShiftRightU32, AggregateType.U32, AggregateType.U32, AggregateType.S32);
|
Add(Instruction.ShiftRightU32, AggregateType.U32, AggregateType.U32, AggregateType.S32);
|
||||||
Add(Instruction.Shuffle, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
|
Add(Instruction.Shuffle, AggregateType.FP32, AggregateType.FP32, AggregateType.U32);
|
||||||
Add(Instruction.ShuffleDown, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
|
Add(Instruction.ShuffleDown, AggregateType.FP32, AggregateType.FP32, AggregateType.U32);
|
||||||
Add(Instruction.ShuffleUp, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
|
Add(Instruction.ShuffleUp, AggregateType.FP32, AggregateType.FP32, AggregateType.U32);
|
||||||
Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32, AggregateType.U32, AggregateType.Bool);
|
Add(Instruction.ShuffleXor, AggregateType.FP32, AggregateType.FP32, AggregateType.U32);
|
||||||
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.Store, AggregateType.Void);
|
Add(Instruction.Store, AggregateType.Void);
|
||||||
@@ -131,7 +132,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Add(Instruction.VoteAll, AggregateType.Bool, AggregateType.Bool);
|
Add(Instruction.VoteAll, AggregateType.Bool, AggregateType.Bool);
|
||||||
Add(Instruction.VoteAllEqual, AggregateType.Bool, AggregateType.Bool);
|
Add(Instruction.VoteAllEqual, AggregateType.Bool, AggregateType.Bool);
|
||||||
Add(Instruction.VoteAny, AggregateType.Bool, AggregateType.Bool);
|
Add(Instruction.VoteAny, AggregateType.Bool, AggregateType.Bool);
|
||||||
#pragma warning restore IDE0055v
|
#pragma warning restore IDE0055
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Add(Instruction inst, AggregateType destType, params AggregateType[] srcTypes)
|
private static void Add(Instruction inst, AggregateType destType, params AggregateType[] srcTypes)
|
||||||
|
@@ -282,18 +282,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
case Instruction.MultiplyHighU32:
|
case Instruction.MultiplyHighU32:
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighU32;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighU32;
|
||||||
break;
|
break;
|
||||||
case Instruction.Shuffle:
|
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.Shuffle;
|
|
||||||
break;
|
|
||||||
case Instruction.ShuffleDown:
|
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.ShuffleDown;
|
|
||||||
break;
|
|
||||||
case Instruction.ShuffleUp:
|
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.ShuffleUp;
|
|
||||||
break;
|
|
||||||
case Instruction.ShuffleXor:
|
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.ShuffleXor;
|
|
||||||
break;
|
|
||||||
case Instruction.SwizzleAdd:
|
case Instruction.SwizzleAdd:
|
||||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd;
|
context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd;
|
||||||
break;
|
break;
|
||||||
|
@@ -112,9 +112,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return context.Add(Instruction.AtomicXor, storageKind, Local(), Const(binding), e0, e1, value);
|
return context.Add(Instruction.AtomicXor, storageKind, Local(), Const(binding), e0, e1, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand Ballot(this EmitterContext context, Operand a)
|
public static Operand Ballot(this EmitterContext context, Operand a, int index)
|
||||||
{
|
{
|
||||||
return context.Add(Instruction.Ballot, Local(), a);
|
Operand dest = Local();
|
||||||
|
|
||||||
|
context.Add(new Operation(Instruction.Ballot, index, dest, a));
|
||||||
|
|
||||||
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Operand Barrier(this EmitterContext context)
|
public static Operand Barrier(this EmitterContext context)
|
||||||
@@ -782,21 +786,41 @@ 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)
|
||||||
|
{
|
||||||
|
return context.Add(Instruction.Shuffle, Local(), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
public static (Operand, 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(), 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)
|
||||||
|
{
|
||||||
|
return context.Add(Instruction.ShuffleDown, Local(), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
public static (Operand, 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(), 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)
|
||||||
|
{
|
||||||
|
return context.Add(Instruction.ShuffleUp, Local(), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
public static (Operand, 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(), 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)
|
||||||
|
{
|
||||||
|
return context.Add(Instruction.ShuffleXor, Local(), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
public static (Operand, 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(), Local()), a, b, c);
|
return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
|
||||||
|
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
InstanceId = 1 << 3,
|
InstanceId = 1 << 3,
|
||||||
DrawParameters = 1 << 4,
|
DrawParameters = 1 << 4,
|
||||||
RtLayer = 1 << 5,
|
RtLayer = 1 << 5,
|
||||||
|
Shuffle = 1 << 6,
|
||||||
FixedFuncAttr = 1 << 9,
|
FixedFuncAttr = 1 << 9,
|
||||||
LocalMemory = 1 << 10,
|
LocalMemory = 1 << 10,
|
||||||
SharedMemory = 1 << 11,
|
SharedMemory = 1 << 11,
|
||||||
|
@@ -56,6 +56,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return functionId;
|
return functionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int GetOrCreateShuffleFunctionId(HelperFunctionName functionName, int subgroupSize)
|
||||||
|
{
|
||||||
|
if (_functionIds.TryGetValue((int)functionName, out int functionId))
|
||||||
|
{
|
||||||
|
return functionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Function function = GenerateShuffleFunction(functionName, subgroupSize);
|
||||||
|
functionId = AddFunction(function);
|
||||||
|
_functionIds.Add((int)functionName, functionId);
|
||||||
|
|
||||||
|
return functionId;
|
||||||
|
}
|
||||||
|
|
||||||
private Function GenerateFunction(HelperFunctionName functionName)
|
private Function GenerateFunction(HelperFunctionName functionName)
|
||||||
{
|
{
|
||||||
return functionName switch
|
return functionName switch
|
||||||
@@ -216,6 +230,137 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, $"SharedStore{bitSize}_{id}", false, 2, 0);
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, $"SharedStore{bitSize}_{id}", false, 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Function GenerateShuffleFunction(HelperFunctionName functionName, int subgroupSize)
|
||||||
|
{
|
||||||
|
return functionName switch
|
||||||
|
{
|
||||||
|
HelperFunctionName.Shuffle => GenerateShuffle(subgroupSize),
|
||||||
|
HelperFunctionName.ShuffleDown => GenerateShuffleDown(subgroupSize),
|
||||||
|
HelperFunctionName.ShuffleUp => GenerateShuffleUp(subgroupSize),
|
||||||
|
HelperFunctionName.ShuffleXor => GenerateShuffleXor(subgroupSize),
|
||||||
|
_ => throw new ArgumentException($"Invalid function name {functionName}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function GenerateShuffle(int subgroupSize)
|
||||||
|
{
|
||||||
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
Operand value = Argument(0);
|
||||||
|
Operand index = Argument(1);
|
||||||
|
Operand mask = Argument(2);
|
||||||
|
|
||||||
|
Operand clamp = context.BitwiseAnd(mask, Const(0x1f));
|
||||||
|
Operand segMask = context.BitwiseAnd(context.ShiftRightU32(mask, Const(8)), Const(0x1f));
|
||||||
|
Operand minThreadId = context.BitwiseAnd(GenerateLoadSubgroupLaneId(context, subgroupSize), segMask);
|
||||||
|
Operand maxThreadId = context.BitwiseOr(context.BitwiseAnd(clamp, context.BitwiseNot(segMask)), minThreadId);
|
||||||
|
Operand srcThreadId = context.BitwiseOr(context.BitwiseAnd(index, context.BitwiseNot(segMask)), minThreadId);
|
||||||
|
Operand valid = context.ICompareLessOrEqualUnsigned(srcThreadId, maxThreadId);
|
||||||
|
|
||||||
|
context.Copy(Argument(3), valid);
|
||||||
|
|
||||||
|
Operand result = context.Shuffle(value, GenerateSubgroupShuffleIndex(context, srcThreadId, subgroupSize));
|
||||||
|
|
||||||
|
context.Return(context.ConditionalSelect(valid, result, value));
|
||||||
|
|
||||||
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "Shuffle", true, 3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function GenerateShuffleDown(int subgroupSize)
|
||||||
|
{
|
||||||
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
Operand value = Argument(0);
|
||||||
|
Operand index = Argument(1);
|
||||||
|
Operand mask = Argument(2);
|
||||||
|
|
||||||
|
Operand clamp = context.BitwiseAnd(mask, Const(0x1f));
|
||||||
|
Operand segMask = context.BitwiseAnd(context.ShiftRightU32(mask, Const(8)), Const(0x1f));
|
||||||
|
Operand laneId = GenerateLoadSubgroupLaneId(context, subgroupSize);
|
||||||
|
Operand minThreadId = context.BitwiseAnd(laneId, segMask);
|
||||||
|
Operand maxThreadId = context.BitwiseOr(context.BitwiseAnd(clamp, context.BitwiseNot(segMask)), minThreadId);
|
||||||
|
Operand srcThreadId = context.IAdd(laneId, index);
|
||||||
|
Operand valid = context.ICompareLessOrEqualUnsigned(srcThreadId, maxThreadId);
|
||||||
|
|
||||||
|
context.Copy(Argument(3), valid);
|
||||||
|
|
||||||
|
Operand result = context.Shuffle(value, GenerateSubgroupShuffleIndex(context, srcThreadId, subgroupSize));
|
||||||
|
|
||||||
|
context.Return(context.ConditionalSelect(valid, result, value));
|
||||||
|
|
||||||
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "ShuffleDown", true, 3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function GenerateShuffleUp(int subgroupSize)
|
||||||
|
{
|
||||||
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
Operand value = Argument(0);
|
||||||
|
Operand index = Argument(1);
|
||||||
|
Operand mask = Argument(2);
|
||||||
|
|
||||||
|
Operand segMask = context.BitwiseAnd(context.ShiftRightU32(mask, Const(8)), Const(0x1f));
|
||||||
|
Operand laneId = GenerateLoadSubgroupLaneId(context, subgroupSize);
|
||||||
|
Operand minThreadId = context.BitwiseAnd(laneId, segMask);
|
||||||
|
Operand srcThreadId = context.ISubtract(laneId, index);
|
||||||
|
Operand valid = context.ICompareGreaterOrEqual(srcThreadId, minThreadId);
|
||||||
|
|
||||||
|
context.Copy(Argument(3), valid);
|
||||||
|
|
||||||
|
Operand result = context.Shuffle(value, GenerateSubgroupShuffleIndex(context, srcThreadId, subgroupSize));
|
||||||
|
|
||||||
|
context.Return(context.ConditionalSelect(valid, result, value));
|
||||||
|
|
||||||
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "ShuffleUp", true, 3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function GenerateShuffleXor(int subgroupSize)
|
||||||
|
{
|
||||||
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
Operand value = Argument(0);
|
||||||
|
Operand index = Argument(1);
|
||||||
|
Operand mask = Argument(2);
|
||||||
|
|
||||||
|
Operand clamp = context.BitwiseAnd(mask, Const(0x1f));
|
||||||
|
Operand segMask = context.BitwiseAnd(context.ShiftRightU32(mask, Const(8)), Const(0x1f));
|
||||||
|
Operand laneId = GenerateLoadSubgroupLaneId(context, subgroupSize);
|
||||||
|
Operand minThreadId = context.BitwiseAnd(laneId, segMask);
|
||||||
|
Operand maxThreadId = context.BitwiseOr(context.BitwiseAnd(clamp, context.BitwiseNot(segMask)), minThreadId);
|
||||||
|
Operand srcThreadId = context.BitwiseExclusiveOr(laneId, index);
|
||||||
|
Operand valid = context.ICompareLessOrEqualUnsigned(srcThreadId, maxThreadId);
|
||||||
|
|
||||||
|
context.Copy(Argument(3), valid);
|
||||||
|
|
||||||
|
Operand result = context.Shuffle(value, GenerateSubgroupShuffleIndex(context, srcThreadId, subgroupSize));
|
||||||
|
|
||||||
|
context.Return(context.ConditionalSelect(valid, result, value));
|
||||||
|
|
||||||
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "ShuffleXor", true, 3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand GenerateLoadSubgroupLaneId(EmitterContext context, int subgroupSize)
|
||||||
|
{
|
||||||
|
if (subgroupSize <= 32)
|
||||||
|
{
|
||||||
|
return context.Load(StorageKind.Input, IoVariable.SubgroupLaneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.BitwiseAnd(context.Load(StorageKind.Input, IoVariable.SubgroupLaneId), Const(0x1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand GenerateSubgroupShuffleIndex(EmitterContext context, Operand srcThreadId, int subgroupSize)
|
||||||
|
{
|
||||||
|
if (subgroupSize <= 32)
|
||||||
|
{
|
||||||
|
return srcThreadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.BitwiseOr(
|
||||||
|
context.BitwiseAnd(context.Load(StorageKind.Input, IoVariable.SubgroupLaneId), Const(0x60)),
|
||||||
|
srcThreadId);
|
||||||
|
}
|
||||||
|
|
||||||
private Function GenerateTexelFetchScaleFunction()
|
private Function GenerateTexelFetchScaleFunction()
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
@@ -2,12 +2,18 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
enum HelperFunctionName
|
enum HelperFunctionName
|
||||||
{
|
{
|
||||||
|
Invalid,
|
||||||
|
|
||||||
ConvertDoubleToFloat,
|
ConvertDoubleToFloat,
|
||||||
ConvertFloatToDouble,
|
ConvertFloatToDouble,
|
||||||
SharedAtomicMaxS32,
|
SharedAtomicMaxS32,
|
||||||
SharedAtomicMinS32,
|
SharedAtomicMinS32,
|
||||||
SharedStore8,
|
SharedStore8,
|
||||||
SharedStore16,
|
SharedStore16,
|
||||||
|
Shuffle,
|
||||||
|
ShuffleDown,
|
||||||
|
ShuffleUp,
|
||||||
|
ShuffleXor,
|
||||||
TexelFetchScale,
|
TexelFetchScale,
|
||||||
TextureSizeUnscale,
|
TextureSizeUnscale,
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,52 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
||||||
|
{
|
||||||
|
class ShufflePass : ITransformPass
|
||||||
|
{
|
||||||
|
public static bool IsEnabled(IGpuAccessor gpuAccessor, ShaderStage stage, TargetLanguage targetLanguage, FeatureFlags usedFeatures)
|
||||||
|
{
|
||||||
|
return usedFeatures.HasFlag(FeatureFlags.Shuffle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LinkedListNode<INode> RunPass(TransformContext context, LinkedListNode<INode> node)
|
||||||
|
{
|
||||||
|
Operation operation = (Operation)node.Value;
|
||||||
|
|
||||||
|
HelperFunctionName functionName = operation.Inst switch
|
||||||
|
{
|
||||||
|
Instruction.Shuffle => HelperFunctionName.Shuffle,
|
||||||
|
Instruction.ShuffleDown => HelperFunctionName.ShuffleDown,
|
||||||
|
Instruction.ShuffleUp => HelperFunctionName.ShuffleUp,
|
||||||
|
Instruction.ShuffleXor => HelperFunctionName.ShuffleXor,
|
||||||
|
_ => HelperFunctionName.Invalid,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (functionName == HelperFunctionName.Invalid || operation.SourcesCount != 3 || operation.DestsCount != 2)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int functionId = context.Hfm.GetOrCreateShuffleFunctionId(functionName, context.GpuAccessor.QueryHostSubgroupSize());
|
||||||
|
|
||||||
|
Operand result = operation.GetDest(0);
|
||||||
|
Operand valid = operation.GetDest(1);
|
||||||
|
Operand value = operation.GetSource(0);
|
||||||
|
Operand index = operation.GetSource(1);
|
||||||
|
Operand mask = operation.GetSource(2);
|
||||||
|
|
||||||
|
operation.Dest = null;
|
||||||
|
|
||||||
|
Operand[] callArgs = new Operand[] { Const(functionId), value, index, mask, valid };
|
||||||
|
|
||||||
|
LinkedListNode<INode> newNode = node.List.AddBefore(node, new Operation(Instruction.Call, 0, result, callArgs));
|
||||||
|
|
||||||
|
Utils.DeleteNode(node, operation);
|
||||||
|
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
|
|||||||
RunPass<TexturePass>(context);
|
RunPass<TexturePass>(context);
|
||||||
RunPass<SharedStoreSmallIntCas>(context);
|
RunPass<SharedStoreSmallIntCas>(context);
|
||||||
RunPass<SharedAtomicSignedCas>(context);
|
RunPass<SharedAtomicSignedCas>(context);
|
||||||
|
RunPass<ShufflePass>(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RunPass<T>(TransformContext context) where T : ITransformPass
|
private static void RunPass<T>(TransformContext context) where T : ITransformPass
|
||||||
|
@@ -148,6 +148,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return _attachments[index];
|
return _attachments[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Auto<DisposableImageView> GetDepthStencilAttachment()
|
||||||
|
{
|
||||||
|
if (!HasDepthStencil)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _attachments[AttachmentsCount - 1];
|
||||||
|
}
|
||||||
|
|
||||||
public ComponentType GetAttachmentComponentType(int index)
|
public ComponentType GetAttachmentComponentType(int index)
|
||||||
{
|
{
|
||||||
if (_colors != null && (uint)index < _colors.Length)
|
if (_colors != null && (uint)index < _colors.Length)
|
||||||
|
@@ -25,7 +25,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly bool SupportsIndirectParameters;
|
public readonly bool SupportsIndirectParameters;
|
||||||
public readonly bool SupportsFragmentShaderInterlock;
|
public readonly bool SupportsFragmentShaderInterlock;
|
||||||
public readonly bool SupportsGeometryShaderPassthrough;
|
public readonly bool SupportsGeometryShaderPassthrough;
|
||||||
public readonly bool SupportsSubgroupSizeControl;
|
|
||||||
public readonly bool SupportsShaderFloat64;
|
public readonly bool SupportsShaderFloat64;
|
||||||
public readonly bool SupportsShaderInt8;
|
public readonly bool SupportsShaderInt8;
|
||||||
public readonly bool SupportsShaderStencilExport;
|
public readonly bool SupportsShaderStencilExport;
|
||||||
@@ -45,9 +44,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly bool SupportsViewportArray2;
|
public readonly bool SupportsViewportArray2;
|
||||||
public readonly bool SupportsHostImportedMemory;
|
public readonly bool SupportsHostImportedMemory;
|
||||||
public readonly bool SupportsDepthClipControl;
|
public readonly bool SupportsDepthClipControl;
|
||||||
public readonly uint MinSubgroupSize;
|
public readonly uint SubgroupSize;
|
||||||
public readonly uint MaxSubgroupSize;
|
|
||||||
public readonly ShaderStageFlags RequiredSubgroupSizeStages;
|
|
||||||
public readonly SampleCountFlags SupportedSampleCounts;
|
public readonly SampleCountFlags SupportedSampleCounts;
|
||||||
public readonly PortabilitySubsetFlags PortabilitySubset;
|
public readonly PortabilitySubsetFlags PortabilitySubset;
|
||||||
public readonly uint VertexBufferAlignment;
|
public readonly uint VertexBufferAlignment;
|
||||||
@@ -64,7 +61,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool supportsIndirectParameters,
|
bool supportsIndirectParameters,
|
||||||
bool supportsFragmentShaderInterlock,
|
bool supportsFragmentShaderInterlock,
|
||||||
bool supportsGeometryShaderPassthrough,
|
bool supportsGeometryShaderPassthrough,
|
||||||
bool supportsSubgroupSizeControl,
|
|
||||||
bool supportsShaderFloat64,
|
bool supportsShaderFloat64,
|
||||||
bool supportsShaderInt8,
|
bool supportsShaderInt8,
|
||||||
bool supportsShaderStencilExport,
|
bool supportsShaderStencilExport,
|
||||||
@@ -84,9 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool supportsViewportArray2,
|
bool supportsViewportArray2,
|
||||||
bool supportsHostImportedMemory,
|
bool supportsHostImportedMemory,
|
||||||
bool supportsDepthClipControl,
|
bool supportsDepthClipControl,
|
||||||
uint minSubgroupSize,
|
uint subgroupSize,
|
||||||
uint maxSubgroupSize,
|
|
||||||
ShaderStageFlags requiredSubgroupSizeStages,
|
|
||||||
SampleCountFlags supportedSampleCounts,
|
SampleCountFlags supportedSampleCounts,
|
||||||
PortabilitySubsetFlags portabilitySubset,
|
PortabilitySubsetFlags portabilitySubset,
|
||||||
uint vertexBufferAlignment,
|
uint vertexBufferAlignment,
|
||||||
@@ -102,7 +96,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SupportsIndirectParameters = supportsIndirectParameters;
|
SupportsIndirectParameters = supportsIndirectParameters;
|
||||||
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
|
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
|
||||||
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
||||||
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
|
|
||||||
SupportsShaderFloat64 = supportsShaderFloat64;
|
SupportsShaderFloat64 = supportsShaderFloat64;
|
||||||
SupportsShaderInt8 = supportsShaderInt8;
|
SupportsShaderInt8 = supportsShaderInt8;
|
||||||
SupportsShaderStencilExport = supportsShaderStencilExport;
|
SupportsShaderStencilExport = supportsShaderStencilExport;
|
||||||
@@ -122,9 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SupportsViewportArray2 = supportsViewportArray2;
|
SupportsViewportArray2 = supportsViewportArray2;
|
||||||
SupportsHostImportedMemory = supportsHostImportedMemory;
|
SupportsHostImportedMemory = supportsHostImportedMemory;
|
||||||
SupportsDepthClipControl = supportsDepthClipControl;
|
SupportsDepthClipControl = supportsDepthClipControl;
|
||||||
MinSubgroupSize = minSubgroupSize;
|
SubgroupSize = subgroupSize;
|
||||||
MaxSubgroupSize = maxSubgroupSize;
|
|
||||||
RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
|
|
||||||
SupportedSampleCounts = supportedSampleCounts;
|
SupportedSampleCounts = supportedSampleCounts;
|
||||||
PortabilitySubset = portabilitySubset;
|
PortabilitySubset = portabilitySubset;
|
||||||
VertexBufferAlignment = vertexBufferAlignment;
|
VertexBufferAlignment = vertexBufferAlignment;
|
||||||
|
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly IProgram _programColorClearF;
|
private readonly IProgram _programColorClearF;
|
||||||
private readonly IProgram _programColorClearSI;
|
private readonly IProgram _programColorClearSI;
|
||||||
private readonly IProgram _programColorClearUI;
|
private readonly IProgram _programColorClearUI;
|
||||||
|
private readonly IProgram _programDepthStencilClear;
|
||||||
private readonly IProgram _programStrideChange;
|
private readonly IProgram _programStrideChange;
|
||||||
private readonly IProgram _programConvertD32S8ToD24S8;
|
private readonly IProgram _programConvertD32S8ToD24S8;
|
||||||
private readonly IProgram _programConvertIndexBuffer;
|
private readonly IProgram _programConvertIndexBuffer;
|
||||||
@@ -105,6 +106,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
new ShaderSource(ReadSpirv("ColorClearUIFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
|
new ShaderSource(ReadSpirv("ColorClearUIFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
}, colorClearResourceLayout);
|
}, colorClearResourceLayout);
|
||||||
|
|
||||||
|
_programDepthStencilClear = gd.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(ReadSpirv("ColorClearVertex.spv"), ShaderStage.Vertex, TargetLanguage.Spirv),
|
||||||
|
new ShaderSource(ReadSpirv("DepthStencilClearFragment.spv"), ShaderStage.Fragment, TargetLanguage.Spirv),
|
||||||
|
}, colorClearResourceLayout);
|
||||||
|
|
||||||
var strideChangeResourceLayout = new ResourceLayoutBuilder()
|
var strideChangeResourceLayout = new ResourceLayoutBuilder()
|
||||||
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
|
||||||
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
|
||||||
@@ -446,10 +453,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
0f,
|
0f,
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
|
||||||
|
|
||||||
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
|
||||||
|
|
||||||
if (dstIsDepthOrStencil)
|
if (dstIsDepthOrStencil)
|
||||||
{
|
{
|
||||||
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
|
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
|
||||||
@@ -470,7 +473,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, dstIsDepthOrStencil, dstFormat);
|
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, dstIsDepthOrStencil, dstFormat);
|
||||||
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
||||||
_pipeline.SetScissors(scissors);
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
|
||||||
|
|
||||||
if (clearAlpha)
|
if (clearAlpha)
|
||||||
{
|
{
|
||||||
@@ -547,12 +550,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
0f,
|
0f,
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
|
||||||
|
|
||||||
scissors[0] = new Rectangle<int>(0, 0, dstWidth, dstHeight);
|
|
||||||
|
|
||||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat);
|
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, (uint)dstSamples, true, dstFormat);
|
||||||
_pipeline.SetScissors(scissors);
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
|
||||||
_pipeline.SetViewports(viewports);
|
_pipeline.SetViewports(viewports);
|
||||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
|
|
||||||
@@ -639,7 +638,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static StencilTestDescriptor CreateStencilTestDescriptor(bool enabled)
|
private static StencilTestDescriptor CreateStencilTestDescriptor(
|
||||||
|
bool enabled,
|
||||||
|
int refValue = 0,
|
||||||
|
int compareMask = 0xff,
|
||||||
|
int writeMask = 0xff)
|
||||||
{
|
{
|
||||||
return new StencilTestDescriptor(
|
return new StencilTestDescriptor(
|
||||||
enabled,
|
enabled,
|
||||||
@@ -647,16 +650,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
StencilOp.Replace,
|
StencilOp.Replace,
|
||||||
StencilOp.Replace,
|
StencilOp.Replace,
|
||||||
StencilOp.Replace,
|
StencilOp.Replace,
|
||||||
0,
|
refValue,
|
||||||
0xff,
|
compareMask,
|
||||||
0xff,
|
writeMask,
|
||||||
CompareOp.Always,
|
CompareOp.Always,
|
||||||
StencilOp.Replace,
|
StencilOp.Replace,
|
||||||
StencilOp.Replace,
|
StencilOp.Replace,
|
||||||
StencilOp.Replace,
|
StencilOp.Replace,
|
||||||
0,
|
refValue,
|
||||||
0xff,
|
compareMask,
|
||||||
0xff);
|
writeMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear(
|
public void Clear(
|
||||||
@@ -695,10 +698,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
0f,
|
0f,
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
|
||||||
|
|
||||||
scissors[0] = scissor;
|
|
||||||
|
|
||||||
IProgram program;
|
IProgram program;
|
||||||
|
|
||||||
if (type == ComponentType.SignedInteger)
|
if (type == ComponentType.SignedInteger)
|
||||||
@@ -718,7 +717,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
|
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
|
||||||
_pipeline.SetRenderTargetColorMasks(new[] { componentMask });
|
_pipeline.SetRenderTargetColorMasks(new[] { componentMask });
|
||||||
_pipeline.SetViewports(viewports);
|
_pipeline.SetViewports(viewports);
|
||||||
_pipeline.SetScissors(scissors);
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
|
||||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
_pipeline.Draw(4, 1, 0, 0);
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
_pipeline.Finish();
|
_pipeline.Finish();
|
||||||
@@ -726,6 +725,56 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd.BufferManager.Delete(bufferHandle);
|
gd.BufferManager.Delete(bufferHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear(
|
||||||
|
VulkanRenderer gd,
|
||||||
|
Auto<DisposableImageView> dst,
|
||||||
|
float depthValue,
|
||||||
|
bool depthMask,
|
||||||
|
int stencilValue,
|
||||||
|
int stencilMask,
|
||||||
|
int dstWidth,
|
||||||
|
int dstHeight,
|
||||||
|
VkFormat dstFormat,
|
||||||
|
Rectangle<int> scissor)
|
||||||
|
{
|
||||||
|
const int ClearColorBufferSize = 16;
|
||||||
|
|
||||||
|
gd.FlushAllCommands();
|
||||||
|
|
||||||
|
using var cbs = gd.CommandBufferPool.Rent();
|
||||||
|
|
||||||
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
|
||||||
|
var bufferHandle = gd.BufferManager.CreateWithHandle(gd, ClearColorBufferSize);
|
||||||
|
|
||||||
|
gd.BufferManager.SetData<float>(bufferHandle, 0, stackalloc float[] { depthValue });
|
||||||
|
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) });
|
||||||
|
|
||||||
|
Span<Viewport> viewports = stackalloc Viewport[1];
|
||||||
|
|
||||||
|
viewports[0] = new Viewport(
|
||||||
|
new Rectangle<float>(0, 0, dstWidth, dstHeight),
|
||||||
|
ViewportSwizzle.PositiveX,
|
||||||
|
ViewportSwizzle.PositiveY,
|
||||||
|
ViewportSwizzle.PositiveZ,
|
||||||
|
ViewportSwizzle.PositiveW,
|
||||||
|
0f,
|
||||||
|
1f);
|
||||||
|
|
||||||
|
_pipeline.SetProgram(_programDepthStencilClear);
|
||||||
|
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, true, dstFormat);
|
||||||
|
_pipeline.SetViewports(viewports);
|
||||||
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
|
||||||
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
|
_pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always));
|
||||||
|
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask));
|
||||||
|
_pipeline.Draw(4, 1, 0, 0);
|
||||||
|
_pipeline.Finish();
|
||||||
|
|
||||||
|
gd.BufferManager.Delete(bufferHandle);
|
||||||
|
}
|
||||||
|
|
||||||
public void DrawTexture(
|
public void DrawTexture(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
PipelineBase pipeline,
|
PipelineBase pipeline,
|
||||||
@@ -778,8 +827,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
0f,
|
0f,
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
|
||||||
|
|
||||||
pipeline.SetProgram(_programColorBlit);
|
pipeline.SetProgram(_programColorBlit);
|
||||||
pipeline.SetViewports(viewports);
|
pipeline.SetViewports(viewports);
|
||||||
pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
@@ -1119,11 +1166,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
0f,
|
0f,
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dst.Width, dst.Height) });
|
||||||
|
|
||||||
scissors[0] = new Rectangle<int>(0, 0, dst.Width, dst.Height);
|
|
||||||
|
|
||||||
_pipeline.SetScissors(scissors);
|
|
||||||
_pipeline.SetViewports(viewports);
|
_pipeline.SetViewports(viewports);
|
||||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
|
|
||||||
@@ -1251,12 +1294,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
0f,
|
0f,
|
||||||
1f);
|
1f);
|
||||||
|
|
||||||
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
|
||||||
|
|
||||||
scissors[0] = new Rectangle<int>(0, 0, dst.Width, dst.Height);
|
|
||||||
|
|
||||||
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
|
||||||
_pipeline.SetScissors(scissors);
|
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dst.Width, dst.Height) });
|
||||||
_pipeline.SetViewports(viewports);
|
_pipeline.SetViewports(viewports);
|
||||||
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
|
||||||
|
|
||||||
@@ -1731,6 +1770,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_programColorClearF.Dispose();
|
_programColorClearF.Dispose();
|
||||||
_programColorClearSI.Dispose();
|
_programColorClearSI.Dispose();
|
||||||
_programColorClearUI.Dispose();
|
_programColorClearUI.Dispose();
|
||||||
|
_programDepthStencilClear.Dispose();
|
||||||
_programStrideChange.Dispose();
|
_programStrideChange.Dispose();
|
||||||
_programConvertIndexBuffer.Dispose();
|
_programConvertIndexBuffer.Dispose();
|
||||||
_programConvertIndirectData.Dispose();
|
_programConvertIndirectData.Dispose();
|
||||||
|
@@ -243,10 +243,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, bool stencilMask)
|
||||||
{
|
{
|
||||||
// TODO: Use stencilMask (fully).
|
|
||||||
|
|
||||||
if (FramebufferParams == null || !FramebufferParams.HasDepthStencil)
|
if (FramebufferParams == null || !FramebufferParams.HasDepthStencil)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -255,7 +253,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue));
|
var clearValue = new ClearValue(null, new ClearDepthStencilValue(depthValue, (uint)stencilValue));
|
||||||
var flags = depthMask ? ImageAspectFlags.DepthBit : 0;
|
var flags = depthMask ? ImageAspectFlags.DepthBit : 0;
|
||||||
|
|
||||||
if (stencilMask != 0)
|
if (stencilMask)
|
||||||
{
|
{
|
||||||
flags |= ImageAspectFlags.StencilBit;
|
flags |= ImageAspectFlags.StencilBit;
|
||||||
}
|
}
|
||||||
|
@@ -81,6 +81,42 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
|
||||||
|
{
|
||||||
|
if (FramebufferParams == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stencilMask != 0 && stencilMask != 0xff)
|
||||||
|
{
|
||||||
|
// We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits,
|
||||||
|
// because on Vulkan, the pipeline state does not affect clears.
|
||||||
|
var dstTexture = FramebufferParams.GetDepthStencilAttachment();
|
||||||
|
if (dstTexture == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Clear only the specified layer.
|
||||||
|
Gd.HelperShader.Clear(
|
||||||
|
Gd,
|
||||||
|
dstTexture,
|
||||||
|
depthValue,
|
||||||
|
depthMask,
|
||||||
|
stencilValue,
|
||||||
|
stencilMask,
|
||||||
|
(int)FramebufferParams.Width,
|
||||||
|
(int)FramebufferParams.Height,
|
||||||
|
FramebufferParams.AttachmentFormats[FramebufferParams.AttachmentsCount - 1],
|
||||||
|
ClearScissor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ClearRenderTargetDepthStencil(layer, layerCount, depthValue, depthMask, stencilValue, stencilMask != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void EndHostConditionalRendering()
|
public void EndHostConditionalRendering()
|
||||||
{
|
{
|
||||||
if (Gd.Capabilities.SupportsConditionalRendering)
|
if (Gd.Capabilities.SupportsConditionalRendering)
|
||||||
|
@@ -352,11 +352,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gd.Capabilities.SupportsSubgroupSizeControl)
|
|
||||||
{
|
|
||||||
UpdateStageRequiredSubgroupSizes(gd, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var pipelineCreateInfo = new ComputePipelineCreateInfo
|
var pipelineCreateInfo = new ComputePipelineCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.ComputePipelineCreateInfo,
|
SType = StructureType.ComputePipelineCreateInfo,
|
||||||
@@ -616,11 +611,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PDynamicStates = dynamicStates,
|
PDynamicStates = dynamicStates,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (gd.Capabilities.SupportsSubgroupSizeControl)
|
|
||||||
{
|
|
||||||
UpdateStageRequiredSubgroupSizes(gd, (int)StagesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
var pipelineCreateInfo = new GraphicsPipelineCreateInfo
|
var pipelineCreateInfo = new GraphicsPipelineCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.GraphicsPipelineCreateInfo,
|
SType = StructureType.GraphicsPipelineCreateInfo,
|
||||||
@@ -659,19 +649,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly unsafe void UpdateStageRequiredSubgroupSizes(VulkanRenderer gd, int count)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < count; index++)
|
|
||||||
{
|
|
||||||
bool canUseExplicitSubgroupSize =
|
|
||||||
(gd.Capabilities.RequiredSubgroupSizeStages & Stages[index].Stage) != 0 &&
|
|
||||||
gd.Capabilities.MinSubgroupSize <= RequiredSubgroupSize &&
|
|
||||||
gd.Capabilities.MaxSubgroupSize >= RequiredSubgroupSize;
|
|
||||||
|
|
||||||
Stages[index].PNext = canUseExplicitSubgroupSize ? StageRequiredSubgroupSizes.Pointer + index : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateVertexAttributeDescriptions(VulkanRenderer gd)
|
private void UpdateVertexAttributeDescriptions(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
// Vertex attributes exceeding the stride are invalid.
|
// Vertex attributes exceeding the stride are invalid.
|
||||||
|
@@ -42,6 +42,7 @@
|
|||||||
<EmbeddedResource Include="Shaders\SpirvBinaries\DepthBlitMsFragment.spv" />
|
<EmbeddedResource Include="Shaders\SpirvBinaries\DepthBlitMsFragment.spv" />
|
||||||
<EmbeddedResource Include="Shaders\SpirvBinaries\DepthDrawToMsFragment.spv" />
|
<EmbeddedResource Include="Shaders\SpirvBinaries\DepthDrawToMsFragment.spv" />
|
||||||
<EmbeddedResource Include="Shaders\SpirvBinaries\DepthDrawToNonMsFragment.spv" />
|
<EmbeddedResource Include="Shaders\SpirvBinaries\DepthDrawToNonMsFragment.spv" />
|
||||||
|
<EmbeddedResource Include="Shaders\SpirvBinaries\DepthStencilClearFragment.spv" />
|
||||||
<EmbeddedResource Include="Shaders\SpirvBinaries\StencilBlitFragment.spv" />
|
<EmbeddedResource Include="Shaders\SpirvBinaries\StencilBlitFragment.spv" />
|
||||||
<EmbeddedResource Include="Shaders\SpirvBinaries\StencilBlitMsFragment.spv" />
|
<EmbeddedResource Include="Shaders\SpirvBinaries\StencilBlitMsFragment.spv" />
|
||||||
<EmbeddedResource Include="Shaders\SpirvBinaries\StencilDrawToMsFragment.spv" />
|
<EmbeddedResource Include="Shaders\SpirvBinaries\StencilDrawToMsFragment.spv" />
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
#version 450 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec4 clear_colour;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_FragDepth = clear_colour.x;
|
||||||
|
}
|
Binary file not shown.
@@ -37,7 +37,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
"VK_EXT_shader_stencil_export",
|
"VK_EXT_shader_stencil_export",
|
||||||
"VK_KHR_shader_float16_int8",
|
"VK_KHR_shader_float16_int8",
|
||||||
"VK_EXT_shader_subgroup_ballot",
|
"VK_EXT_shader_subgroup_ballot",
|
||||||
"VK_EXT_subgroup_size_control",
|
|
||||||
"VK_NV_geometry_shader_passthrough",
|
"VK_NV_geometry_shader_passthrough",
|
||||||
"VK_NV_viewport_array2",
|
"VK_NV_viewport_array2",
|
||||||
"VK_EXT_depth_clip_control",
|
"VK_EXT_depth_clip_control",
|
||||||
|
@@ -151,6 +151,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDeviceProperties2,
|
SType = StructureType.PhysicalDeviceProperties2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PhysicalDeviceSubgroupProperties propertiesSubgroup = new()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDeviceSubgroupProperties,
|
||||||
|
PNext = properties2.PNext,
|
||||||
|
};
|
||||||
|
|
||||||
|
properties2.PNext = &propertiesSubgroup;
|
||||||
|
|
||||||
PhysicalDeviceBlendOperationAdvancedPropertiesEXT propertiesBlendOperationAdvanced = new()
|
PhysicalDeviceBlendOperationAdvancedPropertiesEXT propertiesBlendOperationAdvanced = new()
|
||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt,
|
SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt,
|
||||||
@@ -164,18 +172,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
properties2.PNext = &propertiesBlendOperationAdvanced;
|
properties2.PNext = &propertiesBlendOperationAdvanced;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalDeviceSubgroupSizeControlPropertiesEXT propertiesSubgroupSizeControl = new()
|
|
||||||
{
|
|
||||||
SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool supportsSubgroupSizeControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control");
|
|
||||||
|
|
||||||
if (supportsSubgroupSizeControl)
|
|
||||||
{
|
|
||||||
properties2.PNext = &propertiesSubgroupSizeControl;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName);
|
bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName);
|
||||||
|
|
||||||
PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new()
|
PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new()
|
||||||
@@ -315,7 +311,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
|
||||||
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"),
|
||||||
_physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"),
|
||||||
supportsSubgroupSizeControl,
|
|
||||||
features2.Features.ShaderFloat64,
|
features2.Features.ShaderFloat64,
|
||||||
featuresShaderInt8.ShaderInt8,
|
featuresShaderInt8.ShaderInt8,
|
||||||
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
|
||||||
@@ -335,9 +330,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
|
||||||
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
|
||||||
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
|
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
|
||||||
propertiesSubgroupSizeControl.MinSubgroupSize,
|
propertiesSubgroup.SubgroupSize,
|
||||||
propertiesSubgroupSizeControl.MaxSubgroupSize,
|
|
||||||
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
|
|
||||||
supportedSampleCounts,
|
supportedSampleCounts,
|
||||||
portabilityFlags,
|
portabilityFlags,
|
||||||
vertexBufferAlignment,
|
vertexBufferAlignment,
|
||||||
@@ -623,6 +616,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
maximumImagesPerStage: Constants.MaxImagesPerStage,
|
maximumImagesPerStage: Constants.MaxImagesPerStage,
|
||||||
maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize,
|
maximumComputeSharedMemorySize: (int)limits.MaxComputeSharedMemorySize,
|
||||||
maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy,
|
maximumSupportedAnisotropy: (int)limits.MaxSamplerAnisotropy,
|
||||||
|
shaderSubgroupSize: (int)Capabilities.SubgroupSize,
|
||||||
storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
|
storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
|
||||||
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0);
|
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0);
|
||||||
}
|
}
|
||||||
|
@@ -1,196 +0,0 @@
|
|||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Mm.Types;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Mm
|
|
||||||
{
|
|
||||||
[Service("mm:u")]
|
|
||||||
class IRequest : IpcService
|
|
||||||
{
|
|
||||||
private readonly object _sessionListLock = new();
|
|
||||||
private readonly List<MultiMediaSession> _sessionList = new();
|
|
||||||
|
|
||||||
private uint _uniqueId = 1;
|
|
||||||
|
|
||||||
public IRequest(ServiceCtx context) { }
|
|
||||||
|
|
||||||
[CommandCmif(0)]
|
|
||||||
// InitializeOld(u32, u32, u32)
|
|
||||||
public ResultCode InitializeOld(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
|
|
||||||
int fgmId = context.RequestData.ReadInt32();
|
|
||||||
bool isAutoClearEvent = context.RequestData.ReadInt32() != 0;
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, fgmId, isAutoClearEvent });
|
|
||||||
|
|
||||||
Register(operationType, fgmId, isAutoClearEvent);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(1)]
|
|
||||||
// FinalizeOld(u32)
|
|
||||||
public ResultCode FinalizeOld(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType });
|
|
||||||
|
|
||||||
lock (_sessionListLock)
|
|
||||||
{
|
|
||||||
_sessionList.Remove(GetSessionByType(operationType));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(2)]
|
|
||||||
// SetAndWaitOld(u32, u32, u32)
|
|
||||||
public ResultCode SetAndWaitOld(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
|
|
||||||
uint frequenceHz = context.RequestData.ReadUInt32();
|
|
||||||
int timeout = context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, frequenceHz, timeout });
|
|
||||||
|
|
||||||
lock (_sessionListLock)
|
|
||||||
{
|
|
||||||
GetSessionByType(operationType)?.SetAndWait(frequenceHz, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(3)]
|
|
||||||
// GetOld(u32) -> u32
|
|
||||||
public ResultCode GetOld(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType });
|
|
||||||
|
|
||||||
lock (_sessionListLock)
|
|
||||||
{
|
|
||||||
MultiMediaSession session = GetSessionByType(operationType);
|
|
||||||
|
|
||||||
uint currentValue = session == null ? 0 : session.CurrentValue;
|
|
||||||
|
|
||||||
context.ResponseData.Write(currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(4)]
|
|
||||||
// Initialize(u32, u32, u32) -> u32
|
|
||||||
public ResultCode Initialize(ServiceCtx context)
|
|
||||||
{
|
|
||||||
MultiMediaOperationType operationType = (MultiMediaOperationType)context.RequestData.ReadUInt32();
|
|
||||||
int fgmId = context.RequestData.ReadInt32();
|
|
||||||
bool isAutoClearEvent = context.RequestData.ReadInt32() != 0;
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { operationType, fgmId, isAutoClearEvent });
|
|
||||||
|
|
||||||
uint id = Register(operationType, fgmId, isAutoClearEvent);
|
|
||||||
|
|
||||||
context.ResponseData.Write(id);
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(5)]
|
|
||||||
// Finalize(u32)
|
|
||||||
public ResultCode Finalize(ServiceCtx context)
|
|
||||||
{
|
|
||||||
uint id = context.RequestData.ReadUInt32();
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id });
|
|
||||||
|
|
||||||
lock (_sessionListLock)
|
|
||||||
{
|
|
||||||
_sessionList.Remove(GetSessionById(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(6)]
|
|
||||||
// SetAndWait(u32, u32, u32)
|
|
||||||
public ResultCode SetAndWait(ServiceCtx context)
|
|
||||||
{
|
|
||||||
uint id = context.RequestData.ReadUInt32();
|
|
||||||
uint frequenceHz = context.RequestData.ReadUInt32();
|
|
||||||
int timeout = context.RequestData.ReadInt32();
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id, frequenceHz, timeout });
|
|
||||||
|
|
||||||
lock (_sessionListLock)
|
|
||||||
{
|
|
||||||
GetSessionById(id)?.SetAndWait(frequenceHz, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
[CommandCmif(7)]
|
|
||||||
// Get(u32) -> u32
|
|
||||||
public ResultCode Get(ServiceCtx context)
|
|
||||||
{
|
|
||||||
uint id = context.RequestData.ReadUInt32();
|
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { id });
|
|
||||||
|
|
||||||
lock (_sessionListLock)
|
|
||||||
{
|
|
||||||
MultiMediaSession session = GetSessionById(id);
|
|
||||||
|
|
||||||
uint currentValue = session == null ? 0 : session.CurrentValue;
|
|
||||||
|
|
||||||
context.ResponseData.Write(currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultCode.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiMediaSession GetSessionById(uint id)
|
|
||||||
{
|
|
||||||
foreach (MultiMediaSession session in _sessionList)
|
|
||||||
{
|
|
||||||
if (session.Id == id)
|
|
||||||
{
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MultiMediaSession GetSessionByType(MultiMediaOperationType type)
|
|
||||||
{
|
|
||||||
foreach (MultiMediaSession session in _sessionList)
|
|
||||||
{
|
|
||||||
if (session.Type == type)
|
|
||||||
{
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private uint Register(MultiMediaOperationType type, int fgmId, bool isAutoClearEvent)
|
|
||||||
{
|
|
||||||
lock (_sessionListLock)
|
|
||||||
{
|
|
||||||
// Nintendo ignore the fgm id as the other interfaces were deprecated.
|
|
||||||
MultiMediaSession session = new(_uniqueId++, type, isAutoClearEvent);
|
|
||||||
|
|
||||||
_sessionList.Add(session);
|
|
||||||
|
|
||||||
return session.Id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Mm.Types
|
|
||||||
{
|
|
||||||
enum MultiMediaOperationType : uint
|
|
||||||
{
|
|
||||||
Ram = 2,
|
|
||||||
NvEnc = 5,
|
|
||||||
NvDec = 6,
|
|
||||||
NvJpg = 7,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,24 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Mm.Types
|
|
||||||
{
|
|
||||||
class MultiMediaSession
|
|
||||||
{
|
|
||||||
public MultiMediaOperationType Type { get; }
|
|
||||||
|
|
||||||
public bool IsAutoClearEvent { get; }
|
|
||||||
public uint Id { get; }
|
|
||||||
public uint CurrentValue { get; private set; }
|
|
||||||
|
|
||||||
public MultiMediaSession(uint id, MultiMediaOperationType type, bool isAutoClearEvent)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
Id = id;
|
|
||||||
IsAutoClearEvent = isAutoClearEvent;
|
|
||||||
CurrentValue = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetAndWait(uint value, int timeout)
|
|
||||||
{
|
|
||||||
CurrentValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
160
src/Ryujinx.Horizon/MmNv/Ipc/Request.cs
Normal file
160
src/Ryujinx.Horizon/MmNv/Ipc/Request.cs
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.MmNv;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.MmNv.Ipc
|
||||||
|
{
|
||||||
|
partial class Request : IRequest
|
||||||
|
{
|
||||||
|
private readonly List<Session> _sessionList = new();
|
||||||
|
|
||||||
|
private uint _uniqueId = 1;
|
||||||
|
|
||||||
|
[CmifCommand(0)]
|
||||||
|
public Result InitializeOld(Module module, uint fgmPriority, uint autoClearEvent)
|
||||||
|
{
|
||||||
|
bool isAutoClearEvent = autoClearEvent != 0;
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, fgmPriority, isAutoClearEvent });
|
||||||
|
|
||||||
|
Register(module, fgmPriority, isAutoClearEvent);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(1)]
|
||||||
|
public Result FinalizeOld(Module module)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module });
|
||||||
|
|
||||||
|
lock (_sessionList)
|
||||||
|
{
|
||||||
|
_sessionList.Remove(GetSessionByModule(module));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(2)]
|
||||||
|
public Result SetAndWaitOld(Module module, uint clockRateMin, int clockRateMax)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, clockRateMin, clockRateMax });
|
||||||
|
|
||||||
|
lock (_sessionList)
|
||||||
|
{
|
||||||
|
GetSessionByModule(module)?.SetAndWait(clockRateMin, clockRateMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(3)]
|
||||||
|
public Result GetOld(out uint clockRateActual, Module module)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module });
|
||||||
|
|
||||||
|
lock (_sessionList)
|
||||||
|
{
|
||||||
|
Session session = GetSessionByModule(module);
|
||||||
|
|
||||||
|
clockRateActual = session == null ? 0 : session.ClockRateMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(4)]
|
||||||
|
public Result Initialize(out uint requestId, Module module, uint fgmPriority, uint autoClearEvent)
|
||||||
|
{
|
||||||
|
bool isAutoClearEvent = autoClearEvent != 0;
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { module, fgmPriority, isAutoClearEvent });
|
||||||
|
|
||||||
|
requestId = Register(module, fgmPriority, isAutoClearEvent);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(5)]
|
||||||
|
public Result Finalize(uint requestId)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId });
|
||||||
|
|
||||||
|
lock (_sessionList)
|
||||||
|
{
|
||||||
|
_sessionList.Remove(GetSessionById(requestId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(6)]
|
||||||
|
public Result SetAndWait(uint requestId, uint clockRateMin, int clockRateMax)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId, clockRateMin, clockRateMax });
|
||||||
|
|
||||||
|
lock (_sessionList)
|
||||||
|
{
|
||||||
|
GetSessionById(requestId)?.SetAndWait(clockRateMin, clockRateMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CmifCommand(7)]
|
||||||
|
public Result Get(out uint clockRateActual, uint requestId)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceMm, new { requestId });
|
||||||
|
|
||||||
|
lock (_sessionList)
|
||||||
|
{
|
||||||
|
Session session = GetSessionById(requestId);
|
||||||
|
|
||||||
|
clockRateActual = session == null ? 0 : session.ClockRateMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Session GetSessionById(uint id)
|
||||||
|
{
|
||||||
|
foreach (Session session in _sessionList)
|
||||||
|
{
|
||||||
|
if (session.Id == id)
|
||||||
|
{
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Session GetSessionByModule(Module module)
|
||||||
|
{
|
||||||
|
foreach (Session session in _sessionList)
|
||||||
|
{
|
||||||
|
if (session.Module == module)
|
||||||
|
{
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint Register(Module module, uint fgmPriority, bool isAutoClearEvent)
|
||||||
|
{
|
||||||
|
lock (_sessionList)
|
||||||
|
{
|
||||||
|
// Nintendo ignores the fgm priority as the other services were deprecated.
|
||||||
|
Session session = new(_uniqueId++, module, isAutoClearEvent);
|
||||||
|
|
||||||
|
_sessionList.Add(session);
|
||||||
|
|
||||||
|
return session.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
43
src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs
Normal file
43
src/Ryujinx.Horizon/MmNv/MmNvIpcServer.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using Ryujinx.Horizon.MmNv.Ipc;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sm;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.MmNv
|
||||||
|
{
|
||||||
|
class MmNvIpcServer
|
||||||
|
{
|
||||||
|
private const int MmNvMaxSessionsCount = 9;
|
||||||
|
|
||||||
|
private const int PointerBufferSize = 0;
|
||||||
|
private const int MaxDomains = 0;
|
||||||
|
private const int MaxDomainObjects = 0;
|
||||||
|
private const int MaxPortsCount = 1;
|
||||||
|
|
||||||
|
private static readonly ManagerOptions _mmNvOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
|
||||||
|
|
||||||
|
private SmApi _sm;
|
||||||
|
private ServerManager _serverManager;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
HeapAllocator allocator = new();
|
||||||
|
|
||||||
|
_sm = new SmApi();
|
||||||
|
_sm.Initialize().AbortOnFailure();
|
||||||
|
|
||||||
|
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _mmNvOptions, MmNvMaxSessionsCount);
|
||||||
|
|
||||||
|
_serverManager.RegisterObjectForServer(new Request(), ServiceName.Encode("mm:u"), MmNvMaxSessionsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ServiceRequests()
|
||||||
|
{
|
||||||
|
_serverManager.ServiceRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shutdown()
|
||||||
|
{
|
||||||
|
_serverManager.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/Ryujinx.Horizon/MmNv/MmNvMain.cs
Normal file
17
src/Ryujinx.Horizon/MmNv/MmNvMain.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Ryujinx.Horizon.MmNv
|
||||||
|
{
|
||||||
|
class MmNvMain : IService
|
||||||
|
{
|
||||||
|
public static void Main(ServiceTable serviceTable)
|
||||||
|
{
|
||||||
|
MmNvIpcServer ipcServer = new();
|
||||||
|
|
||||||
|
ipcServer.Initialize();
|
||||||
|
|
||||||
|
serviceTable.SignalServiceReady();
|
||||||
|
|
||||||
|
ipcServer.ServiceRequests();
|
||||||
|
ipcServer.Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs
Normal file
17
src/Ryujinx.Horizon/Sdk/MmNv/IRequest.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using Ryujinx.Horizon.Sdk.Sf;
|
||||||
|
|
||||||
|
namespace Ryujinx.Horizon.Sdk.MmNv
|
||||||
|
{
|
||||||
|
interface IRequest : IServiceObject
|
||||||
|
{
|
||||||
|
Result InitializeOld(Module module, uint fgmPriority, uint autoClearEvent);
|
||||||
|
Result FinalizeOld(Module module);
|
||||||
|
Result SetAndWaitOld(Module module, uint clockRateMin, int clockRateMax);
|
||||||
|
Result GetOld(out uint clockRateActual, Module module);
|
||||||
|
Result Initialize(out uint requestId, Module module, uint fgmPriority, uint autoClearEvent);
|
||||||
|
Result Finalize(uint requestId);
|
||||||
|
Result SetAndWait(uint requestId, uint clockRateMin, int clockRateMax);
|
||||||
|
Result Get(out uint clockRateActual, uint requestId);
|
||||||
|
}
|
||||||
|
}
|
15
src/Ryujinx.Horizon/Sdk/MmNv/Module.cs
Normal file
15
src/Ryujinx.Horizon/Sdk/MmNv/Module.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.MmNv
|
||||||
|
{
|
||||||
|
enum Module : uint
|
||||||
|
{
|
||||||
|
Cpu,
|
||||||
|
Gpu,
|
||||||
|
Emc,
|
||||||
|
SysBus,
|
||||||
|
MSelect,
|
||||||
|
NvDec,
|
||||||
|
NvEnc,
|
||||||
|
NvJpg,
|
||||||
|
Test,
|
||||||
|
}
|
||||||
|
}
|
26
src/Ryujinx.Horizon/Sdk/MmNv/Session.cs
Normal file
26
src/Ryujinx.Horizon/Sdk/MmNv/Session.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
namespace Ryujinx.Horizon.Sdk.MmNv
|
||||||
|
{
|
||||||
|
class Session
|
||||||
|
{
|
||||||
|
public Module Module { get; }
|
||||||
|
public uint Id { get; }
|
||||||
|
public bool IsAutoClearEvent { get; }
|
||||||
|
public uint ClockRateMin { get; private set; }
|
||||||
|
public int ClockRateMax { get; private set; }
|
||||||
|
|
||||||
|
public Session(uint id, Module module, bool isAutoClearEvent)
|
||||||
|
{
|
||||||
|
Module = module;
|
||||||
|
Id = id;
|
||||||
|
IsAutoClearEvent = isAutoClearEvent;
|
||||||
|
ClockRateMin = 0;
|
||||||
|
ClockRateMax = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAndWait(uint clockRateMin, int clockRateMax)
|
||||||
|
{
|
||||||
|
ClockRateMin = clockRateMin;
|
||||||
|
ClockRateMax = clockRateMax;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -31,7 +31,10 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
|
|||||||
|
|
||||||
if (allocator != null)
|
if (allocator != null)
|
||||||
{
|
{
|
||||||
_pointerBuffersBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)options.PointerBufferSize);
|
if (options.PointerBufferSize != 0)
|
||||||
|
{
|
||||||
|
_pointerBuffersBaseAddress = allocator.Allocate((ulong)maxSessions * (ulong)options.PointerBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.CanDeferInvokeRequest)
|
if (options.CanDeferInvokeRequest)
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Horizon.Bcat;
|
using Ryujinx.Horizon.Bcat;
|
||||||
using Ryujinx.Horizon.LogManager;
|
using Ryujinx.Horizon.LogManager;
|
||||||
|
using Ryujinx.Horizon.MmNv;
|
||||||
using Ryujinx.Horizon.Prepo;
|
using Ryujinx.Horizon.Prepo;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -25,6 +26,7 @@ namespace Ryujinx.Horizon
|
|||||||
RegisterService<LmMain>();
|
RegisterService<LmMain>();
|
||||||
RegisterService<PrepoMain>();
|
RegisterService<PrepoMain>();
|
||||||
RegisterService<BcatMain>();
|
RegisterService<BcatMain>();
|
||||||
|
RegisterService<MmNvMain>();
|
||||||
|
|
||||||
_totalServices = entries.Count;
|
_totalServices = entries.Count;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user