Implementation of UBOs instead of uniform constant arrays (#186)

* Sort uniform binding to avoid possible failures in drivers fewer bindings

* Throw exception for Cbuf overflow

* Search for free bindings instead of using locked ones

* EnsureAllocated when binding buffers

* Fix uniform bindings

* Remove spaces

* Use 64 KiB UBOs when available

* Remove double colon

* Use IdentationStr and avoid division in Cbuf offset

* Add spaces
This commit is contained in:
ReinUsesLisp 2018-06-26 02:10:54 -03:00 committed by gdkchan
parent b8be89ab2d
commit 09dfefed1f
3 changed files with 223 additions and 20 deletions

View file

@ -69,6 +69,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public ShaderStage Fragment;
}
const int ConstBuffersPerStage = 18;
private ShaderProgram Current;
private ConcurrentDictionary<long, ShaderStage> Stages;
@ -77,11 +79,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public int CurrentProgramHandle { get; private set; }
private OGLStreamBuffer[][] ConstBuffers;
public OGLShader()
{
Stages = new ConcurrentDictionary<long, ShaderStage>();
Programs = new Dictionary<ShaderProgram, int>();
ConstBuffers = new OGLStreamBuffer[5][];
for (int i = 0; i < 5; i++)
{
ConstBuffers[i] = new OGLStreamBuffer[ConstBuffersPerStage];
}
}
public void Create(IGalMemory Memory, long Key, GalShaderType Type)
@ -119,27 +130,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public void SetConstBuffer(long Key, int Cbuf, byte[] Data)
{
BindProgram();
if (Stages.TryGetValue(Key, out ShaderStage Stage))
{
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf))
{
int Location = GL.GetUniformLocation(CurrentProgramHandle, DeclInfo.Name);
OGLStreamBuffer Buffer = GetConstBuffer(Stage.Type, Cbuf);
int Count = Data.Length >> 2;
int Size = Math.Min(Data.Length, Buffer.Size);
//The Index is the index of the last element,
//so we can add 1 to get the uniform array size.
Count = Math.Min(Count, DeclInfo.Index + 1);
byte[] Destiny = Buffer.Map(Size);
unsafe
{
fixed (byte* Ptr = Data)
{
GL.Uniform1(Location, Count, (float*)Ptr);
}
}
Array.Copy(Data, Destiny, Size);
Buffer.Unmap(Size);
}
}
}
@ -204,11 +207,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
CheckProgramLink(Handle);
BindUniformBlocks(Handle);
Programs.Add(Current, Handle);
}
GL.UseProgram(Handle);
BindUniformBuffers(Handle);
CurrentProgramHandle = Handle;
}
@ -222,6 +229,87 @@ namespace Ryujinx.Graphics.Gal.OpenGL
}
}
private void BindUniformBlocks(int ProgramHandle)
{
int FreeBinding = 0;
int BindUniformBlocksIfNotNull(ShaderStage Stage)
{
if (Stage != null)
{
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage)
{
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++;
}
}
return FreeBinding;
}
BindUniformBlocksIfNotNull(Current.Vertex);
BindUniformBlocksIfNotNull(Current.TessControl);
BindUniformBlocksIfNotNull(Current.TessEvaluation);
BindUniformBlocksIfNotNull(Current.Geometry);
BindUniformBlocksIfNotNull(Current.Fragment);
}
private void BindUniformBuffers(int ProgramHandle)
{
int FreeBinding = 0;
int 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++;
}
}
return 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 = OGLStreamBuffer.Create(BufferTarget.UniformBuffer, Size);
ConstBuffers[StageIndex][Cbuf] = Buffer;
}
return Buffer;
}
public static void CompileAndCheck(int Handle, string Code)
{
GL.ShaderSource(Handle, Code);