From 44c1cf3fe5a3886f34251f93fbcfc3b9c8f0e164 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 1 Nov 2018 01:22:24 -0300 Subject: [PATCH] Implment common and independent blend properly (fixes #458) (#485) * Implment common and independent blend properly * Nits --- Ryujinx.Graphics/Gal/GalBlendEquation.cs | 8 +- Ryujinx.Graphics/Gal/GalBlendFactor.cs | 21 +- Ryujinx.Graphics/Gal/GalPipelineState.cs | 50 +++-- .../Gal/OpenGL/OGLEnumConverter.cs | 162 ++++++++++---- Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs | 207 +++++++++++++----- Ryujinx.Graphics/NvGpuEngine3d.cs | 65 ++++-- Ryujinx.Graphics/NvGpuEngine3dReg.cs | 4 +- 7 files changed, 389 insertions(+), 128 deletions(-) diff --git a/Ryujinx.Graphics/Gal/GalBlendEquation.cs b/Ryujinx.Graphics/Gal/GalBlendEquation.cs index 7fd4ba5fa..7757faae9 100644 --- a/Ryujinx.Graphics/Gal/GalBlendEquation.cs +++ b/Ryujinx.Graphics/Gal/GalBlendEquation.cs @@ -6,6 +6,12 @@ namespace Ryujinx.Graphics.Gal FuncSubtract = 2, FuncReverseSubtract = 3, Min = 4, - Max = 5 + Max = 5, + + FuncAddGl = 0x8006, + FuncSubtractGl = 0x8007, + FuncReverseSubtractGl = 0x8008, + MinGl = 0x800a, + MaxGl = 0x800b } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalBlendFactor.cs b/Ryujinx.Graphics/Gal/GalBlendFactor.cs index 001aaaeca..f70b05011 100644 --- a/Ryujinx.Graphics/Gal/GalBlendFactor.cs +++ b/Ryujinx.Graphics/Gal/GalBlendFactor.cs @@ -21,6 +21,25 @@ namespace Ryujinx.Graphics.Gal OneMinusConstantColor = 0x62, ConstantAlpha = 0x63, OneMinusConstantAlpha = 0x64, - ConstantColorG80 = 0xc001 + + ZeroGl = 0x4000, + OneGl = 0x4001, + SrcColorGl = 0x4300, + OneMinusSrcColorGl = 0x4301, + SrcAlphaGl = 0x4302, + OneMinusSrcAlphaGl = 0x4303, + DstAlphaGl = 0x4304, + OneMinusDstAlphaGl = 0x4305, + DstColorGl = 0x4306, + OneMinusDstColorGl = 0x4307, + SrcAlphaSaturateGl = 0x4308, + ConstantColorGl = 0xc001, + OneMinusConstantColorGl = 0xc002, + ConstantAlphaGl = 0xc003, + OneMinusConstantAlphaGl = 0xc004, + Src1ColorGl = 0xc900, + OneMinusSrc1ColorGl = 0xc901, + Src1AlphaGl = 0xc902, + OneMinusSrc1AlphaGl = 0xc903 } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs index f50f10322..793fb395b 100644 --- a/Ryujinx.Graphics/Gal/GalPipelineState.cs +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -1,8 +1,8 @@ namespace Ryujinx.Graphics.Gal { - public struct ColorMaskRgba + public struct ColorMaskState { - private static readonly ColorMaskRgba _Default = new ColorMaskRgba() + private static readonly ColorMaskState _Default = new ColorMaskState() { Red = true, Green = true, @@ -10,7 +10,7 @@ Alpha = true }; - public static ColorMaskRgba Default => _Default; + public static ColorMaskState Default => _Default; public bool Red; public bool Green; @@ -18,6 +18,32 @@ public bool Alpha; } + public struct BlendState + { + private static readonly BlendState _Default = new BlendState() + { + Enabled = false, + SeparateAlpha = false, + EquationRgb = 0, + FuncSrcRgb = GalBlendFactor.One, + FuncDstRgb = GalBlendFactor.Zero, + EquationAlpha = 0, + FuncSrcAlpha = GalBlendFactor.One, + FuncDstAlpha = GalBlendFactor.Zero + }; + + public static BlendState Default => _Default; + + public bool Enabled; + public bool SeparateAlpha; + public GalBlendEquation EquationRgb; + public GalBlendFactor FuncSrcRgb; + public GalBlendFactor FuncDstRgb; + public GalBlendEquation EquationAlpha; + public GalBlendFactor FuncSrcAlpha; + public GalBlendFactor FuncDstAlpha; + } + public class GalPipelineState { public const int Stages = 5; @@ -65,17 +91,11 @@ 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 BlendIndependent; + public BlendState[] Blends; - public bool ColorMaskCommon; - public ColorMaskRgba[] ColorMasks; + public bool ColorMaskCommon; + public ColorMaskState[] ColorMasks; public bool PrimitiveRestartEnabled; public uint PrimitiveRestartIndex; @@ -89,7 +109,9 @@ ConstBufferKeys[Stage] = new long[ConstBuffersPerStage]; } - ColorMasks = new ColorMaskRgba[RenderTargetsCount]; + Blends = new BlendState[RenderTargetsCount]; + + ColorMasks = new ColorMaskState[RenderTargetsCount]; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 6b3d4fd69..491651012 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -47,32 +47,35 @@ namespace Ryujinx.Graphics.Gal.OpenGL public static DepthFunction GetDepthFunc(GalComparisonOp Func) { - //Looks like the GPU can take it's own values (described in GalComparisonOp) and OpenGL values alike - if ((int)Func >= (int)DepthFunction.Never && - (int)Func <= (int)DepthFunction.Always) - { - return (DepthFunction)Func; - } - - switch (Func) - { - case GalComparisonOp.Never: return DepthFunction.Never; - case GalComparisonOp.Less: return DepthFunction.Less; - case GalComparisonOp.Equal: return DepthFunction.Equal; - case GalComparisonOp.Lequal: return DepthFunction.Lequal; - case GalComparisonOp.Greater: return DepthFunction.Greater; - case GalComparisonOp.NotEqual: return DepthFunction.Notequal; - case GalComparisonOp.Gequal: return DepthFunction.Gequal; - case GalComparisonOp.Always: return DepthFunction.Always; - } - - throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!"); + return (DepthFunction)GetFunc(Func); } public static StencilFunction GetStencilFunc(GalComparisonOp Func) { - //OGL comparison values match, it's just an enum cast - return (StencilFunction)GetDepthFunc(Func); + return (StencilFunction)GetFunc(Func); + } + + private static All GetFunc(GalComparisonOp Func) + { + if ((int)Func >= (int)All.Never && + (int)Func <= (int)All.Always) + { + return (All)Func; + } + + switch (Func) + { + case GalComparisonOp.Never: return All.Never; + case GalComparisonOp.Less: return All.Less; + case GalComparisonOp.Equal: return All.Equal; + case GalComparisonOp.Lequal: return All.Lequal; + case GalComparisonOp.Greater: return All.Greater; + case GalComparisonOp.NotEqual: return All.Notequal; + case GalComparisonOp.Gequal: return All.Gequal; + case GalComparisonOp.Always: return All.Always; + } + + throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!"); } public static DrawElementsType GetDrawElementsType(GalIndexFormat Format) @@ -282,11 +285,25 @@ namespace Ryujinx.Graphics.Gal.OpenGL { switch (BlendEquation) { - case GalBlendEquation.FuncAdd: return BlendEquationMode.FuncAdd; - case GalBlendEquation.FuncSubtract: return BlendEquationMode.FuncSubtract; - case GalBlendEquation.FuncReverseSubtract: return BlendEquationMode.FuncReverseSubtract; - case GalBlendEquation.Min: return BlendEquationMode.Min; - case GalBlendEquation.Max: return BlendEquationMode.Max; + case GalBlendEquation.FuncAdd: + case GalBlendEquation.FuncAddGl: + return BlendEquationMode.FuncAdd; + + case GalBlendEquation.FuncSubtract: + case GalBlendEquation.FuncSubtractGl: + return BlendEquationMode.FuncSubtract; + + case GalBlendEquation.FuncReverseSubtract: + case GalBlendEquation.FuncReverseSubtractGl: + return BlendEquationMode.FuncReverseSubtract; + + case GalBlendEquation.Min: + case GalBlendEquation.MinGl: + return BlendEquationMode.Min; + + case GalBlendEquation.Max: + case GalBlendEquation.MaxGl: + return BlendEquationMode.Max; } throw new ArgumentException(nameof(BlendEquation) + " \"" + BlendEquation + "\" is not valid!"); @@ -296,27 +313,80 @@ namespace Ryujinx.Graphics.Gal.OpenGL { switch (BlendFactor) { - case GalBlendFactor.Zero: return BlendingFactor.Zero; - case GalBlendFactor.One: return BlendingFactor.One; - case GalBlendFactor.SrcColor: return BlendingFactor.SrcColor; - case GalBlendFactor.OneMinusSrcColor: return BlendingFactor.OneMinusSrcColor; - case GalBlendFactor.DstColor: return BlendingFactor.DstColor; - case GalBlendFactor.OneMinusDstColor: return BlendingFactor.OneMinusDstColor; - case GalBlendFactor.SrcAlpha: return BlendingFactor.SrcAlpha; - case GalBlendFactor.OneMinusSrcAlpha: return BlendingFactor.OneMinusSrcAlpha; - case GalBlendFactor.DstAlpha: return BlendingFactor.DstAlpha; - case GalBlendFactor.OneMinusDstAlpha: return BlendingFactor.OneMinusDstAlpha; - case GalBlendFactor.OneMinusConstantColor: return BlendingFactor.OneMinusConstantColor; - case GalBlendFactor.ConstantAlpha: return BlendingFactor.ConstantAlpha; - case GalBlendFactor.OneMinusConstantAlpha: return BlendingFactor.OneMinusConstantAlpha; - case GalBlendFactor.SrcAlphaSaturate: return BlendingFactor.SrcAlphaSaturate; - case GalBlendFactor.Src1Color: return BlendingFactor.Src1Color; - case GalBlendFactor.OneMinusSrc1Color: return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Color; - case GalBlendFactor.Src1Alpha: return BlendingFactor.Src1Alpha; - case GalBlendFactor.OneMinusSrc1Alpha: return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Alpha; + case GalBlendFactor.Zero: + case GalBlendFactor.ZeroGl: + return BlendingFactor.Zero; + + case GalBlendFactor.One: + case GalBlendFactor.OneGl: + return BlendingFactor.One; + + case GalBlendFactor.SrcColor: + case GalBlendFactor.SrcColorGl: + return BlendingFactor.SrcColor; + + case GalBlendFactor.OneMinusSrcColor: + case GalBlendFactor.OneMinusSrcColorGl: + return BlendingFactor.OneMinusSrcColor; + + case GalBlendFactor.DstColor: + case GalBlendFactor.DstColorGl: + return BlendingFactor.DstColor; + + case GalBlendFactor.OneMinusDstColor: + case GalBlendFactor.OneMinusDstColorGl: + return BlendingFactor.OneMinusDstColor; + + case GalBlendFactor.SrcAlpha: + case GalBlendFactor.SrcAlphaGl: + return BlendingFactor.SrcAlpha; + + case GalBlendFactor.OneMinusSrcAlpha: + case GalBlendFactor.OneMinusSrcAlphaGl: + return BlendingFactor.OneMinusSrcAlpha; + + case GalBlendFactor.DstAlpha: + case GalBlendFactor.DstAlphaGl: + return BlendingFactor.DstAlpha; + + case GalBlendFactor.OneMinusDstAlpha: + case GalBlendFactor.OneMinusDstAlphaGl: + return BlendingFactor.OneMinusDstAlpha; + + case GalBlendFactor.OneMinusConstantColor: + case GalBlendFactor.OneMinusConstantColorGl: + return BlendingFactor.OneMinusConstantColor; + + case GalBlendFactor.ConstantAlpha: + case GalBlendFactor.ConstantAlphaGl: + return BlendingFactor.ConstantAlpha; + + case GalBlendFactor.OneMinusConstantAlpha: + case GalBlendFactor.OneMinusConstantAlphaGl: + return BlendingFactor.OneMinusConstantAlpha; + + case GalBlendFactor.SrcAlphaSaturate: + case GalBlendFactor.SrcAlphaSaturateGl: + return BlendingFactor.SrcAlphaSaturate; + + case GalBlendFactor.Src1Color: + case GalBlendFactor.Src1ColorGl: + return BlendingFactor.Src1Color; + + case GalBlendFactor.OneMinusSrc1Color: + case GalBlendFactor.OneMinusSrc1ColorGl: + return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Color; + + case GalBlendFactor.Src1Alpha: + case GalBlendFactor.Src1AlphaGl: + return BlendingFactor.Src1Alpha; + + case GalBlendFactor.OneMinusSrc1Alpha: + case GalBlendFactor.OneMinusSrc1AlphaGl: + return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Alpha; case GalBlendFactor.ConstantColor: - case GalBlendFactor.ConstantColorG80: + case GalBlendFactor.ConstantColorGl: return BlendingFactor.ConstantColor; } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index 6e1cabbab..e81cf8a38 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -121,15 +121,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL 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, + BlendIndependent = false, PrimitiveRestartEnabled = false, PrimitiveRestartIndex = 0 @@ -137,7 +129,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) { - Old.ColorMasks[Index] = ColorMaskRgba.Default; + Old.Blends[Index] = BlendState.Default; + + Old.ColorMasks[Index] = ColorMaskState.Default; } } @@ -268,49 +262,22 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - if (New.BlendEnabled != Old.BlendEnabled) + if (New.BlendIndependent) { - Enable(EnableCap.Blend, New.BlendEnabled); - } - - if (New.BlendEnabled) - { - if (New.BlendSeparateAlpha) + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) { - 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)); - } + SetBlendState(Index, New.Blends[Index], Old.Blends[Index]); + } + } + else + { + if (New.BlendIndependent != Old.BlendIndependent) + { + SetAllBlendState(New.Blends[0]); } 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)); - } + SetBlendState(New.Blends[0], Old.Blends[0]); } } @@ -357,6 +324,136 @@ namespace Ryujinx.Graphics.Gal.OpenGL Old = New; } + private void SetAllBlendState(BlendState New) + { + Enable(EnableCap.Blend, New.Enabled); + + if (New.Enabled) + { + if (New.SeparateAlpha) + { + GL.BlendEquationSeparate( + OGLEnumConverter.GetBlendEquation(New.EquationRgb), + OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + + GL.BlendFuncSeparate( + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + } + else + { + GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + + GL.BlendFunc( + OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + } + } + } + + private void SetBlendState(BlendState New, BlendState Old) + { + if (New.Enabled != Old.Enabled) + { + Enable(EnableCap.Blend, New.Enabled); + } + + if (New.Enabled) + { + if (New.SeparateAlpha) + { + if (New.EquationRgb != Old.EquationRgb || + New.EquationAlpha != Old.EquationAlpha) + { + GL.BlendEquationSeparate( + OGLEnumConverter.GetBlendEquation(New.EquationRgb), + OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + } + + if (New.FuncSrcRgb != Old.FuncSrcRgb || + New.FuncDstRgb != Old.FuncDstRgb || + New.FuncSrcAlpha != Old.FuncSrcAlpha || + New.FuncDstAlpha != Old.FuncDstAlpha) + { + GL.BlendFuncSeparate( + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + } + } + else + { + if (New.EquationRgb != Old.EquationRgb) + { + GL.BlendEquation(OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + } + + if (New.FuncSrcRgb != Old.FuncSrcRgb || + New.FuncDstRgb != Old.FuncDstRgb) + { + GL.BlendFunc( + OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + } + } + } + } + + private void SetBlendState(int Index, BlendState New, BlendState Old) + { + if (New.Enabled != Old.Enabled) + { + Enable(IndexedEnableCap.Blend, Index, New.Enabled); + } + + if (New.Enabled) + { + if (New.SeparateAlpha) + { + if (New.EquationRgb != Old.EquationRgb || + New.EquationAlpha != Old.EquationAlpha) + { + GL.BlendEquationSeparate( + Index, + OGLEnumConverter.GetBlendEquation(New.EquationRgb), + OGLEnumConverter.GetBlendEquation(New.EquationAlpha)); + } + + if (New.FuncSrcRgb != Old.FuncSrcRgb || + New.FuncDstRgb != Old.FuncDstRgb || + New.FuncSrcAlpha != Old.FuncSrcAlpha || + New.FuncDstAlpha != Old.FuncDstAlpha) + { + GL.BlendFuncSeparate( + Index, + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb), + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcAlpha), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstAlpha)); + } + } + else + { + if (New.EquationRgb != Old.EquationRgb) + { + GL.BlendEquation(Index, OGLEnumConverter.GetBlendEquation(New.EquationRgb)); + } + + if (New.FuncSrcRgb != Old.FuncSrcRgb || + New.FuncDstRgb != Old.FuncDstRgb) + { + GL.BlendFunc( + Index, + (BlendingFactorSrc) OGLEnumConverter.GetBlendFactor(New.FuncSrcRgb), + (BlendingFactorDest)OGLEnumConverter.GetBlendFactor(New.FuncDstRgb)); + } + } + } + } + private void BindConstBuffers(GalPipelineState New) { int FreeBinding = OGLShader.ReservedCbufCount; @@ -640,6 +737,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + private void Enable(IndexedEnableCap Cap, int Index, bool Enabled) + { + if (Enabled) + { + GL.Enable(Cap, Index); + } + else + { + GL.Disable(Cap, Index); + } + } + public void ResetDepthMask() { Old.DepthWriteEnabled = true; @@ -647,7 +756,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void ResetColorMask(int Index) { - Old.ColorMasks[Index] = ColorMaskRgba.Default; + Old.ColorMasks[Index] = ColorMaskState.Default; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs index 618d7d9fa..f4a453472 100644 --- a/Ryujinx.Graphics/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/NvGpuEngine3d.cs @@ -407,22 +407,59 @@ namespace Ryujinx.Graphics private void SetBlending(GalPipelineState State) { - //TODO: Support independent blend properly. - State.BlendEnabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable); + bool BlendIndependent = ReadRegisterBool(NvGpuEngine3dReg.BlendIndependent); - if (State.BlendEnabled) + State.BlendIndependent = BlendIndependent; + + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) { - State.BlendSeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.IBlendNSeparateAlpha); + if (BlendIndependent) + { + State.Blends[Index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable + Index); - 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); + if (State.Blends[Index].Enabled) + { + State.Blends[Index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.IBlendNSeparateAlpha + Index * 8); + + State.Blends[Index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationRgb + Index * 8); + State.Blends[Index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcRgb + Index * 8); + State.Blends[Index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstRgb + Index * 8); + State.Blends[Index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationAlpha + Index * 8); + State.Blends[Index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcAlpha + Index * 8); + State.Blends[Index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstAlpha + Index * 8); + } + } + else + { + //It seems that even when independent blend is disabled, the first IBlend enable + //register is still set to indicate whenever blend is enabled or not (?). + State.Blends[Index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable); + + if (State.Blends[Index].Enabled) + { + State.Blends[Index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.BlendSeparateAlpha); + + State.Blends[Index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationRgb); + State.Blends[Index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcRgb); + State.Blends[Index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstRgb); + State.Blends[Index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationAlpha); + State.Blends[Index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcAlpha); + State.Blends[Index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstAlpha); + } + } } } + private GalBlendEquation ReadBlendEquation(NvGpuEngine3dReg Register) + { + return (GalBlendEquation)ReadRegister(Register); + } + + private GalBlendFactor ReadBlendFactor(NvGpuEngine3dReg Register) + { + return (GalBlendFactor)ReadRegister(Register); + } + private void SetColorMask(GalPipelineState State) { bool ColorMaskCommon = ReadRegisterBool(NvGpuEngine3dReg.ColorMaskCommon); @@ -514,10 +551,8 @@ namespace Ryujinx.Graphics { if (TextureHandle == 0) { - //TODO: Is this correct? - //Some games like puyo puyo will have 0 handles. - //It may be just normal behaviour or a bug caused by sync issues. - //The game does initialize the value properly after through. + //FIXME: Some games like puyo puyo will use handles with the value 0. + //This is a bug, most likely caused by sync issues. return; } @@ -603,7 +638,7 @@ namespace Ryujinx.Graphics if (IndexEntrySize > 4) { - throw new InvalidOperationException(); + throw new InvalidOperationException("Invalid index entry size \"" + IndexEntrySize + "\"!"); } if (IndexCount != 0) diff --git a/Ryujinx.Graphics/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/NvGpuEngine3dReg.cs index 6c522eac5..bd61602bf 100644 --- a/Ryujinx.Graphics/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/NvGpuEngine3dReg.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics ZetaVert = 0x48b, ZetaArrayMode = 0x48c, DepthTestEnable = 0x4b3, - IBlendEnable = 0x4b9, + BlendIndependent = 0x4b9, DepthWriteEnable = 0x4ba, DepthTestFunction = 0x4c3, BlendSeparateAlpha = 0x4cf, @@ -47,7 +47,7 @@ namespace Ryujinx.Graphics BlendEquationAlpha = 0x4d3, BlendFuncSrcAlpha = 0x4d4, BlendFuncDstAlpha = 0x4d6, - BlendEnableMaster = 0x4d7, + BlendEnable = 0x4d7, IBlendNEnable = 0x4d8, StencilEnable = 0x4e0, StencilFrontOpFail = 0x4e1,