Add ConstBuffer cache

This commit is contained in:
ReinUsesLisp 2018-07-29 02:47:10 -03:00
parent ce96a45685
commit bb4c39411a
9 changed files with 173 additions and 100 deletions

View file

@ -0,0 +1,18 @@
using System;
namespace Ryujinx.Graphics.Gal
{
public interface IGalConstBuffer
{
void LockCache();
void UnlockCache();
void Create(long Key, long Size);
bool IsCached(long Key, long Size);
void SetData(long Key, long Size, IntPtr HostAddress);
void Bind(GalShaderType ShaderType, int Index, long Key);
}
}

View file

@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Gal
IGalBlend Blend { get; } IGalBlend Blend { get; }
IGalConstBuffer Buffer { get; }
IGalFrameBuffer FrameBuffer { get; } IGalFrameBuffer FrameBuffer { get; }
IGalRasterizer Rasterizer { get; } IGalRasterizer Rasterizer { get; }

View file

@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gal
IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key); IEnumerable<ShaderDeclInfo> GetTextureUsage(long Key);
void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress); void BindConstBuffers();
void EnsureTextureBinding(string UniformName, int Value); void EnsureTextureBinding(string UniformName, int Value);

View file

@ -0,0 +1,78 @@
using OpenTK.Graphics.OpenGL;
using System;
namespace Ryujinx.Graphics.Gal.OpenGL
{
class OGLConstBuffer : IGalConstBuffer
{
public const int ConstBuffersPerStage = 18;
private OGLCachedResource<OGLStreamBuffer> Cache;
private long[][] Keys;
public OGLConstBuffer()
{
Cache = new OGLCachedResource<OGLStreamBuffer>(DeleteBuffer);
Keys = new long[5][];
for (int i = 0; i < Keys.Length; i++)
{
Keys[i] = new long[ConstBuffersPerStage];
}
}
public void LockCache()
{
Cache.Lock();
}
public void UnlockCache()
{
Cache.Unlock();
}
public void Create(long Key, long Size)
{
OGLStreamBuffer Buffer = new OGLStreamBuffer(BufferTarget.UniformBuffer, Size);
Cache.AddOrUpdate(Key, Buffer, Size);
}
public bool IsCached(long Key, long Size)
{
return Cache.TryGetSize(Key, out long CachedSize) && CachedSize == Size;
}
public void SetData(long Key, long Size, IntPtr HostAddress)
{
if (!Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
{
throw new InvalidOperationException();
}
Buffer.SetData(Size, HostAddress);
}
public void Bind(GalShaderType Stage, int Index, long Key)
{
Keys[(int)Stage][Index] = Key;
}
public void PipelineBind(GalShaderType Stage, int Index, int BindingIndex)
{
long Key = Keys[(int)Stage][Index];
if (Key != 0 && Cache.TryGetValue(Key, out OGLStreamBuffer Buffer))
{
GL.BindBufferBase(BufferRangeTarget.UniformBuffer, BindingIndex, Buffer.Handle);
}
}
private static void DeleteBuffer(OGLStreamBuffer Buffer)
{
Buffer.Dispose();
}
}
}

View file

@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
public IGalBlend Blend { get; private set; } public IGalBlend Blend { get; private set; }
public IGalConstBuffer Buffer { get; private set; }
public IGalFrameBuffer FrameBuffer { get; private set; } public IGalFrameBuffer FrameBuffer { get; private set; }
public IGalRasterizer Rasterizer { get; private set; } public IGalRasterizer Rasterizer { get; private set; }
@ -21,11 +23,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
Blend = new OGLBlend(); Blend = new OGLBlend();
Buffer = new OGLConstBuffer();
FrameBuffer = new OGLFrameBuffer(); FrameBuffer = new OGLFrameBuffer();
Rasterizer = new OGLRasterizer(); Rasterizer = new OGLRasterizer();
Shader = new OGLShader(); Shader = new OGLShader(Buffer as OGLConstBuffer);
Texture = new OGLTexture(); Texture = new OGLTexture();

View file

@ -9,7 +9,7 @@ using Buffer = System.Buffer;
namespace Ryujinx.Graphics.Gal.OpenGL namespace Ryujinx.Graphics.Gal.OpenGL
{ {
public class OGLShader : IGalShader class OGLShader : IGalShader
{ {
private class ShaderStage : IDisposable private class ShaderStage : IDisposable
{ {
@ -71,8 +71,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public ShaderStage Fragment; public ShaderStage Fragment;
} }
const int ConstBuffersPerStage = 18;
private ShaderProgram Current; private ShaderProgram Current;
private ConcurrentDictionary<long, ShaderStage> Stages; private ConcurrentDictionary<long, ShaderStage> Stages;
@ -81,20 +79,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL
public int CurrentProgramHandle { get; private set; } public int CurrentProgramHandle { get; private set; }
private OGLStreamBuffer[][] ConstBuffers; private OGLConstBuffer Buffer;
public OGLShader() public OGLShader(OGLConstBuffer Buffer)
{ {
this.Buffer = Buffer;
Stages = new ConcurrentDictionary<long, ShaderStage>(); Stages = new ConcurrentDictionary<long, ShaderStage>();
Programs = new Dictionary<ShaderProgram, int>(); 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) public void Create(IGalMemory Memory, long Key, GalShaderType Type)
@ -153,19 +146,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL
return Enumerable.Empty<ShaderDeclInfo>(); return Enumerable.Empty<ShaderDeclInfo>();
} }
public void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress) public void BindConstBuffers()
{ {
if (Stages.TryGetValue(Key, out ShaderStage Stage)) int FreeBinding = 0;
void BindIfNotNull(ShaderStage Stage)
{ {
foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage.Where(x => x.Cbuf == Cbuf)) if (Stage != null)
{ {
OGLStreamBuffer Buffer = GetConstBuffer(Stage.Type, Cbuf); foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage)
{
Buffer.PipelineBind(Stage.Type, DeclInfo.Cbuf, FreeBinding);
int Size = Math.Min(DataSize, Buffer.Size); FreeBinding++;
}
Buffer.SetData(Size, HostAddress);
} }
} }
BindIfNotNull(Current.Vertex);
BindIfNotNull(Current.TessControl);
BindIfNotNull(Current.TessEvaluation);
BindIfNotNull(Current.Geometry);
BindIfNotNull(Current.Fragment);
} }
public void EnsureTextureBinding(string UniformName, int Value) public void EnsureTextureBinding(string UniformName, int Value)
@ -257,11 +259,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.UseProgram(Handle); GL.UseProgram(Handle);
if (CurrentProgramHandle != Handle)
{
BindUniformBuffers(Handle);
}
CurrentProgramHandle = Handle; CurrentProgramHandle = Handle;
} }
@ -307,51 +304,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
BindUniformBlocksIfNotNull(Current.Fragment); 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) public static void CompileAndCheck(int Handle, string Code)
{ {
GL.ShaderSource(Handle, Code); GL.ShaderSource(Handle, Code);

View file

@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
{ {
public int Handle { get; protected set; } public int Handle { get; protected set; }
public int Size { get; protected set; } public long Size { get; protected set; }
protected BufferTarget Target { get; private set; } protected BufferTarget Target { get; private set; }
public OGLStreamBuffer(BufferTarget Target, int Size) public OGLStreamBuffer(BufferTarget Target, long Size)
{ {
this.Target = Target; this.Target = Target;
this.Size = Size; this.Size = Size;
@ -20,14 +20,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL
GL.BindBuffer(Target, Handle); GL.BindBuffer(Target, Handle);
GL.BufferData(Target, Size, IntPtr.Zero, BufferUsageHint.StreamDraw); GL.BufferData(Target, (IntPtr)Size, IntPtr.Zero, BufferUsageHint.StreamDraw);
} }
public void SetData(int Size, IntPtr HostAddress) public void SetData(long Size, IntPtr HostAddress)
{ {
GL.BindBuffer(Target, Handle); GL.BindBuffer(Target, Handle);
GL.BufferSubData(Target, IntPtr.Zero, Size, HostAddress); GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress);
} }
public void Dispose() public void Dispose()

View file

@ -27,6 +27,8 @@ namespace Ryujinx.HLE.Gpu.Engines
private List<long>[] UploadedKeys; private List<long>[] UploadedKeys;
private bool ConstBufferBindingsChanged;
public NvGpuEngine3d(NvGpu Gpu) public NvGpuEngine3d(NvGpu Gpu)
{ {
this.Gpu = Gpu; this.Gpu = Gpu;
@ -90,7 +92,14 @@ namespace Ryujinx.HLE.Gpu.Engines
Gpu.Renderer.Shader.BindProgram(); Gpu.Renderer.Shader.BindProgram();
//Note: Uncomment SetFrontFace SetCullFace when flipping issues are solved if (ConstBufferBindingsChanged)
{
ConstBufferBindingsChanged = false;
Gpu.Renderer.Shader.BindConstBuffers();
}
//Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved
//SetFrontFace(); //SetFrontFace();
//SetCullFace(); //SetCullFace();
SetDepth(); SetDepth();
@ -99,7 +108,7 @@ namespace Ryujinx.HLE.Gpu.Engines
SetPrimitiveRestart(); SetPrimitiveRestart();
UploadTextures(Vmm, Keys); UploadTextures(Vmm, Keys);
UploadUniforms(Vmm); UploadConstBuffers(Vmm);
UploadVertexArrays(Vmm); UploadVertexArrays(Vmm);
UnlockCaches(); UnlockCaches();
@ -107,12 +116,14 @@ namespace Ryujinx.HLE.Gpu.Engines
private void LockCaches() private void LockCaches()
{ {
Gpu.Renderer.Buffer.LockCache();
Gpu.Renderer.Rasterizer.LockCaches(); Gpu.Renderer.Rasterizer.LockCaches();
Gpu.Renderer.Texture.LockCache(); Gpu.Renderer.Texture.LockCache();
} }
private void UnlockCaches() private void UnlockCaches()
{ {
Gpu.Renderer.Buffer.UnlockCache();
Gpu.Renderer.Rasterizer.UnlockCaches(); Gpu.Renderer.Rasterizer.UnlockCaches();
Gpu.Renderer.Texture.UnlockCache(); Gpu.Renderer.Texture.UnlockCache();
} }
@ -546,32 +557,24 @@ namespace Ryujinx.HLE.Gpu.Engines
Gpu.Renderer.Texture.SetSampler(Sampler); Gpu.Renderer.Texture.SetSampler(Sampler);
} }
private void UploadUniforms(NvGpuVmm Vmm) private void UploadConstBuffers(NvGpuVmm Vmm)
{ {
long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); for (int Stage = 0; Stage < 5; Stage++)
for (int Index = 0; Index < 5; Index++)
{ {
int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + (Index + 1) * 0x10); for (int Index = 0; Index < 18; Index++)
int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + (Index + 1) * 0x10);
//Note: Vertex Program (B) is always enabled.
bool Enable = (Control & 1) != 0 || Index == 0;
if (!Enable)
{ {
continue; ConstBuffer Cb = ConstBuffers[Stage][Index];
}
for (int Cbuf = 0; Cbuf < ConstBuffers[Index].Length; Cbuf++)
{
ConstBuffer Cb = ConstBuffers[Index][Cbuf];
if (Cb.Enabled) if (Cb.Enabled)
{ {
IntPtr DataAddress = Vmm.GetHostAddress(Cb.Position, Cb.Size); long Key = Cb.Position;
Gpu.Renderer.Shader.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Cb.Size, DataAddress); if (QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
{
IntPtr Source = Vmm.GetHostAddress(Key, Cb.Size);
Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source);
}
} }
} }
} }
@ -741,10 +744,25 @@ namespace Ryujinx.HLE.Gpu.Engines
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress);
ConstBuffers[Stage][Index].Position = Position; int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize);
ConstBuffers[Stage][Index].Enabled = Enabled;
ConstBuffers[Stage][Index].Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); if (!Gpu.Renderer.Buffer.IsCached(Position, Size))
{
Gpu.Renderer.Buffer.Create(Position, Size);
}
ConstBuffer Cb = ConstBuffers[Stage][Index];
if (Cb.Position != Position || Cb.Enabled != Enabled || Cb.Size != Size)
{
Gpu.Renderer.Buffer.Bind((GalShaderType)Stage, Index, Enabled ? Position : 0);
ConstBufferBindingsChanged = true;
ConstBuffers[Stage][Index].Position = Position;
ConstBuffers[Stage][Index].Enabled = Enabled;
ConstBuffers[Stage][Index].Size = Size;
}
} }
private float GetFlipSign(NvGpuEngine3dReg Reg) private float GetFlipSign(NvGpuEngine3dReg Reg)

View file

@ -5,6 +5,7 @@ namespace Ryujinx.HLE.Gpu.Memory
Index, Index,
Vertex, Vertex,
Texture, Texture,
ConstBuffer,
Count Count
} }
} }