mirror of https://github.com/AxioDL/metaforce.git
704 lines
28 KiB
C++
704 lines
28 KiB
C++
#include "hecl/Backend/GX.hpp"
|
|
|
|
namespace hecl::Backend {
|
|
|
|
template <>
|
|
void GX::Color::Enumerate<athena::io::DNA<athena::Big>::Read>(typename Read::StreamT& reader) {
|
|
reader.readUBytesToBuf(&num, 4);
|
|
}
|
|
template <>
|
|
void GX::Color::Enumerate<athena::io::DNA<athena::Big>::Write>(typename Write::StreamT& writer) {
|
|
writer.writeUBytes(reinterpret_cast<const atUint8*>(&num), 4);
|
|
}
|
|
template <>
|
|
void GX::Color::Enumerate<athena::io::DNA<athena::Big>::BinarySize>(typename BinarySize::StreamT& s) {
|
|
s += 4;
|
|
}
|
|
|
|
unsigned GX::addKColor(Diagnostics& diag, const SourceLocation& loc, const Color& color) {
|
|
for (unsigned i = 0; i < m_kcolorCount; ++i)
|
|
if (m_kcolors[i] == color)
|
|
return i;
|
|
if (m_kcolorCount >= 4)
|
|
diag.reportBackendErr(loc, "GX KColor overflow");
|
|
m_kcolors[m_kcolorCount] = color;
|
|
m_kcolors[m_kcolorCount].color[3] = 0;
|
|
return m_kcolorCount++;
|
|
}
|
|
|
|
unsigned GX::addKAlpha(Diagnostics& diag, const SourceLocation& loc, float alpha) {
|
|
uint8_t ai = uint8_t(std::min(std::max(alpha * 255.f, 0.f), 255.f));
|
|
for (unsigned i = 0; i < m_kcolorCount; ++i) {
|
|
if (m_kcolors[i].color[3] == ai)
|
|
return i;
|
|
else if (m_kcolors[i].color[3] == 0) {
|
|
m_kcolors[i].color[3] = ai;
|
|
return i;
|
|
}
|
|
}
|
|
if (m_kcolorCount >= 4)
|
|
diag.reportBackendErr(loc, "GX KColor overflow");
|
|
m_kcolors[m_kcolorCount] = ai;
|
|
return m_kcolorCount++;
|
|
}
|
|
|
|
unsigned GX::addTexCoordGen(Diagnostics& diag, const SourceLocation& loc, TexGenSrc src, TexMtx mtx, bool norm,
|
|
PTTexMtx pmtx) {
|
|
for (unsigned i = 0; i < m_tcgCount; ++i) {
|
|
TexCoordGen& tcg = m_tcgs[i];
|
|
if (tcg.m_src == src && tcg.m_mtx == mtx && tcg.m_norm == norm && tcg.m_pmtx == pmtx)
|
|
return i;
|
|
}
|
|
if (m_tcgCount >= 8)
|
|
diag.reportBackendErr(loc, "GX TexCoordGen overflow");
|
|
GX::TexCoordGen& newTcg = m_tcgs[m_tcgCount];
|
|
newTcg.m_src = src;
|
|
newTcg.m_mtx = mtx;
|
|
newTcg.m_norm = norm;
|
|
newTcg.m_pmtx = pmtx;
|
|
return m_tcgCount++;
|
|
}
|
|
|
|
GX::TEVStage& GX::addTEVStage(Diagnostics& diag, const SourceLocation& loc) {
|
|
if (m_tevCount >= 16)
|
|
diag.reportBackendErr(loc, "GX TEV stage overflow");
|
|
GX::TEVStage& newTEV = m_tevs[m_tevCount];
|
|
newTEV.m_loc = loc;
|
|
if (m_tevCount) {
|
|
newTEV.m_prev = &m_tevs[m_tevCount - 1];
|
|
newTEV.m_prev->m_next = &newTEV;
|
|
}
|
|
++m_tevCount;
|
|
return newTEV;
|
|
}
|
|
|
|
GX::TEVStage& GX::addAlphaTEVStage(Diagnostics& diag, const SourceLocation& loc) {
|
|
++m_alphaTraceStage;
|
|
while (m_tevCount < m_alphaTraceStage + 1) {
|
|
TEVStage& stage = addTEVStage(diag, loc);
|
|
stage.m_color[3] = CC_CPREV;
|
|
stage.m_alpha[3] = CA_APREV;
|
|
}
|
|
return m_tevs[m_alphaTraceStage];
|
|
}
|
|
|
|
unsigned GX::RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, TexMtx mtx,
|
|
bool normalize, PTTexMtx pmtx) {
|
|
if (inst.m_op != IR::OpType::Call)
|
|
diag.reportBackendErr(inst.m_loc, "TexCoordGen resolution requires function");
|
|
|
|
const std::string& tcgName = inst.m_call.m_name;
|
|
if (!tcgName.compare("UV")) {
|
|
if (inst.getChildCount() < 1)
|
|
diag.reportBackendErr(inst.m_loc, "TexCoordGen UV(layerIdx) requires one argument");
|
|
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
|
|
auto& idxImm = idxInst.getImmVec();
|
|
return addTexCoordGen(diag, inst.m_loc, TexGenSrc(TG_TEX0 + unsigned(idxImm.simd[0])), mtx, normalize, pmtx);
|
|
} else if (!tcgName.compare("Normal"))
|
|
return addTexCoordGen(diag, inst.m_loc, TG_NRM, mtx, normalize, pmtx);
|
|
else if (!tcgName.compare("View"))
|
|
return addTexCoordGen(diag, inst.m_loc, TG_POS, mtx, normalize, pmtx);
|
|
|
|
/* Otherwise treat as game-specific function */
|
|
const IR::Instruction& tcgSrcInst = inst.getChildInst(ir, 0);
|
|
bool doNorm = normalize || tcgName.back() == 'N';
|
|
unsigned idx = RecursiveTraceTexGen(ir, diag, tcgSrcInst, TexMtx(TEXMTX0 + m_texMtxCount * 3), doNorm,
|
|
doNorm ? PTTexMtx(PTTEXMTX0 + m_texMtxCount * 3) : PTIDENTITY);
|
|
GX::TexCoordGen& tcg = m_tcgs[idx];
|
|
m_texMtxRefs[m_texMtxCount] = &tcg;
|
|
++m_texMtxCount;
|
|
tcg.m_gameFunction = tcgName;
|
|
tcg.m_gameArgs.clear();
|
|
for (int i = 1; i < inst.getChildCount(); ++i) {
|
|
const IR::Instruction& ci = inst.getChildInst(ir, i);
|
|
tcg.m_gameArgs.push_back(ci.getImmVec());
|
|
}
|
|
return idx;
|
|
}
|
|
|
|
GX::TraceResult GX::RecursiveTraceColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
|
|
bool swizzleAlpha) {
|
|
switch (inst.m_op) {
|
|
case IR::OpType::Call: {
|
|
const std::string& name = inst.m_call.m_name;
|
|
bool normalize = false;
|
|
if (!name.compare("Texture") || (normalize = true && !name.compare("TextureN"))) {
|
|
TEVStage& newStage = addTEVStage(diag, inst.m_loc);
|
|
|
|
if (inst.getChildCount() < 2)
|
|
diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
|
|
|
|
const IR::Instruction& mapInst = inst.getChildInst(ir, 0);
|
|
auto& mapImm = mapInst.getImmVec();
|
|
newStage.m_texMapIdx = unsigned(mapImm.simd[0]);
|
|
newStage.m_color[0] = swizzleAlpha ? CC_TEXA : CC_TEXC;
|
|
|
|
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
|
|
newStage.m_texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, IDENTITY, normalize, PTIDENTITY);
|
|
|
|
return TraceResult(&newStage);
|
|
} else if (!name.compare("ColorReg")) {
|
|
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
|
|
unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
|
|
if (swizzleAlpha)
|
|
m_aRegMask |= 1 << idx;
|
|
else
|
|
m_cRegMask |= 1 << idx;
|
|
return TraceResult(TevColorArg((swizzleAlpha ? CC_A0 : CC_C0) + idx * 2));
|
|
} else if (!name.compare("Lighting")) {
|
|
return TraceResult(swizzleAlpha ? CC_RASA : CC_RASC);
|
|
} else
|
|
diag.reportBackendErr(inst.m_loc, "GX backend unable to interpret '%s'", name.c_str());
|
|
break;
|
|
}
|
|
case IR::OpType::LoadImm: {
|
|
const atVec4f& vec = inst.m_loadImm.m_immVec;
|
|
if (vec.simd[0] == 0.f && vec.simd[1] == 0.f && vec.simd[2] == 0.f)
|
|
return TraceResult(CC_ZERO);
|
|
else if (vec.simd[0] == 1.f && vec.simd[1] == 1.f && vec.simd[2] == 1.f)
|
|
return TraceResult(CC_ONE);
|
|
unsigned idx = addKColor(diag, inst.m_loc, vec);
|
|
return TraceResult(TevKColorSel(TEV_KCSEL_K0 + idx));
|
|
}
|
|
case IR::OpType::Arithmetic: {
|
|
ArithmeticOp op = inst.m_arithmetic.m_op;
|
|
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
|
|
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
|
|
TraceResult aTrace;
|
|
TraceResult bTrace;
|
|
if (aInst.m_op != IR::OpType::Arithmetic && bInst.m_op == IR::OpType::Arithmetic) {
|
|
bTrace = RecursiveTraceColor(ir, diag, bInst);
|
|
aTrace = RecursiveTraceColor(ir, diag, aInst);
|
|
} else {
|
|
aTrace = RecursiveTraceColor(ir, diag, aInst);
|
|
bTrace = RecursiveTraceColor(ir, diag, bInst);
|
|
}
|
|
if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVStage &&
|
|
getStageIdx(aTrace.tevStage) > getStageIdx(bTrace.tevStage))
|
|
std::swap(aTrace, bTrace);
|
|
|
|
TevKColorSel newKColor = TEV_KCSEL_1;
|
|
if (aTrace.type == TraceResult::Type::TEVKColorSel && bTrace.type == TraceResult::Type::TEVKColorSel)
|
|
diag.reportBackendErr(inst.m_loc, "unable to handle 2 KColors in one stage");
|
|
else if (aTrace.type == TraceResult::Type::TEVKColorSel) {
|
|
newKColor = aTrace.tevKColorSel;
|
|
aTrace.type = TraceResult::Type::TEVColorArg;
|
|
aTrace.tevColorArg = CC_KONST;
|
|
} else if (bTrace.type == TraceResult::Type::TEVKColorSel) {
|
|
newKColor = bTrace.tevKColorSel;
|
|
bTrace.type = TraceResult::Type::TEVColorArg;
|
|
bTrace.tevColorArg = CC_KONST;
|
|
}
|
|
|
|
switch (op) {
|
|
case ArithmeticOp::Add: {
|
|
if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_prev != a) {
|
|
a->m_cRegOut = TEVLAZY;
|
|
b->m_color[3] = CC_LAZY;
|
|
b->m_lazyCInIdx = m_cRegLazy;
|
|
a->m_lazyCOutIdx = m_cRegLazy++;
|
|
} else if (b == &m_tevs[m_tevCount - 1] && a->m_texMapIdx == b->m_texMapIdx &&
|
|
a->m_texGenIdx == b->m_texGenIdx && a->m_color[3] == CC_ZERO && b->m_color[0] != CC_ZERO) {
|
|
a->m_color[3] = b->m_color[0];
|
|
--m_tevCount;
|
|
return TraceResult(a);
|
|
} else
|
|
b->m_color[3] = CC_CPREV;
|
|
return TraceResult(b);
|
|
} else if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVColorArg) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
if (a->m_color[3] != CC_ZERO)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for add combine");
|
|
a->m_color[3] = bTrace.tevColorArg;
|
|
a->m_kColor = newKColor;
|
|
return TraceResult(a);
|
|
} else if (aTrace.type == TraceResult::Type::TEVColorArg && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_color[3] != CC_ZERO)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for add combine");
|
|
b->m_color[3] = aTrace.tevColorArg;
|
|
b->m_kColor = newKColor;
|
|
return TraceResult(b);
|
|
} else if (aTrace.type == TraceResult::Type::TEVColorArg && bTrace.type == TraceResult::Type::TEVColorArg) {
|
|
TEVStage& stage = addTEVStage(diag, inst.m_loc);
|
|
stage.m_color[0] = aTrace.tevColorArg;
|
|
stage.m_color[3] = bTrace.tevColorArg;
|
|
stage.m_kColor = newKColor;
|
|
return TraceResult(&stage);
|
|
}
|
|
break;
|
|
}
|
|
case ArithmeticOp::Subtract: {
|
|
if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_prev != a) {
|
|
a->m_cRegOut = TEVLAZY;
|
|
b->m_color[3] = CC_LAZY;
|
|
b->m_lazyCInIdx = m_cRegLazy;
|
|
a->m_lazyCOutIdx = m_cRegLazy++;
|
|
} else
|
|
b->m_color[3] = CC_CPREV;
|
|
b->m_cop = TEV_SUB;
|
|
return TraceResult(b);
|
|
} else if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVColorArg) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
if (a->m_color[3] != CC_ZERO)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for subtract combine");
|
|
a->m_color[3] = bTrace.tevColorArg;
|
|
a->m_kColor = newKColor;
|
|
a->m_cop = TEV_SUB;
|
|
return TraceResult(a);
|
|
} else if (aTrace.type == TraceResult::Type::TEVColorArg && bTrace.type == TraceResult::Type::TEVColorArg) {
|
|
TEVStage& stage = addTEVStage(diag, inst.m_loc);
|
|
stage.m_color[0] = aTrace.tevColorArg;
|
|
stage.m_color[3] = bTrace.tevColorArg;
|
|
stage.m_kColor = newKColor;
|
|
stage.m_cop = TEV_SUB;
|
|
return TraceResult(&stage);
|
|
}
|
|
break;
|
|
}
|
|
case ArithmeticOp::Multiply: {
|
|
if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_color[2] != CC_ZERO)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for multiply combine");
|
|
if (b->m_prev != a) {
|
|
a->m_cRegOut = TEVLAZY;
|
|
b->m_color[2] = CC_LAZY;
|
|
b->m_lazyCInIdx = m_cRegLazy;
|
|
a->m_lazyCOutIdx = m_cRegLazy++;
|
|
} else
|
|
b->m_color[2] = CC_CPREV;
|
|
b->m_color[1] = b->m_color[0];
|
|
b->m_color[0] = CC_ZERO;
|
|
b->m_color[3] = CC_ZERO;
|
|
return TraceResult(b);
|
|
} else if (aTrace.type == TraceResult::Type::TEVColorArg && bTrace.type == TraceResult::Type::TEVColorArg) {
|
|
TEVStage& stage = addTEVStage(diag, inst.m_loc);
|
|
stage.m_color[1] = aTrace.tevColorArg;
|
|
stage.m_color[2] = bTrace.tevColorArg;
|
|
stage.m_kColor = newKColor;
|
|
return TraceResult(&stage);
|
|
} else if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVColorArg) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
if (a->m_color[1] != CC_ZERO) {
|
|
if (a->m_cRegOut != TEVPREV)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for multiply combine");
|
|
TEVStage& stage = addTEVStage(diag, inst.m_loc);
|
|
stage.m_color[1] = CC_CPREV;
|
|
stage.m_color[2] = bTrace.tevColorArg;
|
|
stage.m_kColor = newKColor;
|
|
return TraceResult(&stage);
|
|
}
|
|
a->m_color[1] = a->m_color[0];
|
|
a->m_color[0] = CC_ZERO;
|
|
a->m_color[2] = bTrace.tevColorArg;
|
|
a->m_kColor = newKColor;
|
|
return TraceResult(a);
|
|
} else if (aTrace.type == TraceResult::Type::TEVColorArg && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_color[1] != CC_ZERO) {
|
|
if (b->m_cRegOut != TEVPREV)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for multiply combine");
|
|
TEVStage& stage = addTEVStage(diag, inst.m_loc);
|
|
stage.m_color[1] = aTrace.tevColorArg;
|
|
stage.m_color[2] = CC_CPREV;
|
|
stage.m_kColor = newKColor;
|
|
return TraceResult(&stage);
|
|
}
|
|
b->m_color[1] = b->m_color[0];
|
|
b->m_color[0] = CC_ZERO;
|
|
b->m_color[2] = bTrace.tevColorArg;
|
|
b->m_kColor = newKColor;
|
|
return TraceResult(b);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
diag.reportBackendErr(inst.m_loc, "invalid arithmetic op");
|
|
}
|
|
|
|
diag.reportBackendErr(inst.m_loc, "unable to convert arithmetic to TEV stage");
|
|
break;
|
|
}
|
|
case IR::OpType::Swizzle: {
|
|
if (inst.m_swizzle.m_idxs[0] == 3 && inst.m_swizzle.m_idxs[1] == 3 && inst.m_swizzle.m_idxs[2] == 3 &&
|
|
inst.m_swizzle.m_idxs[3] == -1) {
|
|
const IR::Instruction& cInst = inst.getChildInst(ir, 0);
|
|
if (cInst.m_op != IR::OpType::Call)
|
|
diag.reportBackendErr(inst.m_loc, "only functions accepted for alpha swizzle");
|
|
return RecursiveTraceColor(ir, diag, cInst, true);
|
|
} else
|
|
diag.reportBackendErr(inst.m_loc, "only alpha extract may be performed with swizzle operation");
|
|
break;
|
|
}
|
|
default:
|
|
diag.reportBackendErr(inst.m_loc, "invalid color op");
|
|
}
|
|
|
|
return TraceResult();
|
|
}
|
|
|
|
GX::TraceResult GX::RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, const IR::Instruction& inst) {
|
|
switch (inst.m_op) {
|
|
case IR::OpType::Call: {
|
|
const std::string& name = inst.m_call.m_name;
|
|
bool normalize = false;
|
|
if (!name.compare("Texture") || (normalize = true && !name.compare("TextureN"))) {
|
|
if (inst.getChildCount() < 2)
|
|
diag.reportBackendErr(inst.m_loc, "Texture(map, texgen) requires 2 arguments");
|
|
|
|
const IR::Instruction& mapInst = inst.getChildInst(ir, 0);
|
|
const atVec4f& mapImm = mapInst.getImmVec();
|
|
unsigned mapIdx = unsigned(mapImm.simd[0]);
|
|
|
|
int foundStage = -1;
|
|
for (int i = m_alphaTraceStage + 1; i < int(m_tevCount); ++i) {
|
|
TEVStage& testStage = m_tevs[i];
|
|
if (testStage.m_texMapIdx == mapIdx) {
|
|
foundStage = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundStage >= 0) {
|
|
m_alphaTraceStage = foundStage;
|
|
TEVStage& stage = m_tevs[foundStage];
|
|
stage.m_alpha[0] = CA_TEXA;
|
|
return TraceResult(&stage);
|
|
}
|
|
|
|
TEVStage& newStage = addAlphaTEVStage(diag, inst.m_loc);
|
|
newStage.m_color[3] = CC_CPREV;
|
|
|
|
newStage.m_texMapIdx = mapIdx;
|
|
newStage.m_alpha[0] = CA_TEXA;
|
|
|
|
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
|
|
newStage.m_texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, IDENTITY, normalize, PTIDENTITY);
|
|
|
|
return TraceResult(&newStage);
|
|
} else if (!name.compare("ColorReg")) {
|
|
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
|
|
unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
|
|
m_aRegMask |= 1 << idx;
|
|
return TraceResult(TevAlphaArg(CA_A0 + idx));
|
|
} else if (!name.compare("Lighting")) {
|
|
return TraceResult(CA_RASA);
|
|
} else
|
|
diag.reportBackendErr(inst.m_loc, "GX backend unable to interpret '%s'", name.c_str());
|
|
break;
|
|
}
|
|
case IR::OpType::LoadImm: {
|
|
const atVec4f& vec = inst.m_loadImm.m_immVec;
|
|
if (vec.simd[0] == 0.f)
|
|
return TraceResult(CA_ZERO);
|
|
else if (vec.simd[0] == 1.f)
|
|
return TraceResult(TEV_KASEL_1);
|
|
unsigned idx = addKAlpha(diag, inst.m_loc, vec.simd[0]);
|
|
return TraceResult(TevKAlphaSel(TEV_KASEL_K0_A + idx));
|
|
}
|
|
case IR::OpType::Arithmetic: {
|
|
ArithmeticOp op = inst.m_arithmetic.m_op;
|
|
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
|
|
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
|
|
TraceResult aTrace;
|
|
TraceResult bTrace;
|
|
if (aInst.m_op != IR::OpType::Arithmetic && bInst.m_op == IR::OpType::Arithmetic) {
|
|
bTrace = RecursiveTraceAlpha(ir, diag, bInst);
|
|
aTrace = RecursiveTraceAlpha(ir, diag, aInst);
|
|
} else {
|
|
aTrace = RecursiveTraceAlpha(ir, diag, aInst);
|
|
bTrace = RecursiveTraceAlpha(ir, diag, bInst);
|
|
}
|
|
if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVStage &&
|
|
getStageIdx(aTrace.tevStage) > getStageIdx(bTrace.tevStage))
|
|
std::swap(aTrace, bTrace);
|
|
|
|
TevKAlphaSel newKAlpha = TEV_KASEL_1;
|
|
if (aTrace.type == TraceResult::Type::TEVKAlphaSel && bTrace.type == TraceResult::Type::TEVKAlphaSel)
|
|
diag.reportBackendErr(inst.m_loc, "unable to handle 2 KAlphas in one stage");
|
|
else if (aTrace.type == TraceResult::Type::TEVKAlphaSel) {
|
|
newKAlpha = aTrace.tevKAlphaSel;
|
|
aTrace.type = TraceResult::Type::TEVAlphaArg;
|
|
aTrace.tevAlphaArg = CA_KONST;
|
|
} else if (bTrace.type == TraceResult::Type::TEVKAlphaSel) {
|
|
newKAlpha = bTrace.tevKAlphaSel;
|
|
bTrace.type = TraceResult::Type::TEVAlphaArg;
|
|
bTrace.tevAlphaArg = CA_KONST;
|
|
}
|
|
|
|
switch (op) {
|
|
case ArithmeticOp::Add: {
|
|
if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_prev != a) {
|
|
a->m_aRegOut = TEVLAZY;
|
|
b->m_alpha[3] = CA_LAZY;
|
|
if (a->m_lazyAOutIdx != -1)
|
|
b->m_lazyAInIdx = a->m_lazyAOutIdx;
|
|
else {
|
|
b->m_lazyAInIdx = m_aRegLazy;
|
|
a->m_lazyAOutIdx = m_aRegLazy++;
|
|
}
|
|
} else
|
|
b->m_alpha[3] = CA_APREV;
|
|
return TraceResult(b);
|
|
} else if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVAlphaArg) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
if (a->m_alpha[3] != CA_ZERO)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for add combine");
|
|
a->m_alpha[3] = bTrace.tevAlphaArg;
|
|
a->m_kAlpha = newKAlpha;
|
|
return TraceResult(a);
|
|
} else if (aTrace.type == TraceResult::Type::TEVAlphaArg && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_alpha[3] != CA_ZERO)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for add combine");
|
|
b->m_alpha[3] = aTrace.tevAlphaArg;
|
|
b->m_kAlpha = newKAlpha;
|
|
return TraceResult(b);
|
|
} else if (aTrace.type == TraceResult::Type::TEVAlphaArg && bTrace.type == TraceResult::Type::TEVAlphaArg) {
|
|
TEVStage& stage = addAlphaTEVStage(diag, inst.m_loc);
|
|
stage.m_alpha[0] = aTrace.tevAlphaArg;
|
|
stage.m_alpha[3] = bTrace.tevAlphaArg;
|
|
stage.m_kAlpha = newKAlpha;
|
|
return TraceResult(&stage);
|
|
}
|
|
break;
|
|
}
|
|
case ArithmeticOp::Subtract: {
|
|
if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_aop != TEV_SUB)
|
|
diag.reportBackendErr(inst.m_loc, "unable to integrate alpha subtraction into stage chain");
|
|
if (b->m_prev != a) {
|
|
a->m_aRegOut = TEVLAZY;
|
|
b->m_alpha[3] = CA_LAZY;
|
|
if (a->m_lazyAOutIdx != -1)
|
|
b->m_lazyAInIdx = a->m_lazyAOutIdx;
|
|
else {
|
|
b->m_lazyAInIdx = m_aRegLazy;
|
|
a->m_lazyAOutIdx = m_aRegLazy++;
|
|
}
|
|
} else
|
|
b->m_alpha[3] = CA_APREV;
|
|
return TraceResult(b);
|
|
} else if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVAlphaArg) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
if (a->m_aop != TEV_SUB)
|
|
diag.reportBackendErr(inst.m_loc, "unable to integrate alpha subtraction into stage chain");
|
|
if (a->m_alpha[3] != CA_ZERO)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for add combine");
|
|
a->m_alpha[3] = bTrace.tevAlphaArg;
|
|
a->m_kAlpha = newKAlpha;
|
|
return TraceResult(a);
|
|
} else if (aTrace.type == TraceResult::Type::TEVAlphaArg && bTrace.type == TraceResult::Type::TEVAlphaArg) {
|
|
TEVStage& stage = addAlphaTEVStage(diag, inst.m_loc);
|
|
stage.m_alpha[0] = aTrace.tevAlphaArg;
|
|
stage.m_alpha[3] = bTrace.tevAlphaArg;
|
|
stage.m_kAlpha = newKAlpha;
|
|
stage.m_aop = TEV_SUB;
|
|
return TraceResult(&stage);
|
|
}
|
|
break;
|
|
}
|
|
case ArithmeticOp::Multiply: {
|
|
if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_alpha[2] != CA_ZERO)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for multiply combine");
|
|
if (b->m_prev != a) {
|
|
a->m_aRegOut = TEVLAZY;
|
|
b->m_alpha[2] = CA_LAZY;
|
|
b->m_lazyAInIdx = m_aRegLazy;
|
|
a->m_lazyAOutIdx = m_aRegLazy++;
|
|
} else
|
|
b->m_alpha[2] = CA_APREV;
|
|
b->m_alpha[1] = b->m_alpha[0];
|
|
b->m_alpha[0] = CA_ZERO;
|
|
b->m_alpha[3] = CA_ZERO;
|
|
return TraceResult(b);
|
|
} else if (aTrace.type == TraceResult::Type::TEVAlphaArg && bTrace.type == TraceResult::Type::TEVAlphaArg) {
|
|
TEVStage& stage = addAlphaTEVStage(diag, inst.m_loc);
|
|
stage.m_color[3] = CC_CPREV;
|
|
stage.m_alpha[1] = aTrace.tevAlphaArg;
|
|
stage.m_alpha[2] = bTrace.tevAlphaArg;
|
|
stage.m_kAlpha = newKAlpha;
|
|
return TraceResult(&stage);
|
|
} else if (aTrace.type == TraceResult::Type::TEVStage && bTrace.type == TraceResult::Type::TEVAlphaArg) {
|
|
TEVStage* a = aTrace.tevStage;
|
|
if (a->m_alpha[1] != CA_ZERO) {
|
|
if (a->m_aRegOut != TEVPREV)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for multiply combine");
|
|
TEVStage& stage = addAlphaTEVStage(diag, inst.m_loc);
|
|
stage.m_alpha[1] = CA_APREV;
|
|
stage.m_alpha[2] = bTrace.tevAlphaArg;
|
|
stage.m_kAlpha = newKAlpha;
|
|
return TraceResult(&stage);
|
|
}
|
|
a->m_alpha[1] = a->m_alpha[0];
|
|
a->m_alpha[0] = CA_ZERO;
|
|
a->m_alpha[2] = bTrace.tevAlphaArg;
|
|
a->m_kAlpha = newKAlpha;
|
|
return TraceResult(a);
|
|
} else if (aTrace.type == TraceResult::Type::TEVAlphaArg && bTrace.type == TraceResult::Type::TEVStage) {
|
|
TEVStage* b = bTrace.tevStage;
|
|
if (b->m_alpha[1] != CA_ZERO) {
|
|
if (b->m_aRegOut != TEVPREV)
|
|
diag.reportBackendErr(inst.m_loc, "unable to modify TEV stage for multiply combine");
|
|
TEVStage& stage = addAlphaTEVStage(diag, inst.m_loc);
|
|
stage.m_alpha[1] = aTrace.tevAlphaArg;
|
|
stage.m_alpha[2] = CA_APREV;
|
|
stage.m_kAlpha = newKAlpha;
|
|
return TraceResult(&stage);
|
|
}
|
|
b->m_alpha[1] = b->m_alpha[0];
|
|
b->m_alpha[0] = CA_ZERO;
|
|
b->m_alpha[2] = bTrace.tevAlphaArg;
|
|
b->m_kAlpha = newKAlpha;
|
|
return TraceResult(b);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
diag.reportBackendErr(inst.m_loc, "invalid arithmetic op");
|
|
}
|
|
|
|
diag.reportBackendErr(inst.m_loc, "unable to convert arithmetic to TEV stage");
|
|
break;
|
|
}
|
|
case IR::OpType::Swizzle: {
|
|
if (inst.m_swizzle.m_idxs[0] == 3 && inst.m_swizzle.m_idxs[1] == 3 && inst.m_swizzle.m_idxs[2] == 3 &&
|
|
inst.m_swizzle.m_idxs[3] == -1) {
|
|
const IR::Instruction& cInst = inst.getChildInst(ir, 0);
|
|
if (cInst.m_op != IR::OpType::Call)
|
|
diag.reportBackendErr(inst.m_loc, "only functions accepted for alpha swizzle");
|
|
return RecursiveTraceAlpha(ir, diag, cInst);
|
|
} else
|
|
diag.reportBackendErr(inst.m_loc, "only alpha extract may be performed with swizzle operation");
|
|
break;
|
|
}
|
|
default:
|
|
diag.reportBackendErr(inst.m_loc, "invalid alpha op");
|
|
}
|
|
|
|
return TraceResult();
|
|
}
|
|
|
|
void GX::reset(const IR& ir, Diagnostics& diag) {
|
|
diag.setBackend("GX");
|
|
|
|
m_tevCount = 0;
|
|
m_tcgCount = 0;
|
|
m_texMtxCount = 0;
|
|
m_kcolorCount = 0;
|
|
m_cRegMask = 0;
|
|
m_cRegLazy = 0;
|
|
m_aRegMask = 0;
|
|
m_aRegLazy = 0;
|
|
m_alphaTraceStage = -1;
|
|
|
|
/* Final instruction is the root call by hecl convention */
|
|
const IR::Instruction& rootCall = ir.m_instructions.back();
|
|
if (!rootCall.m_call.m_name.compare("HECLOpaque")) {
|
|
m_blendSrc = BL_ONE;
|
|
m_blendDst = BL_ZERO;
|
|
} else if (!rootCall.m_call.m_name.compare("HECLAlpha")) {
|
|
m_blendSrc = BL_SRCALPHA;
|
|
m_blendDst = BL_INVSRCALPHA;
|
|
} else if (!rootCall.m_call.m_name.compare("HECLAdditive")) {
|
|
m_blendSrc = BL_SRCALPHA;
|
|
m_blendDst = BL_ONE;
|
|
} else {
|
|
diag.reportBackendErr(rootCall.m_loc, "GX backend doesn't handle '%s' root", rootCall.m_call.m_name.c_str());
|
|
return;
|
|
}
|
|
|
|
/* Follow Color Chain */
|
|
const IR::Instruction& colorRoot = ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(0));
|
|
TraceResult result = RecursiveTraceColor(ir, diag, colorRoot);
|
|
switch (result.type) {
|
|
case TraceResult::Type::TEVColorArg: {
|
|
TEVStage& stage = addTEVStage(diag, colorRoot.m_loc);
|
|
stage.m_color[3] = result.tevColorArg;
|
|
break;
|
|
}
|
|
case TraceResult::Type::TEVKColorSel: {
|
|
TEVStage& stage = addTEVStage(diag, colorRoot.m_loc);
|
|
stage.m_color[3] = CC_KONST;
|
|
stage.m_kColor = result.tevKColorSel;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Follow Alpha Chain */
|
|
if (rootCall.m_call.m_argInstIdxs.size() > 1) {
|
|
const IR::Instruction& alphaRoot = ir.m_instructions.at(rootCall.m_call.m_argInstIdxs.at(1));
|
|
TraceResult result = RecursiveTraceAlpha(ir, diag, alphaRoot);
|
|
switch (result.type) {
|
|
case TraceResult::Type::TEVAlphaArg: {
|
|
TEVStage& stage = addAlphaTEVStage(diag, alphaRoot.m_loc);
|
|
stage.m_alpha[3] = result.tevAlphaArg;
|
|
break;
|
|
}
|
|
case TraceResult::Type::TEVKAlphaSel: {
|
|
TEVStage& stage = addAlphaTEVStage(diag, alphaRoot.m_loc);
|
|
stage.m_alpha[3] = CA_KONST;
|
|
stage.m_kAlpha = result.tevKAlphaSel;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Ensure Alpha reaches end of chain */
|
|
if (m_alphaTraceStage >= 0)
|
|
for (unsigned i = m_alphaTraceStage + 1; i < m_tevCount; ++i)
|
|
m_tevs[i].m_alpha[3] = CA_APREV;
|
|
}
|
|
|
|
/* Resolve lazy color/alpha regs */
|
|
if (m_cRegLazy) {
|
|
for (int i = 0; i < int(m_tevCount); ++i) {
|
|
TEVStage& stage = m_tevs[i];
|
|
if (stage.m_cRegOut == TEVLAZY) {
|
|
int picked = pickCLazy(diag, stage.m_loc, i);
|
|
stage.m_cRegOut = TevRegID(TEVREG0 + picked);
|
|
for (int j = i + 1; j < int(m_tevCount); ++j) {
|
|
TEVStage& nstage = m_tevs[j];
|
|
if (nstage.m_lazyCInIdx == stage.m_lazyCOutIdx)
|
|
for (int c = 0; c < 4; ++c)
|
|
if (nstage.m_color[c] == CC_LAZY)
|
|
nstage.m_color[c] = TevColorArg(CC_C0 + picked * 2);
|
|
}
|
|
}
|
|
|
|
if (stage.m_aRegOut == TEVLAZY) {
|
|
int picked = pickALazy(diag, stage.m_loc, i);
|
|
stage.m_aRegOut = TevRegID(TEVREG0 + picked);
|
|
for (int j = i + 1; j < int(m_tevCount); ++j) {
|
|
TEVStage& nstage = m_tevs[j];
|
|
if (nstage.m_lazyAInIdx == stage.m_lazyAOutIdx)
|
|
for (int c = 0; c < 4; ++c)
|
|
if (nstage.m_alpha[c] == CA_LAZY)
|
|
nstage.m_alpha[c] = TevAlphaArg(CA_A0 + picked);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace hecl::Backend
|