diff --git a/Ryujinx.Graphics.GAL/BlendDescriptor.cs b/Ryujinx.Graphics.GAL/BlendDescriptor.cs index b35a0169dd..cc1e17c73d 100644 --- a/Ryujinx.Graphics.GAL/BlendDescriptor.cs +++ b/Ryujinx.Graphics.GAL/BlendDescriptor.cs @@ -4,6 +4,7 @@ namespace Ryujinx.Graphics.GAL { public bool Enable { get; } + public ColorF BlendConstant { get; } public BlendOp ColorOp { get; } public BlendFactor ColorSrcFactor { get; } public BlendFactor ColorDstFactor { get; } @@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.GAL public BlendDescriptor( bool enable, + ColorF blendConstant, BlendOp colorOp, BlendFactor colorSrcFactor, BlendFactor colorDstFactor, @@ -21,6 +23,7 @@ namespace Ryujinx.Graphics.GAL BlendFactor alphaDstFactor) { Enable = enable; + BlendConstant = blendConstant; ColorOp = colorOp; ColorSrcFactor = colorSrcFactor; ColorDstFactor = colorDstFactor; diff --git a/Ryujinx.Graphics.GAL/ColorF.cs b/Ryujinx.Graphics.GAL/ColorF.cs index 2e971a62c8..b3002f8c50 100644 --- a/Ryujinx.Graphics.GAL/ColorF.cs +++ b/Ryujinx.Graphics.GAL/ColorF.cs @@ -1,6 +1,8 @@ +using System; + namespace Ryujinx.Graphics.GAL { - public struct ColorF + public struct ColorF : IEquatable { public float Red { get; } public float Green { get; } @@ -14,5 +16,17 @@ namespace Ryujinx.Graphics.GAL Blue = blue; Alpha = alpha; } + + public bool Equals(ColorF color) => Red == color.Red && + Green == color.Green && + Blue == color.Blue && + Alpha == color.Alpha; + + public override bool Equals(object obj) => (obj is ColorF color) && Equals(color); + + public override int GetHashCode() => HashCode.Combine(Red, Green, Blue, Alpha); + + public static bool operator ==(ColorF l, ColorF r) => l.Equals(r); + public static bool operator !=(ColorF l, ColorF r) => !l.Equals(r); } } diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 4c892beaa0..1e685923d5 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -26,8 +26,6 @@ namespace Ryujinx.Graphics.GAL void SetBlendState(int index, BlendDescriptor blend); - void SetBlendColor(ColorF color); - void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp); void SetDepthClamp(bool clampNear, bool clampFar); void SetDepthMode(DepthMode mode); diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index 2e6c9828cd..7bc8501864 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -229,6 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Engine } if (state.QueryModified(MethodOffset.BlendIndependent, + MethodOffset.BlendConstant, MethodOffset.BlendStateCommon, MethodOffset.BlendEnableCommon, MethodOffset.BlendEnable, @@ -749,8 +750,9 @@ namespace Ryujinx.Graphics.Gpu.Engine private void UpdateBlendState(GpuState state) { bool blendIndependent = state.Get(MethodOffset.BlendIndependent); + ColorF blendConstant = state.Get(MethodOffset.BlendConstant); - for (int index = 0; index < 8; index++) + for (int index = 0; index < Constants.TotalRenderTargets; index++) { BlendDescriptor descriptor; @@ -761,6 +763,7 @@ namespace Ryujinx.Graphics.Gpu.Engine descriptor = new BlendDescriptor( enable, + blendConstant, blend.ColorOp, blend.ColorSrcFactor, blend.ColorDstFactor, @@ -775,6 +778,7 @@ namespace Ryujinx.Graphics.Gpu.Engine descriptor = new BlendDescriptor( enable, + blendConstant, blend.ColorOp, blend.ColorSrcFactor, blend.ColorDstFactor, diff --git a/Ryujinx.Graphics.Gpu/State/BlendState.cs b/Ryujinx.Graphics.Gpu/State/BlendState.cs index c4d5a064e8..ba16b8bd8b 100644 --- a/Ryujinx.Graphics.Gpu/State/BlendState.cs +++ b/Ryujinx.Graphics.Gpu/State/BlendState.cs @@ -17,5 +17,15 @@ namespace Ryujinx.Graphics.Gpu.State public BlendFactor AlphaDstFactor; public uint Padding; #pragma warning restore CS0649 + + public static BlendState Default = new BlendState + { + ColorOp = BlendOp.Add, + ColorSrcFactor = BlendFactor.One, + ColorDstFactor = BlendFactor.Zero, + AlphaOp = BlendOp.Add, + AlphaSrcFactor = BlendFactor.One, + AlphaDstFactor = BlendFactor.Zero + }; } } diff --git a/Ryujinx.Graphics.Gpu/State/BlendStateCommon.cs b/Ryujinx.Graphics.Gpu/State/BlendStateCommon.cs index cbb1880b05..f402a5a70e 100644 --- a/Ryujinx.Graphics.Gpu/State/BlendStateCommon.cs +++ b/Ryujinx.Graphics.Gpu/State/BlendStateCommon.cs @@ -17,5 +17,15 @@ namespace Ryujinx.Graphics.Gpu.State public uint Unknown0x1354; public BlendFactor AlphaDstFactor; #pragma warning restore CS0649 + + public static BlendStateCommon Default = new BlendStateCommon + { + ColorOp = BlendOp.Add, + ColorSrcFactor = BlendFactor.One, + ColorDstFactor = BlendFactor.Zero, + AlphaOp = BlendOp.Add, + AlphaSrcFactor = BlendFactor.One, + AlphaDstFactor = BlendFactor.Zero + }; } } diff --git a/Ryujinx.Graphics.Gpu/State/GpuState.cs b/Ryujinx.Graphics.Gpu/State/GpuState.cs index 981b96b7fd..a6671fe841 100644 --- a/Ryujinx.Graphics.Gpu/State/GpuState.cs +++ b/Ryujinx.Graphics.Gpu/State/GpuState.cs @@ -141,6 +141,14 @@ namespace Ryujinx.Graphics.Gpu.State { _backingMemory[(int)MethodOffset.RtColorMask + index] = 0x1111; } + + // Default blend states + Set(MethodOffset.BlendStateCommon, BlendStateCommon.Default); + + for (int index = 0; index < Constants.TotalRenderTargets; index++) + { + Set(MethodOffset.BlendState, index, BlendState.Default); + } } /// @@ -199,7 +207,7 @@ namespace Ryujinx.Graphics.Gpu.State } /// - /// Checks if two registers have been modified since the last call to this method. + /// Checks if three registers have been modified since the last call to this method. /// /// First register offset /// Second register offset @@ -219,7 +227,7 @@ namespace Ryujinx.Graphics.Gpu.State } /// - /// Checks if two registers have been modified since the last call to this method. + /// Checks if four registers have been modified since the last call to this method. /// /// First register offset /// Second register offset @@ -242,7 +250,7 @@ namespace Ryujinx.Graphics.Gpu.State } /// - /// Checks if two registers have been modified since the last call to this method. + /// Checks if five registers have been modified since the last call to this method. /// /// First register offset /// Second register offset @@ -272,6 +280,41 @@ namespace Ryujinx.Graphics.Gpu.State return modified; } + /// + /// Checks if six registers have been modified since the last call to this method. + /// + /// First register offset + /// Second register offset + /// Third register offset + /// Fourth register offset + /// Fifth register offset + /// Sixth register offset + /// True if any register was modified, false otherwise + public bool QueryModified( + MethodOffset m1, + MethodOffset m2, + MethodOffset m3, + MethodOffset m4, + MethodOffset m5, + MethodOffset m6) + { + bool modified = _registers[(int)m1].Modified || + _registers[(int)m2].Modified || + _registers[(int)m3].Modified || + _registers[(int)m4].Modified || + _registers[(int)m5].Modified || + _registers[(int)m6].Modified; + + _registers[(int)m1].Modified = false; + _registers[(int)m2].Modified = false; + _registers[(int)m3].Modified = false; + _registers[(int)m4].Modified = false; + _registers[(int)m5].Modified = false; + _registers[(int)m6].Modified = false; + + return modified; + } + /// /// Gets indexed data from a given register offset. /// @@ -301,5 +344,36 @@ namespace Ryujinx.Graphics.Gpu.State { return MemoryMarshal.Cast(_backingMemory.AsSpan().Slice((int)offset))[0]; } + + /// + /// Sets indexed data to a given register offset. + /// + /// Type of the data + /// Register offset + /// Index for indexed data + /// The data to set + public void Set(MethodOffset offset, int index, T data) where T : struct + { + Register register = _registers[(int)offset]; + + if ((uint)index >= register.Count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + Set(offset + index * register.Stride, data); + } + + /// + /// Sets data to a given register offset. + /// + /// Type of the data + /// Register offset + /// The data to set + public void Set(MethodOffset offset, T data) where T : struct + { + ReadOnlySpan intSpan = MemoryMarshal.Cast(MemoryMarshal.CreateReadOnlySpan(ref data, 1)); + intSpan.CopyTo(_backingMemory.AsSpan().Slice((int)offset, intSpan.Length)); + } } } diff --git a/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs b/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs index e59a3aafbf..65df5f4e67 100644 --- a/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs +++ b/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.GAL; using System; using System.Diagnostics; using System.Runtime.InteropServices; @@ -52,7 +53,7 @@ namespace Ryujinx.Graphics.Gpu.State /// public static TableItem[] Table = new TableItem[] { - new TableItem(MethodOffset.RtColorState, typeof(RtColorState), 8), + new TableItem(MethodOffset.RtColorState, typeof(RtColorState), Constants.TotalRenderTargets), new TableItem(MethodOffset.ViewportTransform, typeof(ViewportTransform), Constants.TotalViewports), new TableItem(MethodOffset.ViewportExtents, typeof(ViewportExtents), Constants.TotalViewports), new TableItem(MethodOffset.VertexBufferDrawState, typeof(VertexBufferDrawState), 1), @@ -62,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.State new TableItem(MethodOffset.RtDepthStencilState, typeof(RtDepthStencilState), 1), new TableItem(MethodOffset.VertexAttribState, typeof(VertexAttribState), 16), new TableItem(MethodOffset.RtDepthStencilSize, typeof(Size3D), 1), - new TableItem(MethodOffset.BlendEnable, typeof(Boolean32), 8), + new TableItem(MethodOffset.BlendEnable, typeof(Boolean32), Constants.TotalRenderTargets), new TableItem(MethodOffset.StencilTestState, typeof(StencilTestState), 1), new TableItem(MethodOffset.SamplerPoolState, typeof(PoolState), 1), new TableItem(MethodOffset.TexturePoolState, typeof(PoolState), 1), @@ -72,9 +73,10 @@ namespace Ryujinx.Graphics.Gpu.State new TableItem(MethodOffset.IndexBufferState, typeof(IndexBufferState), 1), new TableItem(MethodOffset.VertexBufferInstanced, typeof(Boolean32), 16), new TableItem(MethodOffset.FaceState, typeof(FaceState), 1), - new TableItem(MethodOffset.RtColorMask, typeof(RtColorMask), 8), + new TableItem(MethodOffset.RtColorMask, typeof(RtColorMask), Constants.TotalRenderTargets), new TableItem(MethodOffset.VertexBufferState, typeof(VertexBufferState), 16), - new TableItem(MethodOffset.BlendState, typeof(BlendState), 8), + new TableItem(MethodOffset.BlendConstant, typeof(ColorF), 1), + new TableItem(MethodOffset.BlendState, typeof(BlendState), Constants.TotalRenderTargets), new TableItem(MethodOffset.VertexBufferEndAddress, typeof(GpuVa), 16), new TableItem(MethodOffset.ShaderState, typeof(ShaderState), 6), }; diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs index b542d9b8c3..077b09c271 100644 --- a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs +++ b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs @@ -58,6 +58,7 @@ namespace Ryujinx.Graphics.Gpu.State BlendIndependent = 0x4b9, DepthWriteEnable = 0x4ba, DepthTestFunc = 0x4c3, + BlendConstant = 0x4c7, BlendStateCommon = 0x4cf, BlendEnableCommon = 0x4d7, BlendEnable = 0x4d8, diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index e32b5b8553..eec2b6432a 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -35,6 +35,8 @@ namespace Ryujinx.Graphics.OpenGL private bool _scissor0Enable = false; + ColorF _blendConstant = new ColorF(0, 0, 0, 0); + internal Pipeline() { _rasterizerDiscard = false; @@ -478,11 +480,6 @@ namespace Ryujinx.Graphics.OpenGL } } - public void SetBlendColor(ColorF color) - { - GL.BlendColor(color.Red, color.Green, color.Blue, color.Alpha); - } - public void SetBlendState(int index, BlendDescriptor blend) { if (!blend.Enable) @@ -504,6 +501,17 @@ namespace Ryujinx.Graphics.OpenGL (BlendingFactorSrc)blend.AlphaSrcFactor.Convert(), (BlendingFactorDest)blend.AlphaDstFactor.Convert()); + if (_blendConstant != blend.BlendConstant) + { + _blendConstant = blend.BlendConstant; + + GL.BlendColor( + blend.BlendConstant.Red, + blend.BlendConstant.Green, + blend.BlendConstant.Blue, + blend.BlendConstant.Alpha); + } + GL.Enable(IndexedEnableCap.Blend, index); }