diff options
Diffstat (limited to 'src/Ryujinx.Graphics.OpenGL/Program.cs')
-rw-r--r-- | src/Ryujinx.Graphics.OpenGL/Program.cs | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/src/Ryujinx.Graphics.OpenGL/Program.cs b/src/Ryujinx.Graphics.OpenGL/Program.cs new file mode 100644 index 00000000..a6009108 --- /dev/null +++ b/src/Ryujinx.Graphics.OpenGL/Program.cs @@ -0,0 +1,177 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using System; +using System.Buffers.Binary; + +namespace Ryujinx.Graphics.OpenGL +{ + class Program : IProgram + { + private const int MaxShaderLogLength = 2048; + + public int Handle { get; private set; } + + public bool IsLinked + { + get + { + if (_status == ProgramLinkStatus.Incomplete) + { + CheckProgramLink(true); + } + + return _status == ProgramLinkStatus.Success; + } + } + + private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete; + private int[] _shaderHandles; + + public bool HasFragmentShader; + public int FragmentOutputMap { get; } + + public Program(ShaderSource[] shaders, int fragmentOutputMap) + { + Handle = GL.CreateProgram(); + + GL.ProgramParameter(Handle, ProgramParameterName.ProgramBinaryRetrievableHint, 1); + + _shaderHandles = new int[shaders.Length]; + + for (int index = 0; index < shaders.Length; index++) + { + ShaderSource shader = shaders[index]; + + if (shader.Stage == ShaderStage.Fragment) + { + HasFragmentShader = true; + } + + int shaderHandle = GL.CreateShader(shader.Stage.Convert()); + + switch (shader.Language) + { + case TargetLanguage.Glsl: + GL.ShaderSource(shaderHandle, shader.Code); + GL.CompileShader(shaderHandle); + break; + case TargetLanguage.Spirv: + GL.ShaderBinary(1, ref shaderHandle, (BinaryFormat)All.ShaderBinaryFormatSpirVArb, shader.BinaryCode, shader.BinaryCode.Length); + GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null); + break; + } + + GL.AttachShader(Handle, shaderHandle); + + _shaderHandles[index] = shaderHandle; + } + + GL.LinkProgram(Handle); + + FragmentOutputMap = fragmentOutputMap; + } + + public Program(ReadOnlySpan<byte> code, bool hasFragmentShader, int fragmentOutputMap) + { + Handle = GL.CreateProgram(); + + if (code.Length >= 4) + { + BinaryFormat binaryFormat = (BinaryFormat)BinaryPrimitives.ReadInt32LittleEndian(code.Slice(code.Length - 4, 4)); + + unsafe + { + fixed (byte* ptr = code) + { + GL.ProgramBinary(Handle, binaryFormat, (IntPtr)ptr, code.Length - 4); + } + } + } + + HasFragmentShader = hasFragmentShader; + FragmentOutputMap = fragmentOutputMap; + } + + public void Bind() + { + GL.UseProgram(Handle); + } + + public ProgramLinkStatus CheckProgramLink(bool blocking) + { + if (!blocking && HwCapabilities.SupportsParallelShaderCompile) + { + GL.GetProgram(Handle, (GetProgramParameterName)ArbParallelShaderCompile.CompletionStatusArb, out int completed); + + if (completed == 0) + { + return ProgramLinkStatus.Incomplete; + } + } + + GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out int status); + DeleteShaders(); + + if (status == 0) + { + _status = ProgramLinkStatus.Failure; + + string log = GL.GetProgramInfoLog(Handle); + + if (log.Length > MaxShaderLogLength) + { + log = log.Substring(0, MaxShaderLogLength) + "..."; + } + + Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{log}"); + } + else + { + _status = ProgramLinkStatus.Success; + } + + return _status; + } + + public byte[] GetBinary() + { + GL.GetProgram(Handle, (GetProgramParameterName)All.ProgramBinaryLength, out int size); + + byte[] data = new byte[size + 4]; + + GL.GetProgramBinary(Handle, size, out _, out BinaryFormat binFormat, data); + + BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(size, 4), (int)binFormat); + + return data; + } + + private void DeleteShaders() + { + if (_shaderHandles != null) + { + foreach (int shaderHandle in _shaderHandles) + { + GL.DetachShader(Handle, shaderHandle); + GL.DeleteShader(shaderHandle); + } + + _shaderHandles = null; + } + } + + public void Dispose() + { + if (Handle != 0) + { + DeleteShaders(); + GL.DeleteProgram(Handle); + + Handle = 0; + } + } + } +} |