using Ryujinx.Common; using Ryujinx.Graphics.GAL; using System; using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender { /// /// Advanced blend manager. /// class AdvancedBlendManager { private const int InstructionRamSize = 128; private const int InstructionRamSizeMask = InstructionRamSize - 1; private readonly DeviceStateWithShadow _state; private readonly uint[] _code; private int _ip; /// /// Creates a new instance of the advanced blend manager. /// /// GPU state of the channel owning this manager public AdvancedBlendManager(DeviceStateWithShadow state) { _state = state; _code = new uint[InstructionRamSize]; } /// /// Sets the start offset of the blend microcode in memory. /// /// Method call argument public void LoadBlendUcodeStart(int argument) { _ip = argument; } /// /// Pushes one word of blend microcode. /// /// Method call argument public void LoadBlendUcodeInstruction(int argument) { _code[_ip++ & InstructionRamSizeMask] = (uint)argument; } /// /// Tries to identify the current advanced blend function being used, /// given the current state and microcode that was uploaded. /// /// Advanced blend descriptor /// True if the function was found, false otherwise public bool TryGetAdvancedBlend(out AdvancedBlendDescriptor descriptor) { Span currentCode = new Span(_code); byte codeLength = (byte)_state.State.BlendUcodeSize; if (currentCode.Length > codeLength) { currentCode = currentCode.Slice(0, codeLength); } Hash128 hash = XXHash128.ComputeHash(MemoryMarshal.Cast(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; } } }