metaforce/hecl/lib/Backend/ProgrammableCommon.cpp

377 lines
14 KiB
C++

#include "hecl/Backend/ProgrammableCommon.hpp"
#include <map>
namespace hecl::Backend {
const char* ProgrammableCommon::BlendFactorToDefine(BlendFactor factor, BlendFactor defaultFactor) {
switch (factor) {
case BlendFactor::Zero:
return "ZERO";
case BlendFactor::One:
return "ONE";
case BlendFactor::SrcColor:
return "SRCCOLOR";
case BlendFactor::InvSrcColor:
return "INVSRCCOLOR";
case BlendFactor::DstColor:
return "DSTCOLOR";
case BlendFactor::InvDstColor:
return "INVDSTCOLOR";
case BlendFactor::SrcAlpha:
return "SRCALPHA";
case BlendFactor::InvSrcAlpha:
return "INVSRCALPHA";
case BlendFactor::DstAlpha:
return "DSTALPHA";
case BlendFactor::InvDstAlpha:
return "INVDSTALPHA";
case BlendFactor::SrcColor1:
return "SRCCOLOR1";
case BlendFactor::InvSrcColor1:
return "INVSRCCOLOR1";
default:
return BlendFactorToDefine(defaultFactor, BlendFactor::Zero);
}
}
unsigned ProgrammableCommon::addTexCoordGen(TexGenSrc src, int uvIdx, int mtx, bool normalize) {
for (unsigned i = 0; i < m_tcgs.size(); ++i) {
TexCoordGen& tcg = m_tcgs[i];
if (tcg.m_src == src && tcg.m_uvIdx == uvIdx && tcg.m_mtx == mtx && tcg.m_norm == normalize)
return i;
}
m_tcgs.emplace_back();
TexCoordGen& newTcg = m_tcgs.back();
newTcg.m_src = src;
newTcg.m_uvIdx = uvIdx;
newTcg.m_mtx = mtx;
newTcg.m_norm = normalize;
return m_tcgs.size() - 1;
}
unsigned ProgrammableCommon::addTexSampling(unsigned mapIdx, unsigned tcgIdx) {
for (unsigned i = 0; i < m_texSamplings.size(); ++i) {
TexSampling& samp = m_texSamplings[i];
if (samp.mapIdx == mapIdx && samp.tcgIdx == tcgIdx)
return i;
}
m_texSamplings.emplace_back();
TexSampling& samp = m_texSamplings.back();
samp.mapIdx = mapIdx;
samp.tcgIdx = tcgIdx;
if (m_texMapEnd < mapIdx + 1)
m_texMapEnd = mapIdx + 1;
return m_texSamplings.size() - 1;
}
unsigned ProgrammableCommon::RecursiveTraceTexGen(const IR& ir, Diagnostics& diag, const IR::Instruction& inst, int mtx,
bool normalize) {
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(TexGenSrc::UV, int(idxImm.simd[0]), mtx, normalize);
} else if (!tcgName.compare("Normal"))
return addTexCoordGen(TexGenSrc::Normal, -1, mtx, normalize);
else if (!tcgName.compare("View"))
return addTexCoordGen(TexGenSrc::Position, -1, mtx, normalize);
/* Otherwise treat as game-specific function */
const IR::Instruction& tcgSrcInst = inst.getChildInst(ir, 0);
unsigned idx = RecursiveTraceTexGen(ir, diag, tcgSrcInst, m_texMtxRefs.size(), normalize || tcgName.back() == 'N');
TexCoordGen& tcg = m_tcgs[idx];
m_texMtxRefs.push_back(idx);
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;
}
std::string ProgrammableCommon::RecursiveTraceDiffuseColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
bool toSwizzle, bool fallback) {
switch (inst.m_op) {
case IR::OpType::Call: {
const std::string& name = inst.m_call.m_name;
bool normalize = false;
if (!name.compare("TextureD") ||
(fallback && !name.compare("Texture")) ||
((normalize = true) && !name.compare("TextureDN")) ||
((normalize = true) && fallback && !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);
auto& mapImm = mapInst.getImmVec();
unsigned mapIdx = unsigned(mapImm.simd[0]);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx))
: EmitSamplingUseRGB(addTexSampling(mapIdx, texGenIdx));
} else
return std::string();
}
case IR::OpType::LoadImm: {
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVec3(vec);
}
case IR::OpType::Arithmetic: {
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
std::string aTrace = RecursiveTraceDiffuseColor(ir, diag, aInst, false, fallback);
std::string bTrace = RecursiveTraceDiffuseColor(ir, diag, bInst, false, fallback);
return (!aTrace.empty()) ? aTrace : bTrace;
}
case IR::OpType::Swizzle: {
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
std::string aTrace = RecursiveTraceDiffuseColor(ir, diag, aInst, true, fallback);
if (!aTrace.empty())
return EmitSwizzle3(diag, inst.m_loc, aTrace, inst.m_swizzle.m_idxs);
return std::string();
}
default:
diag.reportBackendErr(inst.m_loc, "invalid color op");
}
return std::string();
}
std::string ProgrammableCommon::RecursiveTraceColor(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
bool toSwizzle) {
switch (inst.m_op) {
case IR::OpType::Call: {
const std::string& name = inst.m_call.m_name;
bool normalize = false;
if (!name.compare("Texture") || !name.compare("TextureD") ||
((normalize = true) && !name.compare("TextureN")) ||
((normalize = true) && !name.compare("TextureDN"))) {
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();
unsigned mapIdx = unsigned(mapImm.simd[0]);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx))
: EmitSamplingUseRGB(addTexSampling(mapIdx, texGenIdx));
} else if (!name.compare("ColorReg")) {
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseRGB(idx);
} else if (!name.compare("Lighting")) {
m_lighting = true;
return toSwizzle ? EmitLightingRaw() : EmitLightingRGB();
} else if (!name.compare("vec3")) {
if (inst.getChildCount() < 3)
diag.reportBackendErr(inst.m_loc, "vec3(r,g,b) requires 3 arguments");
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
const IR::Instruction& bInst = inst.getChildInst(ir, 1);
const IR::Instruction& cInst = inst.getChildInst(ir, 2);
return EmitVec3(RecursiveTraceAlpha(ir, diag, aInst, false), RecursiveTraceAlpha(ir, diag, bInst, false),
RecursiveTraceAlpha(ir, diag, cInst, false));
} else
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
break;
}
case IR::OpType::LoadImm: {
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVec3(vec);
}
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);
std::string aTrace = RecursiveTraceColor(ir, diag, aInst, false);
std::string bTrace = RecursiveTraceColor(ir, diag, bInst, false);
switch (op) {
case ArithmeticOp::Add: {
return EmitAdd(aTrace, bTrace);
}
case ArithmeticOp::Subtract: {
return EmitSub(aTrace, bTrace);
}
case ArithmeticOp::Multiply: {
return EmitMult(aTrace, bTrace);
}
case ArithmeticOp::Divide: {
return EmitDiv(aTrace, bTrace);
}
default:
diag.reportBackendErr(inst.m_loc, "invalid arithmetic op");
}
break;
}
case IR::OpType::Swizzle: {
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
std::string aTrace = RecursiveTraceColor(ir, diag, aInst, true);
return EmitSwizzle3(diag, inst.m_loc, aTrace, inst.m_swizzle.m_idxs);
}
default:
diag.reportBackendErr(inst.m_loc, "invalid color op");
}
return std::string();
}
std::string ProgrammableCommon::RecursiveTraceAlpha(const IR& ir, Diagnostics& diag, const IR::Instruction& inst,
bool toSwizzle) {
switch (inst.m_op) {
case IR::OpType::Call: {
const std::string& name = inst.m_call.m_name;
bool normalize = false;
if (!name.compare("Texture") || !name.compare("TextureD") ||
((normalize = true) && !name.compare("TextureN")) ||
((normalize = true) && !name.compare("TextureDN"))) {
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]);
const IR::Instruction& tcgInst = inst.getChildInst(ir, 1);
unsigned texGenIdx = RecursiveTraceTexGen(ir, diag, tcgInst, -1, normalize);
return toSwizzle ? EmitSamplingUseRaw(addTexSampling(mapIdx, texGenIdx))
: EmitSamplingUseAlpha(addTexSampling(mapIdx, texGenIdx));
} else if (!name.compare("ColorReg")) {
const IR::Instruction& idxInst = inst.getChildInst(ir, 0);
unsigned idx = unsigned(idxInst.getImmVec().simd[0]);
return toSwizzle ? EmitColorRegUseRaw(idx) : EmitColorRegUseAlpha(idx);
} else if (!name.compare("Lighting")) {
m_lighting = true;
return toSwizzle ? EmitLightingRaw() : EmitLightingAlpha();
} else
diag.reportBackendErr(inst.m_loc, "unable to interpret '%s'", name.c_str());
break;
}
case IR::OpType::LoadImm: {
const atVec4f& vec = inst.m_loadImm.m_immVec;
return EmitVal(vec.simd[0]);
}
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);
std::string aTrace = RecursiveTraceAlpha(ir, diag, aInst, false);
std::string bTrace = RecursiveTraceAlpha(ir, diag, bInst, false);
switch (op) {
case ArithmeticOp::Add: {
return EmitAdd(aTrace, bTrace);
}
case ArithmeticOp::Subtract: {
return EmitSub(aTrace, bTrace);
}
case ArithmeticOp::Multiply: {
return EmitMult(aTrace, bTrace);
}
case ArithmeticOp::Divide: {
return EmitDiv(aTrace, bTrace);
}
default:
diag.reportBackendErr(inst.m_loc, "invalid arithmetic op");
}
break;
}
case IR::OpType::Swizzle: {
const IR::Instruction& aInst = inst.getChildInst(ir, 0);
std::string aTrace = RecursiveTraceAlpha(ir, diag, aInst, true);
return EmitSwizzle1(diag, inst.m_loc, aTrace, inst.m_swizzle.m_idxs);
}
default:
diag.reportBackendErr(inst.m_loc, "invalid alpha op");
}
return std::string();
}
void ProgrammableCommon::resetResourceAccumulators() {
m_texSamplings.clear();
m_texMapEnd = 0;
m_tcgs.clear();
m_texMtxRefs.clear();
}
void ProgrammableCommon::reset(const IR& ir, Diagnostics& diag, const char* backendName) {
m_lighting = false;
m_texSamplings.clear();
m_texMapEnd = 0;
m_tcgs.clear();
m_texMtxRefs.clear();
m_colorExpr.clear();
m_alphaExpr.clear();
diag.setBackend(backendName);
/* 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 = BlendFactor::One;
m_blendDst = BlendFactor::Zero;
} else if (!rootCall.m_call.m_name.compare("HECLAlpha")) {
m_blendSrc = BlendFactor::SrcAlpha;
m_blendDst = BlendFactor::InvSrcAlpha;
} else if (!rootCall.m_call.m_name.compare("HECLAdditive")) {
m_blendSrc = BlendFactor::SrcAlpha;
m_blendDst = BlendFactor::One;
} else {
diag.reportBackendErr(rootCall.m_loc, "%s backend doesn't handle '%s' root", backendName,
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));
m_diffuseColorExpr = RecursiveTraceDiffuseColor(ir, diag, colorRoot, false, false);
resetResourceAccumulators();
if (m_diffuseColorExpr.empty()) {
m_diffuseColorExpr = RecursiveTraceDiffuseColor(ir, diag, colorRoot, false, true);
resetResourceAccumulators();
}
m_colorExpr = RecursiveTraceColor(ir, diag, colorRoot, false);
/* 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));
m_alphaExpr = RecursiveTraceAlpha(ir, diag, alphaRoot, false);
}
}
static const char SWIZZLE_CHARS[] = "rgba";
std::string ProgrammableCommon::EmitSwizzle3(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const atInt8 swiz[4]) const {
std::string retval = a + '.';
for (int i = 0; i < 3; ++i) {
if (swiz[i] < 0 || swiz[i] > 3)
diag.reportBackendErr(loc, "unable to use swizzle as RGB value");
retval += SWIZZLE_CHARS[swiz[i]];
}
return retval;
}
std::string ProgrammableCommon::EmitSwizzle1(Diagnostics& diag, const SourceLocation& loc, const std::string& a,
const atInt8 swiz[4]) const {
std::string retval = a + '.';
if (swiz[0] < 0 || swiz[0] > 3)
diag.reportBackendErr(loc, "unable to use swizzle as Alpha value");
retval += SWIZZLE_CHARS[swiz[0]];
return retval;
}
} // namespace hecl::Backend