private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. ShaderIrNode OperA = GetOperGpr8(OpCode); ShaderIrNode OperB = GetOperGpr20(OpCode); ShaderIrNode OperC = GetOperImm13_36(OpCode); for (int Ch = 0; Ch < 4; Ch++) { //Assign it to a temp because the destination registers //may be used as texture coord input aswell. ShaderIrOperGpr Dst = new ShaderIrOperGpr(0x100 + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } for (int Ch = 0; Ch < 4; Ch++) { ShaderIrOperGpr Src = new ShaderIrOperGpr(0x100 + Ch); ShaderIrOperGpr Dst = (Ch >> 1) != 0 ? GetOperGpr28(OpCode) : GetOperGpr0(OpCode); Dst.Index += Ch & 1; Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } }
private static void EmitTex(ShaderIrBlock Block, long OpCode, bool GprHandle) { //TODO: Support other formats. ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2]; for (int Index = 0; Index < Coords.Length; Index++) { Coords[Index] = GetOperGpr8(OpCode); Coords[Index].Index += Index; if (Coords[Index].Index > ShaderIrOperGpr.ZRIndex) { Coords[Index].Index = ShaderIrOperGpr.ZRIndex; } } int ChMask = (int)(OpCode >> 31) & 0xf; ShaderIrNode OperC = GprHandle ? (ShaderIrNode)GetOperGpr20(OpCode) : (ShaderIrNode)GetOperImm13_36(OpCode); ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; for (int Ch = 0; Ch < 4; Ch++) { ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } int RegInc = 0; for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrOperGpr Dst = GetOperGpr0(OpCode); Dst.Index += RegInc++; if (Dst.Index >= ShaderIrOperGpr.ZRIndex) { continue; } Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } }
private static void EmitTex(ShaderIrBlock Block, long OpCode, bool GprHandle) { //TODO: Support other formats. ShaderIrOperGpr[] Coords = new ShaderIrOperGpr[2]; for (int Index = 0; Index < Coords.Length; Index++) { ShaderIrOperGpr CoordReg = OpCode.Gpr8(); CoordReg.Index += Index; if (!CoordReg.IsValidRegister) { CoordReg.Index = ShaderIrOperGpr.ZRIndex; } Coords[Index] = ShaderIrOperGpr.MakeTemporary(Index); Block.AddNode(new ShaderIrAsg(Coords[Index], CoordReg)); } int ChMask = OpCode.Read(31, 0xf); ShaderIrNode OperC = GprHandle ? (ShaderIrNode)OpCode.Gpr20() : (ShaderIrNode)OpCode.Imm13_36(); ShaderIrInst Inst = GprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; int RegInc = 0; for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrOperGpr Dst = OpCode.Gpr0(); Dst.Index += RegInc++; if (!Dst.IsValidRegister || Dst.IsConst) { continue; } ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, Coords[0], Coords[1], OperC, Meta); Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); } }
private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. ShaderIrNode OperA = GetOperGpr8(OpCode); ShaderIrNode OperB = GetOperGpr20(OpCode); ShaderIrNode OperC = GetOperImm13_36(OpCode); int LutIndex; LutIndex = GetOperGpr0(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; LutIndex |= GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 2 : 0; int ChMask = MaskLut[LutIndex, (OpCode >> 50) & 7]; for (int Ch = 0; Ch < 4; Ch++) { ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } int RegInc = 0; for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrOperGpr Dst = (RegInc >> 1) != 0 ? GetOperGpr28(OpCode) : GetOperGpr0(OpCode); Dst.Index += RegInc++ & 1; if (Dst.Index >= ShaderIrOperGpr.ZRIndex) { continue; } Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } }
private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. ShaderIrNode OperA = GetOperGpr8(OpCode); ShaderIrNode OperB = GetOperGpr20(OpCode); ShaderIrNode OperC = GetOperImm13_36(OpCode); int LutIndex; LutIndex = GetOperGpr0(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 1 : 0; LutIndex |= GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex ? 2 : 0; if (LutIndex == 0) { //Both registers are RZ, color is not written anywhere. //So, the intruction is basically a no-op. return; } int ChMask = MaskLut[LutIndex, (OpCode >> 50) & 7]; for (int Ch = 0; Ch < 4; Ch++) { ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } int RegInc = 0; ShaderIrOperGpr GetDst() { ShaderIrOperGpr Dst; switch (LutIndex) { case 1: Dst = GetOperGpr0(OpCode); break; case 2: Dst = GetOperGpr28(OpCode); break; case 3: Dst = (RegInc >> 1) != 0 ? GetOperGpr28(OpCode) : GetOperGpr0(OpCode); break; default: throw new InvalidOperationException(); } Dst.Index += RegInc++ & 1; return(Dst); } for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrOperGpr Dst = GetDst(); if (Dst.Index != ShaderIrOperGpr.ZRIndex) { Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } } }
private void Traverse(ShaderIrNode[] nodes, ShaderIrNode parent, ShaderIrNode node) { switch (node) { case ShaderIrAsg asg: { Traverse(nodes, asg, asg.Dst); Traverse(nodes, asg, asg.Src); break; } case ShaderIrCond cond: { Traverse(nodes, cond, cond.Pred); Traverse(nodes, cond, cond.Child); break; } case ShaderIrOp op: { Traverse(nodes, op, op.OperandA); Traverse(nodes, op, op.OperandB); Traverse(nodes, op, op.OperandC); if (op.Inst == ShaderIrInst.Texq || op.Inst == ShaderIrInst.Texs || op.Inst == ShaderIrInst.Tld4 || op.Inst == ShaderIrInst.Txlf) { int handle = ((ShaderIrOperImm)op.OperandC).Value; int index = handle - TexStartIndex; string name = _stagePrefix + TextureName + index; GalTextureTarget textureTarget; TextureInstructionSuffix textureInstructionSuffix; // TODO: Non 2D texture type for TEXQ? if (op.Inst == ShaderIrInst.Texq) { textureTarget = GalTextureTarget.TwoD; textureInstructionSuffix = TextureInstructionSuffix.None; } else { ShaderIrMetaTex meta = ((ShaderIrMetaTex)op.MetaData); textureTarget = meta.TextureTarget; textureInstructionSuffix = meta.TextureInstructionSuffix; } m_Textures.TryAdd(handle, new ShaderDeclInfo(name, handle, false, 0, 1, textureTarget, textureInstructionSuffix)); } else if (op.Inst == ShaderIrInst.Texb) { ShaderIrNode handleSrc = null; int index = Array.IndexOf(nodes, parent) - 1; for (; index >= 0; index--) { ShaderIrNode curr = nodes[index]; if (curr is ShaderIrAsg asg && asg.Dst is ShaderIrOperGpr gpr) { if (gpr.Index == ((ShaderIrOperGpr)op.OperandC).Index) { handleSrc = asg.Src; break; } } } if (handleSrc != null && handleSrc is ShaderIrOperCbuf cbuf) { ShaderIrMetaTex meta = ((ShaderIrMetaTex)op.MetaData); string name = _stagePrefix + TextureName + "_cb" + cbuf.Index + "_" + cbuf.Pos; m_CbTextures.Add(op, new ShaderDeclInfo(name, cbuf.Pos, true, cbuf.Index, 1, meta.TextureTarget, meta.TextureInstructionSuffix)); } else { throw new NotImplementedException("Shader TEX.B instruction is not fully supported!"); } } break; } case ShaderIrOperCbuf cbuf: { if (!m_Uniforms.ContainsKey(cbuf.Index)) { string name = _stagePrefix + UniformName + cbuf.Index; ShaderDeclInfo declInfo = new ShaderDeclInfo(name, cbuf.Pos, true, cbuf.Index); m_Uniforms.Add(cbuf.Index, declInfo); } break; } case ShaderIrOperAbuf abuf: { //This is a built-in variable. if (abuf.Offs == LayerAttr || abuf.Offs == PointSizeAttr || abuf.Offs == PointCoordAttrX || abuf.Offs == PointCoordAttrY || abuf.Offs == VertexIdAttr || abuf.Offs == InstanceIdAttr || abuf.Offs == FaceAttr) { break; } int index = abuf.Offs >> 4; int elem = (abuf.Offs >> 2) & 3; int glslIndex = index - AttrStartIndex; if (glslIndex < 0) { return; } ShaderDeclInfo declInfo; if (parent is ShaderIrAsg asg && asg.Dst == node) { if (!m_OutAttributes.TryGetValue(index, out declInfo)) { declInfo = new ShaderDeclInfo(OutAttrName + glslIndex, glslIndex); m_OutAttributes.Add(index, declInfo); } }
private static void EmitTld4(ShaderIrBlock block, long opCode, GalTextureTarget textureType, TextureInstructionSuffix textureInstructionSuffix, int chMask, int component, bool scalar) { ShaderIrOperGpr operA = opCode.Gpr8(); ShaderIrOperGpr operB = opCode.Gpr20(); ShaderIrOperImm operC = opCode.Imm13_36(); ShaderIrOperGpr[] coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(textureType)]; ShaderIrOperGpr offset = null; ShaderIrOperGpr depthCompare = null; bool isArray = ImageUtils.IsArray(textureType); int operBIndex = 0; if (scalar) { int coordStartIndex = 0; if (isArray) { coordStartIndex++; coords[coords.Length - 1] = operB; } switch (coords.Length - coordStartIndex) { case 1: coords[0] = opCode.Gpr8(); break; case 2: coords[0] = opCode.Gpr8(); coords[0].Index += coordStartIndex; break; case 3: coords[0] = opCode.Gpr8(); coords[0].Index += coordStartIndex; coords[1] = opCode.Gpr8(); coords[1].Index += 1 + coordStartIndex; break; default: throw new NotSupportedException($"{coords.Length - coordStartIndex} coords textures aren't supported in TLD4S"); } if (coords.Length - coordStartIndex != 1) { coords[coords.Length - coordStartIndex - 1] = operB; operBIndex++; } if (textureInstructionSuffix != TextureInstructionSuffix.None && textureType == GalTextureTarget.TwoD) { coords[coords.Length - coordStartIndex - 1] = opCode.Gpr8(); coords[coords.Length - coordStartIndex - 1].Index += coords.Length - coordStartIndex - 1; operBIndex--; } } else { int indexExtraCoord = 0; if (isArray) { indexExtraCoord++; coords[coords.Length - 1] = opCode.Gpr8(); } for (int index = 0; index < coords.Length - indexExtraCoord; index++) { coords[index] = opCode.Gpr8(); coords[index].Index += index; coords[index].Index += indexExtraCoord; if (coords[index].Index > ShaderIrOperGpr.ZrIndex) { coords[index].Index = ShaderIrOperGpr.ZrIndex; } } } if ((textureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) { offset = opCode.Gpr20(); offset.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.Dc) != 0) { depthCompare = opCode.Gpr20(); depthCompare.Index += operBIndex; operBIndex++; } coords = CoordsRegistersToTempRegisters(block, coords); int regInc = 0; for (int ch = 0; ch < 4; ch++) { if (!IsChannelUsed(chMask, ch)) { continue; } ShaderIrOperGpr dst = opCode.Gpr0(); dst.Index += regInc++; if (!dst.IsValidRegister || dst.IsConst) { continue; } ShaderIrMetaTex meta = new ShaderIrMetaTex(ch, textureType, textureInstructionSuffix, coords) { Component = component, Offset = offset, DepthCompare = depthCompare }; ShaderIrOp op = new ShaderIrOp(ShaderIrInst.Tld4, operA, operB, operC, meta); block.AddNode(opCode.PredNode(new ShaderIrAsg(dst, op))); } }
private static void EmitTexs(ShaderIrBlock block, long opCode, ShaderIrInst inst, GalTextureTarget textureTarget, TextureInstructionSuffix textureInstructionSuffix) { if (inst == ShaderIrInst.Txlf && textureTarget == GalTextureTarget.CubeArray) { throw new InvalidOperationException("TLDS instructions cannot use CUBE modifier!"); } bool isArray = ImageUtils.IsArray(textureTarget); ShaderIrOperGpr[] coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(textureTarget)]; ShaderIrOperGpr operA = opCode.Gpr8(); ShaderIrOperGpr operB = opCode.Gpr20(); ShaderIrOperGpr suffixExtra = opCode.Gpr20(); suffixExtra.Index += 1; int coordStartIndex = 0; if (isArray) { coordStartIndex++; coords[coords.Length - 1] = opCode.Gpr8(); } switch (coords.Length - coordStartIndex) { case 1: coords[0] = opCode.Gpr8(); break; case 2: coords[0] = opCode.Gpr8(); coords[0].Index += coordStartIndex; break; case 3: coords[0] = opCode.Gpr8(); coords[0].Index += coordStartIndex; coords[1] = opCode.Gpr8(); coords[1].Index += 1 + coordStartIndex; break; default: throw new NotSupportedException($"{coords.Length - coordStartIndex} coords textures aren't supported in TEXS"); } int operBIndex = 0; ShaderIrOperGpr levelOfDetail = null; ShaderIrOperGpr offset = null; ShaderIrOperGpr depthCompare = null; // OperB is always the last value // Not applicable to 1d textures if (coords.Length - coordStartIndex != 1) { coords[coords.Length - coordStartIndex - 1] = operB; operBIndex++; } // Encoding of TEXS/TLDS is a bit special and change for 2d textures // NOTE: OperA seems to hold at best two args. // On 2D textures, if no suffix need an additional values, Y is stored in OperB, otherwise coords are in OperA and the additional values is in OperB. if (textureInstructionSuffix != TextureInstructionSuffix.None && textureInstructionSuffix != TextureInstructionSuffix.Lz && textureTarget == GalTextureTarget.TwoD) { coords[coords.Length - coordStartIndex - 1] = opCode.Gpr8(); coords[coords.Length - coordStartIndex - 1].Index += coords.Length - coordStartIndex - 1; operBIndex--; } // TODO: Find what MZ does and what changes about the encoding (Maybe Multisample?) if ((textureInstructionSuffix & TextureInstructionSuffix.Ll) != 0) { levelOfDetail = opCode.Gpr20(); levelOfDetail.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) { offset = opCode.Gpr20(); offset.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.Dc) != 0) { depthCompare = opCode.Gpr20(); depthCompare.Index += operBIndex; operBIndex++; } int lutIndex; lutIndex = !opCode.Gpr0().IsConst ? 1 : 0; lutIndex |= !opCode.Gpr28().IsConst ? 2 : 0; if (lutIndex == 0) { //Both destination registers are RZ, do nothing. return; } bool fp16 = !opCode.Read(59); int dstIncrement = 0; ShaderIrOperGpr GetDst() { ShaderIrOperGpr dst; if (fp16) { //FP16 mode, two components are packed on the two //halfs of a 32-bits register, as two half-float values. int halfPart = dstIncrement & 1; switch (lutIndex) { case 1: dst = opCode.GprHalf0(halfPart); break; case 2: dst = opCode.GprHalf28(halfPart); break; case 3: dst = (dstIncrement >> 1) != 0 ? opCode.GprHalf28(halfPart) : opCode.GprHalf0(halfPart); break; default: throw new InvalidOperationException(); } } else { //32-bits mode, each component uses one register. //Two components uses two consecutive registers. switch (lutIndex) { case 1: dst = opCode.Gpr0(); break; case 2: dst = opCode.Gpr28(); break; case 3: dst = (dstIncrement >> 1) != 0 ? opCode.Gpr28() : opCode.Gpr0(); break; default: throw new InvalidOperationException(); } dst.Index += dstIncrement & 1; } dstIncrement++; return(dst); } int chMask = _maskLut[lutIndex, opCode.Read(50, 7)]; if (chMask == 0) { //All channels are disabled, do nothing. return; } ShaderIrNode operC = opCode.Imm13_36(); coords = CoordsRegistersToTempRegisters(block, coords); for (int ch = 0; ch < 4; ch++) { if (!IsChannelUsed(chMask, ch)) { continue; } ShaderIrMetaTex meta = new ShaderIrMetaTex(ch, textureTarget, textureInstructionSuffix, coords) { LevelOfDetail = levelOfDetail, Offset = offset, DepthCompare = depthCompare }; ShaderIrOp op = new ShaderIrOp(inst, operA, operB, operC, meta); ShaderIrOperGpr dst = GetDst(); if (dst.IsValidRegister && !dst.IsConst) { block.AddNode(opCode.PredNode(new ShaderIrAsg(dst, op))); } } }
private static void EmitTex(ShaderIrBlock block, long opCode, TextureInstructionSuffix textureInstructionSuffix, bool gprHandle) { bool isArray = opCode.HasArray(); GalTextureTarget textureTarget = TexToTextureTarget(opCode.Read(28, 6), isArray); bool hasDepthCompare = opCode.Read(0x32); if (hasDepthCompare) { textureInstructionSuffix |= TextureInstructionSuffix.Dc; } ShaderIrOperGpr[] coords = new ShaderIrOperGpr[ImageUtils.GetCoordsCountTextureTarget(textureTarget)]; int indexExtraCoord = 0; if (isArray) { indexExtraCoord++; coords[coords.Length - 1] = opCode.Gpr8(); } for (int index = 0; index < coords.Length - indexExtraCoord; index++) { ShaderIrOperGpr coordReg = opCode.Gpr8(); coordReg.Index += index; coordReg.Index += indexExtraCoord; if (!coordReg.IsValidRegister) { coordReg.Index = ShaderIrOperGpr.ZrIndex; } coords[index] = coordReg; } int chMask = opCode.Read(31, 0xf); ShaderIrOperGpr levelOfDetail = null; ShaderIrOperGpr offset = null; ShaderIrOperGpr depthCompare = null; // TODO: determine first argument when TEX.B is used int operBIndex = gprHandle ? 1 : 0; if ((textureInstructionSuffix & TextureInstructionSuffix.Ll) != 0 || (textureInstructionSuffix & TextureInstructionSuffix.Lb) != 0 || (textureInstructionSuffix & TextureInstructionSuffix.Lba) != 0 || (textureInstructionSuffix & TextureInstructionSuffix.Lla) != 0) { levelOfDetail = opCode.Gpr20(); levelOfDetail.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.AOffI) != 0) { offset = opCode.Gpr20(); offset.Index += operBIndex; operBIndex++; } if ((textureInstructionSuffix & TextureInstructionSuffix.Dc) != 0) { depthCompare = opCode.Gpr20(); depthCompare.Index += operBIndex; operBIndex++; } // ??? ShaderIrNode operC = gprHandle ? (ShaderIrNode)opCode.Gpr20() : (ShaderIrNode)opCode.Imm13_36(); ShaderIrInst inst = gprHandle ? ShaderIrInst.Texb : ShaderIrInst.Texs; coords = CoordsRegistersToTempRegisters(block, coords); int regInc = 0; for (int ch = 0; ch < 4; ch++) { if (!IsChannelUsed(chMask, ch)) { continue; } ShaderIrOperGpr dst = opCode.Gpr0(); dst.Index += regInc++; if (!dst.IsValidRegister || dst.IsConst) { continue; } ShaderIrMetaTex meta = new ShaderIrMetaTex(ch, textureTarget, textureInstructionSuffix, coords) { LevelOfDetail = levelOfDetail, Offset = offset, DepthCompare = depthCompare }; ShaderIrOp op = new ShaderIrOp(inst, coords[0], coords.Length > 1 ? coords[1] : null, operC, meta); block.AddNode(opCode.PredNode(new ShaderIrAsg(dst, op))); } }
private static void EmitTex(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. ShaderIrNode OperA = GetOperGpr8(OpCode); ShaderIrNode OperB = GetOperGpr20(OpCode); ShaderIrNode OperC = GetOperImm13_36(OpCode); bool TwoDests = GetOperGpr28(OpCode).Index != ShaderIrOperGpr.ZRIndex; int ChMask; switch ((OpCode >> 50) & 7) { case 0: ChMask = TwoDests ? 0x7 : 0x1; break; case 1: ChMask = TwoDests ? 0xb : 0x2; break; case 2: ChMask = TwoDests ? 0xd : 0x4; break; case 3: ChMask = TwoDests ? 0xe : 0x8; break; case 4: ChMask = TwoDests ? 0xf : 0x3; break; default: throw new InvalidOperationException(); } for (int Ch = 0; Ch < 4; Ch++) { ShaderIrOperGpr Dst = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, OperA, OperB, OperC, Meta); Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Op), OpCode)); } int RegInc = 0; for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrOperGpr Src = new ShaderIrOperGpr(TempRegStart + Ch); ShaderIrOperGpr Dst = (RegInc >> 1) != 0 ? GetOperGpr28(OpCode) : GetOperGpr0(OpCode); Dst.Index += RegInc++ & 1; if (Dst.Index >= ShaderIrOperGpr.ZRIndex) { continue; } Block.AddNode(GetPredNode(new ShaderIrAsg(Dst, Src), OpCode)); } }
private static void EmitTexs(ShaderIrBlock Block, long OpCode, ShaderIrInst Inst) { //TODO: Support other formats. int LutIndex; LutIndex = !OpCode.Gpr0().IsConst ? 1 : 0; LutIndex |= !OpCode.Gpr28().IsConst ? 2 : 0; if (LutIndex == 0) { //Both destination registers are RZ, do nothing. return; } bool Fp16 = !OpCode.Read(59); int DstIncrement = 0; ShaderIrOperGpr GetDst() { ShaderIrOperGpr Dst; if (Fp16) { //FP16 mode, two components are packed on the two //halfs of a 32-bits register, as two half-float values. int HalfPart = DstIncrement & 1; switch (LutIndex) { case 1: Dst = OpCode.GprHalf0(HalfPart); break; case 2: Dst = OpCode.GprHalf28(HalfPart); break; case 3: Dst = (DstIncrement >> 1) != 0 ? OpCode.GprHalf28(HalfPart) : OpCode.GprHalf0(HalfPart); break; default: throw new InvalidOperationException(); } } else { //32-bits mode, each component uses one register. //Two components uses two consecutive registers. switch (LutIndex) { case 1: Dst = OpCode.Gpr0(); break; case 2: Dst = OpCode.Gpr28(); break; case 3: Dst = (DstIncrement >> 1) != 0 ? OpCode.Gpr28() : OpCode.Gpr0(); break; default: throw new InvalidOperationException(); } Dst.Index += DstIncrement & 1; } DstIncrement++; return(Dst); } int ChMask = MaskLut[LutIndex, OpCode.Read(50, 7)]; if (ChMask == 0) { //All channels are disabled, do nothing. return; } ShaderIrNode OperC = OpCode.Imm13_36(); ShaderIrOperGpr Coord0 = ShaderIrOperGpr.MakeTemporary(0); ShaderIrOperGpr Coord1 = ShaderIrOperGpr.MakeTemporary(1); Block.AddNode(new ShaderIrAsg(Coord0, OpCode.Gpr8())); Block.AddNode(new ShaderIrAsg(Coord1, OpCode.Gpr20())); for (int Ch = 0; Ch < 4; Ch++) { if (!IsChannelUsed(ChMask, Ch)) { continue; } ShaderIrMetaTex Meta = new ShaderIrMetaTex(Ch); ShaderIrOp Op = new ShaderIrOp(Inst, Coord0, Coord1, OperC, Meta); ShaderIrOperGpr Dst = GetDst(); if (Dst.IsValidRegister && !Dst.IsConst) { Block.AddNode(OpCode.PredNode(new ShaderIrAsg(Dst, Op))); } } }