using System; using System.IO; using System.Collections.Generic; using Ryujinx.Graphics.Gal.Shader.SPIRV; namespace Ryujinx.Graphics.Gal.Shader { public class SpirvDecompiler { private class SpirvVariable { public Instruction Id; public StorageClass Storage; public string Name; public int Location = -1; } private enum OperType { Bool, F32, I32 } private delegate int LocationAllocator(); private delegate Instruction GetInstExpr(ShaderIrOp Op); private Dictionary InstsExpr; private GlslDecl Decl; private ShaderIrBlock[] Blocks; private Assembler Assembler; private int UniformCount = 0; private int InAttributeCount = 0; private int OutAttributeCount = 0; private Instruction TypeVoid, TypeBool, TypeBool_Private, TypeInt, TypeUInt, TypeFloat, TypeImage2D, TypeSampler2D, TypeSampler2D_Uniform; private Instruction[] TypeFloats, TypeFloats_In, TypeFloats_Out, TypeFloats_Uniform, TypeFloats_Private; private Instruction TrueConstant, FalseConstant; //Holds debug info, they are not needed but do not hurt to add private List Names; //Types and constants have to be defined sequentially //because some types require constants and most constants require types private List TypesConstants; private List Decorates; //Variables declarations. They are different to "Variables" declared below //These holds instructions private List VarsDeclaration; private List Code; private List Variables; private Instruction Main; private Instruction PerVertexVar = null; private Instruction GlslExtension; private GLSLstd450Builder Glsl450; // private Instruction GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) { return GetExprWithCast(Op, Oper, GetSrcExpr(Oper)); } private Instruction GetFcltExpr(ShaderIrOp Op) { return new OpFOrdLessThan( TypeBool, GetOperExpr(Op, Op.OperandA), GetOperExpr(Op, Op.OperandB)); } private Instruction GetSrcNodeTypeId(ShaderIrOp Op) { switch (GetSrcNodeType(Op)) { case OperType.Bool: return TypeBool; case OperType.F32: return TypeFloat; case OperType.I32: return TypeInt; default: throw new InvalidOperationException(); } } private Instruction GetFcgtExpr(ShaderIrOp Op) { return new OpFOrdGreaterThan( TypeBool, GetOperExpr(Op, Op.OperandA), GetOperExpr(Op, Op.OperandB)); } private Instruction GetFnegExpr(ShaderIrOp Op) { return new OpFNegate(TypeFloat, GetOperExpr(Op, Op.OperandA)); } private Instruction GetFaddExpr(ShaderIrOp Op) { return new OpFAdd( TypeFloat, GetOperExpr(Op, Op.OperandA), GetOperExpr(Op, Op.OperandB)); } private Instruction GetFmulExpr(ShaderIrOp Op) { return new OpFMul( TypeFloat, GetOperExpr(Op, Op.OperandA), GetOperExpr(Op, Op.OperandB)); } private Instruction GetFfmaExpr(ShaderIrOp Op) { return Glsl450.Fma( TypeFloat, GetOperExpr(Op, Op.OperandA), GetOperExpr(Op, Op.OperandB), GetOperExpr(Op, Op.OperandC)); } // public SpirvDecompiler() { InstsExpr = new Dictionary() { { ShaderIrInst.Fadd, GetFaddExpr }, { ShaderIrInst.Fmul, GetFmulExpr }, { ShaderIrInst.Fclt, GetFcltExpr }, { ShaderIrInst.Fcgt, GetFcgtExpr }, { ShaderIrInst.Fneg, GetFnegExpr }, { ShaderIrInst.Ffma, GetFfmaExpr } }; Assembler = new Assembler(); GlslExtension = new OpExtInstImport("GLSL.std.450"); Glsl450 = new GLSLstd450Builder(GlslExtension); TypesConstants = new List(); Names = new List(); Decorates = new List(); VarsDeclaration = new List(); Code = new List(); Variables = new List(); //Declare types types TypeVoid = new OpTypeVoid(); TypeBool = new OpTypeBool(); TypeBool_Private = new OpTypePointer(StorageClass.Private, TypeBool); TrueConstant = new OpConstantTrue(TypeBool); FalseConstant = new OpConstantFalse(TypeBool); TypeInt = new OpTypeInt(32, true); TypeUInt = new OpTypeInt(32, false); TypeFloats = new Instruction[4]; TypeFloats_In = new Instruction[4]; TypeFloats_Out = new Instruction[4]; TypeFloats_Uniform = new Instruction[4]; TypeFloats_Private = new Instruction[4]; TypeFloat = new OpTypeFloat(32); TypeFloats[0] = TypeFloat; for (int i = 0; i < 4; i++) { if (i > 0) { TypeFloats[i] = new OpTypeVector(TypeFloat, i+1); } TypeFloats_In[i] = new OpTypePointer(StorageClass.Input, TypeFloats[i]); TypeFloats_Out[i] = new OpTypePointer(StorageClass.Output, TypeFloats[i]); TypeFloats_Uniform[i] = new OpTypePointer(StorageClass.UniformConstant, TypeFloats[i]); TypeFloats_Private[i] = new OpTypePointer(StorageClass.Private, TypeFloats[i]); } TypeImage2D = new OpTypeImage(TypeFloat, Dim.Dim2D, 0, 0, 0, 0, ImageFormat.Unknown); TypeSampler2D = new OpTypeSampledImage(TypeImage2D); TypeSampler2D_Uniform = new OpTypePointer(StorageClass.UniformConstant, TypeSampler2D); //Add them (these do not need to be added safely) TypesConstants.Add(TypeVoid); TypesConstants.Add(TypeBool); TypesConstants.Add(TypeBool_Private); TypesConstants.Add(TrueConstant); TypesConstants.Add(FalseConstant); TypesConstants.Add(TypeInt); TypesConstants.Add(TypeUInt); for (int i = 0; i < 4; i++) { TypesConstants.Add(TypeFloats[i]); TypesConstants.Add(TypeFloats_In[i]); TypesConstants.Add(TypeFloats_Out[i]); TypesConstants.Add(TypeFloats_Uniform[i]); TypesConstants.Add(TypeFloats_Private[i]); } TypesConstants.Add(TypeImage2D); TypesConstants.Add(TypeSampler2D); TypesConstants.Add(TypeSampler2D_Uniform); } public byte[] Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) { Blocks = ShaderDecoder.Decode(Memory, Position); Decl = new GlslDecl(Blocks, ShaderType); BuildBuiltIns(); BuildTextures(); BuildUniforms(); BuildInAttributes(); BuildOutAttributes(); BuildGprs(); BuildDeclPreds(); BuildMain(); BuildCode(); PrintHeader(); PrintEntryPoint(); Assembler.Add(Names); Assembler.Add(Decorates); Assembler.Add(TypesConstants); Assembler.Add(VarsDeclaration); Assembler.Add(Code); //Temp code ahead var Stream = new MemoryStream(); Assembler.Write(Stream); byte[] Bytecode = Stream.ToArray(); Stream.Close(); return Bytecode; } private void BuildBuiltIns() { switch (Decl.ShaderType) { case GalShaderType.Vertex: Instruction One = AllocConstant(TypeUInt, new LiteralNumber((int)1)); Instruction ArrFloatUInt1 = new OpTypeArray(TypeFloat, One); Instruction PerVertex = new OpTypeStruct(TypeFloats[3], TypeFloat, ArrFloatUInt1, ArrFloatUInt1); Instruction OutputPerVertex = new OpTypePointer(StorageClass.Output, PerVertex); Decorates.Add(new OpMemberDecorate(PerVertex, 0, Decoration.BuiltIn, BuiltIn.Position)); Decorates.Add(new OpMemberDecorate(PerVertex, 1, Decoration.BuiltIn, BuiltIn.PointSize)); Decorates.Add(new OpMemberDecorate(PerVertex, 2, Decoration.BuiltIn, BuiltIn.ClipDistance)); Decorates.Add(new OpMemberDecorate(PerVertex, 3, Decoration.BuiltIn, BuiltIn.CullDistance)); Decorates.Add(new OpDecorate(PerVertex, Decoration.Block)); TypesConstants.Add(ArrFloatUInt1); TypesConstants.Add(PerVertex); TypesConstants.Add(OutputPerVertex); PerVertexVar = new OpVariable(OutputPerVertex, StorageClass.Output); VarsDeclaration.Add(PerVertexVar); Names.Add(new OpName(PerVertex, "gl_PerVertex")); break; } } private void BuildTextures() { foreach (ShaderDeclInfo DeclInfo in Decl.Textures.Values) { Instruction Variable = AllocLocatedVariable( TypeSampler2D_Uniform, StorageClass.UniformConstant, DeclInfo.Name, AllocUniformLocation); //TODO What is a "DescriptorSet"? It sounds like something from Vulkan Decorates.Add(new OpDecorate(Variable, Decoration.DescriptorSet, new LiteralNumber(0))); } } private void BuildUniforms() { if (Decl.ShaderType == GalShaderType.Vertex) { Instruction Float2 = TypeFloats_Uniform[1]; AllocLocatedVariable( Float2, StorageClass.UniformConstant, GalConsts.FlipUniformName, AllocUniformLocation); } foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values) { Instruction ElemType = TypeFloats[DeclInfo.Size - 1]; Operand Size = new LiteralNumber(DeclInfo.Index + 1); Instruction Constant = AllocConstant(TypeInt, Size); Instruction ArrayType = AllocType(new OpTypeArray(ElemType, Constant)); Instruction ArrayTypeUniform = AllocType(new OpTypePointer(StorageClass.UniformConstant, ArrayType)); AllocLocatedVariable( ArrayTypeUniform, StorageClass.UniformConstant, DeclInfo.Name, AllocUniformLocation); } } private void BuildInAttributes() { if (Decl.ShaderType == GalShaderType.Fragment) { AllocLocatedVariable( TypeFloats_In[3], StorageClass.Input, GlslDecl.PositionOutAttrName, AllocInAttributeLocation); } BuildAttributes( Decl.InAttributes.Values, TypeFloats_In, StorageClass.Input, AllocInAttributeLocation); } private void BuildOutAttributes() { if (Decl.ShaderType == GalShaderType.Vertex) { AllocLocatedVariable( TypeFloats_In[3], StorageClass.Output, GlslDecl.PositionOutAttrName, AllocOutAttributeLocation); } BuildAttributes( Decl.OutAttributes.Values, TypeFloats_Out, StorageClass.Output, AllocOutAttributeLocation); } private void BuildGprs() { foreach (ShaderDeclInfo DeclInfo in Decl.Gprs.Values) { Instruction Type = TypeFloats_Private[DeclInfo.Size - 1]; AllocVariable(Type, StorageClass.Private, DeclInfo.Name); } } private void BuildDeclPreds() { foreach (ShaderDeclInfo DeclInfo in Decl.Preds.Values) { AllocVariable(TypeBool_Private, StorageClass.Private, DeclInfo.Name); } } private void BuildAttributes( IEnumerable Decls, Instruction[] TypeFloats_InOut, StorageClass StorageClass, LocationAllocator Allocator) { foreach (ShaderDeclInfo DeclInfo in Decls) { if (DeclInfo.Index >= 0) { Instruction Type = TypeFloats_InOut[DeclInfo.Size - 1]; AllocLocatedVariable(Type, StorageClass, DeclInfo.Name, Allocator); } } } private void BuildMain() { Instruction TypeFunction = AllocType(new OpTypeFunction(TypeVoid)); Main = new OpFunction(TypeVoid, FunctionControl.None, TypeFunction); } private void BuildCode() { Code.Add(Main); // First label is implicit when building first block Dictionary Labels = new Dictionary(); foreach (ShaderIrBlock Block in Blocks) { Labels[Block] = new OpLabel(); } Instruction EndLabel = new OpLabel(); for (int BlockIndex = 0; BlockIndex < Blocks.Length; BlockIndex++) { BuildBlock(BlockIndex, Labels, EndLabel); } Code.Add(EndLabel); //TODO //SB.AppendLine(Identation + "gl_Position.xy *= flip;"); //SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + " = gl_Position;"); //SB.AppendLine(Identation + GlslDecl.PositionOutAttrName + ".w = 1;"); Code.Add(new OpReturn()); Code.Add(new OpFunctionEnd()); } private void BuildBlock( int BlockIndex, Dictionary Labels, Instruction EndLabel) { ShaderIrBlock Block = Blocks[BlockIndex]; Code.Add(Labels[Block]); bool HasBranchTail = BuildNodes(Block, Block.Nodes, Labels, EndLabel); // No unconditional branch instruction found. Branch to next block if (!HasBranchTail) { Instruction Label; if (Block.Next != null) { Label = Labels[Block.Next]; } else if (BlockIndex + 1 < Blocks.Length) { ShaderIrBlock NextBlock = Blocks[BlockIndex + 1]; Label = Labels[NextBlock]; } else { Label = EndLabel; } Code.Add(new OpBranch(Label)); } } private bool BuildNodes( ShaderIrBlock Block, List Nodes, Dictionary Labels, Instruction EndLabel) { foreach (ShaderIrNode Node in Nodes) { if (Node is ShaderIrCond Cond) { Instruction CondExpr = GetSrcExpr(Cond.Pred, true); if (Cond.Not) { CondExpr = new OpLogicalNot(TypeBool, CondExpr); Code.Add(CondExpr); } if (Cond.Child is ShaderIrOp Op && Op.Inst == ShaderIrInst.Bra) { Instruction BranchLabel = Labels[Block.Branch]; Instruction SkipLabel = new OpLabel(); Instruction Branch = new OpBranchConditional(CondExpr, BranchLabel, SkipLabel); Code.Add(Branch); Code.Add(SkipLabel); } else { Instruction ExecuteLabel = new OpLabel(); Instruction SkipLabel = new OpLabel(); Instruction Branch = new OpBranchConditional(CondExpr, ExecuteLabel, SkipLabel); Code.Add(Branch); Code.Add(ExecuteLabel); List ChildList = new List(); ChildList.Add(Cond.Child); bool HasBranchTail = BuildNodes(Block, ChildList, Labels, EndLabel); if (!HasBranchTail) { Code.Add(new OpBranch(SkipLabel)); } Code.Add(SkipLabel); } } else if (Node is ShaderIrAsg Asg) { if (IsValidOutOper(Asg.Dst)) { Instruction SrcExpr = GetSrcExpr(Asg.Src, true); Instruction Expr = GetExprWithCast(Asg.Dst, Asg.Src, SrcExpr); Instruction Target = GetDstOperName(Asg.Dst); Code.Add(new OpStore(Target, Expr)); } } else if (Node is ShaderIrOp Op) { if (Op.Inst == ShaderIrInst.Bra) { Instruction BranchLabel = Labels[Block.Branch]; Code.Add(new OpBranch(BranchLabel)); //Unconditional branch found, ignore following nodes in this hierarchy return true; } else if (Op.Inst == ShaderIrInst.Exit) { Code.Add(new OpBranch(EndLabel)); //Ignore following nodes, same as ^ return true; } else { Instruction Operation = GetSrcExpr(Op, true); } } else if (Node is ShaderIrCmnt Comment) { // Couldn't find a commentary OpCode in Spirv, for now just ignore it } else { throw new InvalidOperationException(); } } return false; } private Instruction GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, Instruction Expr) { //Note: The "DstType" (of the cast) is the type that the operation //uses on the source operands, while the "SrcType" is the destination //type of the operand result (if it is a operation) or just the type //of the variable for registers/uniforms/attributes. OperType DstType = GetSrcNodeType(Dst); OperType SrcType = GetDstNodeType(Src); if (DstType != SrcType) { //Check for invalid casts //(like bool to int/float and others). if (SrcType != OperType.F32 && SrcType != OperType.I32) { throw new InvalidOperationException(); } switch (Src) { case ShaderIrOperGpr Gpr: { //When the Gpr is ZR, just return the 0 value directly, //since the float encoding for 0 is 0. if (Gpr.IsConst) { return AllocConstant(TypeFloat, new LiteralNumber(0f)); } break; } case ShaderIrOperImm Imm: { if (DstType == OperType.F32) { float Value = BitConverter.Int32BitsToSingle(Imm.Value); if (!float.IsNaN(Value) && !float.IsInfinity(Value)) { return AllocConstant(TypeFloat, new LiteralNumber(Value)); } } break; } } switch (DstType) { case OperType.F32: return new OpBitcast(TypeFloat, Expr); case OperType.I32: return new OpBitcast(TypeInt, Expr); } } return Expr; } private static OperType GetDstNodeType(ShaderIrNode Node) { //Special case instructions with the result type different //from the input types (like integer <-> float conversion) here. if (Node is ShaderIrOp Op) { switch (Op.Inst) { case ShaderIrInst.Stof: case ShaderIrInst.Txlf: case ShaderIrInst.Utof: return OperType.F32; case ShaderIrInst.Ftos: case ShaderIrInst.Ftou: return OperType.I32; } } return GetSrcNodeType(Node); } private static OperType GetSrcNodeType(ShaderIrNode Node) { switch (Node) { case ShaderIrOperAbuf Abuf: return Abuf.Offs == GlslDecl.VertexIdAttr ? OperType.I32 : OperType.F32; case ShaderIrOperCbuf Cbuf: return OperType.F32; case ShaderIrOperGpr Gpr: return OperType.F32; case ShaderIrOperImm Imm: return OperType.I32; case ShaderIrOperImmf Immf: return OperType.F32; case ShaderIrOperPred Pred: return OperType.Bool; case ShaderIrOp Op: if (Op.Inst > ShaderIrInst.B_Start && Op.Inst < ShaderIrInst.B_End) { return OperType.Bool; } else if (Op.Inst > ShaderIrInst.F_Start && Op.Inst < ShaderIrInst.F_End) { return OperType.F32; } else if (Op.Inst > ShaderIrInst.I_Start && Op.Inst < ShaderIrInst.I_End) { return OperType.I32; } break; } throw new ArgumentException(nameof(Node)); } private bool IsValidOutOper(ShaderIrNode Node) { if (Node is ShaderIrOperGpr Gpr && Gpr.IsConst) { return false; } else if (Node is ShaderIrOperPred Pred && Pred.IsConst) { return false; } return true; } private Instruction GetDstOperName(ShaderIrNode Node) { if (Node is ShaderIrOperAbuf Abuf) { return GetOutAbufName(Abuf); } else if (Node is ShaderIrOperGpr Gpr) { return GetName(Gpr, true); } else if (Node is ShaderIrOperPred Pred) { return GetName(Pred, true); } throw new ArgumentException(nameof(Node)); } private Instruction GetOutAbufName(ShaderIrOperAbuf Abuf) { return GetName(Decl.OutAttributes, Abuf, true); } private Instruction GetSrcExpr(ShaderIrNode Node, bool Entry = false) { switch (Node) { case ShaderIrOperAbuf Abuf: return GetName (Abuf, false); case ShaderIrOperCbuf Cbuf: return GetName (Cbuf, false); case ShaderIrOperGpr Gpr: return GetName (Gpr, false); case ShaderIrOperImm Imm: return GetValue(Imm); case ShaderIrOperImmf Immf: return GetValue(Immf); case ShaderIrOperPred Pred: return GetName (Pred, false); case ShaderIrOp Op: Instruction Expr; if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr)) { Expr = GetExpr(Op); } else { throw new NotImplementedException(Op.Inst.ToString()); } //GetExpr does not add it to Code Code.Add(Expr); return Expr; default: throw new ArgumentException(nameof(Node)); } } private Instruction GetName(ShaderIrOperAbuf Abuf, bool Pointer) { if (Decl.ShaderType == GalShaderType.Vertex) { switch (Abuf.Offs) { case GlslDecl.VertexIdAttr: return GetVariableValue(TypeInt, "gl_VertexID", Pointer); case GlslDecl.InstanceIdAttr: return GetVariableValue(TypeInt, "gl_InstanceID", Pointer); } } else if (Decl.ShaderType == GalShaderType.TessEvaluation) { switch (Abuf.Offs) { case GlslDecl.TessCoordAttrX: return GetVariableValue(TypeFloat, "gl_TessCoord", 0, Pointer); case GlslDecl.TessCoordAttrY: return GetVariableValue(TypeFloat, "gl_TessCoord", 1, Pointer); case GlslDecl.TessCoordAttrZ: return GetVariableValue(TypeFloat, "gl_TessCoord", 2, Pointer); } } return GetName(Decl.InAttributes, Abuf, Pointer); } private Instruction GetName(IReadOnlyDictionary Dict, ShaderIrOperAbuf Abuf, bool Pointer) { int Index = Abuf.Offs >> 4; int Elem = (Abuf.Offs >> 2) & 3; if (!Dict.TryGetValue(Index, out ShaderDeclInfo DeclInfo)) { throw new InvalidOperationException(); } //Guess types are float-based if (DeclInfo.Size > 1) { return GetVariableValue(TypeFloat, DeclInfo.Name, Elem, Pointer); } else { return GetVariableValue(TypeFloat, DeclInfo.Name, Pointer); } } private Instruction GetName(ShaderIrOperGpr Gpr, bool Pointer) { //Gprs are always float, right? if (Gpr.IsConst) { if (Pointer) { throw new InvalidOperationException("Can't return pointer to a constant"); } return AllocConstant(TypeFloat, new LiteralNumber(0f)); } else { return GetNameWithSwizzle(TypeFloat, Decl.Gprs, Gpr.Index, Pointer); } } private Instruction GetValue(ShaderIrOperImm Imm) { return AllocConstant(TypeInt, new LiteralNumber(Imm.Value)); } private Instruction GetValue(ShaderIrOperImmf Immf) { return AllocConstant(TypeFloat, new LiteralNumber(Immf.Value)); } private Instruction GetName(ShaderIrOperPred Pred, bool Pointer) { if (Pred.IsConst) { return TrueConstant; } else { return GetNameWithSwizzle(TypeBool, Decl.Preds, Pred.Index, Pointer); } } private Instruction GetName(ShaderIrOperCbuf Cbuf, bool Pointer) { if (!Decl.Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo)) { throw new InvalidOperationException(); } Instruction PosConstant = AllocConstant(TypeInt, new LiteralNumber(Cbuf.Pos)); if (Cbuf.Offs != null) { //Note: We assume that the register value is always a multiple of 4. //This may not be always the case. Instruction ShiftConstant = AllocConstant(TypeInt, new LiteralNumber(2)); Instruction Source = GetSrcExpr(Cbuf.Offs); Instruction Casted = new OpBitcast(TypeInt, Source); Instruction Offset = new OpShiftRightLogical(TypeInt, Casted, ShiftConstant); Instruction Index = new OpIAdd(TypeInt, PosConstant, Offset); Code.Add(Source); Code.Add(Casted); Code.Add(Offset); Code.Add(Index); return GetVariableValue(TypeFloat, DeclInfo.Name, Index, Pointer); } else { return GetVariableValue(TypeFloat, DeclInfo.Name, PosConstant, Pointer); } } private Instruction GetNameWithSwizzle( Instruction Type, IReadOnlyDictionary Dict, int Index, bool Pointer) { int VecIndex = Index >> 2; if (Dict.TryGetValue(VecIndex, out ShaderDeclInfo DeclInfo)) { if (DeclInfo.Size > 1 && Index < VecIndex + DeclInfo.Size) { return GetVariableValue(Type, DeclInfo.Name, Index & 3, Pointer); } } if (!Dict.TryGetValue(Index, out DeclInfo)) { throw new InvalidOperationException(); } return GetVariableValue(Type, DeclInfo.Name, Pointer); } private void PrintHeader() { Assembler.Add(new OpCapability(Capability.Shader)); Assembler.Add(GlslExtension); Assembler.Add(new OpMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450)); } private void PrintTypes() { Assembler.Add(TypesConstants); } private void PrintEntryPoint() { List Interface = new List(); foreach (SpirvVariable Variable in Variables) { if (Variable.Storage == StorageClass.Input || Variable.Storage == StorageClass.Output) { Interface.Add(Variable.Id); } } Assembler.Add(new OpEntryPoint( GetExecutionModel(), Main, "main", Interface.ToArray())); } private ExecutionModel GetExecutionModel() { switch (Decl.ShaderType) { case GalShaderType.Vertex: return ExecutionModel.Vertex; case GalShaderType.TessControl: return ExecutionModel.TessellationControl; case GalShaderType.TessEvaluation: return ExecutionModel.TessellationEvaluation; case GalShaderType.Geometry: return ExecutionModel.Geometry; case GalShaderType.Fragment: return ExecutionModel.Fragment; default: throw new InvalidOperationException(); } } private Instruction AllocConstant(Instruction Type, Operand Value) { Instruction NewConstant = new OpConstant(Type, Value); foreach (Instruction Constant in TypesConstants) { if (Constant.Equals(NewConstant)) { return Constant; } } TypesConstants.Add(NewConstant); return NewConstant; } private Instruction GetVariableValue(Instruction ResultType, string Name, bool Pointer) { SpirvVariable Variable = GetVariable(Name); if (Pointer) { return Variable.Id; } Instruction Value = new OpLoad(ResultType, Variable.Id); Code.Add(Value); return Value; } private Instruction GetVariableValue(Instruction ResultType, string Name, int Index, bool Pointer) { Instruction InstIndex = AllocConstant(TypeInt, new LiteralNumber(Index)); return GetVariableValue(ResultType, Name, InstIndex, Pointer); } private Instruction GetVariableValue(Instruction ResultType, string Name, Instruction Index, bool Pointer) { OpTypePointer AccessTypePointer; OpAccessChain Component; switch (Name) { case "gl_Position": Instruction PositionIndex = AllocConstant(TypeInt, new LiteralNumber((int)0)); AccessTypePointer = (OpTypePointer)AllocType(new OpTypePointer(StorageClass.Output, ResultType)); Component = new OpAccessChain(AccessTypePointer, PerVertexVar, PositionIndex, Index); break; default: SpirvVariable Base = GetVariable(Name); AccessTypePointer = (OpTypePointer)AllocType(new OpTypePointer(Base.Storage, ResultType)); Component = new OpAccessChain(AccessTypePointer, Base.Id, Index); break; } Code.Add(Component); if (Pointer) { return Component; } else { Instruction Value = new OpLoad(ResultType, Component); Code.Add(Value); return Value; } } private SpirvVariable GetVariable(string Name) { foreach (SpirvVariable Variable in Variables) { if (Variable.Name == Name) { return Variable; } } throw new InvalidOperationException($"Variable {Name} not declared"); } private Instruction AllocVariable( Instruction Type, StorageClass StorageClass, string Name, int Location = -1) { Instruction InstVariable = new OpVariable(Type, StorageClass); VarsDeclaration.Add(InstVariable); Names.Add(new OpName(InstVariable, Name)); if (Location >= 0) { Operand Literal = new LiteralNumber(Location); Decorates.Add(new OpDecorate(InstVariable, Decoration.Location, Literal)); } SpirvVariable Variable = new SpirvVariable(); Variable.Id = InstVariable; Variable.Storage = StorageClass; Variable.Name = Name; Variable.Location = Location; Variables.Add(Variable); return InstVariable; } private Instruction AllocLocatedVariable( Instruction Type, StorageClass StorageClass, string Name, LocationAllocator Allocator) { return AllocVariable(Type, StorageClass, Name, Allocator()); } private Instruction AllocType(Instruction NewType) { foreach (Instruction StoredType in TypesConstants) { if (StoredType.Equals(NewType)) { return StoredType; } } TypesConstants.Add(NewType); return NewType; } private int AllocInAttributeLocation() { InAttributeCount += 1; return InAttributeCount - 1; } private int AllocOutAttributeLocation() { OutAttributeCount += 1; return OutAttributeCount - 1; } private int AllocUniformLocation() { UniformCount += 1; return UniformCount - 1; } } }