Compare commits

...

7 Commits

Author SHA1 Message Date
gdkchan
3e5c211394 Fix debug assert on services without pointer buffer (#5599) 2023-08-19 18:16:59 +00:00
gdkchan
153b8bfc7c Implement support for masked stencil clears on Vulkan (#5589)
* Implement support for masked stencil clears on Vulkan

* PR feedback
2023-08-18 05:25:54 +00:00
Mary
c6a699414a infra: add missing quotes around @ developers in reviewers.yml 2023-08-17 19:34:48 +02:00
TSRBerry
2563f88de0 Convert app and installation ids to int (#5587) 2023-08-17 19:26:21 +02:00
Ac_K
b0b7843d5c mm: Migrate service in Horizon project (#5580)
* mm: Migrate service in Horizon project

This PR migrate the `mm:u` service to the Horizon project, things were checked by some RE aswell, that's why some vars are renamed, the logic should be the same as before.

Tests are welcome.

* Lock _sessionList instead

* Fix comment

* Fix Session fields order
2023-08-17 09:59:05 -03:00
gdkchan
6ed613a6e6 Fix vote and shuffle shader instructions on AMD GPUs (#5540)
* Move shuffle handling out of the backend to a transform pass

* Handle subgroup sizes higher than 32

* Stop using the subgroup size control extension

* Make GenerateShuffleFunction static

* Shader cache version bump
2023-08-16 21:31:07 -03:00
Marco Carvalho
64079c034c Prefer jagged arrays over multidimensional (#5562)
* fix CA1814

* Update .editorconfig

removing .editorconfig rule
2023-08-16 23:24:44 +02:00
56 changed files with 867 additions and 541 deletions

View File

@@ -29,4 +29,4 @@ infra:
- TSRBerry - TSRBerry
default: default:
- @developers - '@developers'

View File

@@ -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])

View File

@@ -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;
} }

View File

@@ -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";

View File

@@ -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;

View File

@@ -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;
} }
} }

View File

@@ -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.
} }

View File

@@ -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");

View File

@@ -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";
} }
} }

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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}";
} }
} }
} }

View File

@@ -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);

View File

@@ -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})";
}
}
}
}

View File

@@ -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);
} }

View File

@@ -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)
{ {

View File

@@ -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);

View File

@@ -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>

View File

@@ -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>();

View File

@@ -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);

View File

@@ -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;
} }
} }
} }

View File

@@ -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>

View File

@@ -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,
} }

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,

View File

@@ -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();

View File

@@ -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,
} }

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;
} }

View File

@@ -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)

View File

@@ -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.

View File

@@ -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" />

View File

@@ -0,0 +1,8 @@
#version 450 core
layout (location = 0) in vec4 clear_colour;
void main()
{
gl_FragDepth = clear_colour.x;
}

View File

@@ -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",

View File

@@ -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);
} }

View File

@@ -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;
}
}
}
}

View File

@@ -1,10 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Mm.Types
{
enum MultiMediaOperationType : uint
{
Ram = 2,
NvEnc = 5,
NvDec = 6,
NvJpg = 7,
}
}

View File

@@ -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;
}
}
}

View 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;
}
}
}
}

View 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();
}
}
}

View 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();
}
}
}

View 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);
}
}

View File

@@ -0,0 +1,15 @@
namespace Ryujinx.Horizon.Sdk.MmNv
{
enum Module : uint
{
Cpu,
Gpu,
Emc,
SysBus,
MSelect,
NvDec,
NvEnc,
NvJpg,
Test,
}
}

View 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;
}
}
}

View File

@@ -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)
{ {

View File

@@ -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;