Low level graphics API prerequisites (#319)

* Add GalPipelineState and IGalPipeline

* Separate UploadVertex call

* Add ConstBuffer cache

* Move Vertex Assembly into GalPipelineState

* Move Uniform binds to GalPipelineState

* Move framebuffer flip into a buffer

* Rebase

* Fix regression

* Move clear values from VertexEndGl to ClearBuffers

* Rename obscure names O->Old S->New
This commit is contained in:
ReinUsesLisp 2018-08-10 01:09:40 -03:00 committed by gdkchan
parent 652238f526
commit 25dd5f4238
20 changed files with 854 additions and 702 deletions

View file

@ -5,96 +5,29 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Buffer = System.Buffer;
namespace Ryujinx.Graphics.Gal.OpenGL
{
public class OGLShader : IGalShader
class OGLShader : IGalShader
{
private class ShaderStage : IDisposable
{
public int Handle { get; private set; }
public OGLShaderProgram Current;
public bool IsCompiled { get; private set; }
private ConcurrentDictionary<long, OGLShaderStage> Stages;
public GalShaderType Type { get; private set; }
public string Code { get; private set; }
public IEnumerable<ShaderDeclInfo> TextureUsage { get; private set; }
public IEnumerable<ShaderDeclInfo> UniformUsage { get; private set; }
public ShaderStage(
GalShaderType Type,
string Code,
IEnumerable<ShaderDeclInfo> TextureUsage,
IEnumerable<ShaderDeclInfo> UniformUsage)
{
this.Type = Type;
this.Code = Code;
this.TextureUsage = TextureUsage;
this.UniformUsage = UniformUsage;
}
public void Compile()
{
if (Handle == 0)
{
Handle = GL.CreateShader(OGLEnumConverter.GetShaderType(Type));
CompileAndCheck(Handle, Code);
}
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool Disposing)
{
if (Disposing && Handle != 0)
{
GL.DeleteShader(Handle);
Handle = 0;
}
}
}
private struct ShaderProgram
{
public ShaderStage Vertex;
public ShaderStage TessControl;
public ShaderStage TessEvaluation;
public ShaderStage Geometry;
public ShaderStage Fragment;
}
const int ConstBuffersPerStage = 18;
private ShaderProgram Current;
private ConcurrentDictionary<long, ShaderStage> Stages;
private Dictionary<ShaderProgram, int> Programs;
private Dictionary<OGLShaderProgram, int> Programs;
public int CurrentProgramHandle { get; private set; }
private OGLStreamBuffer[][] ConstBuffers;
private OGLConstBuffer Buffer;
public OGLShader()
private int ExtraUboHandle;
public OGLShader(OGLConstBuffer Buffer)
{
Stages = new ConcurrentDictionary<long, ShaderStage>();
this.Buffer = Buffer;
Programs = new Dictionary<ShaderProgram, int>();
Stages = new ConcurrentDictionary<long, OGLShaderStage>();
ConstBuffers = new OGLStreamBuffer[5][];
for (int i = 0; i < 5; i++)
{
ConstBuffers[i] = new OGLStreamBuffer[ConstBuffersPerStage];
}
Programs = new Dictionary<OGLShaderProgram, int>();
}
public void Create(IGalMemory Memory, long Key, GalShaderType Type)
@ -107,7 +40,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Stages.GetOrAdd(Key, (Stage) => ShaderStageFactory(Memory, VpAPos, Key, true, Type));
}
private ShaderStage ShaderStageFactory(
private OGLShaderStage ShaderStageFactory(
IGalMemory Memory,
long Position,
long PositionB,
@ -136,7 +69,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
Program = Decompiler.Decompile(Memory, Position, Type);
}
return new ShaderStage(
return new OGLShaderStage(
Type,
Program.Code,
Program.Textures,
@ -145,7 +78,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key)
{
if (Stages.TryGetValue(Key, out ShaderStage Stage))
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
{
return Stage.TextureUsage;
}
@ -153,21 +86,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return Enumerable.Empty<ShaderDeclInfo>();
}
public void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress)
{
if (Stages.TryGetValue(Key, out ShaderStage Stage))
{
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
{
OGLStreamBuffer Buffer = GetConstBuffer(Stage.Type, Cbuf);
int Size = Math.Min(DataSize, Buffer.Size);
Buffer.SetData(Size, HostAddress);
}
}
}
public void EnsureTextureBinding(string UniformName, int Value)
{
BindProgram();
@ -177,24 +95,33 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.Uniform1(Location, Value);
}
public void SetFlip(float X, float Y)
public unsafe void SetFlip(float X, float Y)
{
BindProgram();
int Location = GL.GetUniformLocation(CurrentProgramHandle, GlslDecl.FlipUniformName);
EnsureExtraBlock();
GL.Uniform2(Location, X, Y);
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
float* Data = stackalloc float[4];
Data[0] = X;
Data[1] = Y;
//Invalidate buffer
GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, 4 * sizeof(float), (IntPtr)Data);
}
public void Bind(long Key)
{
if (Stages.TryGetValue(Key, out ShaderStage Stage))
if (Stages.TryGetValue(Key, out OGLShaderStage Stage))
{
Bind(Stage);
}
}
private void Bind(ShaderStage Stage)
private void Bind(OGLShaderStage Stage)
{
if (Stage.Type == GalShaderType.Geometry)
{
@ -257,15 +184,24 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.UseProgram(Handle);
if (CurrentProgramHandle != Handle)
{
BindUniformBuffers(Handle);
}
CurrentProgramHandle = Handle;
}
private void AttachIfNotNull(int ProgramHandle, ShaderStage Stage)
private void EnsureExtraBlock()
{
if (ExtraUboHandle == 0)
{
ExtraUboHandle = GL.GenBuffer();
GL.BindBuffer(BufferTarget.UniformBuffer, ExtraUboHandle);
GL.BufferData(BufferTarget.UniformBuffer, 4 * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, ExtraUboHandle);
}
}
private void AttachIfNotNull(int ProgramHandle, OGLShaderStage Stage)
{
if (Stage != null)
{
@ -277,9 +213,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
private void BindUniformBlocks(int ProgramHandle)
{
int FreeBinding = 0;
int ExtraBlockindex = GL.GetUniformBlockIndex(ProgramHandle, GlslDecl.ExtraUniformBlockName);
void BindUniformBlocksIfNotNull(ShaderStage Stage)
GL.UniformBlockBinding(ProgramHandle, ExtraBlockindex, 0);
//First index is reserved
int FreeBinding = 1;
void BindUniformBlocksIfNotNull(OGLShaderStage Stage)
{
if (Stage != null)
{
@ -307,71 +248,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
BindUniformBlocksIfNotNull(Current.Fragment);
}
private void BindUniformBuffers(int ProgramHandle)
{
int FreeBinding = 0;
void BindUniformBuffersIfNotNull(ShaderStage Stage)
{
if (Stage != null)
{
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage)
{
OGLStreamBuffer Buffer = GetConstBuffer(Stage.Type, DeclInfo.Cbuf);
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, FreeBinding, Buffer.Handle);
FreeBinding++;
}
}
}
BindUniformBuffersIfNotNull(Current.Vertex);
BindUniformBuffersIfNotNull(Current.TessControl);
BindUniformBuffersIfNotNull(Current.TessEvaluation);
BindUniformBuffersIfNotNull(Current.Geometry);
BindUniformBuffersIfNotNull(Current.Fragment);
}
private OGLStreamBuffer GetConstBuffer(GalShaderType StageType, int Cbuf)
{
int StageIndex = (int)StageType;
OGLStreamBuffer Buffer = ConstBuffers[StageIndex][Cbuf];
if (Buffer == null)
{
//Allocate a maximum of 64 KiB
int Size = Math.Min(GL.GetInteger(GetPName.MaxUniformBlockSize), 64 * 1024);
Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size);
ConstBuffers[StageIndex][Cbuf] = Buffer;
}
return Buffer;
}
public static void CompileAndCheck(int Handle, string Code)
{
GL.ShaderSource(Handle, Code);
GL.CompileShader(Handle);
CheckCompilation(Handle);
}
private static void CheckCompilation(int Handle)
{
int Status = 0;
GL.GetShader(Handle, ShaderParameter.CompileStatus, out Status);
if (Status == 0)
{
throw new ShaderException(GL.GetShaderInfoLog(Handle));
}
}
private static void CheckProgramLink(int Handle)
{
int Status = 0;