Program.cs
1 using OpenTK.Graphics.OpenGL; 2 using Ryujinx.Common.Logging; 3 using Ryujinx.Graphics.GAL; 4 using Ryujinx.Graphics.Shader; 5 using Ryujinx.Graphics.Shader.Translation; 6 using System; 7 using System.Buffers.Binary; 8 9 namespace Ryujinx.Graphics.OpenGL 10 { 11 class Program : IProgram 12 { 13 private const int MaxShaderLogLength = 2048; 14 15 public int Handle { get; private set; } 16 17 public bool IsLinked 18 { 19 get 20 { 21 if (_status == ProgramLinkStatus.Incomplete) 22 { 23 CheckProgramLink(true); 24 } 25 26 return _status == ProgramLinkStatus.Success; 27 } 28 } 29 30 private ProgramLinkStatus _status = ProgramLinkStatus.Incomplete; 31 private int[] _shaderHandles; 32 33 public int FragmentOutputMap { get; } 34 35 public Program(ShaderSource[] shaders, int fragmentOutputMap) 36 { 37 Handle = GL.CreateProgram(); 38 39 GL.ProgramParameter(Handle, ProgramParameterName.ProgramBinaryRetrievableHint, 1); 40 41 _shaderHandles = new int[shaders.Length]; 42 bool hasFragmentShader = false; 43 44 for (int index = 0; index < shaders.Length; index++) 45 { 46 ShaderSource shader = shaders[index]; 47 48 if (shader.Stage == ShaderStage.Fragment) 49 { 50 hasFragmentShader = true; 51 } 52 53 int shaderHandle = GL.CreateShader(shader.Stage.Convert()); 54 55 switch (shader.Language) 56 { 57 case TargetLanguage.Glsl: 58 GL.ShaderSource(shaderHandle, shader.Code); 59 GL.CompileShader(shaderHandle); 60 break; 61 case TargetLanguage.Spirv: 62 GL.ShaderBinary(1, ref shaderHandle, (BinaryFormat)All.ShaderBinaryFormatSpirVArb, shader.BinaryCode, shader.BinaryCode.Length); 63 GL.SpecializeShader(shaderHandle, "main", 0, (int[])null, (int[])null); 64 break; 65 } 66 67 GL.AttachShader(Handle, shaderHandle); 68 69 _shaderHandles[index] = shaderHandle; 70 } 71 72 GL.LinkProgram(Handle); 73 74 FragmentOutputMap = hasFragmentShader ? fragmentOutputMap : 0; 75 } 76 77 public Program(ReadOnlySpan<byte> code, bool hasFragmentShader, int fragmentOutputMap) 78 { 79 Handle = GL.CreateProgram(); 80 81 if (code.Length >= 4) 82 { 83 BinaryFormat binaryFormat = (BinaryFormat)BinaryPrimitives.ReadInt32LittleEndian(code.Slice(code.Length - 4, 4)); 84 85 unsafe 86 { 87 fixed (byte* ptr = code) 88 { 89 GL.ProgramBinary(Handle, binaryFormat, (IntPtr)ptr, code.Length - 4); 90 } 91 } 92 } 93 94 FragmentOutputMap = hasFragmentShader ? fragmentOutputMap : 0; 95 } 96 97 public void Bind() 98 { 99 GL.UseProgram(Handle); 100 } 101 102 public ProgramLinkStatus CheckProgramLink(bool blocking) 103 { 104 if (!blocking && HwCapabilities.SupportsParallelShaderCompile) 105 { 106 GL.GetProgram(Handle, (GetProgramParameterName)ArbParallelShaderCompile.CompletionStatusArb, out int completed); 107 108 if (completed == 0) 109 { 110 return ProgramLinkStatus.Incomplete; 111 } 112 } 113 114 GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out int status); 115 DeleteShaders(); 116 117 if (status == 0) 118 { 119 _status = ProgramLinkStatus.Failure; 120 121 string log = GL.GetProgramInfoLog(Handle); 122 123 if (log.Length > MaxShaderLogLength) 124 { 125 log = log[..MaxShaderLogLength] + "..."; 126 } 127 128 Logger.Warning?.Print(LogClass.Gpu, $"Shader linking failed: \n{log}"); 129 } 130 else 131 { 132 _status = ProgramLinkStatus.Success; 133 } 134 135 return _status; 136 } 137 138 public byte[] GetBinary() 139 { 140 GL.GetProgram(Handle, (GetProgramParameterName)All.ProgramBinaryLength, out int size); 141 142 byte[] data = new byte[size + 4]; 143 144 GL.GetProgramBinary(Handle, size, out _, out BinaryFormat binFormat, data); 145 146 BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan(size, 4), (int)binFormat); 147 148 return data; 149 } 150 151 private void DeleteShaders() 152 { 153 if (_shaderHandles != null) 154 { 155 foreach (int shaderHandle in _shaderHandles) 156 { 157 GL.DetachShader(Handle, shaderHandle); 158 GL.DeleteShader(shaderHandle); 159 } 160 161 _shaderHandles = null; 162 } 163 } 164 165 public void Dispose() 166 { 167 if (Handle != 0) 168 { 169 DeleteShaders(); 170 GL.DeleteProgram(Handle); 171 172 Handle = 0; 173 } 174 } 175 } 176 }