Support making simple display lists on the fly and more data formats (#5)

* Support making simple display lists on the fly and more data formats

* Add a macro for GXCallDisplayListLE

* Resolve conversations
This commit is contained in:
Dávid Balatoni 2025-04-23 16:27:16 +02:00 committed by GitHub
parent c4d91f18a1
commit 23522538e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 518 additions and 191 deletions

View File

@ -7,8 +7,15 @@
extern "C" { extern "C" {
#endif #endif
#ifdef AURORA
#define GXCallDisplayListNative GXCallDisplayListLE
#else
#define GXCallDisplayListNative GXCallDisplayList
#endif
void GXBeginDisplayList(void* list, u32 size); void GXBeginDisplayList(void* list, u32 size);
u32 GXEndDisplayList(void); u32 GXEndDisplayList(void);
void GXCallDisplayListLE(const void* list, u32 nbytes);
void GXCallDisplayList(const void* list, u32 nbytes); void GXCallDisplayList(const void* list, u32 nbytes);
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -2,17 +2,28 @@
#include "../../gfx/model/shader.hpp" #include "../../gfx/model/shader.hpp"
#define ROUNDUP32(x) (((x) + 31) & ~31)
extern "C" { extern "C" {
void GXBeginDisplayList(void* list, u32 size) { void GXBeginDisplayList(void* list, u32 size) {
// TODO CHECK(!g_gxState.dynamicDlBuf, "Display list began twice!");
g_gxState.dynamicDlBuf.emplace(static_cast<u8*>(list), size);
} }
u32 GXEndDisplayList() { u32 GXEndDisplayList() {
// TODO auto &dlBuf = g_gxState.dynamicDlBuf;
return 0; size_t size = dlBuf->size();
size_t paddedSize = ROUNDUP32(size);
dlBuf->append_zeroes(paddedSize - size);
dlBuf.reset();
return paddedSize;
}
void GXCallDisplayListLE(const void* data, u32 nbytes) {
aurora::gfx::model::queue_surface(static_cast<const u8*>(data), nbytes, false);
} }
void GXCallDisplayList(const void* data, u32 nbytes) { void GXCallDisplayList(const void* data, u32 nbytes) {
aurora::gfx::model::queue_surface(static_cast<const u8*>(data), nbytes); aurora::gfx::model::queue_surface(static_cast<const u8*>(data), nbytes, true);
} }
} }

View File

@ -117,6 +117,12 @@ static u16 lastVertexStart = 0;
extern "C" { extern "C" {
void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) { void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) {
if (auto& dlBuf = g_gxState.dynamicDlBuf) {
const u8 fmtAndPrimitive = vtxFmt | primitive;
dlBuf->append(fmtAndPrimitive);
dlBuf->append(nVerts);
return;
}
CHECK(!sStreamState, "Stream began twice!"); CHECK(!sStreamState, "Stream began twice!");
uint16_t vertexSize = 0; uint16_t vertexSize = 0;
@ -180,267 +186,508 @@ void GXBegin(GXPrimitive primitive, GXVtxFmt vtxFmt, u16 nVerts) {
} }
void GXPosition3f32(f32 x, f32 y, f32 z) { void GXPosition3f32(f32 x, f32 y, f32 z) {
sStreamState->check_direct(GX_VA_POS, GX_POS_XYZ, GX_F32); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{x, y, z}); dlBuf->append(aurora::Vec3{x, y, z});
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_POS, GX_POS_XYZ, GX_F32);
stream->append(aurora::Vec3{x, y, z});
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition3u16(u16 x, u16 y, u16 z) { void GXPosition3u16(u16 x, u16 y, u16 z) {
const auto frac = sStreamState->check_direct(GX_VA_POS, GX_POS_XYZ, GX_U16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
dlBuf->append(z);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_POS, GX_POS_XYZ, GX_U16);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
static_cast<f32>(z) / static_cast<f32>(1 << frac), static_cast<f32>(z) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition3s16(s16 x, s16 y, s16 z) { void GXPosition3s16(s16 x, s16 y, s16 z) {
const auto frac = sStreamState->check_direct(GX_VA_POS, GX_POS_XYZ, GX_S16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
dlBuf->append(z);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_POS, GX_POS_XYZ, GX_S16);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
static_cast<f32>(z) / static_cast<f32>(1 << frac), static_cast<f32>(z) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition3u8(u8 x, u8 y, u8 z) { void GXPosition3u8(u8 x, u8 y, u8 z) {
const auto frac = sStreamState->check_direct(GX_VA_POS, GX_POS_XYZ, GX_U8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
dlBuf->append(z);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_POS, GX_POS_XYZ, GX_U8);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
static_cast<f32>(z) / static_cast<f32>(1 << frac), static_cast<f32>(z) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition3s8(s8 x, s8 y, s8 z) { void GXPosition3s8(s8 x, s8 y, s8 z) {
const auto frac = sStreamState->check_direct(GX_VA_POS, GX_POS_XYZ, GX_S8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
dlBuf->append(z);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_POS, GX_POS_XYZ, GX_S8);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
static_cast<f32>(z) / static_cast<f32>(1 << frac), static_cast<f32>(z) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition2f32(f32 x, f32 y) { void GXPosition2f32(f32 x, f32 y) {
sStreamState->check_direct(GX_VA_POS, GX_POS_XY, GX_F32); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{x, y, 0.f}); dlBuf->append(aurora::Vec2{x, y});
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_POS, GX_POS_XY, GX_F32);
stream->append(aurora::Vec3{x, y, 0.f});
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition2u16(u16 x, u16 y) { void GXPosition2u16(u16 x, u16 y) {
const auto frac = sStreamState->check_direct(GX_VA_POS, GX_POS_XY, GX_U16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_POS, GX_POS_XY, GX_U16);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
0.f, 0.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition2s16(s16 x, s16 y) { void GXPosition2s16(s16 x, s16 y) {
const auto frac = sStreamState->check_direct(GX_VA_POS, GX_POS_XY, GX_S16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_POS, GX_POS_XY, GX_S16);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
0.f, 0.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition2u8(u8 x, u8 y) { void GXPosition2u8(u8 x, u8 y) {
const auto frac = sStreamState->check_direct(GX_VA_POS, GX_POS_XY, GX_U8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_POS, GX_POS_XY, GX_U8);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
0.f, 0.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition2s8(s8 x, s8 y) { void GXPosition2s8(s8 x, s8 y) {
const auto frac = sStreamState->check_direct(GX_VA_POS, GX_POS_XY, GX_S8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_POS, GX_POS_XY, GX_S8);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
0.f, 0.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition1x16(u16 idx) { void GXPosition1x16(u16 idx) {
sStreamState->check_indexed(GX_VA_POS, GX_INDEX16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append<u16>(idx); dlBuf->append(idx);
} else if (auto& stream = sStreamState) {
stream->check_indexed(GX_VA_POS, GX_INDEX16);
stream->append<u16>(idx);
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXPosition1x8(u8 idx) { void GXPosition1x8(u8 idx) {
sStreamState->check_indexed(GX_VA_POS, GX_INDEX8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append<u16>(idx); dlBuf->append(idx);
} else if (auto& stream = sStreamState) {
stream->check_indexed(GX_VA_POS, GX_INDEX8);
stream->append<u16>(idx);
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXNormal3f32(f32 x, f32 y, f32 z) { void GXNormal3f32(f32 x, f32 y, f32 z) {
sStreamState->check_direct(GX_VA_NRM, GX_NRM_XYZ, GX_F32); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{x, y, z}); dlBuf->append(aurora::Vec3{x, y, z});
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_NRM, GX_NRM_XYZ, GX_F32);
stream->append(aurora::Vec3{x, y, z});
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXNormal3s16(s16 x, s16 y, s16 z) { void GXNormal3s16(s16 x, s16 y, s16 z) {
const auto frac = sStreamState->check_direct(GX_VA_NRM, GX_NRM_XYZ, GX_S16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
dlBuf->append(z);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_NRM, GX_NRM_XYZ, GX_S16);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
static_cast<f32>(z) / static_cast<f32>(1 << frac), static_cast<f32>(z) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXNormal3s8(s8 x, s8 y, s8 z) { void GXNormal3s8(s8 x, s8 y, s8 z) {
const auto frac = sStreamState->check_direct(GX_VA_NRM, GX_NRM_XYZ, GX_S8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec3{ dlBuf->append(x);
dlBuf->append(y);
dlBuf->append(z);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_NRM, GX_NRM_XYZ, GX_S8);
stream->append(aurora::Vec3{
static_cast<f32>(x) / static_cast<f32>(1 << frac), static_cast<f32>(x) / static_cast<f32>(1 << frac),
static_cast<f32>(y) / static_cast<f32>(1 << frac), static_cast<f32>(y) / static_cast<f32>(1 << frac),
static_cast<f32>(z) / static_cast<f32>(1 << frac), static_cast<f32>(z) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXNormal1x16(u16 index) { void GXNormal1x16(u16 index) {
sStreamState->check_indexed(GX_VA_NRM, GX_INDEX16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append<u16>(index); dlBuf->append(index);
} else if (auto& stream = sStreamState) {
stream->check_indexed(GX_VA_NRM, GX_INDEX16);
stream->append<u16>(index);
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXNormal1x8(u8 index) { void GXNormal1x8(u8 index) {
sStreamState->check_indexed(GX_VA_POS, GX_INDEX8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append<u16>(index); dlBuf->append(index);
} else if (auto& stream = sStreamState) {
stream->check_indexed(GX_VA_POS, GX_INDEX8);
stream->append<u16>(index);
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXColor4f32(f32 r, f32 g, f32 b, f32 a) { void GXColor4f32(f32 r, f32 g, f32 b, f32 a) {
sStreamState->check_direct(GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec4{r, g, b, a}); dlBuf->append(aurora::Vec4{r, g, b, a});
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8);
stream->append(aurora::Vec4{r, g, b, a});
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXColor4u8(u8 r, u8 g, u8 b, u8 a) { void GXColor4u8(u8 r, u8 g, u8 b, u8 a) {
sStreamState->check_direct(GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec4{ dlBuf->append(r);
dlBuf->append(g);
dlBuf->append(b);
dlBuf->append(a);
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8);
stream->append(aurora::Vec4{
static_cast<f32>(r) / 255.f, static_cast<f32>(r) / 255.f,
static_cast<f32>(g) / 255.f, static_cast<f32>(g) / 255.f,
static_cast<f32>(b) / 255.f, static_cast<f32>(b) / 255.f,
static_cast<f32>(a) / 255.f, static_cast<f32>(a) / 255.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXColor3u8(u8 r, u8 g, u8 b) { void GXColor3u8(u8 r, u8 g, u8 b) {
sStreamState->check_direct(GX_VA_CLR0, GX_CLR_RGB, GX_RGB8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec4{ dlBuf->append(r);
dlBuf->append(g);
dlBuf->append(b);
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_CLR0, GX_CLR_RGB, GX_RGB8);
stream->append(aurora::Vec4{
static_cast<f32>(r) / 255.f, static_cast<f32>(r) / 255.f,
static_cast<f32>(g) / 255.f, static_cast<f32>(g) / 255.f,
static_cast<f32>(b) / 255.f, static_cast<f32>(b) / 255.f,
1.f, 1.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXColor1u32(u32 clr) { void GXColor1u32(u32 clr) {
sStreamState->check_direct(GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec4{ dlBuf->append(clr);
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8);
stream->append(aurora::Vec4{
static_cast<f32>((clr >> 24) & 0xff) / 255.f, static_cast<f32>((clr >> 24) & 0xff) / 255.f,
static_cast<f32>((clr >> 16) & 0xff) / 255.f, static_cast<f32>((clr >> 16) & 0xff) / 255.f,
static_cast<f32>((clr >> 8) & 0xff) / 255.f, static_cast<f32>((clr >> 8) & 0xff) / 255.f,
static_cast<f32>(clr & 0xff) / 255.f, static_cast<f32>(clr & 0xff) / 255.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXColor1u16(u16 clr) { void GXColor1u16(u16 clr) {
sStreamState->check_direct(GX_VA_CLR0, GX_CLR_RGB, GX_RGB565); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec4{ dlBuf->append(clr);
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_CLR0, GX_CLR_RGB, GX_RGB565);
stream->append(aurora::Vec4{
static_cast<f32>((clr >> 11) & 0x1f) / 31.f, static_cast<f32>((clr >> 11) & 0x1f) / 31.f,
static_cast<f32>((clr >> 5) & 0x3f) / 63.f, static_cast<f32>((clr >> 5) & 0x3f) / 63.f,
static_cast<f32>(clr & 0x1f) / 31.f, static_cast<f32>(clr & 0x1f) / 31.f,
1.f, 1.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXColor1x16(u16 index) { void GXColor1x16(u16 index) {
sStreamState->check_indexed(GX_VA_CLR0, GX_INDEX16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append<u16>(index); dlBuf->append(index);
} else if (auto& stream = sStreamState) {
stream->check_indexed(GX_VA_CLR0, GX_INDEX16);
stream->append<u16>(index);
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXColor1x8(u8 index) { void GXColor1x8(u8 index) {
sStreamState->check_indexed(GX_VA_CLR0, GX_INDEX8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append<u16>(index); dlBuf->append(index);
} else if (auto& stream = sStreamState) {
stream->check_indexed(GX_VA_CLR0, GX_INDEX8);
stream->append<u16>(index);
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord2f32(f32 s, f32 t) { void GXTexCoord2f32(f32 s, f32 t) {
sStreamState->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_F32); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{s, t}); dlBuf->append(aurora::Vec2{s, t});
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_F32);
stream->append(aurora::Vec2{s, t});
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord2u16(u16 s, u16 t) { void GXTexCoord2u16(u16 s, u16 t) {
const auto frac = sStreamState->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_U16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{ dlBuf->append(s);
dlBuf->append(t);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_U16);
stream->append(aurora::Vec2{
static_cast<f32>(s) / static_cast<f32>(1 << frac), static_cast<f32>(s) / static_cast<f32>(1 << frac),
static_cast<f32>(t) / static_cast<f32>(1 << frac), static_cast<f32>(t) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord2s16(s16 s, s16 t) { void GXTexCoord2s16(s16 s, s16 t) {
const auto frac = sStreamState->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_S16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{ dlBuf->append(s);
dlBuf->append(t);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_S16);
stream->append(aurora::Vec2{
static_cast<f32>(s) / static_cast<f32>(1 << frac), static_cast<f32>(s) / static_cast<f32>(1 << frac),
static_cast<f32>(t) / static_cast<f32>(1 << frac), static_cast<f32>(t) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord2u8(u8 s, u8 t) { void GXTexCoord2u8(u8 s, u8 t) {
const auto frac = sStreamState->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_U8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{ dlBuf->append(s);
dlBuf->append(t);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_U8);
stream->append(aurora::Vec2{
static_cast<f32>(s) / static_cast<f32>(1 << frac), static_cast<f32>(s) / static_cast<f32>(1 << frac),
static_cast<f32>(t) / static_cast<f32>(1 << frac), static_cast<f32>(t) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord2s8(s8 s, s8 t) { void GXTexCoord2s8(s8 s, s8 t) {
const auto frac = sStreamState->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_S8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{ dlBuf->append(s);
dlBuf->append(t);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_TEX0, GX_TEX_ST, GX_S8);
stream->append(aurora::Vec2{
static_cast<f32>(s) / static_cast<f32>(1 << frac), static_cast<f32>(s) / static_cast<f32>(1 << frac),
static_cast<f32>(t) / static_cast<f32>(1 << frac), static_cast<f32>(t) / static_cast<f32>(1 << frac),
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord1f32(f32 s) { void GXTexCoord1f32(f32 s) {
sStreamState->check_direct(GX_VA_TEX0, GX_TEX_S, GX_F32); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{s, 0.f}); dlBuf->append(s);
} else if (auto& stream = sStreamState) {
stream->check_direct(GX_VA_TEX0, GX_TEX_S, GX_F32);
stream->append(aurora::Vec2{s, 0.f});
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord1u16(u16 s) { void GXTexCoord1u16(u16 s) {
const auto frac = sStreamState->check_direct(GX_VA_TEX0, GX_TEX_S, GX_U16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{ dlBuf->append(s);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_TEX0, GX_TEX_S, GX_U16);
stream->append(aurora::Vec2{
static_cast<f32>(s) / static_cast<f32>(1 << frac), static_cast<f32>(s) / static_cast<f32>(1 << frac),
0.f, 0.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord1s16(s16 s) { void GXTexCoord1s16(s16 s) {
const auto frac = sStreamState->check_direct(GX_VA_TEX0, GX_TEX_S, GX_S16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{ dlBuf->append(s);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_TEX0, GX_TEX_S, GX_S16);
stream->append(aurora::Vec2{
static_cast<f32>(s) / static_cast<f32>(1 << frac), static_cast<f32>(s) / static_cast<f32>(1 << frac),
0.f, 0.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord1u8(u8 s) { void GXTexCoord1u8(u8 s) {
const auto frac = sStreamState->check_direct(GX_VA_TEX0, GX_TEX_S, GX_U8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{ dlBuf->append(s);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_TEX0, GX_TEX_S, GX_U8);
stream->append(aurora::Vec2{
static_cast<f32>(s) / static_cast<f32>(1 << frac), static_cast<f32>(s) / static_cast<f32>(1 << frac),
0.f, 0.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord1s8(s8 s) { void GXTexCoord1s8(s8 s) {
const auto frac = sStreamState->check_direct(GX_VA_TEX0, GX_TEX_S, GX_S8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(aurora::Vec2{ dlBuf->append(s);
} else if (auto& stream = sStreamState) {
const auto frac = stream->check_direct(GX_VA_TEX0, GX_TEX_S, GX_S8);
stream->append(aurora::Vec2{
static_cast<f32>(s) / static_cast<f32>(1 << frac), static_cast<f32>(s) / static_cast<f32>(1 << frac),
0.f, 0.f,
}); });
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord1x16(u16 index) { void GXTexCoord1x16(u16 index) {
sStreamState->check_indexed(GX_VA_TEX0, GX_INDEX16); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(index); dlBuf->append(index);
} else if (auto& stream = sStreamState) {
stream->check_indexed(GX_VA_TEX0, GX_INDEX16);
stream->append(index);
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXTexCoord1x8(u8 index) { void GXTexCoord1x8(u8 index) {
sStreamState->check_indexed(GX_VA_TEX0, GX_INDEX8); if (auto& dlBuf = g_gxState.dynamicDlBuf) {
sStreamState->append(static_cast<u16>(index)); dlBuf->append(index);
} else if (auto& stream = sStreamState) {
stream->check_indexed(GX_VA_TEX0, GX_INDEX8);
stream->append(static_cast<u16>(index));
} else {
FATAL("Stream function called with no stream or DL active");
}
} }
void GXEnd() { void GXEnd() {

View File

@ -17,7 +17,7 @@ struct DisplayListCache {
static absl::flat_hash_map<HashType, DisplayListCache> sCachedDisplayLists; static absl::flat_hash_map<HashType, DisplayListCache> sCachedDisplayLists;
static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u16 vtxCount) { static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u16 vtxCount, bool bigEndian) {
using gx::g_gxState; using gx::g_gxState;
struct { struct {
u8 count; u8 count;
@ -114,7 +114,8 @@ static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u
buf.append(static_cast<u16>(*ptr)); buf.append(static_cast<u16>(*ptr));
++ptr; ++ptr;
} else if (g_gxState.vtxDesc[attr] == GX_INDEX16) { } else if (g_gxState.vtxDesc[attr] == GX_INDEX16) {
buf.append(bswap(*reinterpret_cast<const u16*>(ptr))); const auto value = *reinterpret_cast<const u16*>(ptr);
buf.append(bigEndian ? bswap(value) : value);
ptr += 2; ptr += 2;
} }
if (g_gxState.vtxDesc[attr] != GX_DIRECT) { if (g_gxState.vtxDesc[attr] != GX_DIRECT) {
@ -141,23 +142,24 @@ static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u
break; break;
case GX_U16: case GX_U16:
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
const auto value = bswap(reinterpret_cast<const u16*>(ptr)[i]); auto value = reinterpret_cast<const u16*>(ptr)[i];
out[i] = static_cast<f32>(value) / static_cast<f32>(1 << attrFmt.frac); out[i] = static_cast<f32>(bigEndian ? bswap(value) : value) / static_cast<f32>(1 << attrFmt.frac);
} }
buf.append(out.data(), sizeof(f32) * count); buf.append(out.data(), sizeof(f32) * count);
ptr += count * sizeof(u16); ptr += count * sizeof(u16);
break; break;
case GX_S16: case GX_S16:
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
const auto value = bswap(reinterpret_cast<const s16*>(ptr)[i]); const auto value = reinterpret_cast<const s16*>(ptr)[i];
out[i] = static_cast<f32>(value) / static_cast<f32>(1 << attrFmt.frac); out[i] = static_cast<f32>(bigEndian ? bswap(value) : value) / static_cast<f32>(1 << attrFmt.frac);
} }
buf.append(out.data(), sizeof(f32) * count); buf.append(out.data(), sizeof(f32) * count);
ptr += count * sizeof(s16); ptr += count * sizeof(s16);
break; break;
case GX_F32: case GX_F32:
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
out[i] = bswap(reinterpret_cast<const f32*>(ptr)[i]); const auto value = reinterpret_cast<const f32*>(ptr)[i];
out[i] = bigEndian ? bswap(value) : value;
} }
buf.append(out.data(), sizeof(f32) * count); buf.append(out.data(), sizeof(f32) * count);
ptr += count * sizeof(f32); ptr += count * sizeof(f32);
@ -182,7 +184,26 @@ static u32 prepare_vtx_buffer(ByteBuffer& buf, GXVtxFmt vtxfmt, const u8* ptr, u
static u16 prepare_idx_buffer(ByteBuffer& buf, GXPrimitive prim, u16 vtxStart, u16 vtxCount) { static u16 prepare_idx_buffer(ByteBuffer& buf, GXPrimitive prim, u16 vtxStart, u16 vtxCount) {
u16 numIndices = 0; u16 numIndices = 0;
if (prim == GX_TRIANGLES) { if (prim == GX_QUADS) {
buf.reserve_extra((vtxCount / 4) * 6 * sizeof(u16));
for (u16 v = 0; v < vtxCount; v += 4) {
u16 idx0 = vtxStart + v;
u16 idx1 = vtxStart + v + 1;
u16 idx2 = vtxStart + v + 2;
u16 idx3 = vtxStart + v + 3;
buf.append(idx0);
buf.append(idx1);
buf.append(idx2);
numIndices += 3;
buf.append(idx2);
buf.append(idx3);
buf.append(idx0);
numIndices += 3;
}
} else if (prim == GX_TRIANGLES) {
buf.reserve_extra(vtxCount * sizeof(u16)); buf.reserve_extra(vtxCount * sizeof(u16));
for (u16 v = 0; v < vtxCount; ++v) { for (u16 v = 0; v < vtxCount; ++v) {
const u16 idx = vtxStart + v; const u16 idx = vtxStart + v;
@ -222,7 +243,7 @@ static u16 prepare_idx_buffer(ByteBuffer& buf, GXPrimitive prim, u16 vtxStart, u
return numIndices; return numIndices;
} }
auto process_display_list(const u8* dlStart, u32 dlSize) -> DisplayListResult { auto process_display_list(const u8* dlStart, u32 dlSize, bool bigEndian) -> DisplayListResult {
const auto hash = xxh3_hash_s(dlStart, dlSize, 0); const auto hash = xxh3_hash_s(dlStart, dlSize, 0);
Range vertRange, idxRange; Range vertRange, idxRange;
u32 numIndices = 0; u32 numIndices = 0;
@ -259,9 +280,11 @@ auto process_display_list(const u8* dlStart, u32 dlSize) -> DisplayListResult {
FATAL("Vertex format changed mid-display list: {} -> {}", fmt, newFmt); FATAL("Vertex format changed mid-display list: {} -> {}", fmt, newFmt);
} }
fmt = newFmt; fmt = newFmt;
u16 vtxCount = bswap(*reinterpret_cast<const u16*>(data + pos)); u16 vtxCount = *reinterpret_cast<const u16*>(data + pos);
if (bigEndian)
vtxCount = bswap(vtxCount);
pos += 2; pos += 2;
pos += vtxCount * prepare_vtx_buffer(vtxBuf, fmt, data + pos, vtxCount); pos += vtxCount * prepare_vtx_buffer(vtxBuf, fmt, data + pos, vtxCount, bigEndian);
numIndices += prepare_idx_buffer(idxBuf, prim, vtxStart, vtxCount); numIndices += prepare_idx_buffer(idxBuf, prim, vtxStart, vtxCount);
vtxStart += vtxCount; vtxStart += vtxCount;
break; break;

View File

@ -10,5 +10,5 @@ struct DisplayListResult {
GXVtxFmt fmt; GXVtxFmt fmt;
}; };
auto process_display_list(const u8* dlStart, u32 dlSize) -> DisplayListResult; auto process_display_list(const u8* dlStart, u32 dlSize, bool bigEndian) -> DisplayListResult;
}; // namespace aurora::gfx::gx }; // namespace aurora::gfx::gx

View File

@ -294,6 +294,7 @@ struct GXState {
ClipRect texCopySrc; ClipRect texCopySrc;
GXTexFmt texCopyFmt; GXTexFmt texCopyFmt;
absl::flat_hash_map<void*, TextureHandle> copyTextures; absl::flat_hash_map<void*, TextureHandle> copyTextures;
std::optional<ByteBuffer> dynamicDlBuf;
bool depthCompare = true; bool depthCompare = true;
bool depthUpdate = true; bool depthUpdate = true;
bool colorUpdate = true; bool colorUpdate = true;

View File

@ -38,6 +38,20 @@ static inline std::string_view chan_comp(GXTevColorChan chan) noexcept {
} }
} }
u8 color_channel(GXChannelID id) {
switch (id) {
DEFAULT_FATAL("unimplemented color channel {}", id);
case GX_COLOR0:
case GX_ALPHA0:
case GX_COLOR0A0:
return 0;
case GX_COLOR1:
case GX_ALPHA1:
case GX_COLOR1A1:
return 1;
}
}
static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const ShaderConfig& config, static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const ShaderConfig& config,
const TevStage& stage) { const TevStage& stage) {
switch (arg) { switch (arg) {
@ -77,9 +91,7 @@ static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const Shade
if (stage.channelId == GX_COLOR_ZERO) { if (stage.channelId == GX_COLOR_ZERO) {
return "vec3f(0.0)"; return "vec3f(0.0)";
} }
CHECK(stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1, "invalid color channel {} for stage {}", u32 idx = color_channel(stage.channelId);
underlying(stage.channelId), stageIdx);
u32 idx = stage.channelId - GX_COLOR0A0;
const auto& swap = config.tevSwapTable[stage.tevSwapRas]; const auto& swap = config.tevSwapTable[stage.tevSwapRas];
return fmt::format("rast{}.{}{}{}", idx, chan_comp(swap.red), chan_comp(swap.green), chan_comp(swap.blue)); return fmt::format("rast{}.{}{}{}", idx, chan_comp(swap.red), chan_comp(swap.green), chan_comp(swap.blue));
} }
@ -88,9 +100,7 @@ static std::string color_arg_reg(GXTevColorArg arg, size_t stageIdx, const Shade
if (stage.channelId == GX_COLOR_ZERO) { if (stage.channelId == GX_COLOR_ZERO) {
return "vec3f(0.0)"; return "vec3f(0.0)";
} }
CHECK(stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1, "invalid color channel {} for stage {}", u32 idx = color_channel(stage.channelId);
underlying(stage.channelId), stageIdx);
u32 idx = stage.channelId - GX_COLOR0A0;
const auto& swap = config.tevSwapTable[stage.tevSwapRas]; const auto& swap = config.tevSwapTable[stage.tevSwapRas];
return fmt::format("vec3f(rast{}.{})", idx, chan_comp(swap.alpha)); return fmt::format("vec3f(rast{}.{})", idx, chan_comp(swap.alpha));
} }
@ -188,9 +198,7 @@ static std::string alpha_arg_reg(GXTevAlphaArg arg, size_t stageIdx, const Shade
if (stage.channelId == GX_COLOR_ZERO) { if (stage.channelId == GX_COLOR_ZERO) {
return "0.0"; return "0.0";
} }
CHECK(stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1, "invalid color channel {} for stage {}", u32 idx = color_channel(stage.channelId);
underlying(stage.channelId), stageIdx);
u32 idx = stage.channelId - GX_COLOR0A0;
const auto& swap = config.tevSwapTable[stage.tevSwapRas]; const auto& swap = config.tevSwapTable[stage.tevSwapRas];
return fmt::format("rast{}.{}", idx, chan_comp(swap.alpha)); return fmt::format("rast{}.{}", idx, chan_comp(swap.alpha));
} }
@ -319,6 +327,9 @@ static inline std::string vtx_attr(const ShaderConfig& config, GXAttr attr) {
// Default normal // Default normal
return "vec3f(1.0, 0.0, 0.0)"s; return "vec3f(1.0, 0.0, 0.0)"s;
} }
if (attr == GX_VA_CLR0 || attr == GX_VA_CLR1) {
return "vec4f(0.0, 0.0, 0.0, 0.0)"s;
}
UNLIKELY FATAL("unmapped vtx attr {}", underlying(attr)); UNLIKELY FATAL("unmapped vtx attr {}", underlying(attr));
} }
if (attr == GX_VA_POS) { if (attr == GX_VA_POS) {
@ -499,6 +510,16 @@ auto storage_load(const StorageConfig& mapping, u32 attrIdx) -> StorageLoadResul
std::string attrLoad; std::string attrLoad;
switch (compType) { switch (compType) {
case GX_S8:
switch (compCnt) {
case 3:
arrType = "i32";
attrLoad = fmt::format("fetch_i8_3(&v_arr_{}, {}, {})", attrName, idxFetch, mapping.frac);
break;
default:
Log.fatal("storage_load: Unsupported {} count {}", compType, compCnt);
}
break;
case GX_U16: case GX_U16:
switch (compCnt) { switch (compCnt) {
case 2: case 2:
@ -541,6 +562,10 @@ auto storage_load(const StorageConfig& mapping, u32 attrIdx) -> StorageLoadResul
Log.fatal("storage_load: Unsupported {} count {}", compType, compCnt); Log.fatal("storage_load: Unsupported {} count {}", compType, compCnt);
} }
break; break;
case GX_RGBA8:
arrType = "u32";
attrLoad = fmt::format("unpack4x8unorm(v_arr_{}[{}])", attrName, idxFetch);
break;
default: default:
Log.fatal("storage_load: Unimplemented {}", compType); Log.fatal("storage_load: Unimplemented {}", compType);
} }
@ -1136,6 +1161,22 @@ fn fetch_u8_2(p: ptr<storage, array<u32>>, idx: u32, frac: u32) -> vec2<f32> {{
var o1 = select(extractBits(v0, 8, 8), extractBits(v0, 24, 8), r); var o1 = select(extractBits(v0, 8, 8), extractBits(v0, 24, 8), r);
return vec2<f32>(f32(o0), f32(o1)) / f32(1u << frac); return vec2<f32>(f32(o0), f32(o1)) / f32(1u << frac);
}} }}
fn fetch_i8_3(p: ptr<storage, array<i32>>, idx: u32, frac: u32) -> vec3<f32> {{
let byte_idx = idx * 3u;
let word0 = p[byte_idx / 4u];
let word1 = p[(byte_idx + 1u) / 4u];
let word2 = p[(byte_idx + 2u) / 4u];
let shift0 = (byte_idx % 4u) * 8u;
let shift1 = ((byte_idx + 1u) % 4u) * 8u;
let shift2 = ((byte_idx + 2u) % 4u) * 8u;
let o0 = extractBits(word0, shift0, 8);
let o1 = extractBits(word1, shift1, 8);
let o2 = extractBits(word2, shift2, 8);
return vec3<f32>(f32(o0), f32(o1), f32(o2)) / f32(1u << frac);
}}
fn fetch_u16_2(p: ptr<storage, array<u32>>, idx: u32, frac: u32) -> vec2<f32> {{ fn fetch_u16_2(p: ptr<storage, array<u32>>, idx: u32, frac: u32) -> vec2<f32> {{
var v0 = p[idx]; var v0 = p[idx];
var o0 = extractBits(v0, 0, 16); var o0 = extractBits(v0, 0, 16);

View File

@ -10,8 +10,8 @@
namespace aurora::gfx::model { namespace aurora::gfx::model {
static Module Log("aurora::gfx::model"); static Module Log("aurora::gfx::model");
void queue_surface(const u8* dlStart, u32 dlSize) noexcept { void queue_surface(const u8* dlStart, u32 dlSize, bool bigEndian) noexcept {
const auto result = aurora::gfx::gx::process_display_list(dlStart, dlSize); const auto result = aurora::gfx::gx::process_display_list(dlStart, dlSize, bigEndian);
gx::BindGroupRanges ranges{}; gx::BindGroupRanges ranges{};
for (int i = 0; i < GX_VA_MAX_ATTR; ++i) { for (int i = 0; i < GX_VA_MAX_ATTR; ++i) {

View File

@ -23,5 +23,5 @@ State construct_state();
wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config); wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] const PipelineConfig& config);
void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass); void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass);
void queue_surface(const u8* dlStart, u32 dlSize) noexcept; void queue_surface(const u8* dlStart, u32 dlSize, bool bigEndian) noexcept;
} // namespace aurora::gfx::model } // namespace aurora::gfx::model

View File

@ -39,9 +39,7 @@ void color_arg_reg_info(GXTevColorArg arg, const TevStage& stage, ShaderInfo& in
break; break;
case GX_CC_RASC: case GX_CC_RASC:
case GX_CC_RASA: case GX_CC_RASA:
if (stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1) { info.sampledColorChannels.set(color_channel(stage.channelId));
info.sampledColorChannels.set(stage.channelId - GX_COLOR0A0);
}
break; break;
case GX_CC_KONST: case GX_CC_KONST:
switch (stage.kcSel) { switch (stage.kcSel) {
@ -111,9 +109,7 @@ void alpha_arg_reg_info(GXTevAlphaArg arg, const TevStage& stage, ShaderInfo& in
info.sampledTextures.set(stage.texMapId); info.sampledTextures.set(stage.texMapId);
break; break;
case GX_CA_RASA: case GX_CA_RASA:
if (stage.channelId >= GX_COLOR0A0 && stage.channelId <= GX_COLOR1A1) { info.sampledColorChannels.set(color_channel(stage.channelId));
info.sampledColorChannels.set(stage.channelId - GX_COLOR0A0);
}
break; break;
case GX_CA_KONST: case GX_CA_KONST:
switch (stage.kaSel) { switch (stage.kaSel) {

View File

@ -5,4 +5,5 @@
namespace aurora::gfx::gx { namespace aurora::gfx::gx {
ShaderInfo build_shader_info(const ShaderConfig& config) noexcept; ShaderInfo build_shader_info(const ShaderConfig& config) noexcept;
Range build_uniform(const ShaderInfo& info) noexcept; Range build_uniform(const ShaderInfo& info) noexcept;
u8 color_channel(GXChannelID id) noexcept;
}; // namespace aurora::gfx::gx }; // namespace aurora::gfx::gx