| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- using OpenTK.Graphics.OpenGL;
- using Ryujinx.Graphics.Gal.Shader;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Linq;
- namespace Ryujinx.Graphics.Gal.OpenGL
- {
- class OGLShader : IGalShader
- {
- public const int ReservedCbufCount = 1;
- private const int ExtraDataSize = 4;
- public OGLShaderProgram Current;
- private ConcurrentDictionary<long, OGLShaderStage> Stages;
- private Dictionary<OGLShaderProgram, int> Programs;
- public int CurrentProgramHandle { get; private set; }
- private OGLConstBuffer Buffer;
- private int ExtraUboHandle;
- public OGLShader(OGLConstBuffer Buffer)
- {
- this.Buffer = Buffer;
- Stages = new ConcurrentDictionary<long, OGLShaderStage>();
- Programs = new Dictionary<OGLShaderProgram, int>();
- }
- public void Create(IGalMemory Memory, long Key, GalShaderType Type)
- {
- Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, Key, 0, false, Type));
- }
- public void Create(IGalMemory Memory, long VpAPos, long Key, GalShaderType Type)
- {
- Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, VpAPos, Key, true, Type));
- }
- private OGLShaderStage ShaderStageFactory(
- IGalMemory Memory,
- long Position,
- long PositionB,
- bool IsDualVp,
- GalShaderType Type)
- {
- GlslProgram Program;
- GlslDecompiler Decompiler = new GlslDecompiler();
- if (IsDualVp)
- {
- ShaderDumper.Dump(Memory, Position, Type, "a");
- ShaderDumper.Dump(Memory, PositionB, Type, "b");
- Program = Decompiler.Decompile(
- Memory,
- Position,
- PositionB,
- Type);
- }
- else
- {
- ShaderDumper.Dump(Memory, Position, Type);
- Program = Decompiler.Decompile(Memory, Position, Type);
- }
- return new OGLShaderStage(
- Type,
- Program.Code,
- Program.Uniforms,
- Program.Textures);
- }
- public IEnumerable<ShaderDeclInfo> GetConstBufferUsage(long Key)
- {
- if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
- {
- return Stage.ConstBufferUsage;
- }
- return Enumerable.Empty<ShaderDeclInfo>();
- }
- public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
- {
- if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
- {
- return Stage.TextureUsage;
- }
- return Enumerable.Empty<ShaderDeclInfo>();
- }
- public unsafe void SetExtraData(float FlipX, float FlipY, int Instance)
- {
- BindProgram();
- EnsureExtraBlock();
- GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
- float* Data = stackalloc float[ExtraDataSize];
- Data[0] = FlipX;
- Data[1] = FlipY;
- Data[2] = BitConverter.Int32BitsToSingle(Instance);
- //Invalidate buffer
- GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
- GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)Data);
- }
- public void Bind(long Key)
- {
- if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
- {
- Bind(Stage);
- }
- }
- private void Bind(OGLShaderStage Stage)
- {
- if (Stage.Type == GalShaderType.Geometry)
- {
- //Enhanced layouts are required for Geometry shaders
- //skip this stage if current driver has no ARB_enhanced_layouts
- if (!OGLExtension.HasEnhancedLayouts())
- {
- return;
- }
- }
- 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 Unbind(GalShaderType Type)
- {
- switch (Type)
- {
- case GalShaderType.Vertex: Current.Vertex = null; break;
- case GalShaderType.TessControl: Current.TessControl = null; break;
- case GalShaderType.TessEvaluation: Current.TessEvaluation = null; break;
- case GalShaderType.Geometry: Current.Geometry = null; break;
- case GalShaderType.Fragment: Current.Fragment = null; 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);
- BindUniformBlocks(Handle);
- BindTextureLocations(Handle);
- Programs.Add(Current, Handle);
- }
- GL.UseProgram(Handle);
- CurrentProgramHandle = Handle;
- }
- private void EnsureExtraBlock()
- {
- if (ExtraUboHandle == 0)
- {
- ExtraUboHandle = GL.GenBuffer();
- GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
- GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
- GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
- }
- }
- private void AttachIfNotNull(int ProgramHandle, OGLShaderStage Stage)
- {
- if (Stage != null)
- {
- Stage.Compile();
- GL.AttachShader(ProgramHandle, Stage.Handle);
- }
- }
- private void BindUniformBlocks(int ProgramHandle)
- {
- int ExtraBlockindex = GL.GetUniformBlockIndex(ProgramHandle, GlslDecl.ExtraUniformBlockName);
- GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
- int FreeBinding = ReservedCbufCount;
- void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
- {
- if (Stage != null)
- {
- foreach (ShaderDeclInfo DeclInfo in Stage.ConstBufferUsage)
- {
- int BlockIndex = GL.GetUniformBlockIndex(ProgramHandle, DeclInfo.Name);
- if (BlockIndex < 0)
- {
- //It is expected that its found, if it's not then driver might be in a malfunction
- throw new InvalidOperationException();
- }
- GL.UniformBlockBinding(ProgramHandle, BlockIndex, FreeBinding);
- FreeBinding++;
- }
- }
- }
- BindUniformBlocksIfNotNull(Current.Vertex);
- BindUniformBlocksIfNotNull(Current.TessControl);
- BindUniformBlocksIfNotNull(Current.TessEvaluation);
- BindUniformBlocksIfNotNull(Current.Geometry);
- BindUniformBlocksIfNotNull(Current.Fragment);
- }
- private void BindTextureLocations(int ProgramHandle)
- {
- int Index = 0;
- void BindTexturesIfNotNull(OGLShaderStage Stage)
- {
- if (Stage != null)
- {
- foreach (ShaderDeclInfo Decl in Stage.TextureUsage)
- {
- int Location = GL.GetUniformLocation(ProgramHandle, Decl.Name);
- GL.Uniform1(Location, Index);
- Index++;
- }
- }
- }
- GL.UseProgram(ProgramHandle);
- BindTexturesIfNotNull(Current.Vertex);
- BindTexturesIfNotNull(Current.TessControl);
- BindTexturesIfNotNull(Current.TessEvaluation);
- BindTexturesIfNotNull(Current.Geometry);
- BindTexturesIfNotNull(Current.Fragment);
- }
- 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));
- }
- }
- }
- }
|