From cc298c676a7a8bda1978cd18e00e310e042d61ea Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 20 Jun 2018 16:30:18 +0000 Subject: [PATCH] SPIR-V Intermediate Shading Language support * Enable from Ryujinx.conf * Adapt to use OpenTK.NetStandard * Implement usage of UBOs for GLSL and SPIR-V * Fix a NVidia related issue * Use constant from UniformBinding --- Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs | 7 +- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 334 +++- .../Gal/OpenGL/OGLStreamBuffer.cs | 125 ++ Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs | 8 + Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 106 +- Ryujinx.Graphics/Gal/Shader/GlslProgram.cs | 10 +- .../Gal/Shader/SPIRV/SpirvAssembler.cs | 70 + .../Gal/Shader/SPIRV/SpirvBinaryForm.cs | 1125 +++++++++++ .../Gal/Shader/SPIRV/SpirvInstruction.cs | 1739 +++++++++++++++++ .../Gal/Shader/SPIRV/SpirvOperand.cs | 162 ++ Ryujinx.Graphics/Gal/Shader/ShaderBinding.cs | 28 + .../Gal/Shader/ShaderDecompiler.cs | 93 + Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs | 2 +- Ryujinx.Graphics/Gal/Shader/ShaderProgram.cs | 18 + .../Gal/Shader/SpirvDecompiler.cs | 1709 ++++++++++++++++ Ryujinx.Graphics/Gal/Shader/SpirvProgram.cs | 22 + Ryujinx.ShaderTools/Program.cs | 37 +- Ryujinx/Config.cs | 4 + Ryujinx/Ryujinx.conf | 3 + Ryujinx/Ui/GLScreen.cs | 6 + 20 files changed, 5454 insertions(+), 154 deletions(-) create mode 100644 Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvAssembler.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvBinaryForm.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvInstruction.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvOperand.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderBinding.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderDecompiler.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/ShaderProgram.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs create mode 100644 Ryujinx.Graphics/Gal/Shader/SpirvProgram.cs diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index b63c8b358c..7ae7fe7882 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -173,10 +173,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0); } - int Size = AttribElements[Attrib.Size]; - int Offset = Attrib.Offset; + int Location = Attrib.Index + 1; + int Size = AttribElements[Attrib.Size]; + int Offset = Attrib.Offset; - GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Stride, Offset); + GL.VertexAttribPointer(Location, Size, Type, Normalize, Stride, Offset); } GL.BindVertexArray(0); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index be1bb20b45..df7146be8f 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -9,40 +9,33 @@ namespace Ryujinx.Graphics.Gal.OpenGL { class OGLShader { - private class ShaderStage : IDisposable + private enum ShadingLanguage { - public int Handle { get; private set; } + Unknown, + GLSL, + SPIRV + } - public bool IsCompiled { get; private set; } + private abstract class ShaderStage : IDisposable + { + public int Handle { get; protected set; } public GalShaderType Type { get; private set; } - public string Code { get; private set; } - public IEnumerable TextureUsage { get; private set; } public IEnumerable UniformUsage { get; private set; } public ShaderStage( GalShaderType Type, - string Code, IEnumerable TextureUsage, IEnumerable UniformUsage) { - this.Type = Type; - this.Code = Code; + this.Type = Type; this.TextureUsage = TextureUsage; this.UniformUsage = UniformUsage; } - public void Compile() - { - if (Handle == 0) - { - Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); - - CompileAndCheck(Handle, Code); - } - } + public abstract void Compile(); public void Dispose() { @@ -60,6 +53,69 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + private class GlslStage : ShaderStage + { + public string Code { get; private set; } + + public GlslStage( + GalShaderType Type, + string Code, + IEnumerable TextureUsage, + IEnumerable UniformUsage) + : base(Type, TextureUsage, UniformUsage) + { + this.Code = Code; + } + + public override void Compile() + { + if (Handle == 0) + { + Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); + + GL.ShaderSource(Handle, Code); + GL.CompileShader(Handle); + + CheckCompilation(Handle); + } + } + } + + private class SpirvStage : ShaderStage + { + public byte[] Bytecode { get; private set; } + + public IDictionary Locations { get; private set; } + + public SpirvStage( + GalShaderType Type, + byte[] Bytecode, + IEnumerable TextureUsage, + IEnumerable UniformUsage, + IDictionary Locations) + : base(Type, TextureUsage, UniformUsage) + { + this.Bytecode = Bytecode; + this.Locations = Locations; + } + + public override void Compile() + { + if (Handle == 0) + { + Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); + + BinaryFormat SpirvFormat = (BinaryFormat)0x9551; + + GL.ShaderBinary(1, new int[]{Handle}, SpirvFormat, Bytecode, Bytecode.Length); + + GL.SpecializeShader(Handle, "main", 0, new int[]{}, new int[]{}); + + CheckCompilation(Handle); + } + } + } + private struct ShaderProgram { public ShaderStage Vertex; @@ -69,12 +125,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL public ShaderStage Fragment; } + private const int BuffersPerStage = Shader.UniformBinding.BuffersPerStage; + + private const int BufferSize = 16 * 1024; //ARB_uniform_buffer, 16 KiB + private ShaderProgram Current; private ConcurrentDictionary Stages; private Dictionary Programs; + private ShadingLanguage Language = ShadingLanguage.Unknown; + + private OGLStreamBuffer[][] Buffers; + public int CurrentProgramHandle { get; private set; } public OGLShader() @@ -82,6 +146,48 @@ namespace Ryujinx.Graphics.Gal.OpenGL Stages = new ConcurrentDictionary(); Programs = new Dictionary(); + + Buffers = new OGLStreamBuffer[5][]; //one per stage + + for (int Stage = 0; Stage < 5; Stage++) + { + Buffers[Stage] = new OGLStreamBuffer[BuffersPerStage]; + } + } + + public void Prepare(bool TrySPIRV) + { + Console.WriteLine(GL.GetInteger(GetPName.MaxUniformBufferBindings)); + + for (int Stage = 0; Stage < 5; Stage++) + { + for (int Cbuf = 0; Cbuf < BuffersPerStage; Cbuf++) + { + OGLStreamBuffer Buffer = OGLStreamBuffer.Create(BufferTarget.UniformBuffer, BufferSize); + + Buffer.Allocate(); + + Buffers[Stage][Cbuf] = Buffer; + } + } + + if (TrySPIRV) + { + if (HasSPIRV()) + { + Console.WriteLine("SPIR-V Shading Language"); + Language = ShadingLanguage.SPIRV; + } + else + { + Console.WriteLine("GLSL fallback (SPIR-V not available)"); + Language = ShadingLanguage.GLSL; + } + } + else + { + Language = ShadingLanguage.GLSL; + } } public void Create(IGalMemory Memory, long Tag, GalShaderType Type) @@ -91,13 +197,34 @@ namespace Ryujinx.Graphics.Gal.OpenGL private ShaderStage ShaderStageFactory(IGalMemory Memory, long Position, GalShaderType Type) { - GlslProgram Program = GetGlslProgram(Memory, Position, Type); + switch (Language) + { + case ShadingLanguage.SPIRV: + { + SpirvProgram Program = GetSpirvProgram(Memory, Position, Type); - return new ShaderStage( - Type, - Program.Code, - Program.Textures, - Program.Uniforms); + return new SpirvStage( + Type, + Program.Bytecode, + Program.Textures, + Program.Uniforms, + Program.Locations); + } + + case ShadingLanguage.GLSL: + { + GlslProgram Program = GetGlslProgram(Memory, Position, Type); + + return new GlslStage( + Type, + Program.Code, + Program.Textures, + Program.Uniforms); + } + + default: + throw new InvalidOperationException(); + } } private GlslProgram GetGlslProgram(IGalMemory Memory, long Position, GalShaderType Type) @@ -107,6 +234,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL return Decompiler.Decompile(Memory, Position + 0x50, Type); } + private SpirvProgram GetSpirvProgram(IGalMemory Memory, long Position, GalShaderType Type) + { + SpirvDecompiler Decompiler = new SpirvDecompiler(); + + return Decompiler.Decompile(Memory, Position + 0x50, Type); + } + public IEnumerable GetTextureUsage(long Tag) { if (Stages.TryGetValue(Tag, out ShaderStage Stage)) @@ -117,6 +251,73 @@ namespace Ryujinx.Graphics.Gal.OpenGL return Enumerable.Empty(); } + private int GetUniformLocation(string Name, ShaderStage Stage) + { + switch (Language) + { + case ShadingLanguage.SPIRV: + { + SpirvStage Spirv = (SpirvStage)Stage; + + if (Spirv.Locations.TryGetValue(Name, out int Location)) + { + return Location; + } + break; + } + + case ShadingLanguage.GLSL: + { + return GL.GetUniformLocation(CurrentProgramHandle, Name); + } + } + + throw new InvalidOperationException(); + } + + private bool TrySpirvStageLocation(string Name, ShaderStage Stage, out int Location) + { + if (Stage == null) + { + Location = -1; + return false; + } + + SpirvStage Spirv = (SpirvStage)Stage; + + return Spirv.Locations.TryGetValue(Name, out Location); + } + + private int GetSpirvLocation(string Name) + { + int Location; + + if (TrySpirvStageLocation(Name, Current.Vertex, out Location) + || TrySpirvStageLocation(Name, Current.TessControl, out Location) + || TrySpirvStageLocation(Name, Current.TessEvaluation, out Location) + || TrySpirvStageLocation(Name, Current.Geometry, out Location) + || TrySpirvStageLocation(Name, Current.Fragment, out Location)) + { + return Location; + } + + throw new InvalidOperationException(); + } + + private int GetUniformLocation(string Name) + { + switch (Language) + { + case ShadingLanguage.SPIRV: + return GetSpirvLocation(Name); + + case ShadingLanguage.GLSL: + return GL.GetUniformLocation(CurrentProgramHandle, Name); + } + + throw new InvalidOperationException(); + } + public void SetConstBuffer(long Tag, int Cbuf, byte[] Data) { BindProgram(); @@ -125,21 +326,21 @@ namespace Ryujinx.Graphics.Gal.OpenGL { foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf)) { - int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name); - - int Count = Data.Length >> 2; - - //The Index is the index of the last element, - //so we can add 1 to get the uniform array size. - Count = Math.Min(Count, DeclInfo.Index + 1); - - unsafe + if (Cbuf >= BuffersPerStage) { - fixed (byte* Ptr = Data) - { - GL.Uniform1(Location, Count, (float*)Ptr); - } + string Message = $"Game tried to write constant buffer #{Cbuf} but only 0-#{BuffersPerStage-1} are supported"; + throw new NotSupportedException(Message); } + + OGLStreamBuffer Buffer = Buffers[(int)Stage.Type][Cbuf]; + + int Size = Math.Min(Data.Length, BufferSize); + + byte[] Destiny = Buffer.Map(Size); + + Array.Copy(Data, Destiny, Size); + + Buffer.Unmap(Size); } } } @@ -148,7 +349,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { BindProgram(); - int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName); + int Location = GetUniformLocation(UniformName); GL.Uniform1(Location, Value); } @@ -157,7 +358,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { BindProgram(); - int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName); + int Location = GetUniformLocation(UniformName); GL.Uniform2(Location, X, Y); } @@ -204,11 +405,33 @@ namespace Ryujinx.Graphics.Gal.OpenGL CheckProgramLink(Handle); + if (Language == ShadingLanguage.GLSL) + { + BindUniformBlocksIfNotNull(Handle, Current.Vertex); + BindUniformBlocksIfNotNull(Handle, Current.TessControl); + BindUniformBlocksIfNotNull(Handle, Current.TessEvaluation); + BindUniformBlocksIfNotNull(Handle, Current.Geometry); + BindUniformBlocksIfNotNull(Handle, Current.Fragment); + } + Programs.Add(Current, Handle); } GL.UseProgram(Handle); + //TODO: This could be done once, right? + for (int Stage = 0; Stage < 5; Stage++) + { + for (int Cbuf = 0; Cbuf < BuffersPerStage; Cbuf++) + { + OGLStreamBuffer Buffer = Buffers[Stage][Cbuf]; + + int Binding = Shader.UniformBinding.Get((GalShaderType)Stage, Cbuf); + + GL.BindBufferBase(BufferRangeTarget.UniformBuffer, Binding, Buffer.Handle); + } + } + CurrentProgramHandle = Handle; } @@ -222,12 +445,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - public static void CompileAndCheck(int Handle, string Code) + private void BindUniformBlocksIfNotNull(int ProgramHandle, ShaderStage Stage) { - GL.ShaderSource(Handle, Code); - GL.CompileShader(Handle); + if (Stage != null) + { + foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage) + { + int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name); - CheckCompilation(Handle); + if (BlockIndex < 0) + { + throw new InvalidOperationException(); + } + + int Binding = Shader.UniformBinding.Get(Stage.Type, DeclInfo.Cbuf); + + GL.UniformBlockBinding(ProgramHandle, BlockIndex, Binding); + } + } } private static void CheckCompilation(int Handle) @@ -253,5 +488,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL throw new ShaderException(GL.GetProgramInfoLog(Handle)); } } + + private static bool HasSPIRV() + { + int ExtensionCount = GL.GetInteger(GetPName.NumExtensions); + + for (int i = 0; i < ExtensionCount; i++) + { + if (GL.GetString(StringNameIndexed.Extensions, i) == "GL_ARB_gl_spirv") + { + return true; + } + } + + return false; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs new file mode 100644 index 0000000000..3d371edbc3 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs @@ -0,0 +1,125 @@ +using System; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + abstract class OGLStreamBuffer : IDisposable + { + public int Handle { get; protected set; } + + protected BufferTarget Target; + + protected int Size; + + private bool Mapped = false; + + public OGLStreamBuffer(BufferTarget Target, int MaxSize) + { + Handle = 0; + Mapped = false; + + this.Target = Target; + this.Size = MaxSize; + } + + public static OGLStreamBuffer Create(BufferTarget Target, int MaxSize) + { + return new SubDataBuffer(Target, MaxSize); + } + + public void Allocate() + { + if (this.Handle == 0) + { + GL.CreateBuffers(1, out int Handle); + + this.Handle = Handle; + + InternAllocate(); + } + } + + public byte[] Map(int Size) + { + if (Handle == 0 || Mapped || Size > this.Size) + { + throw new InvalidOperationException(); + } + + byte[] Memory = InternMap(Size); + + Mapped = true; + + return Memory; + } + + public void Unmap(int UsedSize) + { + if (Handle == 0 || !Mapped) + { + throw new InvalidOperationException(); + } + + InternUnmap(UsedSize); + + Mapped = false; + } + + protected abstract void InternAllocate(); + + protected abstract byte[] InternMap(int Size); + + protected abstract void InternUnmap(int UsedSize); + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing && Handle != 0) + { + GL.DeleteBuffer(Handle); + + Handle = 0; + } + } + } + + class SubDataBuffer : OGLStreamBuffer + { + private byte[] Memory; + + public SubDataBuffer(BufferTarget Target, int MaxSize) + : base(Target, MaxSize) + { + Memory = new byte[MaxSize]; + } + + protected override void InternAllocate() + { + GL.BindBuffer(Target, Handle); + + GL.BufferData(Target, Size, IntPtr.Zero, BufferUsageHint.StreamDraw); + } + + protected override byte[] InternMap(int Size) + { + return Memory; + } + + protected override void InternUnmap(int UsedSize) + { + GL.BindBuffer(Target, Handle); + + unsafe + { + fixed (byte* MemoryPtr = Memory) + { + GL.BufferSubData(Target, IntPtr.Zero, UsedSize, (IntPtr)MemoryPtr); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs index 4c4bd2caea..4908c9bcd7 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OpenGLRenderer.cs @@ -34,6 +34,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL ActionsQueue = new ConcurrentQueue(); } + public void Initialize(bool TrySPIRV = false) + { + ActionsQueue.Enqueue(() => + { + Shader.Prepare(TrySPIRV); + }); + } + public void QueueAction(Action ActionMthd) { ActionsQueue.Enqueue(ActionMthd); diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 77f16b81fa..b1b714c58f 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -6,19 +6,12 @@ using System.Text; namespace Ryujinx.Graphics.Gal.Shader { - public class GlslDecompiler + public class GlslDecompiler : ShaderDecompiler { private delegate string GetInstExpr(ShaderIrOp Op); private Dictionary InstsExpr; - private enum OperType - { - Bool, - F32, - I32 - } - private const string IdentationStr = " "; private static string[] ElemTypes = new string[] { "float", "vec2", "vec3", "vec4" }; @@ -145,7 +138,9 @@ namespace Ryujinx.Graphics.Gal.Shader foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) { - SB.AppendLine($"uniform {GetDecl(DeclInfo)}[{DeclInfo.Index + 1}];"); + SB.AppendLine($"layout (std140) uniform {DeclInfo.Name} {{"); + SB.AppendLine($" vec4 {DeclInfo.Name}_data[{DeclInfo.Index / 4 + 1}];"); + SB.AppendLine($"}};"); } if (Decl.Uniforms.Count > 0) @@ -158,7 +153,7 @@ namespace Ryujinx.Graphics.Gal.Shader { if (Decl.ShaderType == GalShaderType.Fragment) { - SB.AppendLine("in vec4 " + GlslDecl.PositionOutAttrName + ";"); + SB.AppendLine("layout (location = 0) in vec4 " + GlslDecl.PositionOutAttrName + ";"); } PrintDeclAttributes(Decl.InAttributes.Values, "in"); @@ -168,7 +163,7 @@ namespace Ryujinx.Graphics.Gal.Shader { if (Decl.ShaderType == GalShaderType.Vertex) { - SB.AppendLine("out vec4 " + GlslDecl.PositionOutAttrName + ";"); + SB.AppendLine("layout (location = 0) out vec4 " + GlslDecl.PositionOutAttrName + ";"); } PrintDeclAttributes(Decl.OutAttributes.Values, "out"); @@ -182,7 +177,9 @@ namespace Ryujinx.Graphics.Gal.Shader { if (DeclInfo.Index >= 0) { - SB.AppendLine("layout (location = " + DeclInfo.Index + ") " + InOut + " " + GetDecl(DeclInfo) + ";"); + int Location = DeclInfo.Index + 1; + + SB.AppendLine("layout (location = " + Location + ") " + InOut + " " + GetDecl(DeclInfo) + ";"); Count++; } @@ -232,11 +229,6 @@ namespace Ryujinx.Graphics.Gal.Shader } } - private int DeclKeySelector(ShaderDeclInfo DeclInfo) - { - return DeclInfo.Cbuf << 24 | DeclInfo.Index; - } - private string GetDecl(ShaderDeclInfo DeclInfo) { return ElemTypes[DeclInfo.Size - 1] + " " + DeclInfo.Name; @@ -437,20 +429,6 @@ namespace Ryujinx.Graphics.Gal.Shader return Tail; } - 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 string GetDstOperName(ShaderIrNode Node) { if (Node is ShaderIrOperAbuf Abuf) @@ -534,11 +512,13 @@ namespace Ryujinx.Graphics.Gal.Shader //This may not be aways the case. string Offset = "(floatBitsToInt(" + GetSrcExpr(Cbuf.Offs) + ") >> 2)"; - return DeclInfo.Name + "[" + Cbuf.Pos + " + " + Offset + "]"; + string Index = "(" + Cbuf.Pos + " + " + Offset + ")"; + + return $"{DeclInfo.Name}_data[{Index} / 4][{Index} % 4]"; } else { - return DeclInfo.Name + "[" + Cbuf.Pos + "]"; + return $"{DeclInfo.Name}_data[{Cbuf.Pos / 4}][{Cbuf.Pos % 4}]"; } } @@ -950,65 +930,5 @@ namespace Ryujinx.Graphics.Gal.Shader 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 || - Abuf.Offs == GlslDecl.InstanceIdAttr - ? 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)); - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs index a7af05aef9..58a95ae2f9 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslProgram.cs @@ -2,21 +2,17 @@ using System.Collections.Generic; namespace Ryujinx.Graphics.Gal.Shader { - public struct GlslProgram + public class GlslProgram : ShaderProgram { public string Code { get; private set; } - public IEnumerable Textures { get; private set; } - public IEnumerable Uniforms { get; private set; } - public GlslProgram( string Code, IEnumerable Textures, IEnumerable Uniforms) + : base(Textures, Uniforms) { - this.Code = Code; - this.Textures = Textures; - this.Uniforms = Uniforms; + this.Code = Code; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvAssembler.cs b/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvAssembler.cs new file mode 100644 index 0000000000..147ece22b0 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvAssembler.cs @@ -0,0 +1,70 @@ +using System.IO; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader.SPIRV +{ + public class Assembler + { + private List Instructions; + + public Assembler() + { + Instructions = new List(); + } + + public void Write(Stream Output) + { + uint Bound = DoBindings(); + + BinaryWriter BW = new BinaryWriter(Output); + + BW.Write((uint)BinaryForm.MagicNumber); + BW.Write((uint)BinaryForm.VersionNumber); + BW.Write((uint)BinaryForm.GeneratorMagicNumber); + BW.Write((uint)Bound); + BW.Write((uint)0); // Reserved for instruction schema + + foreach (Instruction Instruction in Instructions) + { + Instruction.Write(BW); + } + } + + public void Add(Instruction Instruction) + { + Instructions.Add(Instruction); + } + + public void Add(Instruction[] Instructions) + { + foreach (Instruction Instruction in Instructions) + { + Add(Instruction); + } + } + + public void Add(List Instructions) + { + foreach (Instruction Instruction in Instructions) + { + Add(Instruction); + } + } + + private uint DoBindings() + { + uint Bind = 1; + + foreach (Instruction Instruction in Instructions) + { + if (Instruction.HoldsResultId) + { + Instruction.ResultId = Bind; + Bind++; + } + } + + return Bind; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvBinaryForm.cs b/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvBinaryForm.cs new file mode 100644 index 0000000000..b354435e70 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvBinaryForm.cs @@ -0,0 +1,1125 @@ +using System; + +namespace Ryujinx.Graphics.Gal.Shader.SPIRV +{ + public class BinaryForm + { + public const uint MagicNumber = 0x07230203; + + public const uint VersionNumber = 0x00010000; + + // I don't think Khronos would give us an Id + public const uint GeneratorMagicNumber = 0; + } + + // https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/spirv.h + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + Max = 0x7fffffff, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + Max = 0x7fffffff, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Max = 0x7fffffff, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + Max = 0x7fffffff, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + Max = 0x7fffffff, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + Max = 0x7fffffff, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, + } + + public enum ImageOperandsShift + { + BiasShift = 0, + LodShift = 1, + GradShift = 2, + ConstOffsetShift = 3, + OffsetShift = 4, + ConstOffsetsShift = 5, + SampleShift = 6, + MinLodShift = 7, + Max = 0x7fffffff, + } + + [Flags] + public enum ImageOperands + { + None = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + } + + [Flags] + public enum FPFastMathMode + { + None = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + Max = 0x7fffffff, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + NonUniformEXT = 5300, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + Max = 0x7fffffff, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + Max = 0x7fffffff, + } + + [Flags] + public enum SelectionControl + { + None = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + [Flags] + public enum LoopControl + { + None = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + } + + [Flags] + public enum FunctionControl + { + None = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + [Flags] + public enum MemorySemantics + { + None = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + } + + [Flags] + public enum MemoryAccess + { + None = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + Max = 0x7fffffff, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + Max = 0x7fffffff, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, + } + + [Flags] + public enum KernelProfilingInfo + { + None = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + Max = 0x7fffffff, + } + + public enum OpCode + { + Nop = 0, + Undef = 1, + SourceContinued = 2, + Source = 3, + SourceExtension = 4, + Name = 5, + MemberName = 6, + String = 7, + Line = 8, + Extension = 10, + ExtInstImport = 11, + ExtInst = 12, + MemoryModel = 14, + EntryPoint = 15, + ExecutionMode = 16, + Capability = 17, + TypeVoid = 19, + TypeBool = 20, + TypeInt = 21, + TypeFloat = 22, + TypeVector = 23, + TypeMatrix = 24, + TypeImage = 25, + TypeSampler = 26, + TypeSampledImage = 27, + TypeArray = 28, + TypeRuntimeArray = 29, + TypeStruct = 30, + TypeOpaque = 31, + TypePointer = 32, + TypeFunction = 33, + TypeEvent = 34, + TypeDeviceEvent = 35, + TypeReserveId = 36, + TypeQueue = 37, + TypePipe = 38, + TypeForwardPointer = 39, + ConstantTrue = 41, + ConstantFalse = 42, + Constant = 43, + ConstantComposite = 44, + ConstantSampler = 45, + ConstantNull = 46, + SpecConstantTrue = 48, + SpecConstantFalse = 49, + SpecConstant = 50, + SpecConstantComposite = 51, + SpecConstantOp = 52, + Function = 54, + FunctionParameter = 55, + FunctionEnd = 56, + FunctionCall = 57, + Variable = 59, + ImageTexelPointer = 60, + Load = 61, + Store = 62, + CopyMemory = 63, + CopyMemorySized = 64, + AccessChain = 65, + InBoundsAccessChain = 66, + PtrAccessChain = 67, + ArrayLength = 68, + GenericPtrMemSemantics = 69, + InBoundsPtrAccessChain = 70, + Decorate = 71, + MemberDecorate = 72, + DecorationGroup = 73, + GroupDecorate = 74, + GroupMemberDecorate = 75, + VectorExtractDynamic = 77, + VectorInsertDynamic = 78, + VectorShuffle = 79, + CompositeConstruct = 80, + CompositeExtract = 81, + CompositeInsert = 82, + CopyObject = 83, + Transpose = 84, + SampledImage = 86, + ImageSampleImplicitLod = 87, + ImageSampleExplicitLod = 88, + ImageSampleDrefImplicitLod = 89, + ImageSampleDrefExplicitLod = 90, + ImageSampleProjImplicitLod = 91, + ImageSampleProjExplicitLod = 92, + ImageSampleProjDrefImplicitLod = 93, + ImageSampleProjDrefExplicitLod = 94, + ImageFetch = 95, + ImageGather = 96, + ImageDrefGather = 97, + ImageRead = 98, + ImageWrite = 99, + Image = 100, + ImageQueryFormat = 101, + ImageQueryOrder = 102, + ImageQuerySizeLod = 103, + ImageQuerySize = 104, + ImageQueryLod = 105, + ImageQueryLevels = 106, + ImageQuerySamples = 107, + ConvertFToU = 109, + ConvertFToS = 110, + ConvertSToF = 111, + ConvertUToF = 112, + UConvert = 113, + SConvert = 114, + FConvert = 115, + QuantizeToF16 = 116, + ConvertPtrToU = 117, + SatConvertSToU = 118, + SatConvertUToS = 119, + ConvertUToPtr = 120, + PtrCastToGeneric = 121, + GenericCastToPtr = 122, + GenericCastToPtrExplicit = 123, + Bitcast = 124, + SNegate = 126, + FNegate = 127, + IAdd = 128, + FAdd = 129, + ISub = 130, + FSub = 131, + IMul = 132, + FMul = 133, + UDiv = 134, + SDiv = 135, + FDiv = 136, + UMod = 137, + SRem = 138, + SMod = 139, + FRem = 140, + FMod = 141, + VectorTimesScalar = 142, + MatrixTimesScalar = 143, + VectorTimesMatrix = 144, + MatrixTimesVector = 145, + MatrixTimesMatrix = 146, + OuterProduct = 147, + Dot = 148, + IAddCarry = 149, + ISubBorrow = 150, + UMulExtended = 151, + SMulExtended = 152, + Any = 154, + All = 155, + IsNan = 156, + IsInf = 157, + IsFinite = 158, + IsNormal = 159, + SignBitSet = 160, + LessOrGreater = 161, + Ordered = 162, + Unordered = 163, + LogicalEqual = 164, + LogicalNotEqual = 165, + LogicalOr = 166, + LogicalAnd = 167, + LogicalNot = 168, + Select = 169, + IEqual = 170, + INotEqual = 171, + UGreaterThan = 172, + SGreaterThan = 173, + UGreaterThanEqual = 174, + SGreaterThanEqual = 175, + ULessThan = 176, + SLessThan = 177, + ULessThanEqual = 178, + SLessThanEqual = 179, + FOrdEqual = 180, + FUnordEqual = 181, + FOrdNotEqual = 182, + FUnordNotEqual = 183, + FOrdLessThan = 184, + FUnordLessThan = 185, + FOrdGreaterThan = 186, + FUnordGreaterThan = 187, + FOrdLessThanEqual = 188, + FUnordLessThanEqual = 189, + FOrdGreaterThanEqual = 190, + FUnordGreaterThanEqual = 191, + ShiftRightLogical = 194, + ShiftRightArithmetic = 195, + ShiftLeftLogical = 196, + BitwiseOr = 197, + BitwiseXor = 198, + BitwiseAnd = 199, + Not = 200, + BitFieldInsert = 201, + BitFieldSExtract = 202, + BitFieldUExtract = 203, + BitReverse = 204, + BitCount = 205, + DPdx = 207, + DPdy = 208, + Fwidth = 209, + DPdxFine = 210, + DPdyFine = 211, + FwidthFine = 212, + DPdxCoarse = 213, + DPdyCoarse = 214, + FwidthCoarse = 215, + EmitVertex = 218, + EndPrimitive = 219, + EmitStreamVertex = 220, + EndStreamPrimitive = 221, + ControlBarrier = 224, + MemoryBarrier = 225, + AtomicLoad = 227, + AtomicStore = 228, + AtomicExchange = 229, + AtomicCompareExchange = 230, + AtomicCompareExchangeWeak = 231, + AtomicIIncrement = 232, + AtomicIDecrement = 233, + AtomicIAdd = 234, + AtomicISub = 235, + AtomicSMin = 236, + AtomicUMin = 237, + AtomicSMax = 238, + AtomicUMax = 239, + AtomicAnd = 240, + AtomicOr = 241, + AtomicXor = 242, + Phi = 245, + LoopMerge = 246, + SelectionMerge = 247, + Label = 248, + Branch = 249, + BranchConditional = 250, + Switch = 251, + Kill = 252, + Return = 253, + ReturnValue = 254, + Unreachable = 255, + LifetimeStart = 256, + LifetimeStop = 257, + GroupAsyncCopy = 259, + GroupWaitEvents = 260, + GroupAll = 261, + GroupAny = 262, + GroupBroadcast = 263, + GroupIAdd = 264, + GroupFAdd = 265, + GroupFMin = 266, + GroupUMin = 267, + GroupSMin = 268, + GroupFMax = 269, + GroupUMax = 270, + GroupSMax = 271, + ReadPipe = 274, + WritePipe = 275, + ReservedReadPipe = 276, + ReservedWritePipe = 277, + ReserveReadPipePackets = 278, + ReserveWritePipePackets = 279, + CommitReadPipe = 280, + CommitWritePipe = 281, + IsValidReserveId = 282, + GetNumPipePackets = 283, + GetMaxPipePackets = 284, + GroupReserveReadPipePackets = 285, + GroupReserveWritePipePackets = 286, + GroupCommitReadPipe = 287, + GroupCommitWritePipe = 288, + EnqueueMarker = 291, + EnqueueKernel = 292, + GetKernelNDrangeSubGroupCount = 293, + GetKernelNDrangeMaxSubGroupSize = 294, + GetKernelWorkGroupSize = 295, + GetKernelPreferredWorkGroupSizeMultiple = 296, + RetainEvent = 297, + ReleaseEvent = 298, + CreateUserEvent = 299, + IsValidEvent = 300, + SetUserEventStatus = 301, + CaptureEventProfilingInfo = 302, + GetDefaultQueue = 303, + BuildNDRange = 304, + ImageSparseSampleImplicitLod = 305, + ImageSparseSampleExplicitLod = 306, + ImageSparseSampleDrefImplicitLod = 307, + ImageSparseSampleDrefExplicitLod = 308, + ImageSparseSampleProjImplicitLod = 309, + ImageSparseSampleProjExplicitLod = 310, + ImageSparseSampleProjDrefImplicitLod = 311, + ImageSparseSampleProjDrefExplicitLod = 312, + ImageSparseFetch = 313, + ImageSparseGather = 314, + ImageSparseDrefGather = 315, + ImageSparseTexelsResident = 316, + NoLine = 317, + AtomicFlagTestAndSet = 318, + AtomicFlagClear = 319, + ImageSparseRead = 320, + SizeOf = 321, + TypePipeStorage = 322, + ConstantPipeStorage = 323, + CreatePipeFromPipeStorage = 324, + GetKernelLocalSizeForSubgroupCount = 325, + GetKernelMaxNumSubgroups = 326, + TypeNamedBarrier = 327, + NamedBarrierInitialize = 328, + MemoryNamedBarrier = 329, + ModuleProcessed = 330, + ExecutionModeId = 331, + DecorateId = 332, + GroupNonUniformElect = 333, + GroupNonUniformAll = 334, + GroupNonUniformAny = 335, + GroupNonUniformAllEqual = 336, + GroupNonUniformBroadcast = 337, + GroupNonUniformBroadcastFirst = 338, + GroupNonUniformBallot = 339, + GroupNonUniformInverseBallot = 340, + GroupNonUniformBallotBitExtract = 341, + GroupNonUniformBallotBitCount = 342, + GroupNonUniformBallotFindLSB = 343, + GroupNonUniformBallotFindMSB = 344, + GroupNonUniformShuffle = 345, + GroupNonUniformShuffleXor = 346, + GroupNonUniformShuffleUp = 347, + GroupNonUniformShuffleDown = 348, + GroupNonUniformIAdd = 349, + GroupNonUniformFAdd = 350, + GroupNonUniformIMul = 351, + GroupNonUniformFMul = 352, + GroupNonUniformSMin = 353, + GroupNonUniformUMin = 354, + GroupNonUniformFMin = 355, + GroupNonUniformSMax = 356, + GroupNonUniformUMax = 357, + GroupNonUniformFMax = 358, + GroupNonUniformBitwiseAnd = 359, + GroupNonUniformBitwiseOr = 360, + GroupNonUniformBitwiseXor = 361, + GroupNonUniformLogicalAnd = 362, + GroupNonUniformLogicalOr = 363, + GroupNonUniformLogicalXor = 364, + GroupNonUniformQuadBroadcast = 365, + GroupNonUniformQuadSwap = 366, + SubgroupBallotKHR = 4421, + SubgroupFirstInvocationKHR = 4422, + SubgroupAllKHR = 4428, + SubgroupAnyKHR = 4429, + SubgroupAllEqualKHR = 4430, + SubgroupReadInvocationKHR = 4432, + GroupIAddNonUniformAMD = 5000, + GroupFAddNonUniformAMD = 5001, + GroupFMinNonUniformAMD = 5002, + GroupUMinNonUniformAMD = 5003, + GroupSMinNonUniformAMD = 5004, + GroupFMaxNonUniformAMD = 5005, + GroupUMaxNonUniformAMD = 5006, + GroupSMaxNonUniformAMD = 5007, + FragmentMaskFetchAMD = 5011, + FragmentFetchAMD = 5012, + GroupNonUniformPartitionNV = 5296, + SubgroupShuffleINTEL = 5571, + SubgroupShuffleDownINTEL = 5572, + SubgroupShuffleUpINTEL = 5573, + SubgroupShuffleXorINTEL = 5574, + SubgroupBlockReadINTEL = 5575, + SubgroupBlockWriteINTEL = 5576, + SubgroupImageBlockReadINTEL = 5577, + SubgroupImageBlockWriteINTEL = 5578, + DecorateStringGOOGLE = 5632, + MemberDecorateStringGOOGLE = 5633, + Max = 0x7fffffff, + } + + // https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/unified1/GLSL.std.450.h + public enum GLSLstd450 + { + Bad = 0, // Don't use + + Round = 1, + RoundEven = 2, + Trunc = 3, + FAbs = 4, + SAbs = 5, + FSign = 6, + SSign = 7, + Floor = 8, + Ceil = 9, + Fract = 10, + + Radians = 11, + Degrees = 12, + Sin = 13, + Cos = 14, + Tan = 15, + Asin = 16, + Acos = 17, + Atan = 18, + Sinh = 19, + Cosh = 20, + Tanh = 21, + Asinh = 22, + Acosh = 23, + Atanh = 24, + Atan2 = 25, + + Pow = 26, + Exp = 27, + Log = 28, + Exp2 = 29, + Log2 = 30, + Sqrt = 31, + InverseSqrt = 32, + + Determinant = 33, + MatrixInverse = 34, + + Modf = 35, // second operand needs an OpVariable to write to + ModfStruct = 36, // no OpVariable operand + FMin = 37, + UMin = 38, + SMin = 39, + FMax = 40, + UMax = 41, + SMax = 42, + FClamp = 43, + UClamp = 44, + SClamp = 45, + FMix = 46, + IMix = 47, // Reserved + Step = 48, + SmoothStep = 49, + + Fma = 50, + Frexp = 51, // second operand needs an OpVariable to write to + FrexpStruct = 52, // no OpVariable operand + Ldexp = 53, + + PackSnorm4x8 = 54, + PackUnorm4x8 = 55, + PackSnorm2x16 = 56, + PackUnorm2x16 = 57, + PackHalf2x16 = 58, + PackDouble2x32 = 59, + UnpackSnorm2x16 = 60, + UnpackUnorm2x16 = 61, + UnpackHalf2x16 = 62, + UnpackSnorm4x8 = 63, + UnpackUnorm4x8 = 64, + UnpackDouble2x32 = 65, + + Length = 66, + Distance = 67, + Cross = 68, + Normalize = 69, + FaceForward = 70, + Reflect = 71, + Refract = 72, + + FindILsb = 73, + FindSMsb = 74, + FindUMsb = 75, + + InterpolateAtCentroid = 76, + InterpolateAtSample = 77, + InterpolateAtOffset = 78, + + NMin = 79, + NMax = 80, + NClamp = 81, + + Count + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvInstruction.cs b/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvInstruction.cs new file mode 100644 index 0000000000..d6d61c0e40 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvInstruction.cs @@ -0,0 +1,1739 @@ +using System; +using System.IO; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader.SPIRV +{ + public class Instruction + { + public bool HoldsResultId { get; } + + private OpCode Opcode; + private List Operands; + private Instruction ResultType; + + public Instruction( + OpCode Opcode, + bool HoldsResultId, + Instruction ResultType = null) + { + this.Opcode = Opcode; + this.HoldsResultId = HoldsResultId; + this.ResultType = ResultType; + + Operands = new List(); + } + + public void Write(BinaryWriter BinaryWriter) + { + BinaryWriter.Write((ushort)Opcode); + BinaryWriter.Write(GetWordCount()); + + if (ResultType != null) + { + BinaryWriter.Write((uint)ResultType.ResultId); + } + + if (HoldsResultId) + { + BinaryWriter.Write((uint)ResultId); + } + + foreach (Operand Operand in Operands) + { + Operand.Write(BinaryWriter); + } + } + + public ushort GetWordCount() + { + int WordCount = 1; // Opcode and WordCount word + + if (ResultType != null) + { + WordCount++; + } + + if (HoldsResultId) + { + WordCount++; + } + + foreach (Operand Operand in Operands) + { + WordCount += Operand.GetWordCount(); + } + + return (ushort)WordCount; + } + + protected void AddOperand(Operand Operand) + { + Operands.Add(Operand); + } + + protected void AddLiteralInteger(int Value) + { + AddOperand(new LiteralNumber(Value)); + } + + protected void AddEnum(int Value) + { + AddLiteralInteger(Value); + } + + protected void AddString(string Value) + { + AddOperand(new LiteralString(Value)); + } + + protected void AddId(Instruction Instruction) + { + AddOperand(new Id(Instruction)); + } + + protected void AddOperands(Operand[] Operands) + { + foreach (var Operand in Operands) + { + AddOperand(Operand); + } + } + + protected void AddIds(Instruction[] Instructions) + { + foreach (var Instruction in Instructions) + { + AddId(Instruction); + } + } + + private uint _ResultId; + public uint ResultId + { + get + { + if (!HoldsResultId) + { + string Message = "Instruction does not hold a Result ID"; + throw new InvalidOperationException(Message); + } + else if (_ResultId == 0) + { + //You forgot to add this instruction to the Assembler + //and it was referenced from other instruction + string Message = "Instruction does not have a Result ID setted"; + throw new InvalidOperationException(Message); + } + + return _ResultId; + } + set + { + if (!HoldsResultId) + { + throw new InvalidOperationException("Instruction does not take Result ID"); + } + else if (_ResultId != 0) + { + throw new InvalidOperationException("Instruction is used twice"); + } + + _ResultId = value; + } + } + } + + public class UnaryInstruction : Instruction + { + public UnaryInstruction( + OpCode Opcode, + Instruction ResultType, + Instruction Operand) + : base(Opcode, true, ResultType) + { + AddId(Operand); + } + } + + public class BinaryInstruction : Instruction + { + public BinaryInstruction( + OpCode Opcode, + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(Opcode, true, ResultType) + { + AddId(Operand1); + AddId(Operand2); + } + } + + public class OpExtInst : Instruction + { + public OpExtInst( + Instruction ResultType, + Instruction ExtensionSet, + int InstructionOpcode, + params Instruction[] Ids) + : base(OpCode.ExtInst, true, ResultType) + { + AddId(ExtensionSet); + AddEnum(InstructionOpcode); + AddIds(Ids); + } + } + + public class OpCapability : Instruction + { + public OpCapability(Capability Capability) + : base(OpCode.Capability, false) + { + AddEnum((int)Capability); + } + } + + public class OpExtInstImport : Instruction + { + public OpExtInstImport(string Name) + : base(OpCode.ExtInstImport, true) + { + AddString(Name); + } + } + + public class OpMemoryModel : Instruction + { + public OpMemoryModel(AddressingModel Addressing, MemoryModel Memory) + : base(OpCode.MemoryModel, false) + { + AddEnum((int)Addressing); + AddEnum((int)Memory); + } + } + + public class OpEntryPoint : Instruction + { + public OpEntryPoint( + ExecutionModel Execution, + Instruction EntryPoint, + string Name, + Instruction[] Interface) + : base(OpCode.EntryPoint, false) + { + AddEnum((int)Execution); + AddId(EntryPoint); + AddString(Name); + AddIds(Interface); + } + } + + public class OpExecutionMode : Instruction + { + public OpExecutionMode( + Instruction EntryPoint, + ExecutionMode Execution, + params Operand[] OptionalLiterals) + : base(OpCode.ExecutionMode, false) + { + AddId(EntryPoint); + AddEnum((int)Execution); + AddOperands(OptionalLiterals); + } + } + + public class OpDecorate : Instruction + { + public OpDecorate( + Instruction Target, + Decoration Decoration, + params Operand[] Literals) + : base(OpCode.Decorate, false) + { + AddId(Target); + AddEnum((int)Decoration); + AddOperands(Literals); + } + + public OpDecorate( + Instruction Target, + BuiltIn BuiltIn) + : this(Target, Decoration.BuiltIn, new LiteralNumber((int)BuiltIn)) + { + } + } + + public class OpTypeVoid : Instruction + { + public OpTypeVoid() + : base(OpCode.TypeVoid, true) + { + } + } + + public class OpTypeFunction : Instruction + { + public OpTypeFunction( + Instruction ReturnType, + params Instruction[] Parameters) + : base(OpCode.TypeFunction, true) + { + AddId(ReturnType); + AddIds(Parameters); + } + } + + public class OpTypeFloat : Instruction + { + public OpTypeFloat(int Width) + : base(OpCode.TypeFloat, true) + { + if (Width != 32 && Width != 64) + { + throw new ArgumentException("Float type size has to be 32 or 64"); + } + + AddLiteralInteger(Width); + } + } + + public class OpTypeVector : Instruction + { + public OpTypeVector(Instruction ComponentType, int ComponentCount) + : base(OpCode.TypeVector, true) + { + AddId(ComponentType); + AddLiteralInteger(ComponentCount); + } + } + + public class OpTypePointer : Instruction + { + public StorageClass Storage; + + public Instruction PointedType; + + public OpTypePointer(StorageClass Storage, Instruction Type) + : base(OpCode.TypePointer, true) + { + AddEnum((int)Storage); + AddId(Type); + + this.Storage = Storage; + this.PointedType = Type; + } + + public override bool Equals(object Object) + { + if (Object is OpTypePointer Other) + { + return this.Storage == Other.Storage + && this.PointedType.Equals(Other.PointedType); + } + + return false; + } + + public override int GetHashCode() + { + return Storage.GetHashCode() + PointedType.GetHashCode(); + } + } + + public class OpVariable : Instruction + { + public StorageClass Storage; + + public OpVariable( + Instruction ResultType, + StorageClass Storage, + Instruction Initializer = null) + : base(OpCode.Variable, true, ResultType) + { + AddEnum((int)Storage); + + if (Initializer != null) + { + AddId(Initializer); + } + + this.Storage = Storage; + } + } + + public class OpConstant : Instruction + { + public Instruction ResultType; + + public Operand[] Literals; + + public OpConstant( + Instruction ResultType, + params Operand[] Literals) + : base(OpCode.Constant, true, ResultType) + { + AddOperands(Literals); + + this.ResultType = ResultType; + this.Literals = Literals; + } + + public override bool Equals(object Object) + { + if (Object is OpConstant Other + && this.ResultType.Equals(Other.ResultType) + && this.Literals.Length == Other.Literals.Length) + { + for (int i = 0; i < Literals.Length; i++) + { + if (!(this.Literals[i].Equals(Other.Literals[i]))) + { + return false; + } + } + + return true; + } + + return false; + } + + public override int GetHashCode() + { + return ResultType.GetHashCode() + Literals.GetHashCode(); + } + } + + public class OpConstantComposite : Instruction + { + public OpConstantComposite( + Instruction ResultType, + params Instruction[] Constituents) + : base(OpCode.ConstantComposite, true, ResultType) + { + AddIds(Constituents); + } + } + + public class OpFunction : Instruction + { + public OpFunction( + Instruction ResultType, + FunctionControl Control, + Instruction FunctionType) + : base(OpCode.Function, true, ResultType) + { + AddEnum((int)Control); + AddId(FunctionType); + } + } + + public class OpStore : Instruction + { + public OpStore( + Instruction Pointer, + Instruction Object, + MemoryAccess MemoryAccess = MemoryAccess.None) + : base(OpCode.Store, false) + { + AddId(Pointer); + AddId(Object); + + if (MemoryAccess != MemoryAccess.None) + { + AddLiteralInteger((int)MemoryAccess); + } + } + } + + public class OpReturn : Instruction + { + public OpReturn() + : base(OpCode.Return, false) + { + } + } + + public class OpFunctionEnd : Instruction + { + public OpFunctionEnd() + : base(OpCode.FunctionEnd, false) + { + } + } + + public class OpLabel : Instruction + { + public OpLabel() + : base(OpCode.Label, true) + { + } + } + + public class OpMemberDecorate : Instruction + { + public OpMemberDecorate( + Instruction StructureType, + int Member, + Decoration Decoration, + params Operand[] Literals) + : base(OpCode.MemberDecorate, false) + { + AddId(StructureType); + AddLiteralInteger(Member); + AddEnum((int)Decoration); + AddOperands(Literals); + } + + public OpMemberDecorate( + Instruction StructureType, + int Member, + BuiltIn BuiltIn) + : this(StructureType, Member, Decoration.BuiltIn, new LiteralNumber((int)BuiltIn)) + { + } + } + + public class OpTypeStruct : Instruction + { + public OpTypeStruct(params Instruction[] MemberIds) + : base(OpCode.TypeStruct, true) + { + AddIds(MemberIds); + } + } + + public class OpTypeInt : Instruction + { + public OpTypeInt(int Width, bool IsSigned) + : base(OpCode.TypeInt, true) + { + // Width shouldn't be checked here because the specification 1.0 does not define it + // but for safety it's locked to 32 and 64 bits + if (Width != 32 && Width != 64) + { + throw new ArgumentException("Integer type size is locked 32 and 64"); + } + + AddLiteralInteger(Width); + AddLiteralInteger(IsSigned ? 1 : 0); + } + } + + public class OpCompositeExtract : Instruction + { + public OpCompositeExtract( + Instruction ResultType, + Instruction Composite, + int[] Indexes) + : base(OpCode.CompositeExtract, true, ResultType) + { + AddId(Composite); + foreach (int Index in Indexes) + { + AddLiteralInteger(Index); + } + } + + public OpCompositeExtract( + Instruction ResultType, + Instruction Composite, + int Index) + : this(ResultType, Composite, new int[]{ Index }) + { + } + } + + public class OpCompositeConstruct : Instruction + { + public OpCompositeConstruct( + Instruction ResultType, + params Instruction[] Constituents) + : base(OpCode.CompositeConstruct, true, ResultType) + { + AddIds(Constituents); + } + } + + public class OpLoad : Instruction + { + public OpLoad( + Instruction ResultType, + Instruction Pointer, + MemoryAccess MemoryAccess = MemoryAccess.None) + : base(OpCode.Load, true, ResultType) + { + AddId(Pointer); + + if (MemoryAccess != MemoryAccess.None) + { + AddLiteralInteger((int)MemoryAccess.None); + } + } + } + + public class OpAccessChain : Instruction + { + public OpAccessChain( + Instruction ResultType, + Instruction Base, + params Instruction[] Indexes) + : base(OpCode.AccessChain, true, ResultType) + { + if (Base is OpVariable Variable) + { + if (ResultType is OpTypePointer Pointer) + { + if (Variable.Storage != Pointer.Storage) + { + throw new ArgumentException("Result type and base have to share the same storage"); + } + } + else + { + throw new ArgumentException("Result type has to be a pointer"); + } + } + else + { + throw new ArgumentException("Base has to be a variable"); + } + + AddId(Base); + AddIds(Indexes); + } + } + + public class OpTypeArray : Instruction + { + public OpTypeArray( + Instruction ElementType, + Instruction Length) + : base(OpCode.TypeArray, true) + { + AddId(ElementType); + AddId(Length); + } + } + + public class OpIAdd : BinaryInstruction + { + public OpIAdd( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.IAdd, ResultType, Operand1, Operand2) + { + } + } + + public class OpFAdd : BinaryInstruction + { + public OpFAdd( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FAdd, ResultType, Operand1, Operand2) + { + } + } + + public class OpISub : BinaryInstruction + { + public OpISub( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.ISub, ResultType, Operand1, Operand2) + { + } + } + + public class OpFSub : BinaryInstruction + { + public OpFSub( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FSub, ResultType, Operand1, Operand2) + { + } + } + + public class OpIMul : BinaryInstruction + { + public OpIMul( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.IMul, ResultType, Operand1, Operand2) + { + } + } + + public class OpFMul : BinaryInstruction + { + public OpFMul( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FMul, ResultType, Operand1, Operand2) + { + } + } + + public class OpUDiv : BinaryInstruction + { + public OpUDiv( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.UDiv, ResultType, Operand1, Operand2) + { + } + } + + public class OpSDiv : BinaryInstruction + { + public OpSDiv( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.SDiv, ResultType, Operand1, Operand2) + { + } + } + + public class OpFDiv : BinaryInstruction + { + public OpFDiv( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FDiv, ResultType, Operand1, Operand2) + { + } + } + + public class OpUMod : BinaryInstruction + { + public OpUMod( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.UMod, ResultType, Operand1, Operand2) + { + } + } + + public class OpSRem : BinaryInstruction + { + public OpSRem( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.SRem, ResultType, Operand1, Operand2) + { + } + } + + public class OpSMod : BinaryInstruction + { + public OpSMod( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.SMod, ResultType, Operand1, Operand2) + { + } + } + + public class OpFRem : BinaryInstruction + { + public OpFRem( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FRem, ResultType, Operand1, Operand2) + { + } + } + + public class OpFMod : BinaryInstruction + { + public OpFMod( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FMod, ResultType, Operand1, Operand2) + { + } + } + + public class OpTypeImage : Instruction + { + public OpTypeImage( + Instruction SampledType, + Dim Dim, + int Depth, + int Arrayed, + int Multisampled, + int Sampled, + ImageFormat ImageFormat, + AccessQualifier AccessQualifier = AccessQualifier.Max) + : base(OpCode.TypeImage, true) + { + AddId(SampledType); + AddEnum((int)Dim); + AddLiteralInteger(Depth); + AddLiteralInteger(Arrayed); + AddLiteralInteger(Multisampled); + AddLiteralInteger(Sampled); + AddEnum((int)ImageFormat); + + if (AccessQualifier != AccessQualifier.Max) + { + AddEnum((int)AccessQualifier); + } + } + } + + public class OpTypeSampledImage : Instruction + { + public OpTypeSampledImage(Instruction ImageType) + : base(OpCode.TypeSampledImage, true) + { + AddId(ImageType); + } + } + + public class OpName : Instruction + { + public OpName( + Instruction Target, + string Name) + : base(OpCode.Name, false) + { + AddId(Target); + AddString(Name); + } + } + + public class OpTypeBool : Instruction + { + public OpTypeBool() + : base(OpCode.TypeBool, true) + { + } + } + + public class OpConstantTrue : Instruction + { + public OpConstantTrue(Instruction ResultType) + : base(OpCode.ConstantTrue, true, ResultType) + { + } + } + + public class OpConstantFalse : Instruction + { + public OpConstantFalse(Instruction ResultType) + : base(OpCode.ConstantFalse, true, ResultType) + { + } + } + + public class OpBitcast : Instruction + { + public OpBitcast( + Instruction ResultType, + Instruction Operand) + : base(OpCode.Bitcast, true, ResultType) + { + AddId(Operand); + } + } + + public class OpShiftRightLogical : Instruction + { + public OpShiftRightLogical( + Instruction ResultType, + Instruction Base, + Instruction Shift) + : base(OpCode.ShiftRightLogical, true, ResultType) + { + AddId(Base); + AddId(Shift); + } + } + + public class OpShiftLeftLogical : Instruction + { + public OpShiftLeftLogical( + Instruction ResultType, + Instruction Base, + Instruction Shift) + : base(OpCode.ShiftLeftLogical, true, ResultType) + { + AddId(Base); + AddId(Shift); + } + } + + public class OpShiftRightArithmetic : Instruction + { + public OpShiftRightArithmetic( + Instruction ResultType, + Instruction Base, + Instruction Shift) + : base(OpCode.ShiftRightArithmetic, true, ResultType) + { + AddId(Base); + AddId(Shift); + } + } + + public class OpLogicalNot : Instruction + { + public OpLogicalNot( + Instruction ResultType, + Instruction Operand) + : base(OpCode.LogicalNot, true, ResultType) + { + AddId(Operand); + } + } + + public class OpBranch : Instruction + { + public OpBranch(Instruction TargetLabel) + : base(OpCode.Branch, false) + { + AddId(TargetLabel); + } + } + + public class OpBranchConditional : Instruction + { + public OpBranchConditional( + Instruction Condition, + Instruction TrueLabel, + Instruction FalseLabel, + Operand TrueWeight = null, + Operand FalseWeight = null) + : base(OpCode.BranchConditional, false) + { + AddId(Condition); + AddId(TrueLabel); + AddId(FalseLabel); + + if (TrueWeight != null || FalseWeight != null) + { + if (TrueWeight == null || FalseWeight == null) + { + throw new InvalidOperationException("There must be either no Weights or two"); + } + + AddOperand(TrueWeight); + AddOperand(FalseWeight); + } + + } + } + + public class OpNop : Instruction + { + public OpNop() + : base(OpCode.Nop, false) + { + } + } + + public class OpFOrdLessThan : BinaryInstruction + { + public OpFOrdLessThan( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FOrdLessThan, ResultType, Operand1, Operand2) + { + } + } + + public class OpFOrdGreaterThan : BinaryInstruction + { + public OpFOrdGreaterThan( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FOrdGreaterThan, ResultType, Operand1, Operand2) + { + } + } + + public class OpFOrdGreaterThanEqual : BinaryInstruction + { + public OpFOrdGreaterThanEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FOrdGreaterThanEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpFOrdLessThanEqual : BinaryInstruction + { + public OpFOrdLessThanEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FOrdLessThanEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpSLessThan : BinaryInstruction + { + public OpSLessThan( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.SLessThan, ResultType, Operand1, Operand2) + { + } + } + + public class OpSLessThanEqual : BinaryInstruction + { + public OpSLessThanEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.SLessThanEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpSGreaterThan : BinaryInstruction + { + public OpSGreaterThan( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.SGreaterThan, ResultType, Operand1, Operand2) + { + } + } + + public class OpSGreaterThanEqual : BinaryInstruction + { + public OpSGreaterThanEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.SGreaterThanEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpIsNan : UnaryInstruction + { + public OpIsNan( + Instruction ResultType, + Instruction Operand) + : base(OpCode.IsNan, ResultType, Operand) + { + } + } + + public class OpIsNormal : UnaryInstruction + { + public OpIsNormal( + Instruction ResultType, + Instruction Operand) + : base(OpCode.IsNormal, ResultType, Operand) + { + } + } + + public class OpKill: Instruction + { + public OpKill() + : base(OpCode.Kill, false) + { + } + } + + public class OpNot : UnaryInstruction + { + public OpNot( + Instruction ResultType, + Instruction Operand) + : base(OpCode.Not, ResultType, Operand) + { + } + } + + public class OpFOrdEqual : BinaryInstruction + { + public OpFOrdEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FOrdEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpFOrdNotEqual : BinaryInstruction + { + public OpFOrdNotEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.FOrdNotEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpFNegate : UnaryInstruction + { + public OpFNegate( + Instruction ResultType, + Instruction Operand) + : base(OpCode.FNegate, ResultType, Operand) + { + } + } + + public class OpBitwiseAnd : BinaryInstruction + { + public OpBitwiseAnd( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.BitwiseAnd, ResultType, Operand1, Operand2) + { + } + } + + public class OpBitwiseOr : BinaryInstruction + { + public OpBitwiseOr( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.BitwiseOr, ResultType, Operand1, Operand2) + { + } + } + + public class OpImage : Instruction + { + public OpImage( + Instruction ResultType, + Instruction SampledImage) + : base(OpCode.Image, true, ResultType) + { + AddId(SampledImage); + } + } + + public class OpImageQuerySizeLod : Instruction + { + public OpImageQuerySizeLod( + Instruction ResultType, + Instruction Image, + Instruction LevelOfDetail) + : base(OpCode.ImageQuerySizeLod, true, ResultType) + { + AddId(Image); + AddId(LevelOfDetail); + } + } + + public class OpImageSampleImplicitLod : Instruction + { + public OpImageSampleImplicitLod( + Instruction ResultType, + Instruction SampledImage, + Instruction Coordinate, + ImageOperands ImageOperands = ImageOperands.None, + params Instruction[] Params) + : base(OpCode.ImageSampleImplicitLod, true, ResultType) + { + AddId(SampledImage); + AddId(Coordinate); + + if (ImageOperands != ImageOperands.None) + { + AddEnum((int)ImageOperands); + AddIds(Params); + } + } + } + + public class OpImageFetch : Instruction + { + public OpImageFetch( + Instruction ResultType, + Instruction Image, + Instruction Coordinate, + ImageOperands ImageOperands = ImageOperands.None, + params Instruction[] Params) + : base(OpCode.ImageFetch, true, ResultType) + { + AddId(Image); + AddId(Coordinate); + + if (ImageOperands != ImageOperands.None) + { + AddEnum((int)ImageOperands); + AddIds(Params); + } + } + } + + public class OpINotEqual : BinaryInstruction + { + public OpINotEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.INotEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpIEqual : BinaryInstruction + { + public OpIEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.IEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpConvertFToU : UnaryInstruction + { + public OpConvertFToU( + Instruction ResultType, + Instruction Operand) + : base(OpCode.ConvertFToU, ResultType, Operand) + { + } + } + + public class OpConvertFToS : UnaryInstruction + { + public OpConvertFToS( + Instruction ResultType, + Instruction Operand) + : base(OpCode.ConvertFToS, ResultType, Operand) + { + } + } + + public class OpConvertUToF : UnaryInstruction + { + public OpConvertUToF( + Instruction ResultType, + Instruction Operand) + : base(OpCode.ConvertUToF, ResultType, Operand) + { + } + } + + public class OpConvertSToF : UnaryInstruction + { + public OpConvertSToF( + Instruction ResultType, + Instruction Operand) + : base(OpCode.ConvertSToF, ResultType, Operand) + { + } + } + + public class OpBitwiseXor : BinaryInstruction + { + public OpBitwiseXor( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.BitwiseXor, ResultType, Operand1, Operand2) + { + } + } + + public class OpSNegate : UnaryInstruction + { + public OpSNegate( + Instruction ResultType, + Instruction Operand) + : base(OpCode.SNegate, ResultType, Operand) + { + } + } + + public class OpLogicalAnd : BinaryInstruction + { + public OpLogicalAnd( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.LogicalAnd, ResultType, Operand1, Operand2) + { + } + } + + public class OpLogicalOr : BinaryInstruction + { + public OpLogicalOr( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.LogicalOr, ResultType, Operand1, Operand2) + { + } + } + + public class OpLogicalNotEqual : BinaryInstruction + { + public OpLogicalNotEqual( + Instruction ResultType, + Instruction Operand1, + Instruction Operand2) + : base(OpCode.LogicalNotEqual, ResultType, Operand1, Operand2) + { + } + } + + public class OpVectorShuffle : Instruction + { + public OpVectorShuffle( + Instruction ResultType, + Instruction Vector1, + Instruction Vector2, + params Operand[] Components) + : base(OpCode.VectorShuffle, true, ResultType) + { + AddId(Vector1); + AddId(Vector2); + AddOperands(Components); + } + } + + public class GLSLstd450Builder + { + private Instruction ExtensionSet; + + public GLSLstd450Builder(Instruction ExtensionSet) + { + this.ExtensionSet = ExtensionSet; + } + + public OpExtInst Round(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Round, X); + } + + public OpExtInst RoundEven(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.RoundEven, X); + } + + public OpExtInst Trunc(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Trunc, X); + } + + public OpExtInst FAbs(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FAbs, X); + } + + public OpExtInst SAbs(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SAbs, X); + } + + public OpExtInst FSign(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FSign, X); + } + + public OpExtInst SSign(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SSign, X); + } + + public OpExtInst Floor(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Floor, X); + } + + public OpExtInst Ceil(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Ceil, X); + } + + public OpExtInst Fract(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Fract, X); + } + + public OpExtInst Radians(Instruction ResultType, Instruction Degrees) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Radians, Degrees); + } + + public OpExtInst Degrees(Instruction ResultType, Instruction Radians) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Degrees, Radians); + } + + public OpExtInst Sin(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Sin, X); + } + + public OpExtInst Cos(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Cos, X); + } + + public OpExtInst Tan(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Tan, X); + } + + public OpExtInst Asin(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Asin, X); + } + + public OpExtInst Acos(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Acos, X); + } + + public OpExtInst Atan(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Atan, X); + } + + public OpExtInst Sinh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Sinh, X); + } + + public OpExtInst Cosh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Cosh, X); + } + + public OpExtInst Tanh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Tanh, X); + } + + public OpExtInst Asinh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Asinh, X); + } + + public OpExtInst Acosh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Acosh, X); + } + + public OpExtInst Atanh(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Atanh, X); + } + + public OpExtInst Atan2(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Atan2, X, Y); + } + + public OpExtInst Pow(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Pow, X, Y); + } + + public OpExtInst Exp(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Exp, X); + } + + public OpExtInst Log(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Log, X); + } + + public OpExtInst Exp2(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Exp2, X); + } + + public OpExtInst Log2(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Log2, X); + } + + public OpExtInst Sqrt(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Sqrt, X); + } + + public OpExtInst InverseSqrt(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.InverseSqrt, X); + } + + public OpExtInst Determinant(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Determinant, X); + } + + public OpExtInst MatrixInverse(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.MatrixInverse, X); + } + + public OpExtInst Modf(Instruction ResultType, Instruction X, Instruction I) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Modf, X, I); + } + + public OpExtInst ModfStruct(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.ModfStruct, X); + } + + public OpExtInst FMin(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FMin, X, Y); + } + + public OpExtInst UMin(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UMin, X, Y); + } + + public OpExtInst SMin(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SMin, X, Y); + } + + public OpExtInst FMax(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FMax, X, Y); + } + + public OpExtInst UMax(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UMax, X, Y); + } + + public OpExtInst SMax(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SMax, X, Y); + } + + public OpExtInst FClamp(Instruction ResultType, Instruction X, Instruction MinVal, Instruction MaxVal) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FClamp, X, MinVal, MaxVal); + } + + public OpExtInst UClamp(Instruction ResultType, Instruction X, Instruction MinVal, Instruction MaxVal) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UClamp, X, MinVal, MaxVal); + } + + public OpExtInst SClamp(Instruction ResultType, Instruction X, Instruction MinVal, Instruction MaxVal) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SClamp, X, MinVal, MaxVal); + } + + public OpExtInst FMix(Instruction ResultType, Instruction X, Instruction Y, Instruction A) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FMix, X, Y, A); + } + + public OpExtInst Step(Instruction ResultType, Instruction Edge, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Step, Edge, X); + } + + public OpExtInst SmoothStep(Instruction ResultType, Instruction Edge1, Instruction Edge2, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.SmoothStep, Edge1, Edge2, X); + } + + public OpExtInst Fma(Instruction ResultType, Instruction A, Instruction B, Instruction C) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Fma, A, B, C); + } + + public OpExtInst Frexp(Instruction ResultType, Instruction X, Instruction Exp) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Frexp, X, Exp); + } + + public OpExtInst FrexpStruct(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FrexpStruct, X); + } + + public OpExtInst Ldexp(Instruction ResultType, Instruction X, Instruction Exp) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Ldexp, X, Exp); + } + + public OpExtInst PackSnorm4x8(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackSnorm4x8, V); + } + + public OpExtInst PackUnorm4x8(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackUnorm4x8, V); + } + + public OpExtInst PackSnorm2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackSnorm2x16, V); + } + + public OpExtInst PackUnorm2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackUnorm2x16, V); + } + + public OpExtInst PackHalf2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackHalf2x16, V); + } + + public OpExtInst PackDouble2x32(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.PackDouble2x32, V); + } + + public OpExtInst UnpackSnorm4x8(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackSnorm4x8, V); + } + + public OpExtInst UnpackUnorm4x8(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackUnorm4x8, V); + } + + public OpExtInst UnpackSnorm2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackSnorm2x16, V); + } + + public OpExtInst UnpackUnorm2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackUnorm2x16, V); + } + + public OpExtInst UnpackHalf2x16(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackHalf2x16, V); + } + + public OpExtInst UnpackDouble2x32(Instruction ResultType, Instruction V) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.UnpackDouble2x32, V); + } + + public OpExtInst Length(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Length, X); + } + + public OpExtInst Distance(Instruction ResultType, Instruction Point1, Instruction Point2) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Distance, Point1, Point2); + } + + public OpExtInst Cross(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Cross, X, Y); + } + + public OpExtInst Normalize(Instruction ResultType, Instruction X) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Normalize, X); + } + + public OpExtInst FaceForward(Instruction ResultType, Instruction N, Instruction I, Instruction Nref) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FaceForward, N, I, Nref); + } + + public OpExtInst Reflect(Instruction ResultType, Instruction I, Instruction N) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Reflect, I, N); + } + + public OpExtInst Refract(Instruction ResultType, Instruction I, Instruction N, Instruction Eta) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.Refract, I, N, Eta); + } + + public OpExtInst FindILsb(Instruction ResultType, Instruction Value) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FindILsb, Value); + } + + public OpExtInst FindSMsb(Instruction ResultType, Instruction Value) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FindSMsb, Value); + } + + public OpExtInst FindUMsb(Instruction ResultType, Instruction Value) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.FindUMsb, Value); + } + + public OpExtInst InterpolateAtCentroid(Instruction ResultType, Instruction Interpolant) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.InterpolateAtCentroid, Interpolant); + } + + public OpExtInst InterpolateAtSample(Instruction ResultType, Instruction Interpolant, Instruction Sample) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.InterpolateAtSample, Interpolant, Sample); + } + + public OpExtInst InterpolateAtOffset(Instruction ResultType, Instruction Interpolant, Instruction Offset) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.InterpolateAtOffset, Interpolant, Offset); + } + + public OpExtInst NMin(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.NMin, X, Y); + } + + public OpExtInst NMax(Instruction ResultType, Instruction X, Instruction Y) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.NMax, X, Y); + } + + public OpExtInst NClamp(Instruction ResultType, Instruction X, Instruction MinVal, Instruction MaxVal) + { + return new OpExtInst(ResultType, ExtensionSet, (int)GLSLstd450.NClamp, X, MinVal, MaxVal); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvOperand.cs b/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvOperand.cs new file mode 100644 index 0000000000..d5c8266c31 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SPIRV/SpirvOperand.cs @@ -0,0 +1,162 @@ +using System; +using System.IO; +using System.Text; + +namespace Ryujinx.Graphics.Gal.Shader.SPIRV +{ + public abstract class Operand + { + public abstract int GetWordCount(); + + public abstract void Write(BinaryWriter BinaryWriter); + } + + public class Id: Operand + { + private Instruction Instruction; + + public Id(Instruction Instruction) + { + this.Instruction = Instruction; + } + + public override void Write(BinaryWriter BinaryWriter) + { + BinaryWriter.Write((uint)Instruction.ResultId); + } + + public override int GetWordCount() + { + return 1; + } + } + + public abstract class Literal: Operand + { + } + + public class LiteralString: Literal + { + public byte[] Value; + + public LiteralString(string String) + { + Value = Encoding.UTF8.GetBytes(String); + } + + public override void Write(BinaryWriter BinaryWriter) + { + BinaryWriter.Write(Value); + + // Write remaining zero bytes + for (int i = 0; i < 4 - (Value.Length % 4); i++) + { + BinaryWriter.Write((byte)0); + } + } + + public override int GetWordCount() + { + return Value.Length / 4 + 1; + } + + public override bool Equals(object Object) + { + if (Object is LiteralString Other) + { + return this.Value == Other.Value; + } + + return false; + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + } + + public class LiteralNumber: Literal + { + public TypeCode Type; + + public int Integer; + + public float Float32; + + public double Float64; + + public LiteralNumber(int Value) + { + Integer = Value; + Type = Value.GetTypeCode(); + } + + public LiteralNumber(float Value) + { + Float32 = Value; + Type = Value.GetTypeCode(); + } + + public LiteralNumber(double Value) + { + Float64 = Value; + Type = Value.GetTypeCode(); + } + + public override void Write(BinaryWriter BinaryWriter) + { + switch (Type) + { + case TypeCode.Int32: + BinaryWriter.Write(Integer); + break; + + case TypeCode.Single: + BinaryWriter.Write(Float32); + break; + + case TypeCode.Double: + BinaryWriter.Write(Float64); + break; + + default: + throw new NotImplementedException(); + } + } + + public override int GetWordCount() + { + switch (Type) + { + case TypeCode.Int32: + case TypeCode.Single: + return 1; + + case TypeCode.Double: + return 2; + + default: + throw new NotImplementedException(); + } + } + + public override bool Equals(object Object) + { + if (Object is LiteralNumber Other && this.Type == Other.Type) + { + return this.Integer == Other.Integer + && this.Float32 == Other.Float32 + && this.Float64 == Other.Float64; + } + + return false; + } + + public override int GetHashCode() + { + return Type.GetHashCode() + Integer.GetHashCode() + + Float32.GetHashCode() + Float64.GetHashCode(); + } + } +} diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderBinding.cs b/Ryujinx.Graphics/Gal/Shader/ShaderBinding.cs new file mode 100644 index 0000000000..8ae9a58a36 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderBinding.cs @@ -0,0 +1,28 @@ +using System; + +namespace Ryujinx.Graphics.Gal.Shader +{ + static class UniformBinding + { + public const int BuffersPerStage = 12; //ARB_uniform_buffer + + public static int Get(GalShaderType Stage, int Cbuf) + { + return GetStageIndex(Stage) * BuffersPerStage + Cbuf; + } + + private static int GetStageIndex(GalShaderType Stage) + { + switch (Stage) + { + case GalShaderType.Vertex: return 0; + case GalShaderType.Fragment: return 1; + case GalShaderType.Geometry: return 2; + case GalShaderType.TessControl: return 3; + case GalShaderType.TessEvaluation: return 4; + } + + throw new ArgumentException(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecompiler.cs new file mode 100644 index 0000000000..e0c08fea18 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecompiler.cs @@ -0,0 +1,93 @@ +using System; + +namespace Ryujinx.Graphics.Gal.Shader +{ + public class ShaderDecompiler + { + protected enum OperType + { + Bool, + F32, + I32 + } + + protected static 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; + } + + protected 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); + } + + protected static OperType GetSrcNodeType(ShaderIrNode Node) + { + switch (Node) + { + case ShaderIrOperAbuf Abuf: + return Abuf.Offs == GlslDecl.VertexIdAttr || + Abuf.Offs == GlslDecl.InstanceIdAttr + ? 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)); + } + + protected static int DeclKeySelector(ShaderDeclInfo DeclInfo) + { + return DeclInfo.Cbuf << 24 | DeclInfo.Index; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs index 2648164a11..ed266128f8 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderIrNode.cs @@ -1,4 +1,4 @@ namespace Ryujinx.Graphics.Gal.Shader { - class ShaderIrNode { } + public class ShaderIrNode { } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderProgram.cs b/Ryujinx.Graphics/Gal/Shader/ShaderProgram.cs new file mode 100644 index 0000000000..4bbac82a53 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/ShaderProgram.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader +{ + public class ShaderProgram + { + public IEnumerable Textures { get; private set; } + public IEnumerable Uniforms { get; private set; } + + public ShaderProgram( + IEnumerable Textures, + IEnumerable Uniforms) + { + this.Textures = Textures; + this.Uniforms = Uniforms; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs new file mode 100644 index 0000000000..1d1d4fef07 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SpirvDecompiler.cs @@ -0,0 +1,1709 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using Ryujinx.Graphics.Gal.Shader.SPIRV; + +namespace Ryujinx.Graphics.Gal.Shader +{ + public class SpirvDecompiler : ShaderDecompiler + { + #region "Declarations" + + private class SpirvVariable + { + public Instruction Id; + public StorageClass Storage; + public string Name; + public int Location = -1; + } + + private const int ShaderStageSafespaceStride = 1024 / 5; + + private delegate Instruction GetInstExpr(ShaderIrOp Op); + + private delegate Instruction OpCompareBuilder(Instruction A, Instruction B); + + private Dictionary InstsExpr; + + private GlslDecl Decl; + + private ShaderIrBlock[] Blocks; + + private Assembler Assembler; + + private Instruction GlslExtension; + private GLSLstd450Builder Glsl450; + + private int UniformLocation = 0; + + //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 Dictionary Locations; + + private Instruction Main; + + private Instruction PerVertex = null; + private Instruction VertexID = null; + private Instruction InstanceID = null; + + private Instruction TypeVoid; + + private Instruction TypeBool, TypeBool_Private; + + private Instruction TypeInt, TypeUInt, TypeInt2; + + private Instruction TypeFloat; + + private Instruction[] TypeFloats, TypeFloats_In, TypeFloats_Out, + TypeFloats_Uniform, TypeFloats_UniformConst, TypeFloats_Private; + + private Instruction TypeImage2D, TypeSampler2D, TypeSampler2D_Uniform; + + private Instruction TrueConstant, FalseConstant; + + #endregion + + public SpirvDecompiler() + { + InstsExpr = new Dictionary() + { + { ShaderIrInst.Abs, GetAbsExpr }, + { ShaderIrInst.Add, GetAddExpr }, + { ShaderIrInst.And, GetAndExpr }, + { ShaderIrInst.Asr, GetAsrExpr }, + { ShaderIrInst.Band, GetBandExpr }, + { ShaderIrInst.Bnot, GetBnotExpr }, + { ShaderIrInst.Bor, GetBorExpr }, + { ShaderIrInst.Bxor, GetBxorExpr }, + { ShaderIrInst.Ceil, GetCeilExpr }, + { ShaderIrInst.Ceq, GetCeqExpr }, + { ShaderIrInst.Cge, GetCgeExpr }, + { ShaderIrInst.Cgt, GetCgtExpr }, + { ShaderIrInst.Clamps, GetClampsExpr }, + { ShaderIrInst.Clampu, GetClampuExpr }, + { ShaderIrInst.Cle, GetCleExpr }, + { ShaderIrInst.Clt, GetCltExpr }, + { ShaderIrInst.Cne, GetCneExpr }, + { ShaderIrInst.Fabs, GetFabsExpr }, + { ShaderIrInst.Fadd, GetFaddExpr }, + { ShaderIrInst.Fceq, GetFceqExpr }, + { ShaderIrInst.Fcequ, GetFcequExpr }, + { ShaderIrInst.Fcge, GetFcgeExpr }, + { ShaderIrInst.Fcgeu, GetFcgeuExpr }, + { ShaderIrInst.Fcgt, GetFcgtExpr }, + { ShaderIrInst.Fcgtu, GetFcgtuExpr }, + { ShaderIrInst.Fclamp, GetFclampExpr }, + { ShaderIrInst.Fcle, GetFcleExpr }, + { ShaderIrInst.Fcleu, GetFcleuExpr }, + { ShaderIrInst.Fclt, GetFcltExpr }, + { ShaderIrInst.Fcltu, GetFcltuExpr }, + { ShaderIrInst.Fcnan, GetFcnanExpr }, + { ShaderIrInst.Fcne, GetFcneExpr }, + { ShaderIrInst.Fcneu, GetFcneuExpr }, + { ShaderIrInst.Fcnum, GetFcnumExpr }, + { ShaderIrInst.Fcos, GetFcosExpr }, + { ShaderIrInst.Fex2, GetFex2Expr }, + { ShaderIrInst.Ffma, GetFfmaExpr }, + { ShaderIrInst.Flg2, GetFlg2Expr }, + { ShaderIrInst.Floor, GetFloorExpr }, + { ShaderIrInst.Fmax, GetFmaxExpr }, + { ShaderIrInst.Fmin, GetFminExpr }, + { ShaderIrInst.Fmul, GetFmulExpr }, + { ShaderIrInst.Fneg, GetFnegExpr }, + { ShaderIrInst.Frcp, GetFrcpExpr }, + { ShaderIrInst.Frsq, GetFrsqExpr }, + { ShaderIrInst.Fsin, GetFsinExpr }, + { ShaderIrInst.Ftos, GetFtosExpr }, + { ShaderIrInst.Ftou, GetFtouExpr }, + { ShaderIrInst.Kil, GetKilExpr }, + { ShaderIrInst.Lsl, GetLslExpr }, + { ShaderIrInst.Lsr, GetLsrExpr }, + { ShaderIrInst.Max, GetMaxExpr }, + { ShaderIrInst.Min, GetMinExpr }, + { ShaderIrInst.Mul, GetMulExpr }, + { ShaderIrInst.Neg, GetNegExpr }, + { ShaderIrInst.Not, GetNotExpr }, + { ShaderIrInst.Or, GetOrExpr }, + { ShaderIrInst.Stof, GetStofExpr }, + { ShaderIrInst.Sub, GetSubExpr }, + { ShaderIrInst.Texq, GetTexqExpr }, + { ShaderIrInst.Texs, GetTexsExpr }, + { ShaderIrInst.Trunc, GetTruncExpr }, + { ShaderIrInst.Txlf, GetTxlfExpr }, + { ShaderIrInst.Utof, GetUtofExpr }, + { ShaderIrInst.Xor, GetXorExpr } + }; + + 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(); + + Locations = new Dictionary(); + } + + public SpirvProgram Decompile(IGalMemory Memory, long Position, GalShaderType ShaderType) + { + Blocks = ShaderDecoder.Decode(Memory, Position); + + Decl = new GlslDecl(Blocks, ShaderType); + + BuildCommonTypes(); + 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); + + using (MemoryStream MS = new MemoryStream()) + { + Assembler.Write(MS); + byte[] SpirvBytecode = MS.ToArray(); + + return new SpirvProgram( + SpirvBytecode, + Locations, + Decl.Textures.Values, + Decl.Uniforms.Values); + } + } + + #region "Builders" + + private void BuildCommonTypes() + { + Instruction Add(Instruction TypeOrConstant, string Name = "") + { + TypesConstants.Add(TypeOrConstant); + + if (Name != "") + { + Names.Add(new OpName(TypeOrConstant, Name)); + } + + return TypeOrConstant; + } + + TypeVoid = Add(new OpTypeVoid(), "void"); + + TypeBool = Add(new OpTypeBool(), "bool"); + TypeBool_Private = Add(new OpTypePointer(StorageClass.Private, TypeBool), "ptrBoolPrivate"); + + TrueConstant = Add(new OpConstantTrue(TypeBool), "true"); + FalseConstant = Add(new OpConstantFalse(TypeBool), "false"); + + TypeInt = Add(new OpTypeInt(32, true), "int"); + TypeUInt = Add(new OpTypeInt(32, false), "uint"); + TypeInt2 = Add(new OpTypeVector(TypeInt, 2), "i2"); + + TypeFloats = new Instruction[4]; + TypeFloats_In = new Instruction[4]; + TypeFloats_Out = new Instruction[4]; + TypeFloats_Uniform = new Instruction[4]; + TypeFloats_UniformConst = new Instruction[4]; + TypeFloats_Private = new Instruction[4]; + + TypeFloat = Add(new OpTypeFloat(32), "float"); + TypeFloats[0] = TypeFloat; + + for (int i = 0; i < 4; i++) + { + string e = i == 0 ? "" : (i + 1).ToString(); + + if (i > 0) + { + TypeFloats[i] = Add(new OpTypeVector(TypeFloat, i+1), "float" + e); + } + + TypeFloats_In[i] = Add(new OpTypePointer(StorageClass.Input, TypeFloats[i]), "ptrFloatIn" + e); + + TypeFloats_Out[i] = Add(new OpTypePointer(StorageClass.Output, TypeFloats[i]), "ptrFloatOut" + e); + + TypeFloats_Uniform[i] = Add(new OpTypePointer(StorageClass.Uniform, TypeFloats[i]), "ptrFloatUniform" + e); + + TypeFloats_UniformConst[i] = + Add(new OpTypePointer(StorageClass.UniformConstant, TypeFloats[i]), "ptrFloatUniformConst" + e); + + TypeFloats_Private[i] = Add(new OpTypePointer(StorageClass.Private, TypeFloats[i]), "ptrFloatPrivate" + e); + } + + TypeImage2D = Add(new OpTypeImage(TypeFloat, Dim.Dim2D, 0, 0, 0, 0, ImageFormat.Unknown), "image2D"); + TypeSampler2D = Add(new OpTypeSampledImage(TypeImage2D), "sampler2D"); + TypeSampler2D_Uniform = Add(new OpTypePointer(StorageClass.UniformConstant, TypeSampler2D), "pSampler2D"); + } + + private void BuildBuiltIns() + { + switch (Decl.ShaderType) + { + case GalShaderType.Vertex: + + //Build gl_PerVertex type and variable + Instruction One = AllocConstant(TypeUInt, new LiteralNumber((int)1)); + Instruction ArrFloatUInt1 = new OpTypeArray(TypeFloat, One); + + Instruction TypePerVertex = new OpTypeStruct(TypeFloats[3], TypeFloat, ArrFloatUInt1, ArrFloatUInt1); + + Instruction OutputPerVertex = new OpTypePointer(StorageClass.Output, TypePerVertex); + + Decorates.Add(new OpMemberDecorate(TypePerVertex, 0, BuiltIn.Position)); + Decorates.Add(new OpMemberDecorate(TypePerVertex, 1, BuiltIn.PointSize)); + Decorates.Add(new OpMemberDecorate(TypePerVertex, 2, BuiltIn.ClipDistance)); + Decorates.Add(new OpMemberDecorate(TypePerVertex, 3, BuiltIn.CullDistance)); + + Decorates.Add(new OpDecorate(TypePerVertex, Decoration.Block)); + + TypesConstants.Add(ArrFloatUInt1); + TypesConstants.Add(TypePerVertex); + TypesConstants.Add(OutputPerVertex); + + PerVertex = new OpVariable(OutputPerVertex, StorageClass.Output); + VarsDeclaration.Add(PerVertex); + + //Build gl_VertexID and gl_InstanceID + Instruction TypeIntInput = AllocType(new OpTypePointer(StorageClass.Input, TypeInt)); + + VertexID = new OpVariable(TypeIntInput, StorageClass.Input); + InstanceID = new OpVariable(TypeIntInput, StorageClass.Input); + + VarsDeclaration.Add(VertexID); + VarsDeclaration.Add(InstanceID); + + Decorates.Add(new OpDecorate(VertexID, BuiltIn.VertexId)); + Decorates.Add(new OpDecorate(InstanceID, BuiltIn.InstanceId)); + + Names.Add(new OpName(TypePerVertex, "struct_PerVertex")); + Names.Add(new OpName(PerVertex, "gl_PerVertex")); + Names.Add(new OpName(VertexID, "gl_VertexID")); + Names.Add(new OpName(InstanceID, "gl_InstanceID")); + + break; + } + } + + private void BuildTextures() + { + foreach (ShaderDeclInfo DeclInfo in Decl.Textures.Values) + { + Instruction Variable = AllocVariable( + TypeSampler2D_Uniform, + StorageClass.UniformConstant, + DeclInfo.Name, + AllocUniformLocation(1)); + + Decorates.Add(new OpDecorate(Variable, Decoration.DescriptorSet, new LiteralNumber(0))); + } + } + + private void BuildUniforms() + { + if (Decl.ShaderType == GalShaderType.Vertex) + { + Instruction Float2 = TypeFloats_UniformConst[1]; + AllocVariable( + Float2, + StorageClass.UniformConstant, + GalConsts.FlipUniformName, + AllocUniformLocation(1)); + } + + foreach (ShaderDeclInfo DeclInfo in Decl.Uniforms.Values.OrderBy(DeclKeySelector)) + { + //Create const buffer type + Operand ArraySize = new LiteralNumber(4096); //16 KiB of floats + + Instruction Array = new OpTypeArray(TypeFloat, AllocConstant(TypeInt, ArraySize)); + + Instruction Struct = new OpTypeStruct(Array); + + Instruction StructPointer = new OpTypePointer(StorageClass.Uniform, Struct); + + Instruction Variable = AllocVariable( + StructPointer, + StorageClass.Uniform, + DeclInfo.Name); + + int Binding = UniformBinding.Get(Decl.ShaderType, DeclInfo.Cbuf); + + Decorates.Add(new OpDecorate(Array, Decoration.ArrayStride, new LiteralNumber(4))); + Decorates.Add(new OpDecorate(Struct, Decoration.Block)); + Decorates.Add(new OpMemberDecorate(Struct, 0, Decoration.Offset, new LiteralNumber(0))); + + Decorates.Add(new OpDecorate(Variable, Decoration.DescriptorSet, new LiteralNumber(0))); + Decorates.Add(new OpDecorate(Variable, Decoration.Binding, new LiteralNumber(Binding))); + + TypesConstants.Add(Array); + TypesConstants.Add(Struct); + TypesConstants.Add(StructPointer); + + //IMPORTANT! nVidia's (propietary) OpenGL driver requires this to be declared + Names.Add(new OpName(Struct, "BLOCK_" + DeclInfo.Name)); + } + } + + private void BuildInAttributes() + { + if (Decl.ShaderType == GalShaderType.Fragment) + { + AllocVariable( + TypeFloats_In[3], + StorageClass.Input, + GlslDecl.PositionOutAttrName, + 0); + } + + BuildAttributes( + Decl.InAttributes.Values, + TypeFloats_In, + StorageClass.Input); + } + + private void BuildOutAttributes() + { + if (Decl.ShaderType == GalShaderType.Vertex) + { + AllocVariable( + TypeFloats_Out[3], + StorageClass.Output, + GlslDecl.PositionOutAttrName, + 0); + } + + BuildAttributes( + Decl.OutAttributes.Values, + TypeFloats_Out, + StorageClass.Output); + } + + private void BuildGprs() + { + foreach (ShaderDeclInfo DeclInfo in Decl.Gprs.Values.OrderBy(DeclKeySelector)) + { + if (GlslDecl.FragmentOutputName == DeclInfo.Name) + { + Instruction Type = TypeFloats_Out[DeclInfo.Size - 1]; + + AllocVariable(Type, StorageClass.Output, DeclInfo.Name); + } + else + { + Instruction Type = TypeFloats_Private[DeclInfo.Size - 1]; + + AllocVariable(Type, StorageClass.Private, DeclInfo.Name); + } + } + } + + private void BuildDeclPreds() + { + foreach (ShaderDeclInfo DeclInfo in Decl.Preds.Values.OrderBy(DeclKeySelector)) + { + AllocVariable(TypeBool_Private, StorageClass.Private, DeclInfo.Name); + } + } + + private void BuildAttributes( + IEnumerable Decls, + Instruction[] TypeFloats_InOut, + StorageClass StorageClass) + { + foreach (ShaderDeclInfo DeclInfo in Decls.OrderBy(DeclKeySelector)) + { + if (DeclInfo.Index >= 0) + { + Instruction Type = TypeFloats_InOut[DeclInfo.Size - 1]; + + int Location = DeclInfo.Index + 1; + + AllocVariable(Type, StorageClass, DeclInfo.Name, Location); + } + } + } + + 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); + + if (Decl.ShaderType == GalShaderType.Vertex) + { + BuildVertexFinish(); + } + + Code.Add(new OpReturn()); + + Code.Add(new OpFunctionEnd()); + } + + public void BuildVertexFinish() + { + //The following code is written from a glslangValidator output for the following GLSL code + //gl_Position.xy *= flip; + //position = gl_Position; + //position.w = 1; + + Instruction Varying = FindVariable(GlslDecl.PositionOutAttrName).Id; + + Instruction FlipXY = GetVariable(TypeFloats[1], GalConsts.FlipUniformName, false); + + Instruction Zero = AllocConstant(TypeInt, new LiteralNumber(0)); + Instruction BuiltInChain = AC(new OpAccessChain(TypeFloats_Out[3], PerVertex, Zero)); + + Instruction BuiltInXYZW = AC(new OpLoad(TypeFloats[3], BuiltInChain)); + + Instruction BuiltInXY = AC(new OpVectorShuffle(TypeFloats[1], BuiltInXYZW, BuiltInXYZW, + new LiteralNumber(0), new LiteralNumber(1))); + + Instruction FlippedXY = AC(new OpFMul(TypeFloats[1], BuiltInXY, FlipXY)); + + BuiltInChain = AC(new OpAccessChain(TypeFloats_Out[3], PerVertex, Zero)); + + BuiltInXYZW = AC(new OpLoad(TypeFloats[3], BuiltInChain)); + + Instruction FinalPosition = AC(new OpVectorShuffle(TypeFloats[3], BuiltInXYZW, FlippedXY, + new LiteralNumber(4), new LiteralNumber(5), new LiteralNumber(2), new LiteralNumber(3))); + + Code.Add(new OpStore(BuiltInChain, FinalPosition)); + + BuiltInChain = AC(new OpAccessChain(TypeFloats_Out[3], PerVertex, Zero)); + + BuiltInXYZW = AC(new OpLoad(TypeFloats[3], BuiltInChain)); + + Code.Add(new OpStore(Varying, BuiltInXYZW)); + + Instruction VaryingW = AC(new OpAccessChain(TypeFloats_Out[0], Varying, + AllocConstant(TypeUInt, new LiteralNumber(3)))); + + Code.Add(new OpStore(VaryingW, AllocConstant(TypeFloat, new LiteralNumber(1f)))); + } + + 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 = AC(new OpLogicalNot(TypeBool, 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 = GetDstOperValue(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); + + if (Operation is OpKill) + { + return 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; + } + + #endregion + + #region "Printers" + + private void PrintHeader() + { + Assembler.Add(new OpCapability(Capability.Shader)); + + Assembler.Add(GlslExtension); + + Assembler.Add(new OpMemoryModel(AddressingModel.Logical, MemoryModel.GLSL450)); + } + + 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); + } + } + + switch (Decl.ShaderType) + { + case GalShaderType.Vertex: + Interface.Add(PerVertex); + Interface.Add(VertexID); + Interface.Add(InstanceID); + break; + + case GalShaderType.Fragment: + Interface.Add(FindVariable(GlslDecl.FragmentOutputName).Id); + break; + } + + Assembler.Add(new OpEntryPoint( + GetExecutionModel(), + Main, + "main", + Interface.ToArray())); + } + + #endregion + + #region "Getter instructions" + + private Instruction GetExprWithCast(ShaderIrNode Dst, ShaderIrNode Src, Instruction Expr) + { + OperType DstType = GetSrcNodeType(Dst); + OperType SrcType = GetDstNodeType(Src); + + if (DstType != SrcType) + { + if (SrcType != OperType.F32 && + SrcType != OperType.I32) + { + throw new InvalidOperationException(); + } + + switch (Src) + { + case ShaderIrOperGpr Gpr: + { + if (Gpr.IsConst) + { + if (DstType == OperType.I32) + { + return AllocConstant(TypeInt, new LiteralNumber(0)); + } + else + { + 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 AC(new OpBitcast(TypeFloat, Expr)); + + case OperType.I32: + return AC(new OpBitcast(TypeInt, Expr)); + } + } + + return Expr; + } + + private Instruction GetDstOperValue(ShaderIrNode Node) + { + if (Node is ShaderIrOperAbuf Abuf) + { + return GetOutAbufValue(Abuf); + } + else if (Node is ShaderIrOperGpr Gpr) + { + return GetValue(Gpr, true); + } + else if (Node is ShaderIrOperPred Pred) + { + return GetValue(Pred, true); + } + + throw new ArgumentException(nameof(Node)); + } + + private Instruction GetOutAbufValue(ShaderIrOperAbuf Abuf) + { + return GetValue(Decl.OutAttributes, Abuf, true); + } + + private Instruction GetSrcExpr(ShaderIrNode Node, bool Entry = false) + { + switch (Node) + { + case ShaderIrOperAbuf Abuf: return GetValue (Abuf, false); + case ShaderIrOperCbuf Cbuf: return GetValue (Cbuf, false); + case ShaderIrOperGpr Gpr: return GetValue (Gpr, false); + case ShaderIrOperImm Imm: return GetValue(Imm); + case ShaderIrOperImmf Immf: return GetValue(Immf); + case ShaderIrOperPred Pred: return GetValue (Pred, false); + + case ShaderIrOp Op: + if (Op.Inst == ShaderIrInst.Ipa) + { + return GetOperExpr(Op, Op.OperandA); + } + else if (InstsExpr.TryGetValue(Op.Inst, out GetInstExpr GetExpr)) + { + return AC(GetExpr(Op)); + } + else + { + throw new NotImplementedException(Op.Inst.ToString()); + } + + default: throw new ArgumentException(nameof(Node)); + } + } + + private Instruction GetValue(ShaderIrOperAbuf Abuf, bool Pointer) + { + if (Decl.ShaderType == GalShaderType.Vertex) + { + switch (Abuf.Offs) + { + case GlslDecl.VertexIdAttr: return GetVariable(TypeInt, "gl_VertexID", Pointer); + case GlslDecl.InstanceIdAttr: return GetVariable(TypeInt, "gl_InstanceID", Pointer); + } + } + else if (Decl.ShaderType == GalShaderType.TessEvaluation) + { + switch (Abuf.Offs) + { + case GlslDecl.TessCoordAttrX: return GetVariable(TypeFloat, "gl_TessCoord", 0, Pointer); + case GlslDecl.TessCoordAttrY: return GetVariable(TypeFloat, "gl_TessCoord", 1, Pointer); + case GlslDecl.TessCoordAttrZ: return GetVariable(TypeFloat, "gl_TessCoord", 2, Pointer); + } + } + + return GetValue(Decl.InAttributes, Abuf, Pointer); + } + + private Instruction GetValue(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 GetVariable(TypeFloat, DeclInfo.Name, Elem, Pointer); + } + else + { + return GetVariable(TypeFloat, DeclInfo.Name, Pointer); + } + } + + private Instruction GetValue(ShaderIrOperGpr Gpr, bool Pointer) + { + if (Gpr.IsConst) + { + if (Pointer) + { + throw new InvalidOperationException("Can't return pointer to a constant"); + } + + return AllocConstant(TypeFloat, new LiteralNumber(0f)); + } + else + { + return GetValueWithSwizzle(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 GetValue(ShaderIrOperPred Pred, bool Pointer) + { + if (Pred.IsConst) + { + return TrueConstant; + } + else + { + return GetValueWithSwizzle(TypeBool, Decl.Preds, Pred.Index, Pointer); + } + } + + private Instruction GetValue(ShaderIrOperCbuf Cbuf, bool Pointer) + { + if (!Decl.Uniforms.TryGetValue(Cbuf.Index, out ShaderDeclInfo DeclInfo)) + { + throw new InvalidOperationException(); + } + + Instruction PosConstant = AllocConstant(TypeInt, new LiteralNumber(Cbuf.Pos)); + + Instruction Index; + + 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 = AC(new OpBitcast(TypeInt, Source)); + + Instruction Offset = AC(new OpShiftRightLogical(TypeInt, Casted, ShiftConstant)); + + Index = AC(new OpIAdd(TypeInt, PosConstant, Offset)); + } + else + { + Index = PosConstant; + } + + SpirvVariable Variable = FindVariable(DeclInfo.Name); + + Instruction MemberIndex = AllocConstant(TypeInt, new LiteralNumber(0)); + + Instruction Access = AC(new OpAccessChain(TypeFloats_Uniform[0], Variable.Id, MemberIndex, Index)); + + if (Pointer) + { + return Access; + } + else + { + return AC(new OpLoad(TypeFloat, Access)); + } + } + + private Instruction GetValueWithSwizzle( + 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 GetVariable(Type, DeclInfo.Name, Index & 3, Pointer); + } + } + + if (!Dict.TryGetValue(Index, out DeclInfo)) + { + throw new InvalidOperationException(); + } + + return GetVariable(Type, DeclInfo.Name, Pointer); + } + + 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(); + } + } + + //Get scalar / vector + private Instruction GetVariable(Instruction ResultType, string Name, bool Pointer) + { + Instruction Variable; + switch (Name) + { + case "gl_VertexID": + Variable = VertexID; + break; + + case "gl_InstanceID": + Variable = InstanceID; + break; + + default: + Variable = FindVariable(Name).Id; + break; + } + + if (Pointer) + { + return Variable; + } + + return AC(new OpLoad(ResultType, Variable)); + } + + private Instruction GetVariable(Instruction ResultType, string Name, int Index, bool Pointer) + { + Instruction InstIndex = AllocConstant(TypeInt, new LiteralNumber(Index)); + + return GetVariable(ResultType, Name, InstIndex, Pointer); + } + + //Get scalar from a compounds + private Instruction GetVariable(Instruction ResultType, string Name, Instruction Index, bool Pointer) + { + Instruction AccessTypePointer; + Instruction Component; + + switch (Name) + { + case "gl_Position": + { + Instruction MemberIndex = AllocConstant(TypeInt, new LiteralNumber((int)0)); + + AccessTypePointer = AllocType(new OpTypePointer(StorageClass.Output, ResultType)); + Component = new OpAccessChain(AccessTypePointer, PerVertex, MemberIndex, Index); + break; + } + + default: + { + SpirvVariable Base = FindVariable(Name); + + AccessTypePointer = 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; + } + } + + #endregion + + #region "Allocators" + + 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 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)); + + //Add it to locations dictionary (used for query) + Locations.Add(Name, Location); + } + + SpirvVariable Variable = new SpirvVariable(); + Variable.Id = InstVariable; + Variable.Storage = StorageClass; + Variable.Name = Name; + Variable.Location = Location; + + Variables.Add(Variable); + + return InstVariable; + } + + private Instruction AllocType(Instruction NewType) + { + foreach (Instruction StoredType in TypesConstants) + { + if (StoredType.Equals(NewType)) + { + return StoredType; + } + } + + TypesConstants.Add(NewType); + + return NewType; + } + + private int AllocUniformLocation(int Count) + { + if (UniformLocation + Count >= ShaderStageSafespaceStride) + { + throw new NotSupportedException("Too many uniform components"); + } + + int Base = ShaderStageSafespaceStride * GetShaderUniformOffset(); + int Location = Base + UniformLocation; + + UniformLocation += Count; + + return Location; + } + + #endregion + + #region "Helpers" + + private SpirvVariable FindVariable(string Name) + { + foreach (SpirvVariable Variable in Variables) + { + if (Variable.Name == Name) + { + return Variable; + } + } + + throw new InvalidOperationException($"Variable {Name} not declared"); + } + + private int GetShaderUniformOffset() + { + switch (Decl.ShaderType) + { + case GalShaderType.Vertex: return 0; + case GalShaderType.TessControl: return 1; + case GalShaderType.TessEvaluation: return 2; + case GalShaderType.Geometry: return 3; + case GalShaderType.Fragment: return 4; + default: + throw new ArgumentException(); + } + } + + //AC stands for Add Code + private Instruction AC(Instruction Instruction) + { + Code.Add(Instruction); + + return Instruction; + } + + #endregion + + #region "IrOp Builders" + + private Instruction GetSrcNodeTypeId(ShaderIrNode 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 GetOperExpr(ShaderIrOp Op, ShaderIrNode Oper) + { + return GetExprWithCast(Op, Oper, GetSrcExpr(Oper)); + } + + private Instruction GetBinaryExprWithNaN(ShaderIrOp Op, OpCompareBuilder Builder) + { + Instruction A = GetOperExpr(Op, Op.OperandA); + Instruction B = GetOperExpr(Op, Op.OperandB); + + Instruction NanA = AC(new OpIsNan(TypeBool, A)); + Instruction NanB = AC(new OpIsNan(TypeBool, B)); + + Instruction IsNan = AC(new OpLogicalOr(TypeBool, NanA, NanB)); + Instruction IsCompared = AC(Builder(A, B)); + + return new OpLogicalOr(TypeBool, IsNan, IsCompared); + } + + private Instruction GetTexSamplerVariable(ShaderIrOp Op) + { + ShaderIrOperImm Node = (ShaderIrOperImm)Op.OperandC; + + int Handle = ((ShaderIrOperImm)Op.OperandC).Value; + + if (!Decl.Textures.TryGetValue(Handle, out ShaderDeclInfo DeclInfo)) + { + throw new InvalidOperationException(); + } + + return FindVariable(DeclInfo.Name).Id; + } + + private Instruction GetTexSamplerImage(ShaderIrOp Op) + { + return AC(new OpImage(TypeImage2D, GetTexSamplerVariable(Op))); + } + + private Instruction GetTexSamplerCoords(ShaderIrOp Op) + { + return AC(new OpCompositeConstruct( + TypeFloats[1], + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB))); + } + + private Instruction GetITexSamplerCoords(ShaderIrOp Op) + { + return AC(new OpCompositeConstruct( + TypeInt2, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB))); + } + + private Instruction GetFnegExpr(ShaderIrOp Op) + => new OpFNegate( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFaddExpr(ShaderIrOp Op) + => new OpFAdd( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFmulExpr(ShaderIrOp Op) + => new OpFMul( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFfmaExpr(ShaderIrOp Op) + => Glsl450.Fma( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB), + GetOperExpr(Op, Op.OperandC)); + + private Instruction GetAndExpr(ShaderIrOp Op) + => new OpBitwiseAnd( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetCneExpr(ShaderIrOp Op) + => new OpINotEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFtouExpr(ShaderIrOp Op) + { + Instruction Unsigned = AC(new OpConvertFToU(TypeUInt, GetOperExpr(Op, Op.OperandA))); + + //Cast to int because all variables are handled as signed integers + return new OpBitcast(TypeInt, Unsigned); + } + + private Instruction GetFtosExpr(ShaderIrOp Op) + => new OpConvertFToS( + TypeInt, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetTruncExpr(ShaderIrOp Op) + => Glsl450.Trunc( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetAddExpr(ShaderIrOp Op) + => new OpIAdd( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetLslExpr(ShaderIrOp Op) + => new OpShiftLeftLogical( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetLsrExpr(ShaderIrOp Op) + => new OpShiftRightLogical( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetCeqExpr(ShaderIrOp Op) + => new OpIEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetAbsExpr(ShaderIrOp Op) + => Glsl450.SAbs( + TypeInt, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetUtofExpr(ShaderIrOp Op) + { + Instruction Unsigned = AC(new OpBitcast(TypeUInt, GetOperExpr(Op, Op.OperandA))); + + return new OpConvertUToF(TypeFloat, Unsigned); + } + + private Instruction GetStofExpr(ShaderIrOp Op) + => new OpConvertSToF( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetXorExpr(ShaderIrOp Op) + => new OpBitwiseXor( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetNegExpr(ShaderIrOp Op) + => new OpSNegate( + TypeInt, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetAsrExpr(ShaderIrOp Op) + => new OpShiftRightArithmetic( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetBandExpr(ShaderIrOp Op) + => new OpLogicalAnd( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetBnotExpr(ShaderIrOp Op) + => new OpLogicalNot( + TypeBool, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetBorExpr(ShaderIrOp Op) + => new OpLogicalOr( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetBxorExpr(ShaderIrOp Op) + => new OpLogicalNotEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetCeilExpr(ShaderIrOp Op) + => Glsl450.Ceil( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetCgeExpr(ShaderIrOp Op) + => new OpSGreaterThan( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetCgtExpr(ShaderIrOp Op) + => new OpSGreaterThanEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetClampsExpr(ShaderIrOp Op) + => Glsl450.SClamp( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB), + GetOperExpr(Op, Op.OperandC)); + + private Instruction GetClampuExpr(ShaderIrOp Op) + { + Instruction X = AC(new OpBitcast(TypeUInt, GetOperExpr(Op, Op.OperandA))); + Instruction MinVal = AC(new OpBitcast(TypeUInt, GetOperExpr(Op, Op.OperandB))); + Instruction MaxVal = AC(new OpBitcast(TypeUInt, GetOperExpr(Op, Op.OperandC))); + Instruction Result = AC(Glsl450.UClamp(TypeUInt, X, MinVal, MaxVal)); + + //There are no variables uint, so cast it to int + return new OpBitcast(TypeInt, Result); + } + + private Instruction GetCleExpr(ShaderIrOp Op) + => new OpSLessThanEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetCltExpr(ShaderIrOp Op) + => new OpSLessThan( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFabsExpr(ShaderIrOp Op) + => Glsl450.FAbs( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFceqExpr(ShaderIrOp Op) + => new OpFOrdEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFcequExpr(ShaderIrOp Op) + { + Instruction Compare(Instruction A, Instruction B) + => new OpFOrdEqual(TypeBool, A, B); + + return GetBinaryExprWithNaN(Op, Compare); + } + + private Instruction GetFcgeExpr(ShaderIrOp Op) + => new OpFOrdGreaterThanEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFcgeuExpr(ShaderIrOp Op) + { + Instruction Compare(Instruction A, Instruction B) + => new OpFOrdGreaterThanEqual(TypeBool, A, B); + + return GetBinaryExprWithNaN(Op, Compare); + } + + private Instruction GetFcgtExpr(ShaderIrOp Op) + => new OpFOrdGreaterThan( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFcgtuExpr(ShaderIrOp Op) + { + Instruction Compare(Instruction A, Instruction B) + => new OpFOrdGreaterThan(TypeBool, A, B); + + return GetBinaryExprWithNaN(Op, Compare); + } + + private Instruction GetFclampExpr(ShaderIrOp Op) + => Glsl450.FClamp( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB), + GetOperExpr(Op, Op.OperandC)); + + private Instruction GetFcleExpr(ShaderIrOp Op) + => new OpFOrdLessThanEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFcleuExpr(ShaderIrOp Op) + { + Instruction Compare(Instruction A, Instruction B) + => new OpFOrdLessThanEqual(TypeBool, A, B); + + return GetBinaryExprWithNaN(Op, Compare); + } + + private Instruction GetFcltExpr(ShaderIrOp Op) + => new OpFOrdLessThan( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFcltuExpr(ShaderIrOp Op) + { + Instruction Compare(Instruction A, Instruction B) + => new OpFOrdLessThan(TypeBool, A, B); + + return GetBinaryExprWithNaN(Op, Compare); + } + + private Instruction GetFcnanExpr(ShaderIrOp Op) + => new OpIsNan( + TypeBool, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFcneExpr(ShaderIrOp Op) + => new OpFOrdNotEqual( + TypeBool, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFcneuExpr(ShaderIrOp Op) + { + Instruction Compare(Instruction A, Instruction B) + => new OpFOrdNotEqual(TypeBool, A, B); + + return GetBinaryExprWithNaN(Op, Compare); + } + + private Instruction GetFcnumExpr(ShaderIrOp Op) + => new OpIsNormal( + TypeBool, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFcosExpr(ShaderIrOp Op) + => Glsl450.Cos( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFex2Expr(ShaderIrOp Op) + => Glsl450.Exp2( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFlg2Expr(ShaderIrOp Op) + => Glsl450.Log2( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFloorExpr(ShaderIrOp Op) + => Glsl450.Floor( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFmaxExpr(ShaderIrOp Op) + => Glsl450.FMax( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFminExpr(ShaderIrOp Op) + => Glsl450.FMin( + TypeFloat, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetFrcpExpr(ShaderIrOp Op) + => new OpFDiv( + TypeFloat, + AllocConstant(TypeFloat, new LiteralNumber(1f)), + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFrsqExpr(ShaderIrOp Op) + => Glsl450.InverseSqrt( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetFsinExpr(ShaderIrOp Op) + => Glsl450.Sin( + TypeFloat, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetKilExpr(ShaderIrOp Op) + => new OpKill(); + + private Instruction GetMaxExpr(ShaderIrOp Op) + => Glsl450.SMax( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetMinExpr(ShaderIrOp Op) + => Glsl450.SMin( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetMulExpr(ShaderIrOp Op) + => new OpIMul( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetNotExpr(ShaderIrOp Op) + => new OpNot( + TypeInt, + GetOperExpr(Op, Op.OperandA)); + + private Instruction GetOrExpr(ShaderIrOp Op) + => new OpBitwiseOr( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetSubExpr(ShaderIrOp Op) + => new OpISub( + TypeInt, + GetOperExpr(Op, Op.OperandA), + GetOperExpr(Op, Op.OperandB)); + + private Instruction GetTexqExpr(ShaderIrOp Op) + { + ShaderIrMetaTexq Meta = (ShaderIrMetaTexq)Op.MetaData; + + int Elem = Meta.Elem; + + if (Meta.Info == ShaderTexqInfo.Dimension) + { + Instruction Image = GetTexSamplerImage(Op); + + Instruction Lod = GetOperExpr(Op, Op.OperandA); + + Instruction Size = AC(new OpImageQuerySizeLod(TypeInt2, Image, Lod)); + + return new OpCompositeExtract(TypeInt, Size, Meta.Elem); + } + else + { + throw new NotImplementedException(Meta.Info.ToString()); + } + } + + private Instruction GetTexsExpr(ShaderIrOp Op) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + Instruction SamplerUniform = GetTexSamplerVariable(Op); + + Instruction Sampler = AC(new OpLoad(TypeSampler2D, SamplerUniform)); + + Instruction Coords = GetTexSamplerCoords(Op); + + Instruction Color = AC(new OpImageSampleImplicitLod(TypeFloats[3], Sampler, Coords)); + + return new OpCompositeExtract(TypeFloat, Color, Meta.Elem); + } + + private Instruction GetTxlfExpr(ShaderIrOp Op) + { + ShaderIrMetaTex Meta = (ShaderIrMetaTex)Op.MetaData; + + Instruction Image = GetTexSamplerImage(Op); + + Instruction Coords = GetITexSamplerCoords(Op); + + Instruction Fetch = AC(new OpImageFetch(TypeFloats[3], Image, Coords)); + + return new OpCompositeExtract(TypeFloat, Fetch, Meta.Elem); + } + + #endregion + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/Shader/SpirvProgram.cs b/Ryujinx.Graphics/Gal/Shader/SpirvProgram.cs new file mode 100644 index 0000000000..20e9b0dd01 --- /dev/null +++ b/Ryujinx.Graphics/Gal/Shader/SpirvProgram.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.Shader +{ + public class SpirvProgram : ShaderProgram + { + public byte[] Bytecode { get; private set; } + + public IDictionary Locations { get; private set; } + + public SpirvProgram( + byte[] Bytecode, + IDictionary Locations, + IEnumerable Textures, + IEnumerable Uniforms) + : base(Textures, Uniforms) + { + this.Bytecode = Bytecode; + this.Locations = Locations; + } + } +} \ No newline at end of file diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index 3597f25624..2be8cbcbd5 100644 --- a/Ryujinx.ShaderTools/Program.cs +++ b/Ryujinx.ShaderTools/Program.cs @@ -2,6 +2,7 @@ using Ryujinx.Graphics.Gal.Shader; using System; using System.IO; +using System.Text; namespace Ryujinx.ShaderTools { @@ -9,13 +10,11 @@ namespace Ryujinx.ShaderTools { static void Main(string[] args) { - if (args.Length == 2) + if (args.Length == 4) { - GlslDecompiler Decompiler = new GlslDecompiler(); - GalShaderType ShaderType = GalShaderType.Vertex; - switch (args[0].ToLower()) + switch (args[1].ToLower()) { case "v": ShaderType = GalShaderType.Vertex; break; case "tc": ShaderType = GalShaderType.TessControl; break; @@ -24,18 +23,40 @@ namespace Ryujinx.ShaderTools case "f": ShaderType = GalShaderType.Fragment; break; } - using (FileStream FS = new FileStream(args[1], FileMode.Open, FileAccess.Read)) + using (FileStream Output = new FileStream(args[3], FileMode.Create)) + using (FileStream FS = new FileStream(args[2], FileMode.Open, FileAccess.Read)) { Memory Mem = new Memory(FS); - GlslProgram Program = Decompiler.Decompile(Mem, 0, ShaderType); + switch (args[0].ToLower()) + { + case "glsl": + { + GlslDecompiler GlslDecompiler = new GlslDecompiler(); - Console.WriteLine(Program.Code); + GlslProgram Program = GlslDecompiler.Decompile(Mem, 0, ShaderType); + + Output.Write(System.Text.Encoding.UTF8.GetBytes(Program.Code)); + + break; + } + + case "spirv": + { + SpirvDecompiler SpirvDecompiler = new SpirvDecompiler(); + + SpirvProgram Program = SpirvDecompiler.Decompile(Mem, 0, ShaderType); + + Output.Write(Program.Bytecode); + + break; + } + } } } else { - Console.WriteLine("Usage: Ryujinx.ShaderTools [v|tc|te|g|f] shader.bin"); + Console.WriteLine("Usage: Ryujinx.ShaderTools [spirv|glsl] [v|tc|te|g|f] shader.bin output.bin"); } } } diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index b437a006dd..173d0e83e9 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -12,6 +12,8 @@ namespace Ryujinx { public static JoyCon FakeJoyCon { get; private set; } + public static bool EnableSPIRV { get; private set; } + public static void Read(Logger Log) { string IniFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); @@ -22,6 +24,8 @@ namespace Ryujinx AOptimizations.DisableMemoryChecks = !Convert.ToBoolean(Parser.Value("Enable_Memory_Checks")); + EnableSPIRV = Convert.ToBoolean(Parser.Value("Enable_SPIRV")); + Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"))); Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"))); Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info"))); diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index 611f320717..1343f69cda 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -1,6 +1,9 @@ #Enable cpu memory checks (slow) Enable_Memory_Checks = false +#Enable SPIR-V shading language (experimental) +Enable_SPIRV = false + #Enable print debug logs Logging_Enable_Debug = false diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index e2024b5bbd..3d329b2206 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -2,6 +2,7 @@ using OpenTK; using OpenTK.Graphics; using OpenTK.Input; using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.Gal.OpenGL; using Ryujinx.HLE; using Ryujinx.HLE.Input; using System; @@ -42,6 +43,11 @@ namespace Ryujinx { VSync = VSyncMode.On; + if (Renderer is OpenGLRenderer OGL) + { + OGL.Initialize(Config.EnableSPIRV); + } + Renderer.SetWindowSize(Width, Height); }