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;
}
}
}