diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs index f06af235a..8feba561b 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Declarations.cs @@ -3,14 +3,41 @@ using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; -using System.Data.Common; using System.Linq; -using System.Numerics; namespace Ryujinx.Graphics.Shader.CodeGen.Msl { static class Declarations { + /* + * Description of MSL Binding Strategy + * + * There are a few fundamental differences between how GLSL and MSL handle I/O. + * This comment will set out to describe the reasons why things are done certain ways + * and to describe the overall binding model that we're striving for here. + * + * Main I/O Structs + * + * Each stage will have a main input and output struct labeled as [Stage][In/Out], i.e VertexIn. + * Every attribute within these structs will be labeled with an [[attribute(n)]] property, + * and the overall struct will be labeled with [[stage_in]] for input structs, and defined as the + * output type of the main shader function for the output struct. This struct also contains special + * attribute-based properties like [[position]], therefore these are not confined to 'user-defined' variables. + * + * Samplers & Textures + * + * Metal does not have a combined image sampler like sampler2D in GLSL, as a result we need to bind + * an individual texture and a sampler object for each instance of a combined image sampler. + * Therefore, the binding indices of straight up textures (i.e. without a sampler) must start + * after the last sampler/texture pair (n + Number of Pairs). + * + * Uniforms + * + * MSL does not have a concept of uniforms comparable to that of GLSL. As a result, instead of + * being declared outside of any function body, uniforms are part of the function signature in MSL. + * This applies to anything bound to the shader not included in the main I/O structs. + */ + public static void Declare(CodeGenContext context, StructuredProgramInfo info) { context.AppendLine("#include "); @@ -25,6 +52,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); + context.AppendLine(); + DeclareOutputAttributes(context, info.IoDefinitions.Where(x => x.StorageKind == StorageKind.Output)); } static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) @@ -32,8 +61,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl return ioDefinition.StorageKind == storageKind && ioDefinition.IoVariable == IoVariable.UserDefined; } - public static void DeclareLocals(CodeGenContext context, StructuredFunction function) + public static void DeclareLocals(CodeGenContext context, StructuredFunction function, ShaderStage stage) { + if (stage == ShaderStage.Vertex) + { + context.AppendLine("VertexOutput out;"); + } + foreach (AstOperand decl in function.Locals) { string name = context.OperandManager.DeclareLocal(decl); @@ -107,5 +141,48 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } } + + private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable inputs) + { + if (context.Definitions.IaIndexing) + { + // Not handled + } + else + { + if (inputs.Any()) + { + string prefix = ""; + + switch (context.Definitions.Stage) + { + case ShaderStage.Vertex: + prefix = "Vertex"; + break; + case ShaderStage.Fragment: + prefix = "Fragment"; + break; + case ShaderStage.Compute: + prefix = "Compute"; + break; + } + + context.AppendLine($"struct {prefix}Output"); + context.EnterScope(); + + foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) + { + string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(ioDefinition.Location, isOutput: true)); + string name = $"{DefaultNames.OAttributePrefix}{ioDefinition.Location}"; + name = ioDefinition.IoVariable == IoVariable.Position ? "position" : name; + string suffix = ioDefinition.IoVariable == IoVariable.Position ? " [[position]]" : ""; + + context.AppendLine($"{type} {name}{suffix};"); + } + + context.LeaveScope(";"); + } + } + } } } \ No newline at end of file diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs index f4eb33516..5113c58c8 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/InstGen.cs @@ -70,6 +70,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions { return $"{op} {GetSourceExpr(context, operation.GetSource(0), context.CurrentFunction.ReturnType)}"; } + else if (inst == Instruction.Return && context.Definitions.Stage == ShaderStage.Vertex) + { + return $"{op} out"; + } int arity = (int)(info.Type & InstType.ArityMask); diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs index 67e78c9f6..c30e317b3 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/Instructions/IoMap.cs @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions IoVariable.InstanceId => ("instance_id", AggregateType.S32), IoVariable.PointCoord => ("point_coord", AggregateType.Vector2), IoVariable.PointSize => ("point_size", AggregateType.FP32), - IoVariable.Position => ("position", AggregateType.Vector4 | AggregateType.FP32), + IoVariable.Position => ("out.position", AggregateType.Vector4 | AggregateType.FP32), IoVariable.PrimitiveId => ("primitive_id", AggregateType.S32), IoVariable.UserDefined => GetUserDefinedVariableName(definitions, location, component, isOutput, isPerPatch), IoVariable.VertexId => ("vertex_id", AggregateType.S32), @@ -52,21 +52,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl.Instructions name += "_" + "xyzw"[component & 3]; } - string prefix = ""; - switch (definitions.Stage) - { - case ShaderStage.Vertex: - prefix = "Vertex"; - break; - case ShaderStage.Fragment: - prefix = "Fragment"; - break; - case ShaderStage.Compute: - prefix = "Compute"; - break; - } - - prefix += isOutput ? "Out" : "In"; + string prefix = isOutput ? "out" : "in"; return (prefix + "." + name, definitions.GetUserDefinedType(location, isOutput)); } diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs index b4d2ecad2..bd03aeeb5 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Msl/MslGenerator.cs @@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl context.AppendLine(GetFunctionSignature(context, function, stage, isMainFunc)); context.EnterScope(); - Declarations.DeclareLocals(context, function); + Declarations.DeclareLocals(context, function, stage); PrintBlock(context, function.MainBlock, isMainFunc); @@ -77,6 +77,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl string funcKeyword = "inline"; string funcName = null; + string returnType = Declarations.GetVarTypeName(context, function.ReturnType); if (isMainFunc) { @@ -84,6 +85,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl { funcKeyword = "vertex"; funcName = "vertexMain"; + returnType = "VertexOutput"; } else if (stage == ShaderStage.Fragment) { @@ -112,7 +114,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Msl } } - return $"{funcKeyword} {Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})"; + return $"{funcKeyword} {returnType} {funcName ?? function.Name}({string.Join(", ", args)})"; } private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction)