#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 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; 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(sStreamState->indices.size()), .bindGroups = aurora::gfx::gx::build_bind_groups(info, config.shaderConfig, {}), .dstAlpha = g_gxState.dstAlpha, }); sStreamState.reset(); }