mirror of
https://github.com/encounter/aurora.git
synced 2025-07-21 04:25:56 +00:00
346 lines
10 KiB
C++
346 lines
10 KiB
C++
#include "shader_info.hpp"
|
|
|
|
namespace aurora::gfx::gx {
|
|
namespace {
|
|
Module Log("aurora::gfx::gx");
|
|
|
|
void color_arg_reg_info(GXTevColorArg arg, const TevStage& stage, ShaderInfo& info) {
|
|
switch (arg) {
|
|
case GX_CC_CPREV:
|
|
case GX_CC_APREV:
|
|
if (!info.writesTevReg.test(GX_TEVPREV)) {
|
|
info.loadsTevReg.set(GX_TEVPREV);
|
|
}
|
|
break;
|
|
case GX_CC_C0:
|
|
case GX_CC_A0:
|
|
if (!info.writesTevReg.test(GX_TEVREG0)) {
|
|
info.loadsTevReg.set(GX_TEVREG0);
|
|
}
|
|
break;
|
|
case GX_CC_C1:
|
|
case GX_CC_A1:
|
|
if (!info.writesTevReg.test(GX_TEVREG1)) {
|
|
info.loadsTevReg.set(GX_TEVREG1);
|
|
}
|
|
break;
|
|
case GX_CC_C2:
|
|
case GX_CC_A2:
|
|
if (!info.writesTevReg.test(GX_TEVREG2)) {
|
|
info.loadsTevReg.set(GX_TEVREG2);
|
|
}
|
|
break;
|
|
case GX_CC_TEXC:
|
|
case GX_CC_TEXA:
|
|
CHECK(stage.texCoordId != GX_TEXCOORD_NULL, "tex coord not bound");
|
|
CHECK(stage.texMapId != GX_TEXMAP_NULL, "tex map not bound");
|
|
info.sampledTexCoords.set(stage.texCoordId);
|
|
info.sampledTextures.set(stage.texMapId);
|
|
break;
|
|
case GX_CC_RASC:
|
|
case GX_CC_RASA:
|
|
if (stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1) {
|
|
info.sampledColorChannels.set(stage.channelId - GX_COLOR0A0);
|
|
}
|
|
break;
|
|
case GX_CC_KONST:
|
|
switch (stage.kcSel) {
|
|
case GX_TEV_KCSEL_K0:
|
|
case GX_TEV_KCSEL_K0_R:
|
|
case GX_TEV_KCSEL_K0_G:
|
|
case GX_TEV_KCSEL_K0_B:
|
|
case GX_TEV_KCSEL_K0_A:
|
|
info.sampledKColors.set(0);
|
|
break;
|
|
case GX_TEV_KCSEL_K1:
|
|
case GX_TEV_KCSEL_K1_R:
|
|
case GX_TEV_KCSEL_K1_G:
|
|
case GX_TEV_KCSEL_K1_B:
|
|
case GX_TEV_KCSEL_K1_A:
|
|
info.sampledKColors.set(1);
|
|
break;
|
|
case GX_TEV_KCSEL_K2:
|
|
case GX_TEV_KCSEL_K2_R:
|
|
case GX_TEV_KCSEL_K2_G:
|
|
case GX_TEV_KCSEL_K2_B:
|
|
case GX_TEV_KCSEL_K2_A:
|
|
info.sampledKColors.set(2);
|
|
break;
|
|
case GX_TEV_KCSEL_K3:
|
|
case GX_TEV_KCSEL_K3_R:
|
|
case GX_TEV_KCSEL_K3_G:
|
|
case GX_TEV_KCSEL_K3_B:
|
|
case GX_TEV_KCSEL_K3_A:
|
|
info.sampledKColors.set(3);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void alpha_arg_reg_info(GXTevAlphaArg arg, const TevStage& stage, ShaderInfo& info) {
|
|
switch (arg) {
|
|
case GX_CA_APREV:
|
|
if (!info.writesTevReg.test(GX_TEVPREV)) {
|
|
info.loadsTevReg.set(GX_TEVPREV);
|
|
}
|
|
break;
|
|
case GX_CA_A0:
|
|
if (!info.writesTevReg.test(GX_TEVREG0)) {
|
|
info.loadsTevReg.set(GX_TEVREG0);
|
|
}
|
|
break;
|
|
case GX_CA_A1:
|
|
if (!info.writesTevReg.test(GX_TEVREG1)) {
|
|
info.loadsTevReg.set(GX_TEVREG1);
|
|
}
|
|
break;
|
|
case GX_CA_A2:
|
|
if (!info.writesTevReg.test(GX_TEVREG2)) {
|
|
info.loadsTevReg.set(GX_TEVREG2);
|
|
}
|
|
break;
|
|
case GX_CA_TEXA:
|
|
CHECK(stage.texCoordId != GX_TEXCOORD_NULL, "tex coord not bound");
|
|
CHECK(stage.texMapId != GX_TEXMAP_NULL, "tex map not bound");
|
|
info.sampledTexCoords.set(stage.texCoordId);
|
|
info.sampledTextures.set(stage.texMapId);
|
|
break;
|
|
case GX_CA_RASA:
|
|
if (stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1) {
|
|
info.sampledColorChannels.set(stage.channelId - GX_COLOR0A0);
|
|
}
|
|
break;
|
|
case GX_CA_KONST:
|
|
switch (stage.kaSel) {
|
|
case GX_TEV_KASEL_K0_R:
|
|
case GX_TEV_KASEL_K0_G:
|
|
case GX_TEV_KASEL_K0_B:
|
|
case GX_TEV_KASEL_K0_A:
|
|
info.sampledKColors.set(0);
|
|
break;
|
|
case GX_TEV_KASEL_K1_R:
|
|
case GX_TEV_KASEL_K1_G:
|
|
case GX_TEV_KASEL_K1_B:
|
|
case GX_TEV_KASEL_K1_A:
|
|
info.sampledKColors.set(1);
|
|
break;
|
|
case GX_TEV_KASEL_K2_R:
|
|
case GX_TEV_KASEL_K2_G:
|
|
case GX_TEV_KASEL_K2_B:
|
|
case GX_TEV_KASEL_K2_A:
|
|
info.sampledKColors.set(2);
|
|
break;
|
|
case GX_TEV_KASEL_K3_R:
|
|
case GX_TEV_KASEL_K3_G:
|
|
case GX_TEV_KASEL_K3_B:
|
|
case GX_TEV_KASEL_K3_A:
|
|
info.sampledKColors.set(3);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
ShaderInfo build_shader_info(const ShaderConfig& config) noexcept {
|
|
ShaderInfo info{
|
|
.uniformSize = sizeof(PnMtx) + sizeof(Mat4x4<float>), // pos_mtx, nrm_mtx, proj
|
|
};
|
|
for (int i = 0; i < config.tevStageCount; ++i) {
|
|
const auto& stage = config.tevStages[i];
|
|
// Color pass
|
|
color_arg_reg_info(stage.colorPass.a, stage, info);
|
|
color_arg_reg_info(stage.colorPass.b, stage, info);
|
|
color_arg_reg_info(stage.colorPass.c, stage, info);
|
|
color_arg_reg_info(stage.colorPass.d, stage, info);
|
|
info.writesTevReg.set(stage.colorOp.outReg);
|
|
|
|
// Alpha pass
|
|
alpha_arg_reg_info(stage.alphaPass.a, stage, info);
|
|
alpha_arg_reg_info(stage.alphaPass.b, stage, info);
|
|
alpha_arg_reg_info(stage.alphaPass.c, stage, info);
|
|
alpha_arg_reg_info(stage.alphaPass.d, stage, info);
|
|
if (!info.writesTevReg.test(stage.alphaOp.outReg)) {
|
|
// If we're writing alpha to a register that's not been
|
|
// written to in the shader, load from uniform buffer
|
|
info.loadsTevReg.set(stage.alphaOp.outReg);
|
|
info.writesTevReg.set(stage.alphaOp.outReg);
|
|
}
|
|
}
|
|
info.uniformSize += info.loadsTevReg.count() * sizeof(Vec4<float>);
|
|
for (int i = 0; i < info.sampledColorChannels.size(); ++i) {
|
|
if (info.sampledColorChannels.test(i)) {
|
|
const auto& cc = config.colorChannels[i];
|
|
const auto& cca = config.colorChannels[i + GX_ALPHA0];
|
|
if (cc.lightingEnabled || cca.lightingEnabled) {
|
|
info.lightingEnabled = true;
|
|
}
|
|
}
|
|
}
|
|
if (info.lightingEnabled) {
|
|
// Lights + light state for all channels
|
|
info.uniformSize += 16 + sizeof(Light) * GX::MaxLights;
|
|
}
|
|
for (int i = 0; i < info.sampledColorChannels.size(); ++i) {
|
|
if (info.sampledColorChannels.test(i)) {
|
|
const auto& cc = config.colorChannels[i];
|
|
if (cc.lightingEnabled && cc.ambSrc == GX_SRC_REG) {
|
|
info.uniformSize += sizeof(Vec4<float>);
|
|
}
|
|
if (cc.matSrc == GX_SRC_REG) {
|
|
info.uniformSize += sizeof(Vec4<float>);
|
|
}
|
|
const auto& cca = config.colorChannels[i + GX_ALPHA0];
|
|
if (cca.lightingEnabled && cca.ambSrc == GX_SRC_REG) {
|
|
info.uniformSize += sizeof(Vec4<float>);
|
|
}
|
|
if (cca.matSrc == GX_SRC_REG) {
|
|
info.uniformSize += sizeof(Vec4<float>);
|
|
}
|
|
}
|
|
}
|
|
info.uniformSize += info.sampledKColors.count() * sizeof(Vec4<float>);
|
|
for (int i = 0; i < info.sampledTexCoords.size(); ++i) {
|
|
if (!info.sampledTexCoords.test(i)) {
|
|
continue;
|
|
}
|
|
const auto& tcg = config.tcgs[i];
|
|
if (tcg.mtx != GX_IDENTITY) {
|
|
u32 texMtxIdx = (tcg.mtx - GX_TEXMTX0) / 3;
|
|
info.usesTexMtx.set(texMtxIdx);
|
|
info.texMtxTypes[texMtxIdx] = tcg.type;
|
|
}
|
|
if (tcg.postMtx != GX_PTIDENTITY) {
|
|
u32 postMtxIdx = (tcg.postMtx - GX_PTTEXMTX0) / 3;
|
|
info.usesPTTexMtx.set(postMtxIdx);
|
|
}
|
|
}
|
|
for (int i = 0; i < info.usesTexMtx.size(); ++i) {
|
|
if (info.usesTexMtx.test(i)) {
|
|
switch (info.texMtxTypes[i]) {
|
|
case GX_TG_MTX2x4:
|
|
info.uniformSize += sizeof(Mat2x4<float>);
|
|
break;
|
|
case GX_TG_MTX3x4:
|
|
info.uniformSize += sizeof(Mat3x4<float>);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
info.uniformSize += info.usesPTTexMtx.count() * sizeof(Mat3x4<float>);
|
|
if (config.fogType != GX_FOG_NONE) {
|
|
info.usesFog = true;
|
|
info.uniformSize += sizeof(Fog);
|
|
}
|
|
info.uniformSize += info.sampledTextures.count() * sizeof(u32);
|
|
info.uniformSize = align_uniform(info.uniformSize);
|
|
return info;
|
|
}
|
|
|
|
Range build_uniform(const ShaderInfo& info) noexcept {
|
|
auto [buf, range] = map_uniform(info.uniformSize);
|
|
{
|
|
buf.append(g_gxState.pnMtx[g_gxState.currentPnMtx]);
|
|
buf.append(g_gxState.proj);
|
|
}
|
|
for (int i = 0; i < info.loadsTevReg.size(); ++i) {
|
|
if (info.loadsTevReg.test(i)) {
|
|
buf.append(g_gxState.colorRegs[i]);
|
|
}
|
|
}
|
|
if (info.lightingEnabled) {
|
|
// Lights
|
|
static_assert(sizeof(g_gxState.lights) == 80 * GX::MaxLights);
|
|
buf.append(g_gxState.lights);
|
|
// Light state for all channels
|
|
for (int i = 0; i < 4; ++i) {
|
|
buf.append<u32>(g_gxState.colorChannelState[i].lightMask.to_ulong());
|
|
}
|
|
}
|
|
for (int i = 0; i < info.sampledColorChannels.size(); ++i) {
|
|
if (!info.sampledColorChannels.test(i)) {
|
|
continue;
|
|
}
|
|
const auto& ccc = g_gxState.colorChannelConfig[i];
|
|
const auto& ccs = g_gxState.colorChannelState[i];
|
|
if (ccc.lightingEnabled && ccc.ambSrc == GX_SRC_REG) {
|
|
buf.append(ccs.ambColor);
|
|
}
|
|
if (ccc.matSrc == GX_SRC_REG) {
|
|
buf.append(ccs.matColor);
|
|
}
|
|
const auto& ccca = g_gxState.colorChannelConfig[i + GX_ALPHA0];
|
|
const auto& ccsa = g_gxState.colorChannelState[i + GX_ALPHA0];
|
|
if (ccca.lightingEnabled && ccca.ambSrc == GX_SRC_REG) {
|
|
buf.append(ccsa.ambColor);
|
|
}
|
|
if (ccca.matSrc == GX_SRC_REG) {
|
|
buf.append(ccsa.matColor);
|
|
}
|
|
}
|
|
for (int i = 0; i < info.sampledKColors.size(); ++i) {
|
|
if (info.sampledKColors.test(i)) {
|
|
buf.append(g_gxState.kcolors[i]);
|
|
}
|
|
}
|
|
for (int i = 0; i < info.usesTexMtx.size(); ++i) {
|
|
if (!info.usesTexMtx.test(i)) {
|
|
continue;
|
|
}
|
|
switch (info.texMtxTypes[i]) {
|
|
DEFAULT_FATAL("unhandled tex mtx type {}", underlying(info.texMtxTypes[i]));
|
|
case GX_TG_MTX2x4:
|
|
if (std::holds_alternative<Mat2x4<float>>(g_gxState.texMtxs[i])) {
|
|
buf.append(std::get<Mat2x4<float>>(g_gxState.texMtxs[i]));
|
|
} else
|
|
UNLIKELY FATAL("expected 2x4 mtx in idx {}", i);
|
|
break;
|
|
case GX_TG_MTX3x4:
|
|
if (std::holds_alternative<Mat3x4<float>>(g_gxState.texMtxs[i])) {
|
|
buf.append(std::get<Mat3x4<float>>(g_gxState.texMtxs[i]));
|
|
} else
|
|
UNLIKELY FATAL("expected 3x4 mtx in idx {}", i);
|
|
break;
|
|
}
|
|
}
|
|
for (int i = 0; i < info.usesPTTexMtx.size(); ++i) {
|
|
if (info.usesPTTexMtx.test(i)) {
|
|
buf.append(g_gxState.ptTexMtxs[i]);
|
|
}
|
|
}
|
|
if (info.usesFog) {
|
|
const auto& state = g_gxState.fog;
|
|
Fog fog{.color = state.color};
|
|
if (state.nearZ != state.farZ && state.startZ != state.endZ) {
|
|
const float depthRange = state.farZ - state.nearZ;
|
|
const float fogRange = state.endZ - state.startZ;
|
|
fog.a = (state.farZ * state.nearZ) / (depthRange * fogRange);
|
|
fog.b = state.farZ / depthRange;
|
|
fog.c = state.startZ / fogRange;
|
|
}
|
|
buf.append(fog);
|
|
}
|
|
for (int i = 0; i < info.sampledTextures.size(); ++i) {
|
|
if (!info.sampledTextures.test(i)) {
|
|
continue;
|
|
}
|
|
const auto& tex = get_texture(static_cast<GXTexMapID>(i));
|
|
CHECK(tex, "unbound texture {}", i);
|
|
buf.append(tex.texObj.lodBias);
|
|
}
|
|
g_gxState.stateDirty = false;
|
|
return range;
|
|
}
|
|
} // namespace aurora::gfx::gx
|