diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs new file mode 100644 index 00000000..d1ffbe76 --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -0,0 +1,73 @@ +namespace Ryujinx.Graphics.Gal +{ + public struct GalVertexBinding + { + //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3 + + public bool Enabled; + public int Stride; + public long VboKey; + public GalVertexAttrib[] Attribs; + } + + public class GalPipelineState + { + public const int Stages = 5; + public const int ConstBuffersPerStage = 18; + + public long[][] ConstBufferKeys; + + public GalVertexBinding[] VertexBindings; + + public float FlipX; + public float FlipY; + + public GalFrontFace FrontFace; + + public bool CullFaceEnabled; + public GalCullFace CullFace; + + public bool DepthTestEnabled; + public GalComparisonOp DepthFunc; + + public bool StencilTestEnabled; + + public GalComparisonOp StencilBackFuncFunc; + public int StencilBackFuncRef; + public uint StencilBackFuncMask; + public GalStencilOp StencilBackOpFail; + public GalStencilOp StencilBackOpZFail; + public GalStencilOp StencilBackOpZPass; + public uint StencilBackMask; + + public GalComparisonOp StencilFrontFuncFunc; + public int StencilFrontFuncRef; + public uint StencilFrontFuncMask; + public GalStencilOp StencilFrontOpFail; + public GalStencilOp StencilFrontOpZFail; + public GalStencilOp StencilFrontOpZPass; + public uint StencilFrontMask; + + public bool BlendEnabled; + public bool BlendSeparateAlpha; + public GalBlendEquation BlendEquationRgb; + public GalBlendFactor BlendFuncSrcRgb; + public GalBlendFactor BlendFuncDstRgb; + public GalBlendEquation BlendEquationAlpha; + public GalBlendFactor BlendFuncSrcAlpha; + public GalBlendFactor BlendFuncDstAlpha; + + public bool PrimitiveRestartEnabled; + public uint PrimitiveRestartIndex; + + public GalPipelineState() + { + ConstBufferKeys = new long[Stages][]; + + for (int Stage = 0; Stage < Stages; Stage++) + { + ConstBufferKeys[Stage] = new long[ConstBuffersPerStage]; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalBlend.cs b/Ryujinx.Graphics/Gal/IGalBlend.cs deleted file mode 100644 index 5c96a492..00000000 --- a/Ryujinx.Graphics/Gal/IGalBlend.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public interface IGalBlend - { - void Enable(); - - void Disable(); - - void Set( - GalBlendEquation Equation, - GalBlendFactor FuncSrc, - GalBlendFactor FuncDst); - - void SetSeparate( - GalBlendEquation EquationRgb, - GalBlendEquation EquationAlpha, - GalBlendFactor FuncSrcRgb, - GalBlendFactor FuncDstRgb, - GalBlendFactor FuncSrcAlpha, - GalBlendFactor FuncDstAlpha); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs new file mode 100644 index 00000000..37545b2a --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs @@ -0,0 +1,16 @@ +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); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalPipeline.cs b/Ryujinx.Graphics/Gal/IGalPipeline.cs new file mode 100644 index 00000000..d8cf266a --- /dev/null +++ b/Ryujinx.Graphics/Gal/IGalPipeline.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.Graphics.Gal +{ + public interface IGalPipeline + { + void Bind(GalPipelineState State); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index a87d36c3..89e50b1f 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -7,52 +7,20 @@ namespace Ryujinx.Graphics.Gal void LockCaches(); void UnlockCaches(); - void ClearBuffers(GalClearBufferFlags Flags); + void ClearBuffers( + GalClearBufferFlags Flags, + float Red, float Green, float Blue, float Alpha, + float Depth, + int Stencil); bool IsVboCached(long Key, long DataSize); bool IsIboCached(long Key, long DataSize); - void SetFrontFace(GalFrontFace FrontFace); - - void EnableCullFace(); - - void DisableCullFace(); - - void SetCullFace(GalCullFace CullFace); - - void EnableDepthTest(); - - void DisableDepthTest(); - - void SetDepthFunction(GalComparisonOp Func); - - void SetClearDepth(float Depth); - - void EnableStencilTest(); - - void DisableStencilTest(); - - void SetStencilFunction(bool IsFrontFace, GalComparisonOp Func, int Ref, int Mask); - - void SetStencilOp(bool IsFrontFace, GalStencilOp Fail, GalStencilOp ZFail, GalStencilOp ZPass); - - void SetStencilMask(bool IsFrontFace, int Mask); - - void SetClearStencil(int Stencil); - - void EnablePrimitiveRestart(); - - void DisablePrimitiveRestart(); - - void SetPrimitiveRestartIndex(uint Index); - void CreateVbo(long Key, int DataSize, IntPtr HostAddress); void CreateIbo(long Key, int DataSize, IntPtr HostAddress); - void SetVertexArray(int Stride, long VboKey, GalVertexAttrib[] Attribs); - void SetIndexArray(int Size, GalIndexFormat Format); void DrawArrays(int First, int PrimCount, GalPrimitiveType PrimType); diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs index c6324c4a..b47ac71d 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderer.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Gal void RunActions(); - IGalBlend Blend { get; } + IGalConstBuffer Buffer { get; } IGalFrameBuffer FrameBuffer { get; } @@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Gal IGalShader Shader { get; } + IGalPipeline Pipeline { get; } + IGalTexture Texture { get; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs index 56235a07..a9bd1381 100644 --- a/Ryujinx.Graphics/Gal/IGalShader.cs +++ b/Ryujinx.Graphics/Gal/IGalShader.cs @@ -11,12 +11,8 @@ namespace Ryujinx.Graphics.Gal IEnumerable GetTextureUsage(long Key); - void SetConstBuffer(long Key, int Cbuf, int DataSize, IntPtr HostAddress); - void EnsureTextureBinding(string UniformName, int Value); - void SetFlip(float X, float Y); - void Bind(long Key); void Unbind(GalShaderType Type); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs deleted file mode 100644 index 7175e3a0..00000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLBlend.cs +++ /dev/null @@ -1,49 +0,0 @@ -using OpenTK.Graphics.OpenGL; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - public class OGLBlend : IGalBlend - { - public void Enable() - { - GL.Enable(EnableCap.Blend); - } - - public void Disable() - { - GL.Disable(EnableCap.Blend); - } - - public void Set( - GalBlendEquation Equation, - GalBlendFactor FuncSrc, - GalBlendFactor FuncDst) - { - GL.BlendEquation( - OGLEnumConverter.GetBlendEquation(Equation)); - - GL.BlendFunc( - OGLEnumConverter.GetBlendFactor(FuncSrc), - OGLEnumConverter.GetBlendFactor(FuncDst)); - } - - public void SetSeparate( - GalBlendEquation EquationRgb, - GalBlendEquation EquationAlpha, - GalBlendFactor FuncSrcRgb, - GalBlendFactor FuncDstRgb, - GalBlendFactor FuncSrcAlpha, - GalBlendFactor FuncDstAlpha) - { - GL.BlendEquationSeparate( - OGLEnumConverter.GetBlendEquation(EquationRgb), - OGLEnumConverter.GetBlendEquation(EquationAlpha)); - - GL.BlendFuncSeparate( - (BlendingFactorSrc)OGLEnumConverter.GetBlendFactor(FuncSrcRgb), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(FuncDstRgb), - (BlendingFactorSrc)OGLEnumConverter.GetBlendFactor(FuncSrcAlpha), - (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(FuncDstAlpha)); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs new file mode 100644 index 00000000..50825541 --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLConstBuffer.cs @@ -0,0 +1,66 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OGLConstBuffer : IGalConstBuffer + { + private OGLCachedResource Cache; + + public OGLConstBuffer() + { + Cache = new OGLCachedResource(DeleteBuffer); + } + + 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 bool TryGetUbo(long Key, out int UboHandle) + { + if (Cache.TryGetValue(Key, out OGLStreamBuffer Buffer)) + { + UboHandle = Buffer.Handle; + + return true; + } + + UboHandle = 0; + + return false; + } + + private static void DeleteBuffer(OGLStreamBuffer Buffer) + { + Buffer.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs new file mode 100644 index 00000000..54f984cd --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -0,0 +1,379 @@ +using OpenTK.Graphics.OpenGL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OGLPipeline : IGalPipeline + { + private static Dictionary AttribElements = + new Dictionary() + { + { GalVertexAttribSize._32_32_32_32, 4 }, + { GalVertexAttribSize._32_32_32, 3 }, + { GalVertexAttribSize._16_16_16_16, 4 }, + { GalVertexAttribSize._32_32, 2 }, + { GalVertexAttribSize._16_16_16, 3 }, + { GalVertexAttribSize._8_8_8_8, 4 }, + { GalVertexAttribSize._16_16, 2 }, + { GalVertexAttribSize._32, 1 }, + { GalVertexAttribSize._8_8_8, 3 }, + { GalVertexAttribSize._8_8, 2 }, + { GalVertexAttribSize._16, 1 }, + { GalVertexAttribSize._8, 1 }, + { GalVertexAttribSize._10_10_10_2, 4 }, + { GalVertexAttribSize._11_11_10, 3 } + }; + + private static Dictionary AttribTypes = + new Dictionary() + { + { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._32_32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._16_16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._32, VertexAttribPointerType.Int }, + { GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._8_8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._16, VertexAttribPointerType.Short }, + { GalVertexAttribSize._8, VertexAttribPointerType.Byte }, + { GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int }, //? + { GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //? + }; + + private GalPipelineState Old; + + private OGLConstBuffer Buffer; + private OGLRasterizer Rasterizer; + private OGLShader Shader; + + private int VaoHandle; + + public OGLPipeline(OGLConstBuffer Buffer, OGLRasterizer Rasterizer, OGLShader Shader) + { + this.Buffer = Buffer; + this.Rasterizer = Rasterizer; + this.Shader = Shader; + + //These values match OpenGL's defaults + Old = new GalPipelineState + { + FrontFace = GalFrontFace.CCW, + + CullFaceEnabled = false, + CullFace = GalCullFace.Back, + + DepthTestEnabled = false, + DepthFunc = GalComparisonOp.Less, + + StencilTestEnabled = false, + + StencilBackFuncFunc = GalComparisonOp.Always, + StencilBackFuncRef = 0, + StencilBackFuncMask = UInt32.MaxValue, + StencilBackOpFail = GalStencilOp.Keep, + StencilBackOpZFail = GalStencilOp.Keep, + StencilBackOpZPass = GalStencilOp.Keep, + StencilBackMask = UInt32.MaxValue, + + StencilFrontFuncFunc = GalComparisonOp.Always, + StencilFrontFuncRef = 0, + StencilFrontFuncMask = UInt32.MaxValue, + StencilFrontOpFail = GalStencilOp.Keep, + StencilFrontOpZFail = GalStencilOp.Keep, + StencilFrontOpZPass = GalStencilOp.Keep, + StencilFrontMask = UInt32.MaxValue, + + BlendEnabled = false, + BlendSeparateAlpha = false, + + BlendEquationRgb = 0, + BlendFuncSrcRgb = GalBlendFactor.One, + BlendFuncDstRgb = GalBlendFactor.Zero, + BlendEquationAlpha = 0, + BlendFuncSrcAlpha = GalBlendFactor.One, + BlendFuncDstAlpha = GalBlendFactor.Zero, + + PrimitiveRestartEnabled = false, + PrimitiveRestartIndex = 0 + }; + } + + public void Bind(GalPipelineState New) + { + BindConstBuffers(New); + + BindVertexLayout(New); + + if (New.FlipX != Old.FlipX || New.FlipY != Old.FlipY) + { + Shader.SetFlip(New.FlipX, New.FlipY); + } + + //Note: Uncomment SetFrontFace and SetCullFace when flipping issues are solved + + //if (New.FrontFace != O.FrontFace) + //{ + // GL.FrontFace(OGLEnumConverter.GetFrontFace(New.FrontFace)); + //} + + //if (New.CullFaceEnabled != O.CullFaceEnabled) + //{ + // Enable(EnableCap.CullFace, New.CullFaceEnabled); + //} + + //if (New.CullFaceEnabled) + //{ + // if (New.CullFace != O.CullFace) + // { + // GL.CullFace(OGLEnumConverter.GetCullFace(New.CullFace)); + // } + //} + + if (New.DepthTestEnabled != Old.DepthTestEnabled) + { + Enable(EnableCap.DepthTest, New.DepthTestEnabled); + } + + if (New.DepthTestEnabled) + { + if (New.DepthFunc != Old.DepthFunc) + { + GL.DepthFunc(OGLEnumConverter.GetDepthFunc(New.DepthFunc)); + } + } + + if (New.StencilTestEnabled != Old.StencilTestEnabled) + { + Enable(EnableCap.StencilTest, New.StencilTestEnabled); + } + + if (New.StencilTestEnabled) + { + if (New.StencilBackFuncFunc != Old.StencilBackFuncFunc || + New.StencilBackFuncRef != Old.StencilBackFuncRef || + New.StencilBackFuncMask != Old.StencilBackFuncMask) + { + GL.StencilFuncSeparate( + StencilFace.Back, + OGLEnumConverter.GetStencilFunc(New.StencilBackFuncFunc), + New.StencilBackFuncRef, + New.StencilBackFuncMask); + } + + if (New.StencilBackOpFail != Old.StencilBackOpFail || + New.StencilBackOpZFail != Old.StencilBackOpZFail || + New.StencilBackOpZPass != Old.StencilBackOpZPass) + { + GL.StencilOpSeparate( + StencilFace.Back, + OGLEnumConverter.GetStencilOp(New.StencilBackOpFail), + OGLEnumConverter.GetStencilOp(New.StencilBackOpZFail), + OGLEnumConverter.GetStencilOp(New.StencilBackOpZPass)); + } + + if (New.StencilBackMask != Old.StencilBackMask) + { + GL.StencilMaskSeparate(StencilFace.Back, New.StencilBackMask); + } + + if (New.StencilFrontFuncFunc != Old.StencilFrontFuncFunc || + New.StencilFrontFuncRef != Old.StencilFrontFuncRef || + New.StencilFrontFuncMask != Old.StencilFrontFuncMask) + { + GL.StencilFuncSeparate( + StencilFace.Front, + OGLEnumConverter.GetStencilFunc(New.StencilFrontFuncFunc), + New.StencilFrontFuncRef, + New.StencilFrontFuncMask); + } + + if (New.StencilFrontOpFail != Old.StencilFrontOpFail || + New.StencilFrontOpZFail != Old.StencilFrontOpZFail || + New.StencilFrontOpZPass != Old.StencilFrontOpZPass) + { + GL.StencilOpSeparate( + StencilFace.Front, + OGLEnumConverter.GetStencilOp(New.StencilFrontOpFail), + OGLEnumConverter.GetStencilOp(New.StencilFrontOpZFail), + OGLEnumConverter.GetStencilOp(New.StencilFrontOpZPass)); + } + + if (New.StencilFrontMask != Old.StencilFrontMask) + { + GL.StencilMaskSeparate(StencilFace.Front, New.StencilFrontMask); + } + } + + if (New.BlendEnabled != Old.BlendEnabled) + { + Enable(EnableCap.Blend, New.BlendEnabled); + } + + if (New.BlendEnabled) + { + if (New.BlendSeparateAlpha) + { + if (New.BlendEquationRgb != Old.BlendEquationRgb || + New.BlendEquationAlpha != Old.BlendEquationAlpha) + { + GL.BlendEquationSeparate( + OGLEnumConverter.GetBlendEquation(New.BlendEquationRgb), + OGLEnumConverter.GetBlendEquation(New.BlendEquationAlpha)); + } + + if (New.BlendFuncSrcRgb != Old.BlendFuncSrcRgb || + New.BlendFuncDstRgb != Old.BlendFuncDstRgb || + New.BlendFuncSrcAlpha != Old.BlendFuncSrcAlpha || + New.BlendFuncDstAlpha != Old.BlendFuncDstAlpha) + { + GL.BlendFuncSeparate( + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.BlendFuncDstRgb), + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.BlendFuncDstAlpha)); + } + } + else + { + if (New.BlendEquationRgb != Old.BlendEquationRgb) + { + GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.BlendEquationRgb)); + } + + if (New.BlendFuncSrcRgb != Old.BlendFuncSrcRgb || + New.BlendFuncDstRgb != Old.BlendFuncDstRgb) + { + GL.BlendFunc( + OGLEnumConverter.GetBlendFactor(New.BlendFuncSrcRgb), + OGLEnumConverter.GetBlendFactor(New.BlendFuncDstRgb)); + } + } + } + + if (New.PrimitiveRestartEnabled != Old.PrimitiveRestartEnabled) + { + Enable(EnableCap.PrimitiveRestart, New.PrimitiveRestartEnabled); + } + + if (New.PrimitiveRestartEnabled) + { + if (New.PrimitiveRestartIndex != Old.PrimitiveRestartIndex) + { + GL.PrimitiveRestartIndex(New.PrimitiveRestartIndex); + } + } + + Old = New; + } + + private void BindConstBuffers(GalPipelineState New) + { + //Index 0 is reserved + int FreeBinding = 1; + + void BindIfNotNull(OGLShaderStage Stage) + { + if (Stage != null) + { + foreach (ShaderDeclInfo DeclInfo in Stage.UniformUsage) + { + long Key = New.ConstBufferKeys[(int)Stage.Type][DeclInfo.Cbuf]; + + if (Key != 0 && Buffer.TryGetUbo(Key, out int UboHandle)) + { + GL.BindBufferBase(BufferRangeTarget.UniformBuffer, FreeBinding, UboHandle); + } + + FreeBinding++; + } + } + } + + BindIfNotNull(Shader.Current.Vertex); + BindIfNotNull(Shader.Current.TessControl); + BindIfNotNull(Shader.Current.TessEvaluation); + BindIfNotNull(Shader.Current.Geometry); + BindIfNotNull(Shader.Current.Fragment); + } + + private void BindVertexLayout(GalPipelineState New) + { + foreach (GalVertexBinding Binding in New.VertexBindings) + { + if (!Binding.Enabled || !Rasterizer.TryGetVbo(Binding.VboKey, out int VboHandle)) + { + continue; + } + + if (VaoHandle == 0) + { + VaoHandle = GL.GenVertexArray(); + + //Vertex arrays shouldn't be used anywhere else in OpenGL's backend + //if you want to use it, move this line out of the if + GL.BindVertexArray(VaoHandle); + } + + foreach (GalVertexAttrib Attrib in Binding.Attribs) + { + GL.EnableVertexAttribArray(Attrib.Index); + + GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); + + bool Unsigned = + Attrib.Type == GalVertexAttribType.Unorm || + Attrib.Type == GalVertexAttribType.Uint || + Attrib.Type == GalVertexAttribType.Uscaled; + + bool Normalize = + Attrib.Type == GalVertexAttribType.Snorm || + Attrib.Type == GalVertexAttribType.Unorm; + + VertexAttribPointerType Type = 0; + + if (Attrib.Type == GalVertexAttribType.Float) + { + Type = VertexAttribPointerType.Float; + } + else + { + Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0); + } + + int Size = AttribElements[Attrib.Size]; + int Offset = Attrib.Offset; + + if (Attrib.Type == GalVertexAttribType.Sint || + Attrib.Type == GalVertexAttribType.Uint) + { + IntPtr Pointer = new IntPtr(Offset); + + VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + + GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer); + } + else + { + GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset); + } + } + } + } + + private void Enable(EnableCap Cap, bool Enabled) + { + if (Enabled) + { + GL.Enable(Cap); + } + else + { + GL.Disable(Cap); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index c5166b51..08021478 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -6,46 +6,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public class OGLRasterizer : IGalRasterizer { - private static Dictionary AttribElements = - new Dictionary() - { - { GalVertexAttribSize._32_32_32_32, 4 }, - { GalVertexAttribSize._32_32_32, 3 }, - { GalVertexAttribSize._16_16_16_16, 4 }, - { GalVertexAttribSize._32_32, 2 }, - { GalVertexAttribSize._16_16_16, 3 }, - { GalVertexAttribSize._8_8_8_8, 4 }, - { GalVertexAttribSize._16_16, 2 }, - { GalVertexAttribSize._32, 1 }, - { GalVertexAttribSize._8_8_8, 3 }, - { GalVertexAttribSize._8_8, 2 }, - { GalVertexAttribSize._16, 1 }, - { GalVertexAttribSize._8, 1 }, - { GalVertexAttribSize._10_10_10_2, 4 }, - { GalVertexAttribSize._11_11_10, 3 } - }; - - private static Dictionary AttribTypes = - new Dictionary() - { - { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int }, //? - { GalVertexAttribSize._11_11_10, VertexAttribPointerType.Int } //? - }; - - private int VaoHandle; - private int[] VertexBuffers; private OGLCachedResource VboCache; @@ -83,7 +43,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL IboCache.Unlock(); } - public void ClearBuffers(GalClearBufferFlags Flags) + public void ClearBuffers( + GalClearBufferFlags Flags, + float Red, float Green, float Blue, float Alpha, + float Depth, + int Stencil) { ClearBufferMask Mask = ClearBufferMask.ColorBufferBit; @@ -103,6 +67,12 @@ namespace Ryujinx.Graphics.Gal.OpenGL Mask |= ClearBufferMask.StencilBufferBit; } + GL.ClearColor(Red, Green, Blue, Alpha); + + GL.ClearDepth(Depth); + + GL.ClearStencil(Stencil); + GL.Clear(Mask); GL.ColorMask(true, true, true, true); @@ -118,99 +88,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL return IboCache.TryGetSize(Key, out long Size) && Size == DataSize; } - public void SetFrontFace(GalFrontFace FrontFace) - { - GL.FrontFace(OGLEnumConverter.GetFrontFace(FrontFace)); - } - - public void EnableCullFace() - { - GL.Enable(EnableCap.CullFace); - } - - public void DisableCullFace() - { - GL.Disable(EnableCap.CullFace); - } - - public void SetCullFace(GalCullFace CullFace) - { - GL.CullFace(OGLEnumConverter.GetCullFace(CullFace)); - } - - public void EnableDepthTest() - { - GL.Enable(EnableCap.DepthTest); - } - - public void DisableDepthTest() - { - GL.Disable(EnableCap.DepthTest); - } - - public void SetDepthFunction(GalComparisonOp Func) - { - GL.DepthFunc(OGLEnumConverter.GetDepthFunc(Func)); - } - - public void SetClearDepth(float Depth) - { - GL.ClearDepth(Depth); - } - - public void EnableStencilTest() - { - GL.Enable(EnableCap.StencilTest); - } - - public void DisableStencilTest() - { - GL.Disable(EnableCap.StencilTest); - } - - public void SetStencilFunction(bool IsFrontFace, GalComparisonOp Func, int Ref, int Mask) - { - GL.StencilFuncSeparate( - IsFrontFace ? StencilFace.Front : StencilFace.Back, - OGLEnumConverter.GetStencilFunc(Func), - Ref, - Mask); - } - - public void SetStencilOp(bool IsFrontFace, GalStencilOp Fail, GalStencilOp ZFail, GalStencilOp ZPass) - { - GL.StencilOpSeparate( - IsFrontFace ? StencilFace.Front : StencilFace.Back, - OGLEnumConverter.GetStencilOp(Fail), - OGLEnumConverter.GetStencilOp(ZFail), - OGLEnumConverter.GetStencilOp(ZPass)); - } - - public void SetStencilMask(bool IsFrontFace, int Mask) - { - GL.StencilMaskSeparate(IsFrontFace ? StencilFace.Front : StencilFace.Back, Mask); - } - - public void SetClearStencil(int Stencil) - { - GL.ClearStencil(Stencil); - } - - public void EnablePrimitiveRestart() - { - GL.Enable(EnableCap.PrimitiveRestart); - } - - public void DisablePrimitiveRestart() - { - GL.Disable(EnableCap.PrimitiveRestart); - } - - public void SetPrimitiveRestartIndex(uint Index) - { - GL.PrimitiveRestartIndex(Index); - } - public void CreateVbo(long Key, int DataSize, IntPtr HostAddress) { int Handle = GL.GenBuffer(); @@ -235,65 +112,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw); } - public void SetVertexArray(int Stride, long VboKey, GalVertexAttrib[] Attribs) - { - if (!VboCache.TryGetValue(VboKey, out int VboHandle)) - { - return; - } - - if (VaoHandle == 0) - { - VaoHandle = GL.GenVertexArray(); - } - - GL.BindVertexArray(VaoHandle); - - foreach (GalVertexAttrib Attrib in Attribs) - { - GL.EnableVertexAttribArray(Attrib.Index); - - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); - - bool Unsigned = - Attrib.Type == GalVertexAttribType.Unorm || - Attrib.Type == GalVertexAttribType.Uint || - Attrib.Type == GalVertexAttribType.Uscaled; - - bool Normalize = - Attrib.Type == GalVertexAttribType.Snorm || - Attrib.Type == GalVertexAttribType.Unorm; - - VertexAttribPointerType Type = 0; - - if (Attrib.Type == GalVertexAttribType.Float) - { - Type = VertexAttribPointerType.Float; - } - else - { - Type = AttribTypes[Attrib.Size] + (Unsigned ? 1 : 0); - } - - int Size = AttribElements[Attrib.Size]; - int Offset = Attrib.Offset; - - if (Attrib.Type == GalVertexAttribType.Sint || - Attrib.Type == GalVertexAttribType.Uint) - { - IntPtr Pointer = new IntPtr(Offset); - - VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; - - GL.VertexAttribIPointer(Attrib.Index, Size, IType, Stride, Pointer); - } - else - { - GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Stride, Offset); - } - } - } - public void SetIndexArray(int Size, GalIndexFormat Format) { IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format); @@ -310,8 +128,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL return; } - GL.BindVertexArray(VaoHandle); - GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, PrimCount); } @@ -324,8 +140,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL PrimitiveType Mode = OGLEnumConverter.GetPrimitiveType(PrimType); - GL.BindVertexArray(VaoHandle); - GL.BindBuffer(BufferTarget.ElementArrayBuffer, IboHandle); First <<= IndexBuffer.ElemSizeLog2; @@ -341,5 +155,10 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.DrawElements(Mode, IndexBuffer.Count, IndexBuffer.Type, First); } } + + public bool TryGetVbo(long VboKey, out int VboHandle) + { + return VboCache.TryGetValue(VboKey, out VboHandle); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs index ca70d4f6..b0f6da45 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderer.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { public class OGLRenderer : IGalRenderer { - public IGalBlend Blend { get; private set; } + public IGalConstBuffer Buffer { get; private set; } public IGalFrameBuffer FrameBuffer { get; private set; } @@ -13,19 +13,23 @@ namespace Ryujinx.Graphics.Gal.OpenGL public IGalShader Shader { get; private set; } + public IGalPipeline Pipeline { get; private set; } + public IGalTexture Texture { get; private set; } private ConcurrentQueue ActionsQueue; public OGLRenderer() { - Blend = new OGLBlend(); + Buffer = new OGLConstBuffer(); FrameBuffer = new OGLFrameBuffer(); Rasterizer = new OGLRasterizer(); - Shader = new OGLShader(); + Shader = new OGLShader(Buffer as OGLConstBuffer); + + Pipeline = new OGLPipeline(Buffer as OGLConstBuffer, Rasterizer as OGLRasterizer, Shader as OGLShader); Texture = new OGLTexture(); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 3f3f23b8..4792bc5e 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -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 Stages; - public GalShaderType Type { get; private set; } - - public string Code { get; private set; } - - public IEnumerable TextureUsage { get; private set; } - public IEnumerable UniformUsage { get; private set; } - - public ShaderStage( - GalShaderType Type, - string Code, - IEnumerable TextureUsage, - IEnumerable 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 Stages; - - private Dictionary Programs; + private Dictionary 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(); + this.Buffer = Buffer; - Programs = new Dictionary(); + Stages = new ConcurrentDictionary(); - ConstBuffers = new OGLStreamBuffer[5][]; - - for (int i = 0; i < 5; i++) - { - ConstBuffers[i] = new OGLStreamBuffer[ConstBuffersPerStage]; - } + Programs = new Dictionary(); } 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 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(); } - 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; diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs new file mode 100644 index 00000000..731994ce --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShaderProgram.cs @@ -0,0 +1,86 @@ +using OpenTK.Graphics.OpenGL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + struct OGLShaderProgram + { + public OGLShaderStage Vertex; + public OGLShaderStage TessControl; + public OGLShaderStage TessEvaluation; + public OGLShaderStage Geometry; + public OGLShaderStage Fragment; + } + + class OGLShaderStage : IDisposable + { + public int Handle { get; private set; } + + public bool IsCompiled { get; private set; } + + public GalShaderType Type { get; private set; } + + public string Code { get; private set; } + + public IEnumerable TextureUsage { get; private set; } + public IEnumerable UniformUsage { get; private set; } + + public OGLShaderStage( + GalShaderType Type, + string Code, + IEnumerable TextureUsage, + IEnumerable 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; + } + } + + 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)); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLStreamBuffer.cs index 0d5dee93..94639405 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.Graphics/Gal/Shader/GlslDecl.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs index 7688545c..a0c747ba 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecl.cs @@ -35,6 +35,7 @@ namespace Ryujinx.Graphics.Gal.Shader public const string FragmentOutputName = "FragColor"; + public const string ExtraUniformBlockName = "Extra"; public const string FlipUniformName = "flip"; public const string ProgramName = "program"; diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 5261d677..aa1803a5 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -220,7 +220,11 @@ namespace Ryujinx.Graphics.Gal.Shader { if (Decl.ShaderType == GalShaderType.Vertex) { - SB.AppendLine("uniform vec2 " + GlslDecl.FlipUniformName + ";"); + SB.AppendLine("layout (std140) uniform " + GlslDecl.ExtraUniformBlockName + "{"); + + SB.AppendLine(IdentationStr + "vec2 " + GlslDecl.FlipUniformName + ";"); + + SB.AppendLine("};"); } SB.AppendLine(); diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs index 3dd0e98e..0576601f 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3d.cs @@ -84,35 +84,41 @@ namespace Ryujinx.HLE.Gpu.Engines { LockCaches(); + GalPipelineState State = new GalPipelineState(); + + SetFlip(State); + SetFrontFace(State); + SetCullFace(State); + SetDepth(State); + SetStencil(State); + SetAlphaBlending(State); + SetPrimitiveRestart(State); + SetFrameBuffer(Vmm, 0); long[] Keys = UploadShaders(Vmm); Gpu.Renderer.Shader.BindProgram(); - //Note: Uncomment SetFrontFace SetCullFace when flipping issues are solved - //SetFrontFace(); - //SetCullFace(); - SetDepth(); - SetStencil(); - SetAlphaBlending(); - SetPrimitiveRestart(); + UploadTextures(Vmm, State, Keys); + UploadConstBuffers(Vmm, State); + UploadVertexArrays(Vmm, State); - UploadTextures(Vmm, Keys); - UploadUniforms(Vmm); - UploadVertexArrays(Vmm); + DispatchRender(Vmm, State); UnlockCaches(); } 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(); } @@ -125,9 +131,22 @@ namespace Ryujinx.HLE.Gpu.Engines GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); + float Red = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 0); + float Green = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 1); + float Blue = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 2); + float Alpha = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 3); + + float Depth = ReadRegisterFloat(NvGpuEngine3dReg.ClearDepth); + + int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); + SetFrameBuffer(Vmm, FbIndex); - Gpu.Renderer.Rasterizer.ClearBuffers(Flags); + Gpu.Renderer.Rasterizer.ClearBuffers( + Flags, + Red, Green, Blue, Alpha, + Depth, + Stencil); } private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) @@ -185,6 +204,8 @@ namespace Ryujinx.HLE.Gpu.Engines long VpAPos = BasePosition + (uint)VpAOffset; long VpBPos = BasePosition + (uint)VpBOffset; + Keys[(int)GalShaderType.Vertex] = VpBPos; + Gpu.Renderer.Shader.Create(Vmm, VpAPos, VpBPos, GalShaderType.Vertex); Gpu.Renderer.Shader.Bind(VpBPos); @@ -216,11 +237,6 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Shader.Bind(Key); } - float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); - float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); - - Gpu.Renderer.Shader.SetFlip(SignX, SignY); - return Keys; } @@ -239,7 +255,13 @@ namespace Ryujinx.HLE.Gpu.Engines throw new ArgumentOutOfRangeException(nameof(Program)); } - private void SetFrontFace() + private void SetFlip(GalPipelineState State) + { + State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); + State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); + } + + private void SetFrontFace(GalPipelineState State) { float SignX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); float SignY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); @@ -261,198 +283,82 @@ namespace Ryujinx.HLE.Gpu.Engines } } - Gpu.Renderer.Rasterizer.SetFrontFace(FrontFace); + State.FrontFace = FrontFace; } - private void SetCullFace() + private void SetCullFace(GalPipelineState State) { - bool Enable = (ReadRegister(NvGpuEngine3dReg.CullFaceEnable) & 1) != 0; + State.CullFaceEnabled = (ReadRegister(NvGpuEngine3dReg.CullFaceEnable) & 1) != 0; - if (Enable) + if (State.CullFaceEnabled) { - Gpu.Renderer.Rasterizer.EnableCullFace(); + State.CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace); } - else - { - Gpu.Renderer.Rasterizer.DisableCullFace(); - } - - if (!Enable) - { - return; - } - - GalCullFace CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace); - - Gpu.Renderer.Rasterizer.SetCullFace(CullFace); } - private void SetDepth() + private void SetDepth(GalPipelineState State) { - float ClearDepth = ReadRegisterFloat(NvGpuEngine3dReg.ClearDepth); + State.DepthTestEnabled = (ReadRegister(NvGpuEngine3dReg.DepthTestEnable) & 1) != 0; - Gpu.Renderer.Rasterizer.SetClearDepth(ClearDepth); - - bool Enable = (ReadRegister(NvGpuEngine3dReg.DepthTestEnable) & 1) != 0; - - if (Enable) + if (State.DepthTestEnabled) { - Gpu.Renderer.Rasterizer.EnableDepthTest(); + State.DepthFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); } - else - { - Gpu.Renderer.Rasterizer.DisableDepthTest(); - } - - if (!Enable) - { - return; - } - - GalComparisonOp Func = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); - - Gpu.Renderer.Rasterizer.SetDepthFunction(Func); } - private void SetStencil() + private void SetStencil(GalPipelineState State) { - int ClearStencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); - - Gpu.Renderer.Rasterizer.SetClearStencil(ClearStencil); - - bool Enable = (ReadRegister(NvGpuEngine3dReg.StencilEnable) & 1) != 0; - - if (Enable) + State.StencilTestEnabled = (ReadRegister(NvGpuEngine3dReg.StencilEnable) & 1) != 0; + + if (State.StencilTestEnabled) { - Gpu.Renderer.Rasterizer.EnableStencilTest(); + State.StencilBackFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilBackFuncFunc); + State.StencilBackFuncRef = ReadRegister(NvGpuEngine3dReg.StencilBackFuncRef); + State.StencilBackFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackFuncMask); + State.StencilBackOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpFail); + State.StencilBackOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZFail); + State.StencilBackOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZPass); + State.StencilBackMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackMask); + + State.StencilFrontFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncFunc); + State.StencilFrontFuncRef = ReadRegister(NvGpuEngine3dReg.StencilFrontFuncRef); + State.StencilFrontFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncMask); + State.StencilFrontOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpFail); + State.StencilFrontOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZFail); + State.StencilFrontOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZPass); + State.StencilFrontMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontMask); } - else - { - Gpu.Renderer.Rasterizer.DisableStencilTest(); - } - - if (!Enable) - { - return; - } - - void SetFaceStencil( - bool IsFrontFace, - NvGpuEngine3dReg Func, - NvGpuEngine3dReg FuncRef, - NvGpuEngine3dReg FuncMask, - NvGpuEngine3dReg OpFail, - NvGpuEngine3dReg OpZFail, - NvGpuEngine3dReg OpZPass, - NvGpuEngine3dReg Mask) - { - Gpu.Renderer.Rasterizer.SetStencilFunction( - IsFrontFace, - (GalComparisonOp)ReadRegister(Func), - ReadRegister(FuncRef), - ReadRegister(FuncMask)); - - Gpu.Renderer.Rasterizer.SetStencilOp( - IsFrontFace, - (GalStencilOp)ReadRegister(OpFail), - (GalStencilOp)ReadRegister(OpZFail), - (GalStencilOp)ReadRegister(OpZPass)); - - Gpu.Renderer.Rasterizer.SetStencilMask(IsFrontFace, ReadRegister(Mask)); - } - - SetFaceStencil(false, - NvGpuEngine3dReg.StencilBackFuncFunc, - NvGpuEngine3dReg.StencilBackFuncRef, - NvGpuEngine3dReg.StencilBackFuncMask, - NvGpuEngine3dReg.StencilBackOpFail, - NvGpuEngine3dReg.StencilBackOpZFail, - NvGpuEngine3dReg.StencilBackOpZPass, - NvGpuEngine3dReg.StencilBackMask); - - SetFaceStencil(true, - NvGpuEngine3dReg.StencilFrontFuncFunc, - NvGpuEngine3dReg.StencilFrontFuncRef, - NvGpuEngine3dReg.StencilFrontFuncMask, - NvGpuEngine3dReg.StencilFrontOpFail, - NvGpuEngine3dReg.StencilFrontOpZFail, - NvGpuEngine3dReg.StencilFrontOpZPass, - NvGpuEngine3dReg.StencilFrontMask); } - private void SetAlphaBlending() + private void SetAlphaBlending(GalPipelineState State) { //TODO: Support independent blend properly. - bool Enable = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; + State.BlendEnabled = (ReadRegister(NvGpuEngine3dReg.IBlendNEnable) & 1) != 0; - if (Enable) + if (State.BlendEnabled) { - Gpu.Renderer.Blend.Enable(); - } - else - { - Gpu.Renderer.Blend.Disable(); - } + State.BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0; - if (!Enable) - { - //If blend is not enabled, then the other values have no effect. - //Note that if it is disabled, the register may contain invalid values. - return; - } - - bool BlendSeparateAlpha = (ReadRegister(NvGpuEngine3dReg.IBlendNSeparateAlpha) & 1) != 0; - - GalBlendEquation EquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb); - - GalBlendFactor FuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb); - GalBlendFactor FuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb); - - if (BlendSeparateAlpha) - { - GalBlendEquation EquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha); - - GalBlendFactor FuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); - GalBlendFactor FuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); - - Gpu.Renderer.Blend.SetSeparate( - EquationRgb, - EquationAlpha, - FuncSrcRgb, - FuncDstRgb, - FuncSrcAlpha, - FuncDstAlpha); - } - else - { - Gpu.Renderer.Blend.Set(EquationRgb, FuncSrcRgb, FuncDstRgb); + State.BlendEquationRgb = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationRgb); + State.BlendFuncSrcRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb); + State.BlendFuncDstRgb = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb); + State.BlendEquationAlpha = (GalBlendEquation)ReadRegister(NvGpuEngine3dReg.IBlendNEquationAlpha); + State.BlendFuncSrcAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha); + State.BlendFuncDstAlpha = (GalBlendFactor)ReadRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha); } } - private void SetPrimitiveRestart() + private void SetPrimitiveRestart(GalPipelineState State) { - bool Enable = (ReadRegister(NvGpuEngine3dReg.PrimRestartEnable) & 1) != 0; + State.PrimitiveRestartEnabled = (ReadRegister(NvGpuEngine3dReg.PrimRestartEnable) & 1) != 0; - if (Enable) + if (State.PrimitiveRestartEnabled) { - Gpu.Renderer.Rasterizer.EnablePrimitiveRestart(); + State.PrimitiveRestartIndex = (uint)ReadRegister(NvGpuEngine3dReg.PrimRestartIndex); } - else - { - Gpu.Renderer.Rasterizer.DisablePrimitiveRestart(); - } - - if (!Enable) - { - return; - } - - uint Index = (uint)ReadRegister(NvGpuEngine3dReg.PrimRestartIndex); - - Gpu.Renderer.Rasterizer.SetPrimitiveRestartIndex(Index); } - private void UploadTextures(NvGpuVmm Vmm, long[] Keys) + private void UploadTextures(NvGpuVmm Vmm, GalPipelineState State, long[] Keys) { long BaseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); @@ -546,45 +452,35 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Texture.SetSampler(Sampler); } - private void UploadUniforms(NvGpuVmm Vmm) + private void UploadConstBuffers(NvGpuVmm Vmm, GalPipelineState State) { - long BasePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - - for (int Index = 0; Index < 5; Index++) + for (int Stage = 0; Stage < State.ConstBufferKeys.Length; 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 < State.ConstBufferKeys[Stage].Length; Index++) { - continue; - } + ConstBuffer Cb = ConstBuffers[Stage][Index]; - for (int Cbuf = 0; Cbuf < ConstBuffers[Index].Length; Cbuf++) - { - ConstBuffer Cb = ConstBuffers[Index][Cbuf]; + long Key = Cb.Position; - if (Cb.Enabled) + if (Cb.Enabled && QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) { - IntPtr DataAddress = Vmm.GetHostAddress(Cb.Position, Cb.Size); + IntPtr Source = Vmm.GetHostAddress(Key, Cb.Size); - Gpu.Renderer.Shader.SetConstBuffer(BasePosition + (uint)Offset, Cbuf, Cb.Size, DataAddress); + Gpu.Renderer.Buffer.SetData(Key, Cb.Size, Source); } + + State.ConstBufferKeys[Stage][Index] = Key; } } } - private void UploadVertexArrays(NvGpuVmm Vmm) + private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State) { long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); long IboKey = Vmm.GetPhysicalAddress(IndexPosition); int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); - int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt; @@ -634,10 +530,7 @@ namespace Ryujinx.HLE.Gpu.Engines ((Packed >> 31) & 0x1) != 0)); } - int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); - int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); - - int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); + State.VertexBindings = new GalVertexBinding[32]; for (int Index = 0; Index < 32; Index++) { @@ -650,14 +543,14 @@ namespace Ryujinx.HLE.Gpu.Engines bool Enable = (Control & 0x1000) != 0; - long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); - long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2); - if (!Enable) { continue; } + long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + Index * 4); + long VertexEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + Index * 2); + long VboKey = Vmm.GetPhysicalAddress(VertexPosition); int Stride = Control & 0xfff; @@ -673,19 +566,39 @@ namespace Ryujinx.HLE.Gpu.Engines Gpu.Renderer.Rasterizer.CreateVbo(VboKey, (int)VbSize, DataAddress); } - Gpu.Renderer.Rasterizer.SetVertexArray(Stride, VboKey, Attribs[Index].ToArray()); + State.VertexBindings[Index].Enabled = true; + State.VertexBindings[Index].Stride = Stride; + State.VertexBindings[Index].VboKey = VboKey; + State.VertexBindings[Index].Attribs = Attribs[Index].ToArray(); } + } + + private void DispatchRender(NvGpuVmm Vmm, GalPipelineState State) + { + int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); + int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); + Gpu.Renderer.Pipeline.Bind(State); + if (IndexCount != 0) { - int VertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); + int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); + int IndexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); + int VertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); + + long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); + + long IboKey = Vmm.GetPhysicalAddress(IndexPosition); Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType); } else { + int VertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); + int VertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); + Gpu.Renderer.Rasterizer.DrawArrays(VertexFirst, VertexCount, PrimType); } } @@ -741,10 +654,21 @@ 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) + { + 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/Engines/NvGpuEngine3dReg.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs index e7dabe44..39a5ee8c 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuEngine3dReg.cs @@ -16,6 +16,7 @@ namespace Ryujinx.HLE.Gpu.Engines ViewportNVert = 0x301, VertexArrayFirst = 0x35d, VertexArrayCount = 0x35e, + ClearNColor = 0x360, ClearDepth = 0x364, ClearStencil = 0x368, StencilBackFuncRef = 0x3d5, diff --git a/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs b/Ryujinx.HLE/Gpu/Memory/NvGpuBufferType.cs index 469cd6cd..a6c03f42 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