diff --git a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs new file mode 100644 index 000000000..3e87d0641 --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs @@ -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); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index c6324c4a3..c08a6199c 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Gal IGalBlend Blend { get; } + IGalConstBuffer Buffer { get; } + IGalFrameBuffer FrameBuffer { get; } IGalRasterizer Rasterizer { get; } diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs index 56235a070..7103227cb 100644 --- a/Ryujinx.Graphics/Gal/IGalShader.cs +++ b/Ryujinx.Graphics/Gal/IGalShader.cs @@ -11,7 +11,7 @@ namespace Ryujinx.Graphics.Gal IEnumerable GetTextureUsage(long Key); - void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress); + void BindConstBuffers(); void EnsureTextureBinding(string UniformName, int Value); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs new file mode 100644 index 000000000..3451de2c7 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs @@ -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 Cache; + + private long[][] Keys; + + public OGLConstBuffer() + { + Cache = new OGLCachedResource(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(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs index ca70d4f66..6a393a8d0 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs @@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public IGalBlend Blend { get; private set; } + public IGalConstBuffer Buffer { get; private set; } + public IGalFrameBuffer FrameBuffer { get; private set; } public IGalRasterizer Rasterizer { get; private set; } @@ -21,11 +23,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL { Blend = new OGLBlend(); + Buffer = new OGLConstBuffer(); + FrameBuffer = new OGLFrameBuffer(); Rasterizer = new OGLRasterizer(); - Shader = new OGLShader(); + Shader = new OGLShader(Buffer as OGLConstBuffer); Texture = new OGLTexture(); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 3f3f23b8a..25c231439 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -9,7 +9,7 @@ using Buffer = System.Buffer; namespace Ryujinx.Graphics.Gal.OpenGL { - public class OGLShader : IGalShader + class OGLShader : IGalShader { private class ShaderStage : IDisposable { @@ -71,8 +71,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL public ShaderStage Fragment; } - const int ConstBuffersPerStage = 18; - private ShaderProgram Current; private ConcurrentDictionary Stages; @@ -81,20 +79,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL public int CurrentProgramHandle { get; private set; } - private OGLStreamBuffer[][] ConstBuffers; + private OGLConstBuffer Buffer; - public OGLShader() + public OGLShader(OGLConstBuffer Buffer) { + this.Buffer = Buffer; + Stages = new ConcurrentDictionary(); Programs = new Dictionary(); - - 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) @@ -153,19 +146,28 @@ namespace Ryujinx.Graphics.Gal.OpenGL return Enumerable.Empty(); } - 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); - - Buffer.SetData(Size, HostAddress); + FreeBinding++; + } } } + + BindIfNotNull(Current.Vertex); + BindIfNotNull(Current.TessControl); + BindIfNotNull(Current.TessEvaluation); + BindIfNotNull(Current.Geometry); + BindIfNotNull(Current.Fragment); } public void EnsureTextureBinding(string UniformName, int Value) @@ -257,11 +259,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.UseProgram(Handle); - if (CurrentProgramHandle != Handle) - { - BindUniformBuffers(Handle); - } - CurrentProgramHandle = Handle; } @@ -307,51 +304,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); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs index 0d5dee93f..946394059 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs @@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public int Handle { get; protected set; } - public int Size { get; protected set; } + public long Size { get; protected set; } protected BufferTarget Target { get; private set; } - public OGLStreamBuffer(BufferTarget Target, int Size) + public OGLStreamBuffer(BufferTarget Target, long Size) { this.Target = Target; this.Size = Size; @@ -20,14 +20,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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.BufferSubData(Target, IntPtr.Zero, Size, HostAddress); + GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)Size, HostAddress); } public void Dispose() diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index 3dd0e98e6..9b13aaa95 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -27,6 +27,8 @@ namespace Ryujinx.HLE.Gpu.Engines private List[] UploadedKeys; + private bool ConstBufferBindingsChanged; + public NvGpuEngine3d(NvGpu Gpu) { this.Gpu = Gpu; @@ -90,7 +92,14 @@ namespace Ryujinx.HLE.Gpu.Engines 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(); //SetCullFace(); SetDepth(); @@ -99,7 +108,7 @@ namespace Ryujinx.HLE.Gpu.Engines SetPrimitiveRestart(); UploadTextures(Vmm, Keys); - UploadUniforms(Vmm); + UploadConstBuffers(Vmm); UploadVertexArrays(Vmm); UnlockCaches(); @@ -107,12 +116,14 @@ namespace Ryujinx.HLE.Gpu.Engines private void LockCaches() { + Gpu.Renderer.Buffer.LockCache(); Gpu.Renderer.Rasterizer.LockCaches(); Gpu.Renderer.Texture.LockCache(); } private void UnlockCaches() { + Gpu.Renderer.Buffer.UnlockCache(); Gpu.Renderer.Rasterizer.UnlockCaches(); Gpu.Renderer.Texture.UnlockCache(); } @@ -546,32 +557,24 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Texture.SetSampler(Sampler); } - private void UploadUniforms(NvGpuVmm Vmm) + private void UploadConstBuffers(NvGpuVmm Vmm) { - long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - - for (int Index = 0; Index < 5; Index++) + for (int Stage = 0; Stage < 5; Stage++) { - int Control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + (Index + 1) * 0x10); - int Offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + (Index + 1) * 0x10); - - //Note: Vertex Program (B) is always enabled. - bool Enable = (Control & 1) != 0 || Index == 0; - - if (!Enable) + for (int Index = 0; Index < 18; Index++) { - continue; - } - - for (int Cbuf = 0; Cbuf < ConstBuffers[Index].Length; Cbuf++) - { - ConstBuffer Cb = ConstBuffers[Index][Cbuf]; + ConstBuffer Cb = ConstBuffers[Stage][Index]; 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); - ConstBuffers[Stage][Index].Position = Position; - ConstBuffers[Stage][Index].Enabled = Enabled; + int Size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); - 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) diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs index 469cd6cd0..a6c03f425 100644 --- a/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs +++ b/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs @@ -5,6 +5,7 @@ namespace Ryujinx.HLE.Gpu.Memory Index, Vertex, Texture, + ConstBuffer, Count } } \ No newline at end of file