metaforce/aurora/lib/gfx/stream.cpp

161 lines
5.5 KiB
C++

#include "stream/shader.hpp"
#include "../gpu.hpp"
#include "common.hpp"
#include "gx.hpp"
static logvisor::Module Log("aurora::gfx::stream");
using aurora::gfx::gx::g_gxState;
#ifndef NDEBUG
static inline GX::Attr next_attr(size_t begin) {
auto iter = std::find_if(g_gxState.vtxDesc.begin() + begin, g_gxState.vtxDesc.end(),
[](const auto type) { return type != GX::NONE; });
if (begin > 0 && iter == g_gxState.vtxDesc.end()) {
// wrap around
iter = std::find_if(g_gxState.vtxDesc.begin(), g_gxState.vtxDesc.end(),
[](const auto type) { return type != GX::NONE; });
}
return GX::Attr(iter - g_gxState.vtxDesc.begin());
}
#endif
struct SStreamState {
GX::Primitive primitive;
u16 vertexCount = 0;
aurora::ByteBuffer vertexBuffer;
std::vector<u16> indices;
#ifndef NDEBUG
GX::Attr nextAttr;
#endif
explicit SStreamState(GX::Primitive primitive, u16 numVerts, u16 vertexSize) noexcept : primitive(primitive) {
vertexBuffer.reserve_extra(size_t(numVerts) * vertexSize);
if (numVerts > 3 && (primitive == GX::TRIANGLEFAN || primitive == GX::TRIANGLESTRIP)) {
indices.reserve((u32(numVerts) - 3) * 3 + 3);
} else if (numVerts > 4 && primitive == GX::QUADS) {
indices.reserve(u32(numVerts) / 4 * 6);
} else {
indices.reserve(numVerts);
}
#ifndef NDEBUG
nextAttr = next_attr(0);
#endif
}
};
static std::optional<SStreamState> sStreamState;
void GXBegin(GX::Primitive primitive, GX::VtxFmt vtxFmt, u16 nVerts) noexcept {
#ifndef NDEBUG
if (sStreamState) {
Log.report(logvisor::Fatal, FMT_STRING("Stream began twice!"));
unreachable();
}
#endif
uint16_t vertexSize = 0;
for (GX::Attr attr{}; const auto type : g_gxState.vtxDesc) {
if (type == GX::DIRECT) {
if (attr == GX::VA_POS || attr == GX::VA_NRM) {
vertexSize += 12;
} else if (attr == GX::VA_CLR0 || attr == GX::VA_CLR1) {
vertexSize += 16;
} else if (attr >= GX::VA_TEX0 && attr <= GX::VA_TEX7) {
vertexSize += 8;
} else {
Log.report(logvisor::Fatal, FMT_STRING("don't know how to handle attr {}"), attr);
unreachable();
}
} else if (type == GX::INDEX8 || type == GX::INDEX16) {
vertexSize += 2;
}
attr = GX::Attr(attr + 1);
}
if (vertexSize == 0) {
Log.report(logvisor::Fatal, FMT_STRING("no vtx attributes enabled?"));
unreachable();
}
sStreamState.emplace(primitive, nVerts, vertexSize);
}
static inline void check_attr_order(GX::Attr attr) noexcept {
#ifndef NDEBUG
if (!sStreamState) {
Log.report(logvisor::Fatal, FMT_STRING("Stream not started!"));
unreachable();
}
if (sStreamState->nextAttr != attr) {
Log.report(logvisor::Fatal, FMT_STRING("bad attribute order: {}, expected {}"), attr, sStreamState->nextAttr);
unreachable();
}
sStreamState->nextAttr = next_attr(attr + 1);
#endif
}
void GXPosition3f32(const zeus::CVector3f& pos) noexcept {
check_attr_order(GX::VA_POS);
auto& state = *sStreamState;
state.vertexBuffer.append(&pos, 12);
if (state.primitive == GX::TRIANGLES || state.vertexCount < 3) {
// pass
} else if (state.primitive == GX::TRIANGLEFAN) {
state.indices.push_back(0);
state.indices.push_back(state.vertexCount - 1);
} else if (state.primitive == GX::TRIANGLESTRIP) {
if ((state.vertexCount & 1) == 0) {
state.indices.push_back(state.vertexCount - 2);
state.indices.push_back(state.vertexCount - 1);
} else {
state.indices.push_back(state.vertexCount - 1);
state.indices.push_back(state.vertexCount - 2);
}
} else if (state.primitive == GX::QUADS) {
if ((state.vertexCount & 3) == 3) {
state.indices.push_back(state.vertexCount - 3);
state.indices.push_back(state.vertexCount - 1);
}
}
state.indices.push_back(state.vertexCount);
++state.vertexCount;
}
void GXNormal3f32(const zeus::CVector3f& nrm) noexcept {
check_attr_order(GX::VA_NRM);
sStreamState->vertexBuffer.append(&nrm, 12);
}
void GXColor4f32(const zeus::CColor& color) noexcept {
check_attr_order(GX::VA_CLR0);
sStreamState->vertexBuffer.append(&color, 16);
}
void GXTexCoord2f32(const zeus::CVector2f& uv) noexcept {
check_attr_order(GX::VA_TEX0);
sStreamState->vertexBuffer.append(&uv, 8);
}
void GXPosition1x16(u16 idx) noexcept {
check_attr_order(GX::VA_POS);
// keep aligned
if (sStreamState->vertexBuffer.size() % 4 != 0) {
sStreamState->vertexBuffer.append_zeroes(4 - (sStreamState->vertexBuffer.size() % 4));
}
sStreamState->vertexBuffer.append(&idx, 2);
}
void GXEnd() noexcept {
if (sStreamState->vertexCount == 0) {
sStreamState.reset();
return;
}
const auto vertRange = aurora::gfx::push_verts(sStreamState->vertexBuffer.data(), sStreamState->vertexBuffer.size());
const auto indexRange = aurora::gfx::push_indices(aurora::ArrayRef{sStreamState->indices});
aurora::gfx::stream::PipelineConfig config{};
populate_pipeline_config(config, GX::TRIANGLES);
const auto info = aurora::gfx::gx::build_shader_info(config.shaderConfig);
const auto pipeline = aurora::gfx::pipeline_ref(config);
aurora::gfx::push_draw_command(aurora::gfx::stream::DrawData{
.pipeline = pipeline,
.vertRange = vertRange,
.uniformRange = build_uniform(info),
.indexRange = indexRange,
.indexCount = static_cast<uint32_t>(sStreamState->indices.size()),
.bindGroups = aurora::gfx::gx::build_bind_groups(info, config.shaderConfig, {}),
.dstAlpha = g_gxState.dstAlpha,
});
sStreamState.reset();
}