using System; using System.Collections.Generic; namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender { /// <summary> /// Blend microcode instruction. /// </summary> enum Instruction { Mmadd = 0, Mmsub = 1, Min = 2, Max = 3, Rcp = 4, Add = 5, Sub = 6 } /// <summary> /// Blend microcode condition code. /// </summary> enum CC { F = 0, T = 1, EQ = 2, NE = 3, LT = 4, LE = 5, GT = 6, GE = 7 } /// <summary> /// Blend microcode opend B or D value. /// </summary> enum OpBD { ConstantZero = 0x0, ConstantOne = 0x1, SrcRGB = 0x2, SrcAAA = 0x3, OneMinusSrcAAA = 0x4, DstRGB = 0x5, DstAAA = 0x6, OneMinusDstAAA = 0x7, Temp0 = 0x9, Temp1 = 0xa, Temp2 = 0xb, PBR = 0xc, ConstantRGB = 0xd } /// <summary> /// Blend microcode operand A or C value. /// </summary> enum OpAC { SrcRGB = 0, DstRGB = 1, SrcAAA = 2, DstAAA = 3, Temp0 = 4, Temp1 = 5, Temp2 = 6, PBR = 7 } /// <summary> /// Blend microcode destination operand. /// </summary> enum OpDst { Temp0 = 0, Temp1 = 1, Temp2 = 2, PBR = 3 } /// <summary> /// Blend microcode input swizzle. /// </summary> enum Swizzle { RGB = 0, GBR = 1, RRR = 2, GGG = 3, BBB = 4, RToA = 5 } /// <summary> /// Blend microcode output components. /// </summary> enum WriteMask { RGB = 0, R = 1, G = 2, B = 3 } /// <summary> /// Floating-point RGB color values. /// </summary> struct RgbFloat { /// <summary> /// Red component value. /// </summary> public float R { get; } /// <summary> /// Green component value. /// </summary> public float G { get; } /// <summary> /// Blue component value. /// </summary> public float B { get; } /// <summary> /// Creates a new floating-point RGB value. /// </summary> /// <param name="r">Red component value</param> /// <param name="g">Green component value</param> /// <param name="b">Blue component value</param> public RgbFloat(float r, float g, float b) { R = r; G = g; B = b; } } /// <summary> /// Blend microcode destination operand, including swizzle, write mask and condition code update flag. /// </summary> struct Dest { public static Dest Temp0 => new Dest(OpDst.Temp0, Swizzle.RGB, WriteMask.RGB, false); public static Dest Temp1 => new Dest(OpDst.Temp1, Swizzle.RGB, WriteMask.RGB, false); public static Dest Temp2 => new Dest(OpDst.Temp2, Swizzle.RGB, WriteMask.RGB, false); public static Dest PBR => new Dest(OpDst.PBR, Swizzle.RGB, WriteMask.RGB, false); public Dest GBR => new Dest(Dst, Swizzle.GBR, WriteMask, WriteCC); public Dest RRR => new Dest(Dst, Swizzle.RRR, WriteMask, WriteCC); public Dest GGG => new Dest(Dst, Swizzle.GGG, WriteMask, WriteCC); public Dest BBB => new Dest(Dst, Swizzle.BBB, WriteMask, WriteCC); public Dest RToA => new Dest(Dst, Swizzle.RToA, WriteMask, WriteCC); public Dest R => new Dest(Dst, Swizzle, WriteMask.R, WriteCC); public Dest G => new Dest(Dst, Swizzle, WriteMask.G, WriteCC); public Dest B => new Dest(Dst, Swizzle, WriteMask.B, WriteCC); public Dest CC => new Dest(Dst, Swizzle, WriteMask, true); public OpDst Dst { get; } public Swizzle Swizzle { get; } public WriteMask WriteMask { get; } public bool WriteCC { get; } /// <summary> /// Creates a new blend microcode destination operand. /// </summary> /// <param name="dst">Operand</param> /// <param name="swizzle">Swizzle</param> /// <param name="writeMask">Write maks</param> /// <param name="writeCC">Indicates if condition codes should be updated</param> public Dest(OpDst dst, Swizzle swizzle, WriteMask writeMask, bool writeCC) { Dst = dst; Swizzle = swizzle; WriteMask = writeMask; WriteCC = writeCC; } } /// <summary> /// Blend microcode operaiton. /// </summary> struct UcodeOp { public readonly uint Word; /// <summary> /// Creates a new blend microcode operation. /// </summary> /// <param name="cc">Condition code that controls whenever the operation is executed or not</param> /// <param name="inst">Instruction</param> /// <param name="constIndex">Index on the constant table of the constant used by any constant operand</param> /// <param name="dest">Destination operand</param> /// <param name="srcA">First input operand</param> /// <param name="srcB">Second input operand</param> /// <param name="srcC">Third input operand</param> /// <param name="srcD">Fourth input operand</param> public UcodeOp(CC cc, Instruction inst, int constIndex, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD) { Word = (uint)cc | ((uint)inst << 3) | ((uint)constIndex << 6) | ((uint)srcA << 9) | ((uint)srcB << 12) | ((uint)srcC << 16) | ((uint)srcD << 19) | ((uint)dest.Swizzle << 23) | ((uint)dest.WriteMask << 26) | ((uint)dest.Dst << 28) | (dest.WriteCC ? (1u << 31) : 0); } } /// <summary> /// Blend microcode assembler. /// </summary> struct UcodeAssembler { private List<uint> _code; private RgbFloat[] _constants; private int _constantIndex; public void Mul(CC cc, Dest dest, OpAC srcA, OpBD srcB) { Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero); } public void Madd(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC) { Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, srcC, OpBD.ConstantOne); } public void Mmadd(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD) { Assemble(cc, Instruction.Mmadd, dest, srcA, srcB, srcC, srcD); } public void Mmsub(CC cc, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD) { Assemble(cc, Instruction.Mmsub, dest, srcA, srcB, srcC, srcD); } public void Min(CC cc, Dest dest, OpAC srcA, OpBD srcB) { Assemble(cc, Instruction.Min, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero); } public void Max(CC cc, Dest dest, OpAC srcA, OpBD srcB) { Assemble(cc, Instruction.Max, dest, srcA, srcB, OpAC.SrcRGB, OpBD.ConstantZero); } public void Rcp(CC cc, Dest dest, OpAC srcA) { Assemble(cc, Instruction.Rcp, dest, srcA, OpBD.ConstantZero, OpAC.SrcRGB, OpBD.ConstantZero); } public void Mov(CC cc, Dest dest, OpBD srcB) { Assemble(cc, Instruction.Add, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, OpBD.ConstantZero); } public void Add(CC cc, Dest dest, OpBD srcB, OpBD srcD) { Assemble(cc, Instruction.Add, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, srcD); } public void Sub(CC cc, Dest dest, OpBD srcB, OpBD srcD) { Assemble(cc, Instruction.Sub, dest, OpAC.SrcRGB, srcB, OpAC.SrcRGB, srcD); } private void Assemble(CC cc, Instruction inst, Dest dest, OpAC srcA, OpBD srcB, OpAC srcC, OpBD srcD) { (_code ??= new List<uint>()).Add(new UcodeOp(cc, inst, _constantIndex, dest, srcA, srcB, srcC, srcD).Word); } public void SetConstant(int index, float r, float g, float b) { if (_constants == null) { _constants = new RgbFloat[index + 1]; } else if (_constants.Length <= index) { Array.Resize(ref _constants, index + 1); } _constants[index] = new RgbFloat(r, g, b); _constantIndex = index; } public uint[] GetCode() { return _code?.ToArray(); } public RgbFloat[] GetConstants() { return _constants; } } }