#include "hecl/Backend/GX.hpp" namespace hecl::Backend { template <> void GX::Color::Enumerate::Read>(typename Read::StreamT& reader) { reader.readUBytesToBuf(&num, 4); } template <> void GX::Color::Enumerate::Write>(typename Write::StreamT& writer) { writer.writeUBytes(reinterpret_cast(&num), 4); } template <> void GX::Color::Enumerate::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