Files
aurora/lib/dolphin/gx/GXManage.cpp
T
Pieter-Jan Briers 50fda393a1 GX optimizations (#107)
* handle_draw optimization

1. cache vtxSize
2. remove heap allocation from draw call merging index buffer
3. move things that aren't on the "draw call merging hot path" out to other functions, to reduce stack frame size of handle_draw (it was previously using __chkstk)

* Make ByteBuffer expand exponentially

Fix half a second of startup time being just that.

* Babysit the compiler's inlining decisions

* Merged draw call idxBuf now a global static

* GXFlush() doesn't write NOPs to FIFO

I'm sure this made sense on actual hardware

* Don't bind unused textures/samplers

* Remove C++ RAII types from build_bind_groups

Most of this function's code was fucking with WebGPU AddRef and ReleaseRef. Seriously.

* More refs in common.hpp

* Index buffer in handle_draw_unmerged now not dynamically allocated for small draws

* Fix gx_test_stubs.cpp

* Some cleanup & renaming

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-04-08 19:51:06 -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;
}
};