From b9aa3966c00b4bb3ff0292dc28ed53ad26cf284b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 8 Apr 2018 16:17:35 -0300 Subject: Merge shader branch, adding support for GLSL decompilation, a macro interpreter, and a rewrite of the GPU code. --- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 253 +++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs (limited to 'Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs') diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs new file mode 100644 index 00000000..6d6ac555 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -0,0 +1,253 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Gal.Shader; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OGLShader + { + private class ShaderStage : IDisposable + { + public int Handle { get; private set; } + + public bool IsCompiled { get; private 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.TextureUsage = TextureUsage; + this.UniformUsage = UniformUsage; + } + + public void Compile() + { + if (Handle == 0) + { + Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type)); + + CompileAndCheck(Handle, Code); + } + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing && Handle != 0) + { + GL.DeleteShader(Handle); + + Handle = 0; + } + } + } + + private struct ShaderProgram + { + public ShaderStage Vertex; + public ShaderStage TessControl; + public ShaderStage TessEvaluation; + public ShaderStage Geometry; + public ShaderStage Fragment; + } + + private ShaderProgram Current; + + private ConcurrentDictionary Stages; + + private Dictionary Programs; + + public int CurrentProgramHandle { get; private set; } + + public OGLShader() + { + Stages = new ConcurrentDictionary(); + + Programs = new Dictionary(); + } + + public void Create(long Tag, GalShaderType Type, byte[] Data) + { + Stages.GetOrAdd(Tag, (Key) => ShaderStageFactory(Type, Data)); + } + + private ShaderStage ShaderStageFactory(GalShaderType Type, byte[] Data) + { + GlslProgram Program = GetGlslProgram(Data, Type); + + return new ShaderStage( + Type, + Program.Code, + Program.Textures, + Program.Uniforms); + } + + private GlslProgram GetGlslProgram(byte[] Data, GalShaderType Type) + { + int[] Code = new int[(Data.Length - 0x50) >> 2]; + + using (MemoryStream MS = new MemoryStream(Data)) + { + MS.Seek(0x50, SeekOrigin.Begin); + + BinaryReader Reader = new BinaryReader(MS); + + for (int Index = 0; Index < Code.Length; Index++) + { + Code[Index] = Reader.ReadInt32(); + } + } + + GlslDecompiler Decompiler = new GlslDecompiler(); + + return Decompiler.Decompile(Code, Type); + } + + public IEnumerable GetTextureUsage(long Tag) + { + if (Stages.TryGetValue(Tag, out ShaderStage Stage)) + { + return Stage.TextureUsage; + } + + return Enumerable.Empty(); + } + + public void SetConstBuffer(long Tag, int Cbuf, byte[] Data) + { + BindProgram(); + + if (Stages.TryGetValue(Tag, out ShaderStage Stage)) + { + foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf)) + { + float Value = BitConverter.ToSingle(Data, DeclInfo.Index * 4); + + int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name); + + GL.Uniform1(Location, Value); + } + } + } + + public void SetUniform1(string UniformName, int Value) + { + BindProgram(); + + int Location = GL.GetUniformLocation(CurrentProgramHandle, UniformName); + + GL.Uniform1(Location, Value); + } + + public void Bind(long Tag) + { + if (Stages.TryGetValue(Tag, out ShaderStage Stage)) + { + Bind(Stage); + } + } + + private void Bind(ShaderStage Stage) + { + switch (Stage.Type) + { + case GalShaderType.Vertex: Current.Vertex = Stage; break; + case GalShaderType.TessControl: Current.TessControl = Stage; break; + case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break; + case GalShaderType.Geometry: Current.Geometry = Stage; break; + case GalShaderType.Fragment: Current.Fragment = Stage; break; + } + } + + public void BindProgram() + { + if (Current.Vertex == null || + Current.Fragment == null) + { + return; + } + + if (!Programs.TryGetValue(Current, out int Handle)) + { + Handle = GL.CreateProgram(); + + AttachIfNotNull(Handle, Current.Vertex); + AttachIfNotNull(Handle, Current.TessControl); + AttachIfNotNull(Handle, Current.TessEvaluation); + AttachIfNotNull(Handle, Current.Geometry); + AttachIfNotNull(Handle, Current.Fragment); + + GL.LinkProgram(Handle); + + CheckProgramLink(Handle); + + Programs.Add(Current, Handle); + } + + GL.UseProgram(Handle); + + CurrentProgramHandle = Handle; + } + + private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage) + { + if (Stage != null) + { + Stage.Compile(); + + GL.AttachShader(ProgramHandle, Stage.Handle); + } + } + + public static void CompileAndCheck(int Handle, string Code) + { + GL.ShaderSource(Handle, Code); + GL.CompileShader(Handle); + + CheckCompilation(Handle); + } + + private static void CheckCompilation(int Handle) + { + int Status = 0; + + GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status); + + if (Status == 0) + { + throw new ShaderException(GL.GetShaderInfoLog(Handle)); + } + } + + private static void CheckProgramLink(int Handle) + { + int Status = 0; + + GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out Status); + + if (Status == 0) + { + throw new ShaderException(GL.GetProgramInfoLog(Handle)); + } + } + } +} \ No newline at end of file -- cgit v1.2.3