aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs115
1 files changed, 115 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs
new file mode 100644
index 00000000..8072c6af
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs
@@ -0,0 +1,115 @@
+using Ryujinx.Common;
+using Ryujinx.Graphics.GAL;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender
+{
+ /// <summary>
+ /// Advanced blend manager.
+ /// </summary>
+ class AdvancedBlendManager
+ {
+ private const int InstructionRamSize = 128;
+ private const int InstructionRamSizeMask = InstructionRamSize - 1;
+
+ private readonly DeviceStateWithShadow<ThreedClassState> _state;
+
+ private readonly uint[] _code;
+ private int _ip;
+
+ /// <summary>
+ /// Creates a new instance of the advanced blend manager.
+ /// </summary>
+ /// <param name="state">GPU state of the channel owning this manager</param>
+ public AdvancedBlendManager(DeviceStateWithShadow<ThreedClassState> state)
+ {
+ _state = state;
+ _code = new uint[InstructionRamSize];
+ }
+
+ /// <summary>
+ /// Sets the start offset of the blend microcode in memory.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void LoadBlendUcodeStart(int argument)
+ {
+ _ip = argument;
+ }
+
+ /// <summary>
+ /// Pushes one word of blend microcode.
+ /// </summary>
+ /// <param name="argument">Method call argument</param>
+ public void LoadBlendUcodeInstruction(int argument)
+ {
+ _code[_ip++ & InstructionRamSizeMask] = (uint)argument;
+ }
+
+ /// <summary>
+ /// Tries to identify the current advanced blend function being used,
+ /// given the current state and microcode that was uploaded.
+ /// </summary>
+ /// <param name="descriptor">Advanced blend descriptor</param>
+ /// <returns>True if the function was found, false otherwise</returns>
+ public bool TryGetAdvancedBlend(out AdvancedBlendDescriptor descriptor)
+ {
+ Span<uint> currentCode = new Span<uint>(_code);
+ byte codeLength = (byte)_state.State.BlendUcodeSize;
+
+ if (currentCode.Length > codeLength)
+ {
+ currentCode = currentCode.Slice(0, codeLength);
+ }
+
+ Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast<uint, byte>(currentCode));
+
+ descriptor = default;
+
+ if (!AdvancedBlendPreGenTable.Entries.TryGetValue(hash, out var entry))
+ {
+ return false;
+ }
+
+ if (entry.Constants != null)
+ {
+ bool constantsMatch = true;
+
+ for (int i = 0; i < entry.Constants.Length; i++)
+ {
+ RgbFloat constant = entry.Constants[i];
+ RgbHalf constant2 = _state.State.BlendUcodeConstants[i];
+
+ if ((Half)constant.R != constant2.UnpackR() ||
+ (Half)constant.G != constant2.UnpackG() ||
+ (Half)constant.B != constant2.UnpackB())
+ {
+ constantsMatch = false;
+ break;
+ }
+ }
+
+ if (!constantsMatch)
+ {
+ return false;
+ }
+ }
+
+ if (entry.Alpha.Enable != _state.State.BlendUcodeEnable)
+ {
+ return false;
+ }
+
+ if (entry.Alpha.Enable == BlendUcodeEnable.EnableRGBA &&
+ (entry.Alpha.AlphaOp != _state.State.BlendStateCommon.AlphaOp ||
+ entry.Alpha.AlphaSrcFactor != _state.State.BlendStateCommon.AlphaSrcFactor ||
+ entry.Alpha.AlphaDstFactor != _state.State.BlendStateCommon.AlphaDstFactor))
+ {
+ return false;
+ }
+
+ descriptor = new AdvancedBlendDescriptor(entry.Op, entry.Overlap, entry.SrcPreMultiplied);
+ return true;
+ }
+ }
+} \ No newline at end of file