Просмотр исходного кода

Implment common and independent blend properly (fixes #458) (#485)

* Implment common and independent blend properly

* Nits
gdkchan 7 лет назад
Родитель
Сommit
44c1cf3fe5

+ 7 - 1
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
     }
 }

+ 20 - 1
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
     }
 }

+ 36 - 14
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];
         }
     }
 }

+ 112 - 42
Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs

@@ -47,34 +47,37 @@ 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)GetFunc(Func);
+        }
+
+        public static StencilFunction GetStencilFunc(GalComparisonOp Func)
+        {
+            return (StencilFunction)GetFunc(Func);
+        }
+
+        private static All GetFunc(GalComparisonOp Func)
+        {
+            if ((int)Func >= (int)All.Never &&
+                (int)Func <= (int)All.Always)
             {
-                return (DepthFunction)Func;
+                return (All)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;
+                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 StencilFunction GetStencilFunc(GalComparisonOp Func)
-        {
-            //OGL comparison values match, it's just an enum cast
-            return (StencilFunction)GetDepthFunc(Func);
-        }
-
         public static DrawElementsType GetDrawElementsType(GalIndexFormat Format)
         {
             switch (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;
             }
 

+ 156 - 47
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);
+                for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
+                {
+                    SetBlendState(Index, New.Blends[Index], Old.Blends[Index]);
+                }
             }
-
-            if (New.BlendEnabled)
+            else
             {
-                if (New.BlendSeparateAlpha)
+                if (New.BlendIndependent != Old.BlendIndependent)
                 {
-                    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));
-                    }
+                    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;
         }
     }
 }

+ 50 - 15
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);
+
+                    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);
 
-                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.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)

+ 2 - 2
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,