OGLShader.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. using OpenTK.Graphics.OpenGL;
  2. using Ryujinx.Graphics.Gal.Shader;
  3. using System;
  4. using System.Collections.Concurrent;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. namespace Ryujinx.Graphics.Gal.OpenGL
  8. {
  9. class OGLShader : IGalShader
  10. {
  11. public const int ReservedCbufCount = 1;
  12. private const int ExtraDataSize = 4;
  13. public OGLShaderProgram Current;
  14. private ConcurrentDictionary<long, OGLShaderStage> Stages;
  15. private Dictionary<OGLShaderProgram, int> Programs;
  16. public int CurrentProgramHandle { get; private set; }
  17. private OGLConstBuffer Buffer;
  18. private int ExtraUboHandle;
  19. public OGLShader(OGLConstBuffer Buffer)
  20. {
  21. this.Buffer = Buffer;
  22. Stages = new ConcurrentDictionary<long, OGLShaderStage>();
  23. Programs = new Dictionary<OGLShaderProgram, int>();
  24. }
  25. public void Create(IGalMemory Memory, long Key, GalShaderType Type)
  26. {
  27. Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, 0, false, Type));
  28. }
  29. public void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type)
  30. {
  31. Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, VpAPos, Key, true, Type));
  32. }
  33. private OGLShaderStage ShaderStageFactory(
  34. IGalMemory Memory,
  35. long Position,
  36. long PositionB,
  37. bool IsDualVp,
  38. GalShaderType Type)
  39. {
  40. GlslProgram Program;
  41. GlslDecompiler Decompiler = new GlslDecompiler();
  42. if (IsDualVp)
  43. {
  44. ShaderDumper.Dump(Memory, Position, Type, "a");
  45. ShaderDumper.Dump(Memory, PositionB, Type, "b");
  46. Program = Decompiler.Decompile(
  47. Memory,
  48. Position,
  49. PositionB,
  50. Type);
  51. }
  52. else
  53. {
  54. ShaderDumper.Dump(Memory, Position, Type);
  55. Program = Decompiler.Decompile(Memory, Position, Type);
  56. }
  57. return new OGLShaderStage(
  58. Type,
  59. Program.Code,
  60. Program.Uniforms,
  61. Program.Textures);
  62. }
  63. public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
  64. {
  65. if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
  66. {
  67. return Stage.ConstBufferUsage;
  68. }
  69. return Enumerable.Empty<ShaderDeclInfo>();
  70. }
  71. public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
  72. {
  73. if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
  74. {
  75. return Stage.TextureUsage;
  76. }
  77. return Enumerable.Empty<ShaderDeclInfo>();
  78. }
  79. public unsafe void SetExtraData(float FlipX, float FlipY, int Instance)
  80. {
  81. BindProgram();
  82. EnsureExtraBlock();
  83. GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
  84. float* Data = stackalloc float[ExtraDataSize];
  85. Data[0] = FlipX;
  86. Data[1] = FlipY;
  87. Data[2] = BitConverter.Int32BitsToSingle(Instance);
  88. //Invalidate buffer
  89. GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
  90. GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)Data);
  91. }
  92. public void Bind(long Key)
  93. {
  94. if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
  95. {
  96. Bind(Stage);
  97. }
  98. }
  99. private void Bind(OGLShaderStage Stage)
  100. {
  101. if (Stage.Type == GalShaderType.Geometry)
  102. {
  103. //Enhanced layouts are required for Geometry shaders
  104. //skip this stage if current driver has no ARB_enhanced_layouts
  105. if (!OGLExtension.HasEnhancedLayouts())
  106. {
  107. return;
  108. }
  109. }
  110. switch (Stage.Type)
  111. {
  112. case GalShaderType.Vertex: Current.Vertex = Stage; break;
  113. case GalShaderType.TessControl: Current.TessControl = Stage; break;
  114. case GalShaderType.TessEvaluation: Current.TessEvaluation = Stage; break;
  115. case GalShaderType.Geometry: Current.Geometry = Stage; break;
  116. case GalShaderType.Fragment: Current.Fragment = Stage; break;
  117. }
  118. }
  119. public void Unbind(GalShaderType Type)
  120. {
  121. switch (Type)
  122. {
  123. case GalShaderType.Vertex: Current.Vertex = null; break;
  124. case GalShaderType.TessControl: Current.TessControl = null; break;
  125. case GalShaderType.TessEvaluation: Current.TessEvaluation = null; break;
  126. case GalShaderType.Geometry: Current.Geometry = null; break;
  127. case GalShaderType.Fragment: Current.Fragment = null; break;
  128. }
  129. }
  130. public void BindProgram()
  131. {
  132. if (Current.Vertex == null ||
  133. Current.Fragment == null)
  134. {
  135. return;
  136. }
  137. if (!Programs.TryGetValue(Current, out int Handle))
  138. {
  139. Handle = GL.CreateProgram();
  140. AttachIfNotNull(Handle, Current.Vertex);
  141. AttachIfNotNull(Handle, Current.TessControl);
  142. AttachIfNotNull(Handle, Current.TessEvaluation);
  143. AttachIfNotNull(Handle, Current.Geometry);
  144. AttachIfNotNull(Handle, Current.Fragment);
  145. GL.LinkProgram(Handle);
  146. CheckProgramLink(Handle);
  147. BindUniformBlocks(Handle);
  148. BindTextureLocations(Handle);
  149. Programs.Add(Current, Handle);
  150. }
  151. GL.UseProgram(Handle);
  152. CurrentProgramHandle = Handle;
  153. }
  154. private void EnsureExtraBlock()
  155. {
  156. if (ExtraUboHandle == 0)
  157. {
  158. ExtraUboHandle = GL.GenBuffer();
  159. GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
  160. GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
  161. GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
  162. }
  163. }
  164. private void AttachIfNotNull(int ProgramHandle, OGLShaderStage Stage)
  165. {
  166. if (Stage != null)
  167. {
  168. Stage.Compile();
  169. GL.AttachShader(ProgramHandle, Stage.Handle);
  170. }
  171. }
  172. private void BindUniformBlocks(int ProgramHandle)
  173. {
  174. int ExtraBlockindex = GL.GetUniformBlockIndex(ProgramHandle, GlslDecl.ExtraUniformBlockName);
  175. GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
  176. int FreeBinding = ReservedCbufCount;
  177. void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
  178. {
  179. if (Stage != null)
  180. {
  181. foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
  182. {
  183. int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name);
  184. if (BlockIndex < 0)
  185. {
  186. //It is expected that its found, if it's not then driver might be in a malfunction
  187. throw new InvalidOperationException();
  188. }
  189. GL.UniformBlockBinding(ProgramHandle, BlockIndex, FreeBinding);
  190. FreeBinding++;
  191. }
  192. }
  193. }
  194. BindUniformBlocksIfNotNull(Current.Vertex);
  195. BindUniformBlocksIfNotNull(Current.TessControl);
  196. BindUniformBlocksIfNotNull(Current.TessEvaluation);
  197. BindUniformBlocksIfNotNull(Current.Geometry);
  198. BindUniformBlocksIfNotNull(Current.Fragment);
  199. }
  200. private void BindTextureLocations(int ProgramHandle)
  201. {
  202. int Index = 0;
  203. void BindTexturesIfNotNull(OGLShaderStage Stage)
  204. {
  205. if (Stage != null)
  206. {
  207. foreach (ShaderDeclInfo Decl in Stage.TextureUsage)
  208. {
  209. int Location = GL.GetUniformLocation(ProgramHandle, Decl.Name);
  210. GL.Uniform1(Location, Index);
  211. Index++;
  212. }
  213. }
  214. }
  215. GL.UseProgram(ProgramHandle);
  216. BindTexturesIfNotNull(Current.Vertex);
  217. BindTexturesIfNotNull(Current.TessControl);
  218. BindTexturesIfNotNull(Current.TessEvaluation);
  219. BindTexturesIfNotNull(Current.Geometry);
  220. BindTexturesIfNotNull(Current.Fragment);
  221. }
  222. private static void CheckProgramLink(int Handle)
  223. {
  224. int Status = 0;
  225. GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out Status);
  226. if (Status == 0)
  227. {
  228. throw new ShaderException(GL.GetProgramInfoLog(Handle));
  229. }
  230. }
  231. }
  232. }