Files
Luke Street b1bf531884 FIFOify texture binding (#109)
* FIFOify texture binding

* Use __cntlzw helper

* Some cleanup

* Fix texture eviction & cleanup
2026-04-09 00:14:00 -06:00

503 lines
17 KiB
C++

#include "gx.hpp"
#include "__gx.h"
#include "../../gx/fifo.hpp"
#include <cstring>
// Global shadow register instance
static __GXData_struct sGXData;
__GXData_struct* __gx = &sGXData;
static GXFifoObj sFifoObj;
extern "C" {
static GXDrawDoneCallback DrawDoneCB = nullptr;
GXFifoObj* GXInit(void* base, u32 size) {
GXRenderModeObj* rmode;
f32 identity_mtx[3][4];
GXColor clear = {64, 64, 64, 255};
GXColor black = {0, 0, 0, 0};
GXColor white = {255, 255, 255, 255};
u32 i;
std::memset(&sGXData, 0, sizeof(sGXData));
__gx = &sGXData;
__gx->inDispList = 0;
__gx->dlSaveContext = 1;
__gx->tcsManEnab = 0;
__gx->vNum = 0;
// Initialize FIFO subsystem
aurora::gx::fifo::init();
GXInitFifoBase(&sFifoObj, base, size);
GXSetCPUFifo(&sFifoObj);
GXSetGPFifo(&sFifoObj);
// Initialize shadow registers: genMode, bpMask, lpSize
__gx->genMode = 0;
SET_REG_FIELD(0, __gx->genMode, 8, 24, 0x00);
__gx->bpMask = 0xFF;
SET_REG_FIELD(0, __gx->bpMask, 8, 24, 0x0F);
__gx->lpSize = 0;
SET_REG_FIELD(0, __gx->lpSize, 8, 24, 0x22);
// TEV / tref / ksel shadow registers
for (i = 0; i < 16; i++) {
__gx->tevc[i] = 0;
__gx->teva[i] = 0;
__gx->tref[i / 2] = 0;
__gx->texmapId[i] = GX_TEXMAP_NULL;
SET_REG_FIELD(0, __gx->tevc[i], 8, 24, 0xC0 + i * 2);
SET_REG_FIELD(0, __gx->teva[i], 8, 24, 0xC1 + i * 2);
SET_REG_FIELD(0, __gx->tevKsel[i / 2], 8, 24, 0xF6 + i / 2);
SET_REG_FIELD(0, __gx->tref[i / 2], 8, 24, 0x28 + i / 2);
}
// iref and SU texture scale registers
__gx->iref = 0;
SET_REG_FIELD(0, __gx->iref, 8, 24, 0x27);
for (i = 0; i < 8; i++) {
__gx->suTs0[i] = 0;
__gx->suTs1[i] = 0;
SET_REG_FIELD(0, __gx->suTs0[i], 8, 24, 0x30 + i * 2);
SET_REG_FIELD(0, __gx->suTs1[i], 8, 24, 0x31 + i * 2);
}
// Other BP command byte init
SET_REG_FIELD(0, __gx->suScis0, 8, 24, 0x20);
SET_REG_FIELD(0, __gx->suScis1, 8, 24, 0x21);
SET_REG_FIELD(0, __gx->cmode0, 8, 24, 0x41);
SET_REG_FIELD(0, __gx->cmode1, 8, 24, 0x42);
SET_REG_FIELD(0, __gx->zmode, 8, 24, 0x40);
SET_REG_FIELD(0, __gx->peCtrl, 8, 24, 0x43);
SET_REG_FIELD(0, __gx->IndTexScale0, 8, 24, 0x25);
SET_REG_FIELD(0, __gx->IndTexScale1, 8, 24, 0x26);
__gx->dirtyState = 0;
__gx->dirtyVAT = 0;
// VAT initialization: set default bits and write vatB to CP
for (i = 0; i < GX_MAX_VTXFMT; i++) {
SET_REG_FIELD(0, __gx->vatA[i], 1, 30, 1);
SET_REG_FIELD(0, __gx->vatB[i], 1, 31, 1);
GX_WRITE_U8(8);
GX_WRITE_U8(i | 0x80);
GX_WRITE_U32(__gx->vatB[i]);
}
// XF register init: error/mode control + dual-tex transform
{
u32 reg1 = 0;
u32 reg2 = 0;
SET_REG_FIELD(0, reg1, 1, 0, 1);
SET_REG_FIELD(0, reg1, 1, 1, 1);
SET_REG_FIELD(0, reg1, 1, 2, 1);
SET_REG_FIELD(0, reg1, 1, 3, 1);
SET_REG_FIELD(0, reg1, 1, 4, 1);
SET_REG_FIELD(0, reg1, 1, 5, 1);
GX_WRITE_XF_REG(0, reg1);
SET_REG_FIELD(0, reg2, 1, 0, 1);
GX_WRITE_XF_REG(0x12, reg2);
}
// BP 0x58 register init
{
u32 reg = 0;
SET_REG_FIELD(0, reg, 1, 0, 1);
SET_REG_FIELD(0, reg, 1, 1, 1);
SET_REG_FIELD(0, reg, 1, 2, 1);
SET_REG_FIELD(0, reg, 1, 3, 1);
SET_REG_FIELD(0, reg, 8, 24, 0x58);
GX_WRITE_RAS_REG(reg);
}
// Default render mode (NTSC)
rmode = &GXNtsc480IntDf;
// Default state initialization via API calls
GXSetCopyClear(clear, 0xFFFFFF);
GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, 0x3C);
GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, 0x3C);
GXSetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX2, 0x3C);
GXSetTexCoordGen(GX_TEXCOORD3, GX_TG_MTX2x4, GX_TG_TEX3, 0x3C);
GXSetTexCoordGen(GX_TEXCOORD4, GX_TG_MTX2x4, GX_TG_TEX4, 0x3C);
GXSetTexCoordGen(GX_TEXCOORD5, GX_TG_MTX2x4, GX_TG_TEX5, 0x3C);
GXSetTexCoordGen(GX_TEXCOORD6, GX_TG_MTX2x4, GX_TG_TEX6, 0x3C);
GXSetTexCoordGen(GX_TEXCOORD7, GX_TG_MTX2x4, GX_TG_TEX7, 0x3C);
GXSetNumTexGens(1);
GXClearVtxDesc();
GXInvalidateVtxCache();
GXSetLineWidth(6, GX_TO_ZERO);
GXSetPointSize(6, GX_TO_ZERO);
GXEnableTexOffsets(GX_TEXCOORD0, GX_DISABLE, GX_DISABLE);
GXEnableTexOffsets(GX_TEXCOORD1, GX_DISABLE, GX_DISABLE);
GXEnableTexOffsets(GX_TEXCOORD2, GX_DISABLE, GX_DISABLE);
GXEnableTexOffsets(GX_TEXCOORD3, GX_DISABLE, GX_DISABLE);
GXEnableTexOffsets(GX_TEXCOORD4, GX_DISABLE, GX_DISABLE);
GXEnableTexOffsets(GX_TEXCOORD5, GX_DISABLE, GX_DISABLE);
GXEnableTexOffsets(GX_TEXCOORD6, GX_DISABLE, GX_DISABLE);
GXEnableTexOffsets(GX_TEXCOORD7, GX_DISABLE, GX_DISABLE);
// Identity matrix
identity_mtx[0][0] = 1.0f;
identity_mtx[0][1] = 0.0f;
identity_mtx[0][2] = 0.0f;
identity_mtx[0][3] = 0.0f;
identity_mtx[1][0] = 0.0f;
identity_mtx[1][1] = 1.0f;
identity_mtx[1][2] = 0.0f;
identity_mtx[1][3] = 0.0f;
identity_mtx[2][0] = 0.0f;
identity_mtx[2][1] = 0.0f;
identity_mtx[2][2] = 1.0f;
identity_mtx[2][3] = 0.0f;
GXLoadPosMtxImm(identity_mtx, GX_PNMTX0);
GXLoadNrmMtxImm(identity_mtx, GX_PNMTX0);
GXSetCurrentMtx(GX_PNMTX0);
GXLoadTexMtxImm(identity_mtx, GX_IDENTITY, GX_MTX3x4);
GXLoadTexMtxImm(identity_mtx, GX_PTIDENTITY, GX_MTX3x4);
GXSetViewport(0.0f, 0.0f, rmode->fbWidth, rmode->xfbHeight, 0.0f, 1.0f);
GXSetCoPlanar(GX_DISABLE);
GXSetCullMode(GX_CULL_BACK);
GXSetClipMode(GX_CLIP_ENABLE);
GXSetScissor(0, 0, rmode->fbWidth, rmode->efbHeight);
// GXSetScissorBoxOffset(0, 0);
GXSetNumChans(0);
GXSetChanCtrl(GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);
GXSetChanAmbColor(GX_COLOR0A0, black);
GXSetChanMatColor(GX_COLOR0A0, white);
GXSetChanCtrl(GX_COLOR1A1, GX_DISABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE);
GXSetChanAmbColor(GX_COLOR1A1, black);
GXSetChanMatColor(GX_COLOR1A1, white);
GXInvalidateTexAll();
// __gx->nextTexRgn = 0;
// for (i = 0; i < 8; i++)
// GXInitTexCacheRegion(&__gx->TexRegions[i], 0, i * 0x8000, 0, 0x80000 + i * 0x8000, 0);
// __gx->nextTexRgnCI = 0;
// for (i = 0; i < 4; i++)
// GXInitTexCacheRegion(&__gx->TexRegionsCI[i], 0, (i * 2 + 8) * 0x8000, 0, (i * 2 + 9) * 0x8000, 0);
// for (i = 0; i < 16; i++)
// GXInitTlutRegion(&__gx->TlutRegions[i], 0xC0000 + i * 0x2000, 16);
// for (i = 0; i < 4; i++)
// GXInitTlutRegion(&__gx->TlutRegions[i + 16], 0xE0000 + i * 0x8000, 64);
// GXSetTexRegionCallback(__GXDefaultTexRegionCallback);
// GXSetTlutRegionCallback(__GXDefaultTlutRegionCallback);
GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR0A0);
GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD2, GX_TEXMAP2, GX_COLOR0A0);
GXSetTevOrder(GX_TEVSTAGE3, GX_TEXCOORD3, GX_TEXMAP3, GX_COLOR0A0);
GXSetTevOrder(GX_TEVSTAGE4, GX_TEXCOORD4, GX_TEXMAP4, GX_COLOR0A0);
GXSetTevOrder(GX_TEVSTAGE5, GX_TEXCOORD5, GX_TEXMAP5, GX_COLOR0A0);
GXSetTevOrder(GX_TEVSTAGE6, GX_TEXCOORD6, GX_TEXMAP6, GX_COLOR0A0);
GXSetTevOrder(GX_TEVSTAGE7, GX_TEXCOORD7, GX_TEXMAP7, GX_COLOR0A0);
GXSetTevOrder(GX_TEVSTAGE8, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
GXSetTevOrder(GX_TEVSTAGE9, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
GXSetTevOrder(GX_TEVSTAGE10, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
GXSetTevOrder(GX_TEVSTAGE11, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
GXSetTevOrder(GX_TEVSTAGE12, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
GXSetTevOrder(GX_TEVSTAGE13, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
GXSetTevOrder(GX_TEVSTAGE14, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
GXSetTevOrder(GX_TEVSTAGE15, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
GXSetNumTevStages(1);
GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
GXSetZTexture(GX_ZT_DISABLE, GX_TF_Z8, 0);
for (i = GX_TEVSTAGE0; i < GX_MAX_TEVSTAGE; i++) {
GXSetTevKColorSel(static_cast<GXTevStageID>(i), GX_TEV_KCSEL_1_4);
GXSetTevKAlphaSel(static_cast<GXTevStageID>(i), GX_TEV_KASEL_1);
GXSetTevSwapMode(static_cast<GXTevStageID>(i), GX_TEV_SWAP0, GX_TEV_SWAP0);
}
GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA);
GXSetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA);
GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA);
for (i = GX_TEVSTAGE0; i < GX_MAX_TEVSTAGE; i++)
GXSetTevDirect(static_cast<GXTevStageID>(i));
GXSetNumIndStages(0);
GXSetIndTexCoordScale(GX_INDTEXSTAGE0, GX_ITS_1, GX_ITS_1);
GXSetIndTexCoordScale(GX_INDTEXSTAGE1, GX_ITS_1, GX_ITS_1);
GXSetIndTexCoordScale(GX_INDTEXSTAGE2, GX_ITS_1, GX_ITS_1);
GXSetIndTexCoordScale(GX_INDTEXSTAGE3, GX_ITS_1, GX_ITS_1);
GXSetFog(GX_FOG_NONE, 0.0f, 1.0f, 0.1f, 1.0f, black);
GXSetFogRangeAdj(GX_DISABLE, 0, nullptr);
GXSetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
GXSetColorUpdate(GX_ENABLE);
GXSetAlphaUpdate(GX_ENABLE);
GXSetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
GXSetZCompLoc(GX_TRUE);
GXSetDither(GX_ENABLE);
GXSetDstAlpha(GX_DISABLE, 0);
GXSetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
GXSetFieldMask(GX_ENABLE, GX_ENABLE);
GXSetFieldMode(rmode->field_rendering, ((rmode->viHeight == 2 * rmode->xfbHeight) ? GX_ENABLE : GX_DISABLE));
GXSetDispCopySrc(0, 0, rmode->fbWidth, rmode->efbHeight);
GXSetDispCopyDst(rmode->fbWidth, rmode->efbHeight);
GXSetDispCopyYScale(static_cast<f32>(rmode->xfbHeight) / static_cast<f32>(rmode->efbHeight));
// GXSetCopyClamp(static_cast<GXFBClamp>(GX_CLAMP_TOP | GX_CLAMP_BOTTOM));
GXSetCopyFilter(rmode->aa, rmode->sample_pattern, GX_TRUE, rmode->vfilter);
GXSetDispCopyGamma(GX_GM_1_0);
// GXSetDispCopyFrame2Field(GX_COPY_PROGRESSIVE);
// GXClearBoundingBox();
// GXPokeColorUpdate(GX_TRUE);
// GXPokeAlphaUpdate(GX_TRUE);
// GXPokeDither(GX_FALSE);
// GXPokeBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ONE, GX_LO_SET);
// GXPokeAlphaMode(GX_ALWAYS, 0);
// GXPokeAlphaRead(GX_READ_FF);
// GXPokeDstAlpha(GX_DISABLE, 0);
// GXPokeZMode(GX_TRUE, GX_ALWAYS, GX_TRUE);
return &sFifoObj;
}
void GXDrawDone() {
if (DrawDoneCB != nullptr)
DrawDoneCB();
}
void GXSetDrawDone() {
if (DrawDoneCB != nullptr)
DrawDoneCB();
}
GXDrawDoneCallback GXSetDrawDoneCallback(GXDrawDoneCallback cb) {
GXDrawDoneCallback old = DrawDoneCB;
DrawDoneCB = cb;
return old;
}
void GXFlush() {
if (__gx->dirtyState) {
__GXSetDirtyState();
}
}
void GXPixModeSync() {
GX_WRITE_RAS_REG(__gx->peCtrl);
__gx->bpSent = 1;
}
void GXTexModeSync() {
GX_WRITE_RAS_REG(0x63000000);
__gx->bpSent = 1;
}
// Dirty state flush functions
void __GXSetDirtyState() {
if (__gx->dirtyState & 1) {
__GXSetSUTexRegs();
}
if (__gx->dirtyState & 2) {
__GXUpdateBPMask();
}
if (__gx->dirtyState & 4) {
__GXSetGenMode();
}
if (__gx->dirtyState & 8) {
__GXSetVCD();
}
if (__gx->dirtyState & 0x10) {
__GXSetVAT();
}
__gx->dirtyState = 0;
}
void __GXSetGenMode() {
GX_WRITE_RAS_REG(__gx->genMode);
__gx->bpSent = 1;
}
void __GXSendFlushPrim() {
// Originally, this writes a dummy triangle strip draw to force the GP
// to process the FIFO up to this point, flushing pending BP register changes.
// We can skip the FIFO writes and just clear the bpSent flag.
// GX_WRITE_U8(0x98);
// GX_WRITE_U16(__gx->vNum);
// for (u32 i = 0; i < __gx->vNum * __gx->vLim; i += 4) {
// GX_WRITE_U32(0);
// }
__gx->bpSent = 0;
}
void __GXSetVCD() {
// Write VCD lo and hi to CP registers
GX_WRITE_SOME_REG4(8, 0x50, __gx->vcdLo, -12);
GX_WRITE_SOME_REG4(8, 0x60, __gx->vcdHi, -12);
// Write XF vertex specs
u32 nCols = 0;
nCols = GET_REG_FIELD(__gx->vcdLo, 2, 13) ? 1 : 0;
nCols += GET_REG_FIELD(__gx->vcdLo, 2, 15) ? 1 : 0;
u32 nNrm = __gx->hasBiNrms ? 2 : __gx->hasNrms ? 1 : 0;
u32 nTex = 0;
nTex += GET_REG_FIELD(__gx->vcdHi, 2, 0) ? 1 : 0;
nTex += GET_REG_FIELD(__gx->vcdHi, 2, 2) ? 1 : 0;
nTex += GET_REG_FIELD(__gx->vcdHi, 2, 4) ? 1 : 0;
nTex += GET_REG_FIELD(__gx->vcdHi, 2, 6) ? 1 : 0;
nTex += GET_REG_FIELD(__gx->vcdHi, 2, 8) ? 1 : 0;
nTex += GET_REG_FIELD(__gx->vcdHi, 2, 10) ? 1 : 0;
nTex += GET_REG_FIELD(__gx->vcdHi, 2, 12) ? 1 : 0;
nTex += GET_REG_FIELD(__gx->vcdHi, 2, 14) ? 1 : 0;
u32 reg = (nCols) | (nNrm << 2) | (nTex << 4);
GX_WRITE_XF_REG(8, reg);
__gx->bpSent = 0;
// Calculate vertex data limit for flush prim
if (__gx->vNum != 0) {
static u8 tbl1[] = {0, 4, 1, 2};
static u8 tbl2[] = {0, 8, 1, 2};
static u8 tbl3[] = {0, 12, 1, 2};
u32 vl = __gx->vcdLo;
u32 vh = __gx->vcdHi;
u32 vlm = 0;
vlm = GET_REG_FIELD(vl, 1, 0);
vlm += static_cast<u8>(GET_REG_FIELD(vl, 1, 1));
vlm += static_cast<u8>(GET_REG_FIELD(vl, 1, 2));
vlm += static_cast<u8>(GET_REG_FIELD(vl, 1, 3));
vlm += static_cast<u8>(GET_REG_FIELD(vl, 1, 4));
vlm += static_cast<u8>(GET_REG_FIELD(vl, 1, 5));
vlm += static_cast<u8>(GET_REG_FIELD(vl, 1, 6));
vlm += static_cast<u8>(GET_REG_FIELD(vl, 1, 7));
vlm += static_cast<u8>(GET_REG_FIELD(vl, 1, 8));
vlm += tbl3[static_cast<u8>(GET_REG_FIELD(vl, 2, 9))];
u32 b = (__gx->hasBiNrms << 1) + 1;
vlm += tbl3[static_cast<u8>(GET_REG_FIELD(vl, 2, 11))] * b;
vlm += tbl1[static_cast<u8>(GET_REG_FIELD(vl, 2, 13))];
vlm += tbl1[static_cast<u8>(GET_REG_FIELD(vl, 2, 15))];
vlm += tbl2[static_cast<u8>(GET_REG_FIELD(vh, 2, 0))];
vlm += tbl2[static_cast<u8>(GET_REG_FIELD(vh, 2, 2))];
vlm += tbl2[static_cast<u8>(GET_REG_FIELD(vh, 2, 4))];
vlm += tbl2[static_cast<u8>(GET_REG_FIELD(vh, 2, 6))];
vlm += tbl2[static_cast<u8>(GET_REG_FIELD(vh, 2, 8))];
vlm += tbl2[static_cast<u8>(GET_REG_FIELD(vh, 2, 10))];
vlm += tbl2[static_cast<u8>(GET_REG_FIELD(vh, 2, 12))];
vlm += tbl2[static_cast<u8>(GET_REG_FIELD(vh, 2, 14))];
__gx->vLim = vlm;
}
}
void __GXSetVAT() {
for (u8 i = 0; i < 8; i++) {
if (__gx->dirtyVAT & (1 << i)) {
GX_WRITE_SOME_REG4(8, i | 0x70, __gx->vatA[i], i - 12);
GX_WRITE_SOME_REG4(8, i | 0x80, __gx->vatB[i], i - 12);
GX_WRITE_SOME_REG4(8, i | 0x90, __gx->vatC[i], i - 12);
}
}
__gx->dirtyVAT = 0;
}
static void __SetSURegs(u32 tmap, u32 tcoord) {
// Copy texture dimensions from tImage0 to SU registers
u32 w = GET_REG_FIELD(__gx->tImage0[tmap], 10, 0);
u32 h = GET_REG_FIELD(__gx->tImage0[tmap], 10, 10);
SET_REG_FIELD(0, __gx->suTs0[tcoord], 16, 0, w);
SET_REG_FIELD(0, __gx->suTs1[tcoord], 16, 0, h);
// Bias from wrap mode
u8 s_bias = GET_REG_FIELD(__gx->tMode0[tmap], 2, 0) == 1;
u8 t_bias = GET_REG_FIELD(__gx->tMode0[tmap], 2, 2) == 1;
SET_REG_FIELD(0, __gx->suTs0[tcoord], 1, 16, s_bias);
SET_REG_FIELD(0, __gx->suTs1[tcoord], 1, 16, t_bias);
GX_WRITE_RAS_REG(__gx->suTs0[tcoord]);
GX_WRITE_RAS_REG(__gx->suTs1[tcoord]);
__gx->bpSent = 1;
}
void __GXSetSUTexRegs() {
// Write SU texture size/bias registers for each active TEV stage and indirect stage.
// Skip coords that have manual scale enabled (tcsManEnab bit set).
// If all coords are manual (0xFF), skip entirely.
if (__gx->tcsManEnab == 0xFF) {
return;
}
u32 nStages = GET_REG_FIELD(__gx->genMode, 4, 10) + 1;
u32 nIndStages = GET_REG_FIELD(__gx->genMode, 3, 16);
// Indirect texture stages
for (u32 i = 0; i < nIndStages; i++) {
u32 tmap, coord;
switch (i) {
case 0:
tmap = GET_REG_FIELD(__gx->iref, 3, 0);
coord = GET_REG_FIELD(__gx->iref, 3, 3);
break;
case 1:
tmap = GET_REG_FIELD(__gx->iref, 3, 6);
coord = GET_REG_FIELD(__gx->iref, 3, 9);
break;
case 2:
tmap = GET_REG_FIELD(__gx->iref, 3, 12);
coord = GET_REG_FIELD(__gx->iref, 3, 15);
break;
default:
tmap = GET_REG_FIELD(__gx->iref, 3, 18);
coord = GET_REG_FIELD(__gx->iref, 3, 21);
break;
}
if (!(__gx->tcsManEnab & (1 << coord))) {
__SetSURegs(tmap, coord);
}
}
// Direct TEV stages
for (u32 i = 0; i < nStages; i++) {
u32* ptref = &__gx->tref[i / 2];
u32 map = __gx->texmapId[i];
u32 tmap = map & ~0x100u;
u32 coord;
if (i & 1) {
coord = GET_REG_FIELD(*ptref, 3, 15);
} else {
coord = GET_REG_FIELD(*ptref, 3, 3);
}
if (tmap != 0xFF && !(__gx->tcsManEnab & (1 << coord))) {
__SetSURegs(tmap, coord);
}
}
}
void __GXUpdateBPMask() {
// Mark texture maps used by indirect texture stages in the BP mask
u32 nIndStages = GET_REG_FIELD(__gx->genMode, 3, 16);
u32 newImask = 0;
for (u32 i = 0; i < nIndStages; i++) {
u32 tmap = GET_REG_FIELD(__gx->iref, 3, i * 6);
newImask |= 1u << tmap;
}
if (static_cast<u8>(__gx->bpMask) != static_cast<u8>(newImask)) {
SET_REG_FIELD(0, __gx->bpMask, 8, 0, newImask);
GX_WRITE_RAS_REG(__gx->bpMask);
__gx->bpSent = 1;
}
}
void __GXSetMatrixIndex(GXAttr matIdxAttr) {
if (matIdxAttr < GX_VA_TEX4MTXIDX) {
GX_WRITE_SOME_REG4(8, 0x30, __gx->matIdxA, -12);
GX_WRITE_XF_REG(24, __gx->matIdxA);
} else {
GX_WRITE_SOME_REG4(8, 0x40, __gx->matIdxB, -12);
GX_WRITE_XF_REG(25, __gx->matIdxB);
}
__gx->bpSent = 0;
}
};