Program.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. using OpenTK.Graphics.OpenGL;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Shader.CodeGen.Glsl;
  5. using System;
  6. using System.Buffers.Binary;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. namespace Ryujinx.Graphics.OpenGL
  10. {
  11. class Program : IProgram
  12. {
  13. public int Handle { get; private set; }
  14. public int FragmentIsBgraUniform { get; }
  15. public int FragmentRenderScaleUniform { get; }
  16. public int ComputeRenderScaleUniform { get; }
  17. public bool IsLinked { get; private set; }
  18. public Program(IShader[] shaders, TransformFeedbackDescriptor[] transformFeedbackDescriptors)
  19. {
  20. Handle = GL.CreateProgram();
  21. GL.ProgramParameter(Handle, ProgramParameterName.ProgramBinaryRetrievableHint, 1);
  22. for (int index = 0; index < shaders.Length; index++)
  23. {
  24. int shaderHandle = ((Shader)shaders[index]).Handle;
  25. GL.AttachShader(Handle, shaderHandle);
  26. }
  27. if (transformFeedbackDescriptors != null)
  28. {
  29. List<string> varyings = new List<string>();
  30. int cbi = 0;
  31. foreach (var tfd in transformFeedbackDescriptors.OrderBy(x => x.BufferIndex))
  32. {
  33. if (tfd.VaryingLocations.Length == 0)
  34. {
  35. continue;
  36. }
  37. while (cbi < tfd.BufferIndex)
  38. {
  39. varyings.Add("gl_NextBuffer");
  40. cbi++;
  41. }
  42. int stride = Math.Min(128 * 4, (tfd.Stride + 3) & ~3);
  43. int j = 0;
  44. for (; j < tfd.VaryingLocations.Length && j * 4 < stride; j++)
  45. {
  46. byte location = tfd.VaryingLocations[j];
  47. varyings.Add(Varying.GetName(location) ?? "gl_SkipComponents1");
  48. j += Varying.GetSize(location) - 1;
  49. }
  50. int feedbackBytes = j * 4;
  51. while (feedbackBytes < stride)
  52. {
  53. int bytes = Math.Min(16, stride - feedbackBytes);
  54. varyings.Add($"gl_SkipComponents{(bytes / 4)}");
  55. feedbackBytes += bytes;
  56. }
  57. }
  58. GL.TransformFeedbackVaryings(Handle, varyings.Count, varyings.ToArray(), TransformFeedbackMode.InterleavedAttribs);
  59. }
  60. GL.LinkProgram(Handle);
  61. for (int index = 0; index < shaders.Length; index++)
  62. {
  63. int shaderHandle = ((Shader)shaders[index]).Handle;
  64. GL.DetachShader(Handle, shaderHandle);
  65. }
  66. CheckProgramLink();
  67. FragmentIsBgraUniform = GL.GetUniformLocation(Handle, "is_bgra");
  68. FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale");
  69. ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale");
  70. }
  71. public Program(ReadOnlySpan<byte> code)
  72. {
  73. BinaryFormat binaryFormat = (BinaryFormat)BinaryPrimitives.ReadInt32LittleEndian(code.Slice(code.Length - 4, 4));
  74. Handle = GL.CreateProgram();
  75. unsafe
  76. {
  77. fixed (byte* ptr = code)
  78. {
  79. GL.ProgramBinary(Handle, binaryFormat, (IntPtr)ptr, code.Length - 4);
  80. }
  81. }
  82. CheckProgramLink();
  83. FragmentIsBgraUniform = GL.GetUniformLocation(Handle, "is_bgra");
  84. FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale");
  85. ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale");
  86. }
  87. public void Bind()
  88. {
  89. GL.UseProgram(Handle);
  90. }
  91. private void CheckProgramLink()
  92. {
  93. GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out int status);
  94. if (status == 0)
  95. {
  96. // Use GL.GetProgramInfoLog(Handle), it may be too long to print on the log.
  97. Logger.Debug?.Print(LogClass.Gpu, "Shader linking failed.");
  98. }
  99. else
  100. {
  101. IsLinked = true;
  102. }
  103. }
  104. public byte[] GetBinary()
  105. {
  106. GL.GetProgram(Handle, (GetProgramParameterName)All.ProgramBinaryLength, out int size);
  107. byte[] data = new byte[size + 4];
  108. GL.GetProgramBinary(Handle, size, out _, out BinaryFormat binFormat, data);
  109. BinaryPrimitives.WriteInt32LittleEndian(data.AsSpan().Slice(size, 4), (int)binFormat);
  110. return data;
  111. }
  112. public void Dispose()
  113. {
  114. if (Handle != 0)
  115. {
  116. GL.DeleteProgram(Handle);
  117. Handle = 0;
  118. }
  119. }
  120. }
  121. }