Add GX_AUTO Aurora extension for GXBegin

This commit is contained in:
Luke Street
2026-06-09 23:38:22 -06:00
parent 55d720eed2
commit 5143394381
6 changed files with 78 additions and 12 deletions
+8
View File
@@ -61,6 +61,14 @@ extern "C" {
#define GX_LOAD_AURORA_DESTROY_COPY_TEX 0x0034
/**
* Draw primitives with the vertex count derived from a byte length, as written by
* GXBegin(prim, fmt, GX_AUTO). Must be followed by a u8 draw opcode (vtxfmt|prim),
* a u32 vertex data byte length, then that many bytes of vertex data. The byte length
* must be a whole multiple of the current vertex size or zero (no draw).
*/
#define GX_LOAD_AURORA_DRAW_SIZED 0x0040
#define GX2_SET_POLYGON_OFFSET 0x1000
+7
View File
@@ -14,6 +14,13 @@ void GXSetVtxAttrFmtv(GXVtxFmt vtxfmt, const GXVtxAttrFmtList* list);
void GXSetVtxAttrFmt(GXVtxFmt vtxfmt, GXAttr attr, GXCompCnt cnt, GXCompType type, u8 frac);
void GXSetNumTexGens(u8 nTexGens);
void GXBegin(GXPrimitive type, GXVtxFmt vtxfmt, u16 nverts);
#ifdef TARGET_PC
/**
* Aurora extension: pass as GXBegin's vertex count to have it derived automatically from
* the number of bytes written before GXEnd. Not supported while recording a display list.
*/
#define GX_AUTO 0xFFFF
#endif
void GXSetTexCoordGen2(GXTexCoordID dst_coord, GXTexGenType func, GXTexGenSrc src_param, u32 mtx, GXBool normalize,
u32 postmtx);
void GXSetLineWidth(u8 width, GXTexOffset texOffsets);
+19 -3
View File
@@ -6,6 +6,9 @@
static u16 sBeginNVerts = 0;
static u32 sBeginFifoSize = 0;
static bool sInBegin = false;
// GX_AUTO: offset of the u32 byte-length placeholder to patch in GXEnd
static u32 sBeginSizeOffset = 0;
static bool sBeginAuto = false;
extern "C" {
@@ -22,8 +25,18 @@ void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) {
__GXSendFlushPrim();
}
GX_WRITE_U8(vtxFmt | primitive);
GX_WRITE_U16(nVerts);
const u8 drawCmd = static_cast<u8>(vtxFmt) | static_cast<u8>(primitive);
sBeginAuto = nVerts == GX_AUTO;
if (sBeginAuto) {
ASSERT(!aurora::gx::fifo::in_display_list(), "GXBegin: GX_AUTO not supported in display lists");
GX_WRITE_AURORA(GX_LOAD_AURORA_DRAW_SIZED);
GX_WRITE_U8(drawCmd);
sBeginSizeOffset = aurora::gx::fifo::get_buffer_size();
GX_WRITE_U32(0);
} else {
GX_WRITE_U8(drawCmd);
GX_WRITE_U16(nVerts);
}
// Record state for vertex count validation in GXEnd
sBeginNVerts = nVerts;
@@ -34,7 +47,10 @@ void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) {
void GXEnd() {
if (sInBegin) {
u32 bytesWritten = aurora::gx::fifo::get_buffer_size() - sBeginFifoSize;
if (sBeginNVerts > 0 && bytesWritten > 0) {
if (sBeginAuto) {
aurora::gx::fifo::patch_u32(sBeginSizeOffset, bytesWritten);
sBeginAuto = false;
} else if (sBeginNVerts > 0 && bytesWritten > 0) {
u32 vtxSize = bytesWritten / sBeginNVerts;
u32 remainder = bytesWritten % sBeginNVerts;
if (remainder != 0) {
+33 -9
View File
@@ -1542,16 +1542,8 @@ static void handle_draw_unmerged(GXPrimitive prim, GXVtxFmt fmt, u16 vtxCount, g
// Draw command handler - parses vertices inline and caches results
static ByteBuffer handle_draw_idx_buf;
static void handle_draw(u8 cmd, const u8* data, u32& pos, u32 size, bool bigEndian) {
static void draw_prim(GXPrimitive prim, GXVtxFmt fmt, u16 vtxCount, const u8* data, u32& pos, u32 size) {
ZoneScoped;
u8 opcode = cmd & CP_OPCODE_MASK;
GXVtxFmt fmt = static_cast<GXVtxFmt>(cmd & CP_VAT_MASK);
GXPrimitive prim = static_cast<GXPrimitive>(opcode);
CHECK(pos + 2 <= size, "draw vtxCount read overrun");
u16 vtxCount = read_u16(data + pos, bigEndian);
pos += 2;
u32 vtxSize;
if (g_gxState.lastVtxFmt == fmt)
LIKELY { vtxSize = g_gxState.lastVtxSize; }
@@ -1608,6 +1600,17 @@ static void handle_draw(u8 cmd, const u8* data, u32& pos, u32 size, bool bigEndi
handle_draw_unmerged(prim, fmt, vtxCount, vertRange);
}
static void handle_draw(u8 cmd, const u8* data, u32& pos, u32 size, bool bigEndian) {
GXVtxFmt fmt = static_cast<GXVtxFmt>(cmd & CP_VAT_MASK);
GXPrimitive prim = static_cast<GXPrimitive>(cmd & CP_OPCODE_MASK);
CHECK(pos + 2 <= size, "draw vtxCount read overrun");
u16 vtxCount = read_u16(data + pos, bigEndian);
pos += 2;
draw_prim(prim, fmt, vtxCount, data, pos, size);
}
static ByteBuffer handle_draw_unmerged_idxBuf;
static void handle_draw_unmerged(GXPrimitive prim, GXVtxFmt fmt, u16 vtxCount, gfx::Range vertRange) {
@@ -1810,6 +1813,27 @@ void handle_aurora(const u8* data, u32& pos, u32 size, bool bigEndian) {
CHECK(pos + 8 <= size, "GX_LOAD_AURORA_DESTROY_COPY_TEX read overrun");
evict_copy_texture(reinterpret_cast<const void*>(read_u64(data + pos, bigEndian)));
pos += 8;
} else if (subCmd == GX_LOAD_AURORA_DRAW_SIZED) {
CHECK(pos + 5 <= size, "GX_LOAD_AURORA_DRAW_SIZED read overrun");
u8 cmd = data[pos];
pos += 1;
u32 byteLen = read_u32(data + pos, bigEndian);
pos += 4;
GXVtxFmt fmt = static_cast<GXVtxFmt>(cmd & CP_VAT_MASK);
GXPrimitive prim = static_cast<GXPrimitive>(cmd & CP_OPCODE_MASK);
if (byteLen != 0) {
u32 vtxSize;
if (g_gxState.lastVtxFmt == fmt) {
vtxSize = g_gxState.lastVtxSize;
} else {
vtxSize = calculate_last_vtx_size(fmt);
}
ASSERT(vtxSize != 0 && byteLen % vtxSize == 0,
"GX_LOAD_AURORA_DRAW_SIZED: {} bytes is not a whole number of size-{} vertices", byteLen, vtxSize);
u32 vtxCount = byteLen / vtxSize;
ASSERT(vtxCount <= 0xFFFF, "GX_LOAD_AURORA_DRAW_SIZED: too many vertices ({})", vtxCount);
draw_prim(prim, fmt, static_cast<u16>(vtxCount), data, pos, size);
}
} else if (subCmd == GX_LOAD_AURORA_DEBUG_GROUP_PUSH) {
auto label = read_string(data, pos, size, bigEndian);
gfx::push_debug_group(std::move(label));
+7
View File
@@ -39,6 +39,13 @@ void write_data_grow(const void* data, uint32_t length) {
detail::sBufferCapacity = newCap;
}
void patch_u32(const uint32_t offset, const uint32_t val) {
ASSERT(!detail::sInDisplayList && offset + sizeof(uint32_t) <= detail::sBufferSize,
"fifo::patch_u32: invalid patch offset {} (buffer size {})", offset, detail::sBufferSize);
const auto out = bswap(val);
std::memcpy(detail::sBufferData + offset, &out, sizeof(out));
}
void begin_display_list(uint8_t* buf, uint32_t size) {
detail::sInDisplayList = true;
detail::sDlBuffer = buf;
+4
View File
@@ -73,6 +73,10 @@ inline void write_f32(const float val) {
write_data(&out, sizeof(out));
}
// Overwrites a u32 previously written to the FIFO buffer at the given offset.
// Not valid during display list recording.
void patch_u32(uint32_t offset, uint32_t val);
// Display list recording
void begin_display_list(uint8_t* buf, uint32_t size);
uint32_t end_display_list();