diff --git a/Runtime/CStringExtras.hpp b/Runtime/CStringExtras.hpp index ad9072860..99040af27 100644 --- a/Runtime/CStringExtras.hpp +++ b/Runtime/CStringExtras.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace metaforce { class CInputStream; diff --git a/Runtime/Graphics/CFont.cpp b/Runtime/Graphics/CFont.cpp index 3fe8f0a85..16d771e11 100644 --- a/Runtime/Graphics/CFont.cpp +++ b/Runtime/Graphics/CFont.cpp @@ -23,6 +23,10 @@ CFont::CFont(float scale) : x0_fontSize(16.f * scale), x4_scale(scale) { ++sNumInstances; } +void CFont::Shutdown() { + mpTexture.reset(); +} + void CFont::TileCopy8(u8* dest, const u8* src) { for (u32 i = 0; i < 4; ++i) { dest[0] = src[0]; diff --git a/Runtime/Graphics/CFont.hpp b/Runtime/Graphics/CFont.hpp index 195fb0fde..f37e54bfb 100644 --- a/Runtime/Graphics/CFont.hpp +++ b/Runtime/Graphics/CFont.hpp @@ -24,5 +24,7 @@ public: u32 StringWidth(const char* str) const; u32 CharsWidth(const char* str, u32 len) const; u32 CharWidth(const char chr) const; + + static void Shutdown(); }; } \ No newline at end of file diff --git a/Runtime/Graphics/CGraphics.cpp b/Runtime/Graphics/CGraphics.cpp index fd8f0eadf..156f31a77 100644 --- a/Runtime/Graphics/CGraphics.cpp +++ b/Runtime/Graphics/CGraphics.cpp @@ -178,14 +178,14 @@ void CGraphics::Render2D(CTexture& tex, u32 x, u32 y, u32 w, u32 h, const zeus:: SetCullMode(ERglCullMode::None); tex.Load(GX::TEXMAP0, EClampMode::Repeat); -// float hPad, vPad; -// if (CGraphics::GetViewportAspect() >= 1.78f) { -// hPad = 1.78f / CGraphics::GetViewportAspect(); -// vPad = 1.78f / 1.33f; -// } else { -// hPad = 1.f; -// vPad = CGraphics::GetViewportAspect() / 1.33f; -// } + // float hPad, vPad; + // if (CGraphics::GetViewportAspect() >= 1.78f) { + // hPad = 1.78f / CGraphics::GetViewportAspect(); + // vPad = 1.78f / 1.33f; + // } else { + // hPad = 1.f; + // vPad = CGraphics::GetViewportAspect() / 1.33f; + // } // TODO make this right float scaledX = static_cast(x) / 640.f * static_cast(g_Viewport.x8_width); float scaledY = static_cast(y) / 448.f * static_cast(g_Viewport.xc_height); @@ -565,7 +565,10 @@ void CGraphics::StreamVertex(const zeus::CVector3f& pos) { aurora::gfx::stream_vertex(sStreamFlags, pos, sQueuedNormal, sQueuedColor, sQueuedTexCoord); } -void CGraphics::StreamEnd() { aurora::gfx::stream_end(); } +void CGraphics::StreamEnd() { + SetTevStates(sStreamFlags); + aurora::gfx::stream_end(); +} void CGraphics::DrawPrimitive(GX::Primitive primitive, const zeus::CVector3f* pos, const zeus::CVector3f& normal, const zeus::CColor& col, s32 numVerts) { @@ -577,4 +580,16 @@ void CGraphics::DrawPrimitive(GX::Primitive primitive, const zeus::CVector3f* po } StreamEnd(); } + +void CGraphics::SetTevStates(EStreamFlags flags) noexcept { + if (flags & EStreamFlagBits::fHasTexture) { + aurora::gfx::set_tev_order(GX::TEVSTAGE0, GX::TEXCOORD0, GX::TEXMAP0, GX::COLOR0A0); + aurora::gfx::set_tev_order(GX::TEVSTAGE1, GX::TEXCOORD1, GX::TEXMAP1, GX::COLOR0A0); + } else { + aurora::gfx::set_tev_order(GX::TEVSTAGE0, GX::TEXCOORD_NULL, GX::TEXMAP_NULL, GX::COLOR0A0); + aurora::gfx::set_tev_order(GX::TEVSTAGE1, GX::TEXCOORD_NULL, GX::TEXMAP_NULL, GX::COLOR0A0); + } + // TODO load TCGs + aurora::gfx::set_chan_mat_src(GX::COLOR0A0, flags & EStreamFlagBits::fHasColor ? GX::SRC_VTX : GX::SRC_REG); +} } // namespace metaforce diff --git a/Runtime/Graphics/CGraphics.hpp b/Runtime/Graphics/CGraphics.hpp index 16662d666..93fa7c674 100644 --- a/Runtime/Graphics/CGraphics.hpp +++ b/Runtime/Graphics/CGraphics.hpp @@ -272,6 +272,7 @@ public: // static void DrawArray(size_t start, size_t count) { g_BooMainCommandQueue->draw(start, count); } // static void DrawArrayIndexed(size_t start, size_t count) { g_BooMainCommandQueue->drawIndexed(start, count); } + static void SetTevStates(EStreamFlags flags) noexcept; static void SetTevOp(ERglTevStage stage, const CTevCombiners::CTevPass& pass); static void StreamBegin(GX::Primitive primitive); static void StreamNormal(const zeus::CVector3f& nrm); diff --git a/Runtime/Graphics/CTevCombiners.cpp b/Runtime/Graphics/CTevCombiners.cpp index 96941c250..3348f165d 100644 --- a/Runtime/Graphics/CTevCombiners.cpp +++ b/Runtime/Graphics/CTevCombiners.cpp @@ -8,6 +8,8 @@ void CTevPass::Execute(ERglTevStage stage) const { aurora::gfx::disable_tev_stage(stage); } else { aurora::gfx::update_tev_stage(stage, x4_colorPass, x14_alphaPass, x24_colorOp, x38_alphaOp); + aurora::gfx::set_tev_order(static_cast(stage), GX::TEXCOORD_NULL, static_cast(stage), + GX::COLOR_NULL); } } diff --git a/Runtime/MP1/MP1.cpp b/Runtime/MP1/MP1.cpp index 433dc66b5..0b9a8a4aa 100644 --- a/Runtime/MP1/MP1.cpp +++ b/Runtime/MP1/MP1.cpp @@ -23,6 +23,7 @@ #include "Runtime/Character/CSkinRules.hpp" #include "Runtime/Collision/CCollidableOBBTreeGroup.hpp" #include "Runtime/Collision/CCollisionResponseData.hpp" +#include "Runtime/Graphics/CFont.hpp" #include "Runtime/Graphics/CModel.hpp" #include "Runtime/Graphics/CTexture.hpp" #include "Runtime/GuiSys/CGuiFrame.hpp" @@ -702,6 +703,7 @@ void CMain::ShutdownSubsystems() { CMappableObject::Shutdown(); // Metaforce additions CMoviePlayer::Shutdown(); + CFont::Shutdown(); } void CMain::Shutdown() { diff --git a/Runtime/Streams/ContainerReaders.hpp b/Runtime/Streams/ContainerReaders.hpp index e98a2ec21..65a321cec 100644 --- a/Runtime/Streams/ContainerReaders.hpp +++ b/Runtime/Streams/ContainerReaders.hpp @@ -6,7 +6,7 @@ namespace metaforce { template void read_reserved_vector(rstl::reserved_vector& v, CInputStream& in) { - u32 count = in.ReadLong(); + u32 count = in.ReadUint32(); v.resize(count); for (u32 i = 0; i < count; ++i) { v[i] = in.Get(); @@ -15,7 +15,7 @@ void read_reserved_vector(rstl::reserved_vector& v, CInputStream& in) { template void read_vector(std::vector& v, CInputStream& in) { - u32 count = in.ReadLong(); + u32 count = in.ReadUint32(); v.reserve(count); for (u32 i = 0; i < count; ++i) { v.emplace_back(in.Get()); diff --git a/Runtime/Streams/ContainerWriters.hpp b/Runtime/Streams/ContainerWriters.hpp index ca5330cc6..3490ac465 100644 --- a/Runtime/Streams/ContainerWriters.hpp +++ b/Runtime/Streams/ContainerWriters.hpp @@ -6,7 +6,7 @@ namespace metaforce { template void write_reserved_vector(const rstl::reserved_vector& v, COutputStream& out) { - out.Put(v.size()); + out.Put(v.size()); for (const auto& t : v) { out.Put(t); } @@ -14,7 +14,7 @@ void write_reserved_vector(const rstl::reserved_vector& v, COutputStream& template void write_vector(const std::vector& v, COutputStream& out) { - out.Put(v.size()); + out.Put(v.size()); for (const auto& t : v) { out.Put(t); } diff --git a/aurora/include/aurora/common.hpp b/aurora/include/aurora/common.hpp index b8f54686d..356e39a66 100644 --- a/aurora/include/aurora/common.hpp +++ b/aurora/include/aurora/common.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include diff --git a/aurora/include/aurora/gfx.hpp b/aurora/include/aurora/gfx.hpp index 7e4f5aa44..abc5bb362 100644 --- a/aurora/include/aurora/gfx.hpp +++ b/aurora/include/aurora/gfx.hpp @@ -284,6 +284,7 @@ void set_tev_k_color_sel(GX::TevStageID id, GX::TevKColorSel sel) noexcept; void set_tev_k_alpha_sel(GX::TevStageID id, GX::TevKAlphaSel sel) noexcept; void set_chan_amb_color(GX::ChannelID id, const zeus::CColor& color) noexcept; void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept; +void set_chan_mat_src(GX::ChannelID id, GX::ColorSrc src) noexcept; // Model state void set_alpha_discard(bool v); diff --git a/aurora/lib/aurora.cpp b/aurora/lib/aurora.cpp index b4e096f47..f288187be 100644 --- a/aurora/lib/aurora.cpp +++ b/aurora/lib/aurora.cpp @@ -301,10 +301,10 @@ void app_run(std::unique_ptr app, Icon icon, int argc, char** argv) .storeOp = wgpu::StoreOp::Store, .clearColor = { - .r = gfx::g_clearColor.r(), - .g = gfx::g_clearColor.g(), - .b = gfx::g_clearColor.b(), - .a = gfx::g_clearColor.a(), + .r = gfx::gx::g_clearColor.r(), + .g = gfx::gx::g_clearColor.g(), + .b = gfx::gx::g_clearColor.b(), + .a = gfx::gx::g_clearColor.a(), }, }, }; diff --git a/aurora/lib/gfx/colored_quad/shader.cpp b/aurora/lib/gfx/colored_quad/shader.cpp index 58b8358b0..c1db158fe 100644 --- a/aurora/lib/gfx/colored_quad/shader.cpp +++ b/aurora/lib/gfx/colored_quad/shader.cpp @@ -285,7 +285,7 @@ DrawData make_draw_data_verts(const State& state, CameraFilterType filter_type, const auto vertRange = push_verts(ArrayRef{verts}); const auto uniform = Uniform{ - .xf = get_combined_matrix(), + .xf = gx::get_combined_matrix(), .color = color, }; const auto uniformRange = push_uniform(uniform); diff --git a/aurora/lib/gfx/common.cpp b/aurora/lib/gfx/common.cpp index cea547e58..9d0e97ee1 100644 --- a/aurora/lib/gfx/common.cpp +++ b/aurora/lib/gfx/common.cpp @@ -66,7 +66,7 @@ struct Command { }; using NewPipelineCallback = std::function; -static std::mutex g_pipelineMutex; +std::mutex g_pipelineMutex; static std::thread g_pipelineThread; static std::atomic_bool g_pipelineThreadEnd; static std::condition_variable g_pipelineCv; @@ -277,7 +277,10 @@ void shutdown() { g_pipelineCv.notify_all(); g_pipelineThread.join(); + gx::shutdown(); + g_cachedBindGroups.clear(); + g_cachedSamplers.clear(); g_pipelines.clear(); g_vertexBuffer = {}; g_uniformBuffer = {}; diff --git a/aurora/lib/gfx/common.hpp b/aurora/lib/gfx/common.hpp index 0a2317298..c21a06640 100644 --- a/aurora/lib/gfx/common.hpp +++ b/aurora/lib/gfx/common.hpp @@ -11,6 +11,8 @@ #endif namespace aurora { +template +static inline void xxh3_update(XXH3_state_t& state, const T& input); static inline XXH64_hash_t xxh3_hash(const void* input, size_t len, XXH64_hash_t seed = 0) { return XXH3_64bits_withSeed(input, len, seed); } diff --git a/aurora/lib/gfx/gx.cpp b/aurora/lib/gfx/gx.cpp index bf2c104ce..359989cf1 100644 --- a/aurora/lib/gfx/gx.cpp +++ b/aurora/lib/gfx/gx.cpp @@ -1,13 +1,142 @@ #include "gx.hpp" -#include "common.hpp" #include "../gpu.hpp" +#include "common.hpp" +#include #include namespace aurora::gfx { static logvisor::Module Log("aurora::gfx::gx"); +// TODO remove this hack for build_shader +extern std::mutex g_pipelineMutex; + +// GX state +void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHandle& tex, float lod) noexcept { + gx::g_textures[static_cast(id)] = {tex, clamp, lod}; +} +void unbind_texture(GX::TexMapID id) noexcept { gx::g_textures[static_cast(id)].reset(); } + +void set_cull_mode(metaforce::ERglCullMode mode) noexcept { gx::g_cullMode = mode; } +void set_blend_mode(metaforce::ERglBlendMode mode, metaforce::ERglBlendFactor src, metaforce::ERglBlendFactor dst, + metaforce::ERglLogicOp op) noexcept { + gx::g_blendMode = mode; + gx::g_blendFacSrc = src; + gx::g_blendFacDst = dst; + gx::g_blendOp = op; +} +void set_depth_mode(bool compare_enable, metaforce::ERglEnum func, bool update_enable) noexcept { + gx::g_depthCompare = compare_enable; + gx::g_depthFunc = func; + gx::g_depthUpdate = update_enable; +} +void set_tev_reg_color(GX::TevRegID id, const zeus::CColor& color) noexcept { + if (id < GX::TEVREG0 || id > GX::TEVREG2) { + Log.report(logvisor::Fatal, FMT_STRING("set_tev_reg_color: bad reg {}"), id); + unreachable(); + } + gx::g_colorRegs[id - 1] = color; +} +void set_tev_k_color(GX::TevKColorID id, const zeus::CColor& color) noexcept { + if (id >= GX::MAX_KCOLOR) { + Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_color: bad reg {}"), id); + unreachable(); + } + gx::g_kcolors[id] = color; +} +void set_alpha_update(bool enabled) noexcept { gx::g_alphaUpdate = enabled; } +void set_dst_alpha(bool enabled, float value) noexcept { + if (enabled) { + gx::g_dstAlpha = value; + } else { + gx::g_dstAlpha.reset(); + } +} +void set_clear_color(const zeus::CColor& color) noexcept { gx::g_clearColor = color; } +void set_alpha_discard(bool v) { gx::g_alphaDiscard = v; } + +void update_model_view(const zeus::CMatrix4f& mv, const zeus::CMatrix4f& mv_inv) noexcept { + gx::g_mv = mv; + gx::g_mvInv = mv_inv; +} +constexpr zeus::CMatrix4f DepthCorrect{ + // clang-format off + 1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 0.5f, 0.5f, + 0.f, 0.f, 0.f, 1.f, + // clang-format on +}; +void update_projection(const zeus::CMatrix4f& proj) noexcept { gx::g_proj = DepthCorrect * proj; } +void update_fog_state(const metaforce::CFogState& state) noexcept { gx::g_fogState = state; } + +void disable_tev_stage(metaforce::ERglTevStage stage) noexcept { gx::g_tevStages[static_cast(stage)].reset(); } +void update_tev_stage(metaforce::ERglTevStage stage, const metaforce::CTevCombiners::ColorPass& colPass, + const metaforce::CTevCombiners::AlphaPass& alphaPass, + const metaforce::CTevCombiners::CTevOp& colorOp, + const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept { + gx::g_tevStages[static_cast(stage)] = {colPass, alphaPass, colorOp, alphaOp}; +} + +void set_tev_order(GX::TevStageID id, GX::TexCoordID tcid, GX::TexMapID tmid, GX::ChannelID cid) noexcept { + auto& stage = gx::g_tevStages[id]; + if (!stage) { + // Log.report(logvisor::Fatal, FMT_STRING("set_tev_order: disabled stage {}"), id); + // unreachable(); + return; + } + stage->texCoordId = tcid; + stage->texMapId = tmid; + stage->channelId = cid; +} +void set_tev_k_color_sel(GX::TevStageID id, GX::TevKColorSel sel) noexcept { + auto& stage = gx::g_tevStages[id]; + if (!stage) { + Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_color_sel: disabled stage {}"), id); + unreachable(); + } + stage->kcSel = sel; +} +void set_tev_k_alpha_sel(GX::TevStageID id, GX::TevKAlphaSel sel) noexcept { + auto& stage = gx::g_tevStages[id]; + if (!stage) { + Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_alpha_sel: disabled stage {}"), id); + unreachable(); + } + stage->kaSel = sel; +} + +void set_chan_amb_color(GX::ChannelID id, const zeus::CColor& color) noexcept { + if (id < GX::COLOR0A0 || id > GX::COLOR1A1) { + Log.report(logvisor::Fatal, FMT_STRING("set_chan_amb_color: invalid channel {}"), id); + unreachable(); + } + gx::g_colorChannels[id - GX::COLOR0A0].ambColor = color; +} +void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept { + if (id < GX::COLOR0A0 || id > GX::COLOR1A1) { + Log.report(logvisor::Fatal, FMT_STRING("set_chan_mat_color: invalid channel {}"), id); + unreachable(); + } + gx::g_colorChannels[id - GX::COLOR0A0].matColor = color; +} +void set_chan_mat_src(GX::ChannelID id, GX::ColorSrc src) noexcept { + if (id < GX::COLOR0A0 || id > GX::COLOR1A1) { + Log.report(logvisor::Fatal, FMT_STRING("set_chan_mat_src: invalid channel {}"), id); + unreachable(); + } + gx::g_colorChannels[id - GX::COLOR0A0].matSrc = src; +} + +void load_light(GX::LightID id, const Light& light) noexcept { gx::g_lights[id] = light; } +void load_light_ambient(GX::LightID id, const zeus::CColor& ambient) noexcept { gx::g_lights[id] = ambient; } +void set_light_state(std::bitset bits) noexcept { gx::g_lightState = bits; } + +namespace gx { +using gpu::g_device; +using gpu::g_graphicsConfig; + zeus::CMatrix4f g_mv; zeus::CMatrix4f g_mvInv; zeus::CMatrix4f g_proj; @@ -32,123 +161,10 @@ std::array g_lights; std::bitset g_lightState; std::array, maxTevStages> g_tevStages; -std::array g_textures; +std::array g_textures; -// GX state -void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHandle& tex, float lod) noexcept { - g_textures[static_cast(id)] = {tex, clamp, lod}; -} -void unbind_texture(GX::TexMapID id) noexcept { g_textures[static_cast(id)].reset(); } -const STextureBind& get_texture(GX::TexMapID id) noexcept { return g_textures[static_cast(id)]; } +const gx::TextureBind& get_texture(GX::TexMapID id) noexcept { return gx::g_textures[static_cast(id)]; } -void set_cull_mode(metaforce::ERglCullMode mode) noexcept { g_cullMode = mode; } -void set_blend_mode(metaforce::ERglBlendMode mode, metaforce::ERglBlendFactor src, metaforce::ERglBlendFactor dst, - metaforce::ERglLogicOp op) noexcept { - g_blendMode = mode; - g_blendFacSrc = src; - g_blendFacDst = dst; - g_blendOp = op; -} -void set_depth_mode(bool compare_enable, metaforce::ERglEnum func, bool update_enable) noexcept { - g_depthCompare = compare_enable; - g_depthFunc = func; - g_depthUpdate = update_enable; -} -void set_tev_reg_color(GX::TevRegID id, const zeus::CColor& color) noexcept { - if (id < GX::TEVREG0 || id > GX::TEVREG2) { - Log.report(logvisor::Fatal, FMT_STRING("set_tev_reg_color: bad reg {}"), id); - unreachable(); - } - g_colorRegs[id - 1] = color; -} -void set_tev_k_color(GX::TevKColorID id, const zeus::CColor& color) noexcept { - if (id >= GX::MAX_KCOLOR) { - Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_color: bad reg {}"), id); - unreachable(); - } - g_kcolors[id] = color; -} -void set_alpha_update(bool enabled) noexcept { g_alphaUpdate = enabled; } -void set_dst_alpha(bool enabled, float value) noexcept { - if (enabled) { - g_dstAlpha = value; - } else { - g_dstAlpha.reset(); - } -} -void set_clear_color(const zeus::CColor& color) noexcept { g_clearColor = color; } -void set_alpha_discard(bool v) { g_alphaDiscard = v; } - -void update_model_view(const zeus::CMatrix4f& mv, const zeus::CMatrix4f& mv_inv) noexcept { - g_mv = mv; - g_mvInv = mv_inv; -} -constexpr zeus::CMatrix4f DepthCorrect{ - // clang-format off - 1.f, 0.f, 0.f, 0.f, - 0.f, 1.f, 0.f, 0.f, - 0.f, 0.f, 0.5f, 0.5f, - 0.f, 0.f, 0.f, 1.f, - // clang-format on -}; -void update_projection(const zeus::CMatrix4f& proj) noexcept { g_proj = DepthCorrect * proj; } -void update_fog_state(const metaforce::CFogState& state) noexcept { g_fogState = state; } - -void disable_tev_stage(metaforce::ERglTevStage stage) noexcept { g_tevStages[static_cast(stage)].reset(); } -void update_tev_stage(metaforce::ERglTevStage stage, const metaforce::CTevCombiners::ColorPass& colPass, - const metaforce::CTevCombiners::AlphaPass& alphaPass, - const metaforce::CTevCombiners::CTevOp& colorOp, - const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept { - g_tevStages[static_cast(stage)] = {colPass, alphaPass, colorOp, alphaOp}; -} - -void set_tev_order(GX::TevStageID id, GX::TexCoordID tcid, GX::TexMapID tmid, GX::ChannelID cid) noexcept { - auto& stage = g_tevStages[id]; - if (!stage) { - Log.report(logvisor::Fatal, FMT_STRING("set_tev_order: disabled stage {}"), id); - unreachable(); - } - stage->texCoordId = tcid; - stage->texMapId = tmid; - stage->channelId = cid; -} -void set_tev_k_color_sel(GX::TevStageID id, GX::TevKColorSel sel) noexcept { - auto& stage = g_tevStages[id]; - if (!stage) { - Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_color_sel: disabled stage {}"), id); - unreachable(); - } - stage->kcSel = sel; -} -void set_tev_k_alpha_sel(GX::TevStageID id, GX::TevKAlphaSel sel) noexcept { - auto& stage = g_tevStages[id]; - if (!stage) { - Log.report(logvisor::Fatal, FMT_STRING("set_tev_k_alpha_sel: disabled stage {}"), id); - unreachable(); - } - stage->kaSel = sel; -} - -void set_chan_amb_color(GX::ChannelID id, const zeus::CColor& color) noexcept { - if (id < GX::COLOR0A0 || id > GX::COLOR1A1) { - Log.report(logvisor::Fatal, FMT_STRING("set_chan_amb_color: invalid channel {}"), id); - unreachable(); - } - g_colorChannels[id - GX::COLOR0A0].ambColor = color; -} -void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept { - if (id < GX::COLOR0A0 || id > GX::COLOR1A1) { - Log.report(logvisor::Fatal, FMT_STRING("set_chan_mat_color: invalid channel {}"), id); - unreachable(); - } - g_colorChannels[id - GX::COLOR0A0].matColor = color; -} - -void load_light(GX::LightID id, const Light& light) noexcept { g_lights[id] = light; } -void load_light_ambient(GX::LightID id, const zeus::CColor& ambient) noexcept { g_lights[id] = ambient; } -void set_light_state(std::bitset bits) noexcept { g_lightState = bits; } - -// Pipeline helpers static inline wgpu::BlendFactor to_blend_factor(metaforce::ERglBlendFactor fac) { switch (fac) { case metaforce::ERglBlendFactor::Zero: @@ -259,9 +275,9 @@ static inline wgpu::PrimitiveState to_primitive_state(GX::Primitive gx_prim, met }; } -wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef vtxBuffers, - const GXPipelineConfig& config, zstring_view label) noexcept { - using gpu::g_graphicsConfig; +wgpu::RenderPipeline build_pipeline(const PipelineConfig& config, const ShaderInfo& info, + ArrayRef vtxBuffers, wgpu::ShaderModule shader, + zstring_view label) noexcept { const auto depthStencil = wgpu::DepthStencilState{ .format = g_graphicsConfig.depthFormat, .depthWriteEnabled = config.depthUpdate, @@ -273,19 +289,30 @@ wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef(info.sampledTextures.any() ? bindGroupLayouts.size() : 1), + .bindGroupLayouts = bindGroupLayouts.data(), + }; + auto pipelineLayout = g_device.CreatePipelineLayout(&pipelineLayoutDescriptor); const auto descriptor = wgpu::RenderPipelineDescriptor{ .label = label.c_str(), - .layout = std::move(layout), + .layout = std::move(pipelineLayout), .vertex = { - .module = shader, + .module = std::move(shader), .entryPoint = "vs_main", .bufferCount = static_cast(vtxBuffers.size()), .buffers = vtxBuffers.data(), @@ -298,11 +325,10 @@ wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef enabledTextures) noexcept { +ShaderInfo populate_pipeline_config(PipelineConfig& config, GX::Primitive primitive) noexcept { for (size_t idx = 0; const auto& item : g_tevStages) { // Copy until disabled TEV stage (indicating end) if (!item) { @@ -310,12 +336,9 @@ void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primiti } config.shaderConfig.tevStages[idx++] = item; } + config.shaderConfig.channelMatSrcs[0] = g_colorChannels[0].matSrc; + config.shaderConfig.channelMatSrcs[1] = g_colorChannels[1].matSrc; config.shaderConfig.alphaDiscard = g_alphaDiscard; - for (size_t idx = 0; const auto& item : g_textures) { - if (enabledTextures.test(idx) && item) { - config.shaderConfig.boundTextures.set(idx); - } - } config = { .shaderConfig = config.shaderConfig, .primitive = primitive, @@ -330,9 +353,182 @@ void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primiti .depthUpdate = g_depthUpdate, .alphaUpdate = g_alphaUpdate, }; + // TODO separate shader info from build_shader for async + { + std::lock_guard lk{g_pipelineMutex}; + const auto [_, info] = build_shader(config.shaderConfig); + return info; + } } -wgpu::SamplerDescriptor STextureBind::get_descriptor() const noexcept { +Range build_uniform(const ShaderInfo& info) noexcept { + ByteBuffer uniBuf; + { + const auto xf = get_combined_matrix(); + uniBuf.append(&xf, 64); + } + for (int i = 0; i < info.sampledColorChannels.size(); ++i) { + if (!info.sampledColorChannels.test(i)) { + continue; + } + uniBuf.append(&g_colorChannels[i].ambColor, 16); + uniBuf.append(&g_colorChannels[i].matColor, 16); + } + for (int i = 0; i < info.sampledKcolors.size(); ++i) { + if (!info.sampledKcolors.test(i)) { + continue; + } + uniBuf.append(&g_kcolors[i], 16); + } + for (int i = 0; i < info.sampledTextures.size(); ++i) { + if (!info.sampledTextures.test(i)) { + continue; + } + const auto& tex = get_texture(static_cast(i)); + if (!tex) { + Log.report(logvisor::Fatal, FMT_STRING("unbound texture {}"), i); + unreachable(); + } + uniBuf.append(&tex.lod, 4); + } + return push_uniform(uniBuf.data(), uniBuf.size()); +} + +static std::unordered_map sUniformBindGroupLayouts; +static std::unordered_map> sTextureBindGroupLayouts; + +GXBindGroups build_bind_groups(const ShaderInfo& info) noexcept { + const auto layouts = build_bind_group_layouts(info); + u32 textureCount = info.sampledTextures.count(); + + const std::array uniformEntries{wgpu::BindGroupEntry{ + .binding = 0, + .buffer = g_uniformBuffer, + .size = info.uniformSize, + }}; + std::array samplerEntries; + std::array textureEntries; + for (u32 texIdx = 0, i = 0; texIdx < info.sampledTextures.size(); ++texIdx) { + if (!info.sampledTextures.test(texIdx)) { + continue; + } + const auto& tex = g_textures[texIdx]; + if (!tex) { + Log.report(logvisor::Fatal, FMT_STRING("unbound texture {}"), texIdx); + unreachable(); + } + samplerEntries[i] = { + .binding = i, + .sampler = sampler_ref(tex.get_descriptor()), + }; + textureEntries[i] = { + .binding = i, + .textureView = tex.handle.ref->view, + }; + i++; + } + return { + .uniformBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ + .label = "GX Uniform Bind Group", + .layout = layouts.uniformLayout, + .entryCount = uniformEntries.size(), + .entries = uniformEntries.data(), + }), + .samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ + .label = "GX Sampler Bind Group", + .layout = layouts.samplerLayout, + .entryCount = textureCount, + .entries = samplerEntries.data(), + }), + .textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ + .label = "GX Texture Bind Group", + .layout = layouts.textureLayout, + .entryCount = textureCount, + .entries = textureEntries.data(), + }), + }; +} + +GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info) noexcept { + GXBindGroupLayouts out; + if (sUniformBindGroupLayouts.contains(info.uniformSize)) { + out.uniformLayout = sUniformBindGroupLayouts[info.uniformSize]; + } else { + const std::array uniformLayoutEntries{wgpu::BindGroupLayoutEntry{ + .binding = 0, + .visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment, + .buffer = + wgpu::BufferBindingLayout{ + .type = wgpu::BufferBindingType::Uniform, + .hasDynamicOffset = true, + .minBindingSize = info.uniformSize, + }, + }}; + const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{ + .label = "GX Uniform Bind Group Layout", + .entryCount = uniformLayoutEntries.size(), + .entries = uniformLayoutEntries.data(), + }; + out.uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor); + sUniformBindGroupLayouts.try_emplace(info.uniformSize, out.uniformLayout); + } + + u32 textureCount = info.sampledTextures.count(); + if (sTextureBindGroupLayouts.contains(textureCount)) { + const auto& [sl, tl] = sTextureBindGroupLayouts[textureCount]; + out.samplerLayout = sl; + out.textureLayout = tl; + } else { + std::array samplerEntries; + std::array textureEntries; + for (u32 i = 0; i < textureCount; ++i) { + samplerEntries[i] = { + .binding = i, + .visibility = wgpu::ShaderStage::Fragment, + .sampler = {.type = wgpu::SamplerBindingType::Filtering}, + }; + textureEntries[i] = { + .binding = i, + .visibility = wgpu::ShaderStage::Fragment, + .texture = + { + .sampleType = wgpu::TextureSampleType::Float, + .viewDimension = wgpu::TextureViewDimension::e2D, + }, + }; + } + { + const wgpu::BindGroupLayoutDescriptor descriptor{ + .label = "GX Sampler Bind Group", + .entryCount = textureCount, + .entries = samplerEntries.data(), + }; + out.samplerLayout = g_device.CreateBindGroupLayout(&descriptor); + } + { + const wgpu::BindGroupLayoutDescriptor descriptor{ + .label = "GX Texture Bind Group", + .entryCount = textureCount, + .entries = textureEntries.data(), + }; + out.textureLayout = g_device.CreateBindGroupLayout(&descriptor); + } + sTextureBindGroupLayouts.try_emplace(textureCount, out.samplerLayout, out.textureLayout); + } + return out; +} + +// TODO this is awkward +extern std::unordered_map> g_gxCachedShaders; +void shutdown() noexcept { + // TODO we should probably store this all in g_state.gx instead + sUniformBindGroupLayouts.clear(); + sTextureBindGroupLayouts.clear(); + g_textures.fill({}); + g_gxCachedShaders.clear(); +} + +wgpu::SamplerDescriptor TextureBind::get_descriptor() const noexcept { wgpu::AddressMode mode; switch (clampMode) { case metaforce::EClampMode::Clamp: @@ -354,7 +550,8 @@ wgpu::SamplerDescriptor STextureBind::get_descriptor() const noexcept { .magFilter = wgpu::FilterMode::Linear, .minFilter = wgpu::FilterMode::Linear, .mipmapFilter = wgpu::FilterMode::Linear, - .maxAnisotropy = gpu::g_graphicsConfig.textureAnistropy, + .maxAnisotropy = g_graphicsConfig.textureAnistropy, }; } +} // namespace gx } // namespace aurora::gfx diff --git a/aurora/lib/gfx/gx.hpp b/aurora/lib/gfx/gx.hpp index 95dee9dce..522e805a0 100644 --- a/aurora/lib/gfx/gx.hpp +++ b/aurora/lib/gfx/gx.hpp @@ -4,7 +4,7 @@ #include -namespace aurora::gfx { +namespace aurora::gfx::gx { extern zeus::CMatrix4f g_mv; extern zeus::CMatrix4f g_mvInv; extern zeus::CMatrix4f g_proj; @@ -27,6 +27,7 @@ extern bool g_alphaDiscard; struct SChannelState { zeus::CColor matColor; zeus::CColor ambColor; + GX::ColorSrc matSrc; }; extern std::array g_colorChannels; using LightVariant = std::variant; @@ -50,13 +51,13 @@ struct STevStage { const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept : colorPass(colPass), alphaPass(alphaPass), colorOp(colorOp), alphaOp(alphaOp) {} }; -struct STextureBind { +struct TextureBind { aurora::gfx::TextureHandle handle; metaforce::EClampMode clampMode; float lod; - STextureBind() noexcept = default; - STextureBind(aurora::gfx::TextureHandle handle, metaforce::EClampMode clampMode, float lod) noexcept + TextureBind() noexcept = default; + TextureBind(aurora::gfx::TextureHandle handle, metaforce::EClampMode clampMode, float lod) noexcept : handle(std::move(handle)), clampMode(clampMode), lod(lod) {} void reset() noexcept { handle.reset(); }; [[nodiscard]] wgpu::SamplerDescriptor get_descriptor() const noexcept; @@ -66,22 +67,21 @@ struct STextureBind { constexpr u32 maxTevStages = GX::MAX_TEVSTAGE; extern std::array, maxTevStages> g_tevStages; constexpr u32 maxTextures = 8; -extern std::array g_textures; +extern std::array g_textures; static inline Mat4x4 get_combined_matrix() noexcept { return g_proj * g_mv; } -const STextureBind& get_texture(GX::TexMapID id) noexcept; +void shutdown() noexcept; +const TextureBind& get_texture(GX::TexMapID id) noexcept; -struct GXShaderConfig { +struct ShaderConfig { std::array, maxTevStages> tevStages; + std::array channelMatSrcs; bool alphaDiscard; bool denormalizedVertexAttributes; - bool denormalizedNorm; - bool denormalizedColor; - std::bitset boundTextures; }; -struct GXPipelineConfig { - GXShaderConfig shaderConfig; +struct PipelineConfig { + ShaderConfig shaderConfig; GX::Primitive primitive; metaforce::ERglEnum depthFunc; metaforce::ERglCullMode cullMode; @@ -91,9 +91,65 @@ struct GXPipelineConfig { std::optional dstAlpha; bool depthCompare, depthUpdate, alphaUpdate; }; -void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primitive, - std::bitset enabledTextures) noexcept; -wgpu::ShaderModule build_shader(const GXShaderConfig& config); -wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef vtxBuffers, - const GXPipelineConfig& config, zstring_view label) noexcept; -} // namespace aurora::gfx +// Output info from shader generation +struct ShaderInfo { + std::bitset sampledTextures; + std::bitset<4> sampledKcolors; + std::bitset<2> sampledColorChannels; + u32 uniformSize = 0; + bool usesVtxColor : 1 = false; + bool usesNormal : 1 = false; +}; +ShaderInfo populate_pipeline_config(PipelineConfig& config, GX::Primitive primitive) noexcept; +wgpu::RenderPipeline build_pipeline(const PipelineConfig& config, const ShaderInfo& info, + ArrayRef vtxBuffers, wgpu::ShaderModule shader, + zstring_view label) noexcept; +std::pair build_shader(const ShaderConfig& config) noexcept; + +// Range build_vertex_buffer(const GXShaderInfo& info) noexcept; +Range build_uniform(const ShaderInfo& info) noexcept; + +struct GXBindGroupLayouts { + wgpu::BindGroupLayout uniformLayout; + wgpu::BindGroupLayout samplerLayout; + wgpu::BindGroupLayout textureLayout; +}; +struct GXBindGroups { + BindGroupRef uniformBindGroup; + BindGroupRef samplerBindGroup; + BindGroupRef textureBindGroup; +}; +GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info) noexcept; +GXBindGroups build_bind_groups(const ShaderInfo& info) noexcept; +} // namespace aurora::gfx::gx + +namespace aurora { +template <> +inline void xxh3_update(XXH3_state_t& state, const gfx::gx::STevStage& input) { + XXH3_64bits_update(&state, &input.colorPass, sizeof(metaforce::CTevCombiners::ColorPass)); + XXH3_64bits_update(&state, &input.alphaPass, sizeof(metaforce::CTevCombiners::AlphaPass)); + XXH3_64bits_update(&state, &input.colorOp, sizeof(metaforce::CTevCombiners::CTevOp)); + XXH3_64bits_update(&state, &input.alphaOp, sizeof(metaforce::CTevCombiners::CTevOp)); + XXH3_64bits_update(&state, &input.kcSel, sizeof(GX::TevKColorSel)); + XXH3_64bits_update(&state, &input.kaSel, sizeof(GX::TevKAlphaSel)); + XXH3_64bits_update(&state, &input.texCoordId, sizeof(GX::TexCoordID)); + XXH3_64bits_update(&state, &input.texMapId, sizeof(GX::TexMapID)); + XXH3_64bits_update(&state, &input.channelId, sizeof(GX::ChannelID)); +} +template <> +inline XXH64_hash_t xxh3_hash(const gfx::gx::ShaderConfig& input, XXH64_hash_t seed) { + XXH3_state_t state; + XXH3_INITSTATE(&state); + XXH3_64bits_reset_withSeed(&state, seed); + for (const auto& item : input.tevStages) { + if (!item) { + break; + } + xxh3_update(state, *item); + } + XXH3_64bits_update(&state, input.channelMatSrcs.data(), input.channelMatSrcs.size() * sizeof(GX::ColorSrc)); + XXH3_64bits_update(&state, &input.alphaDiscard, sizeof(bool)); + XXH3_64bits_update(&state, &input.denormalizedVertexAttributes, sizeof(bool)); + return XXH3_64bits_digest(&state); +} +} // namespace aurora \ No newline at end of file diff --git a/aurora/lib/gfx/gx_shader.cpp b/aurora/lib/gfx/gx_shader.cpp index 001411e35..5c44c51e6 100644 --- a/aurora/lib/gfx/gx_shader.cpp +++ b/aurora/lib/gfx/gx_shader.cpp @@ -3,14 +3,16 @@ #include "../gpu.hpp" #include "gx.hpp" -namespace aurora::gfx { +#include + +namespace aurora::gfx::gx { using namespace fmt::literals; static logvisor::Module Log("aurora::gfx::gx"); -std::unordered_map g_gxCachedShaders; +std::unordered_map> g_gxCachedShaders; -static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) { +static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx, const STevStage& stage, ShaderInfo& info) { switch (arg) { case GX::CC_CPREV: return "prev.rgb"; @@ -24,26 +26,145 @@ static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) { case GX::CC_A2: Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), arg); unreachable(); - case GX::CC_TEXC: - return fmt::format(FMT_STRING("sampled{}.rgb"), stageIdx); - case GX::CC_TEXA: - return fmt::format(FMT_STRING("sampled{}.a"), stageIdx); - case GX::CC_RASC: - return "rast.rgb"; - case GX::CC_RASA: - return "rast.a"; + case GX::CC_TEXC: { + if (stage.texMapId == GX::TEXMAP_NULL) { + Log.report(logvisor::Fatal, FMT_STRING("unmapped texture for stage {}"), stageIdx); + unreachable(); + } else if (stage.texMapId < GX::TEXMAP0 || stage.texMapId > GX::TEXMAP7) { + Log.report(logvisor::Fatal, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx); + unreachable(); + } + info.sampledTextures.set(stage.texMapId); + return fmt::format(FMT_STRING("sampled{}.rgb"), stage.texMapId); + } + case GX::CC_TEXA: { + if (stage.texMapId == GX::TEXMAP_NULL) { + Log.report(logvisor::Fatal, FMT_STRING("unmapped texture for stage {}"), stageIdx); + unreachable(); + } else if (stage.texMapId < GX::TEXMAP0 || stage.texMapId > GX::TEXMAP7) { + Log.report(logvisor::Fatal, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx); + unreachable(); + } + info.sampledTextures.set(stage.texMapId); + return fmt::format(FMT_STRING("sampled{}.a"), stage.texMapId); + } + case GX::CC_RASC: { + if (stage.channelId == GX::COLOR_NULL) { + Log.report(logvisor::Fatal, FMT_STRING("unmapped color channel for stage {}"), stageIdx); + unreachable(); + } else if (stage.channelId < GX::COLOR0A0 || stage.channelId > GX::COLOR1A1) { + Log.report(logvisor::Fatal, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx); + unreachable(); + } + u32 idx = stage.channelId - GX::COLOR0A0; + info.sampledColorChannels.set(idx); + return fmt::format(FMT_STRING("rast{}.rgb"), idx); + } + case GX::CC_RASA: { + if (stage.channelId == GX::COLOR_NULL) { + Log.report(logvisor::Fatal, FMT_STRING("unmapped color channel for stage {}"), stageIdx); + unreachable(); + } else if (stage.channelId < GX::COLOR0A0 || stage.channelId > GX::COLOR1A1) { + Log.report(logvisor::Fatal, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx); + unreachable(); + } + u32 idx = stage.channelId - GX::COLOR0A0; + info.sampledColorChannels.set(idx); + return fmt::format(FMT_STRING("rast{}.a"), idx); + } case GX::CC_ONE: return "1.0"; case GX::CC_HALF: return "0.5"; - case GX::CC_KONST: - return fmt::format(FMT_STRING("ubuf.kcolor{}.rgb"), stageIdx); + case GX::CC_KONST: { + switch (stage.kcSel) { + case GX::TEV_KCSEL_8_8: + return "1.0"; + case GX::TEV_KCSEL_7_8: + return "(7.0/8.0)"; + case GX::TEV_KCSEL_6_8: + return "(6.0/8.0)"; + case GX::TEV_KCSEL_5_8: + return "(5.0/8.0)"; + case GX::TEV_KCSEL_4_8: + return "(4.0/8.0)"; + case GX::TEV_KCSEL_3_8: + return "(3.0/8.0)"; + case GX::TEV_KCSEL_2_8: + return "(2.0/8.0)"; + case GX::TEV_KCSEL_1_8: + return "(1.0/8.0)"; + case GX::TEV_KCSEL_K0: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.rgb"; + case GX::TEV_KCSEL_K1: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.rgb"; + case GX::TEV_KCSEL_K2: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.rgb"; + case GX::TEV_KCSEL_K3: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.rgb"; + case GX::TEV_KCSEL_K0_R: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.r"; + case GX::TEV_KCSEL_K1_R: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.r"; + case GX::TEV_KCSEL_K2_R: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.r"; + case GX::TEV_KCSEL_K3_R: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.r"; + case GX::TEV_KCSEL_K0_G: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.g"; + case GX::TEV_KCSEL_K1_G: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.g"; + case GX::TEV_KCSEL_K2_G: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.g"; + case GX::TEV_KCSEL_K3_G: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.g"; + case GX::TEV_KCSEL_K0_B: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.b"; + case GX::TEV_KCSEL_K1_B: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.b"; + case GX::TEV_KCSEL_K2_B: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.b"; + case GX::TEV_KCSEL_K3_B: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.b"; + case GX::TEV_KCSEL_K0_A: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.a"; + case GX::TEV_KCSEL_K1_A: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.a"; + case GX::TEV_KCSEL_K2_A: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.a"; + case GX::TEV_KCSEL_K3_A: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.a"; + default: + Log.report(logvisor::Fatal, FMT_STRING("invalid kcSel {}"), stage.kcSel); + unreachable(); + } + } case GX::CC_ZERO: return "0.0"; } } -static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) { +static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx, const STevStage& stage, ShaderInfo& info) { switch (arg) { case GX::CA_APREV: return "prev.a"; @@ -52,12 +173,100 @@ static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) { case GX::CA_A2: Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), arg); unreachable(); - case GX::CA_TEXA: - return fmt::format(FMT_STRING("sampled{}.a"), stageIdx); - case GX::CA_RASA: - return "rast.a"; - case GX::CA_KONST: - return fmt::format(FMT_STRING("ubuf.kcolor{}.a"), stageIdx); + case GX::CA_TEXA: { + if (stage.texMapId == GX::TEXMAP_NULL) { + Log.report(logvisor::Fatal, FMT_STRING("unmapped texture for stage {}"), stageIdx); + unreachable(); + } else if (stage.texMapId < GX::TEXMAP0 || stage.texMapId > GX::TEXMAP7) { + Log.report(logvisor::Fatal, FMT_STRING("invalid texture {} for stage {}"), stage.texMapId, stageIdx); + unreachable(); + } + info.sampledTextures.set(stage.texMapId); + return fmt::format(FMT_STRING("sampled{}.a"), stage.texMapId); + } + case GX::CA_RASA: { + if (stage.channelId == GX::COLOR_NULL) { + Log.report(logvisor::Fatal, FMT_STRING("unmapped color channel for stage {}"), stageIdx); + unreachable(); + } else if (stage.channelId < GX::COLOR0A0 || stage.channelId > GX::COLOR1A1) { + Log.report(logvisor::Fatal, FMT_STRING("invalid color channel {} for stage {}"), stage.channelId, stageIdx); + unreachable(); + } + u32 idx = stage.channelId - GX::COLOR0A0; + info.sampledColorChannels.set(idx); + return fmt::format(FMT_STRING("rast{}.a"), idx); + } + case GX::CA_KONST: { + switch (stage.kaSel) { + case GX::TEV_KASEL_8_8: + return "1.0"; + case GX::TEV_KASEL_7_8: + return "(7.0/8.0)"; + case GX::TEV_KASEL_6_8: + return "(6.0/8.0)"; + case GX::TEV_KASEL_5_8: + return "(5.0/8.0)"; + case GX::TEV_KASEL_4_8: + return "(4.0/8.0)"; + case GX::TEV_KASEL_3_8: + return "(3.0/8.0)"; + case GX::TEV_KASEL_2_8: + return "(2.0/8.0)"; + case GX::TEV_KASEL_1_8: + return "(1.0/8.0)"; + case GX::TEV_KASEL_K0_R: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.r"; + case GX::TEV_KASEL_K1_R: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.r"; + case GX::TEV_KASEL_K2_R: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.r"; + case GX::TEV_KASEL_K3_R: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.r"; + case GX::TEV_KASEL_K0_G: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.g"; + case GX::TEV_KASEL_K1_G: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.g"; + case GX::TEV_KASEL_K2_G: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.g"; + case GX::TEV_KASEL_K3_G: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.g"; + case GX::TEV_KASEL_K0_B: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.b"; + case GX::TEV_KASEL_K1_B: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.b"; + case GX::TEV_KASEL_K2_B: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.b"; + case GX::TEV_KASEL_K3_B: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.b"; + case GX::TEV_KASEL_K0_A: + info.sampledKcolors.set(0); + return "ubuf.kcolor0.a"; + case GX::TEV_KASEL_K1_A: + info.sampledKcolors.set(1); + return "ubuf.kcolor1.a"; + case GX::TEV_KASEL_K2_A: + info.sampledKcolors.set(2); + return "ubuf.kcolor2.a"; + case GX::TEV_KASEL_K3_A: + info.sampledKcolors.set(3); + return "ubuf.kcolor3.a"; + default: + Log.report(logvisor::Fatal, FMT_STRING("invalid kaSel {}"), stage.kaSel); + unreachable(); + } + } case GX::CA_ZERO: return "0.0"; } @@ -99,12 +308,51 @@ static std::string_view tev_scale(GX::TevScale scale) { } } -wgpu::ShaderModule build_shader(const GXShaderConfig& config) { +std::pair build_shader(const ShaderConfig& config) noexcept { const auto hash = xxh3_hash(config); if (g_gxCachedShaders.contains(hash)) { return g_gxCachedShaders[hash]; } + Log.report(logvisor::Info, FMT_STRING("Shader config (hash {:x}):"), hash); + { + for (int i = 0; i < config.tevStages.size(); ++i) { + const auto& stage = config.tevStages[i]; + if (!stage) { + break; + } + Log.report(logvisor::Info, FMT_STRING(" tevStages[{}]:"), i); + Log.report(logvisor::Info, FMT_STRING(" color_a: {}"), stage->colorPass.x0_a); + Log.report(logvisor::Info, FMT_STRING(" color_b: {}"), stage->colorPass.x4_b); + Log.report(logvisor::Info, FMT_STRING(" color_c: {}"), stage->colorPass.x8_c); + Log.report(logvisor::Info, FMT_STRING(" color_d: {}"), stage->colorPass.xc_d); + Log.report(logvisor::Info, FMT_STRING(" alpha_a: {}"), stage->alphaPass.x0_a); + Log.report(logvisor::Info, FMT_STRING(" alpha_b: {}"), stage->alphaPass.x4_b); + Log.report(logvisor::Info, FMT_STRING(" alpha_c: {}"), stage->alphaPass.x8_c); + Log.report(logvisor::Info, FMT_STRING(" alpha_d: {}"), stage->alphaPass.xc_d); + Log.report(logvisor::Info, FMT_STRING(" color_op_clamp: {}"), stage->colorOp.x0_clamp); + Log.report(logvisor::Info, FMT_STRING(" color_op_op: {}"), stage->colorOp.x4_op); + Log.report(logvisor::Info, FMT_STRING(" color_op_bias: {}"), stage->colorOp.x8_bias); + Log.report(logvisor::Info, FMT_STRING(" color_op_scale: {}"), stage->colorOp.xc_scale); + Log.report(logvisor::Info, FMT_STRING(" color_op_reg_id: {}"), stage->colorOp.x10_regId); + Log.report(logvisor::Info, FMT_STRING(" alpha_op_clamp: {}"), stage->alphaOp.x0_clamp); + Log.report(logvisor::Info, FMT_STRING(" alpha_op_op: {}"), stage->alphaOp.x4_op); + Log.report(logvisor::Info, FMT_STRING(" alpha_op_bias: {}"), stage->alphaOp.x8_bias); + Log.report(logvisor::Info, FMT_STRING(" alpha_op_scale: {}"), stage->alphaOp.xc_scale); + Log.report(logvisor::Info, FMT_STRING(" alpha_op_reg_id: {}"), stage->alphaOp.x10_regId); + Log.report(logvisor::Info, FMT_STRING(" kc_sel: {}"), stage->kcSel); + Log.report(logvisor::Info, FMT_STRING(" ka_sel: {}"), stage->kaSel); + Log.report(logvisor::Info, FMT_STRING(" texCoordId: {}"), stage->texCoordId); + Log.report(logvisor::Info, FMT_STRING(" texMapId: {}"), stage->texMapId); + Log.report(logvisor::Info, FMT_STRING(" channelId: {}"), stage->channelId); + } + for (int i = 0; i < config.channelMatSrcs.size(); ++i) { + Log.report(logvisor::Info, FMT_STRING(" channelMatSrcs[{}]: {}"), i, config.channelMatSrcs[i]); + } + Log.report(logvisor::Info, FMT_STRING(" alphaDiscard: {}"), config.alphaDiscard); + Log.report(logvisor::Info, FMT_STRING(" denormalizedVertexAttributes: {}"), config.denormalizedVertexAttributes); + } + std::string uniBufAttrs; std::string sampBindings; std::string texBindings; @@ -112,62 +360,23 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) { std::string vtxInAttrs; std::string vtxXfrAttrs; size_t locIdx = 0; - if (config.denormalizedNorm) { - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) nrm: vec3;"), locIdx); - vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_nrm: vec3"), ++locIdx); - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.nrm = in_nrm;")); - } - if (config.denormalizedColor) { - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) clr: vec4;"), locIdx); - vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_clr: vec4"), ++locIdx); - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.clr = in_clr;")); - } - for (int i = 0; i < maxTextures; ++i) { - if (!config.boundTextures.test(i)) { - continue; - } - uniBufAttrs += fmt::format(FMT_STRING("\n tex{}_lod: f32;"), i); - sampBindings += fmt::format(FMT_STRING("\n@group(1) @binding({0})\n" - "var tex{0}_samp: sampler;"), i); - texBindings += fmt::format(FMT_STRING("\n@group(2) @binding({0})\n" - "var tex{0}: texture_2d;"), i); - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex{}_uv: vec2;"), locIdx, i); - vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_tex{}_uv: vec2"), locIdx + 1, i); - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{0}_uv = in_tex{0}_uv;"), i); - locIdx++; - } + // TODO + // if (config.denormalizedNorm) { + // vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) nrm: vec3;"), locIdx); + // vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_nrm: vec3"), ++locIdx); + // vtxXfrAttrs += fmt::format(FMT_STRING("\n out.nrm = in_nrm;")); + // } + ShaderInfo info{ + .uniformSize = 64, // MVP MTX + }; + std::string fragmentFnPre; std::string fragmentFn; - bool hasRast = false; - for (size_t idx = 0; const auto& stage : config.tevStages) { - if (!stage) { - break; - } - if (stage->colorPass.x0_a == GX::TevColorArg::CC_TEXC || stage->colorPass.x4_b == GX::TevColorArg::CC_TEXC || - stage->colorPass.x8_c == GX::TevColorArg::CC_TEXC || stage->colorPass.xc_d == GX::TevColorArg::CC_TEXC || - stage->alphaPass.x0_a == GX::TevAlphaArg::CA_TEXA || stage->alphaPass.x4_b == GX::TevAlphaArg::CA_TEXA || - stage->alphaPass.x8_c == GX::TevAlphaArg::CA_TEXA || stage->alphaPass.xc_d == GX::TevAlphaArg::CA_TEXA) { - fragmentFn += fmt::format( - FMT_STRING("\n var sampled{0} = textureSampleBias(tex{0}, tex{0}_samp, in.tex{0}_uv, ubuf.tex{0}_lod);"), - idx); - } - if (!hasRast) { - if (stage->colorPass.x0_a == GX::TevColorArg::CC_RASC || stage->colorPass.x4_b == GX::TevColorArg::CC_RASC || - stage->colorPass.x8_c == GX::TevColorArg::CC_RASC || stage->colorPass.xc_d == GX::TevColorArg::CC_RASC || - stage->alphaPass.x0_a == GX::TevAlphaArg::CA_RASA || stage->alphaPass.x4_b == GX::TevAlphaArg::CA_RASA || - stage->alphaPass.x8_c == GX::TevAlphaArg::CA_RASA || stage->alphaPass.xc_d == GX::TevAlphaArg::CA_RASA) { - fragmentFn += fmt::format(FMT_STRING("\n var rast = in.clr; // TODO lighting")); // TODO lighting - hasRast = true; - } - } - idx++; - } for (size_t idx = 0; const auto& stage : config.tevStages) { if (!stage) { break; } { - std::string op; std::string outReg; switch (stage->colorOp.x10_regId) { case GX::TevRegID::TEVPREV: @@ -176,15 +385,16 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) { default: Log.report(logvisor::Fatal, FMT_STRING("TODO: colorOp outReg {}"), stage->colorOp.x10_regId); } - op = fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"), - color_arg_reg(stage->colorPass.x0_a, idx), color_arg_reg(stage->colorPass.x4_b, idx), - color_arg_reg(stage->colorPass.x8_c, idx), color_arg_reg(stage->colorPass.xc_d, idx), - tev_op(stage->colorOp.x4_op), tev_bias(stage->colorOp.x8_bias), - tev_scale(stage->colorOp.xc_scale)); + std::string op = + fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"), + color_arg_reg(stage->colorPass.x0_a, idx, *stage, info), + color_arg_reg(stage->colorPass.x4_b, idx, *stage, info), + color_arg_reg(stage->colorPass.x8_c, idx, *stage, info), + color_arg_reg(stage->colorPass.xc_d, idx, *stage, info), tev_op(stage->colorOp.x4_op), + tev_bias(stage->colorOp.x8_bias), tev_scale(stage->colorOp.xc_scale)); fragmentFn += fmt::format(FMT_STRING("\n {0} = vec4({1}, {0}.a);"), outReg, op); } { - std::string op; std::string outReg; switch (stage->alphaOp.x10_regId) { case GX::TevRegID::TEVPREV: @@ -193,15 +403,74 @@ wgpu::ShaderModule build_shader(const GXShaderConfig& config) { default: Log.report(logvisor::Fatal, FMT_STRING("TODO: alphaOp outReg {}"), stage->alphaOp.x10_regId); } - op = fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"), - alpha_arg_reg(stage->alphaPass.x0_a, idx), alpha_arg_reg(stage->alphaPass.x4_b, idx), - alpha_arg_reg(stage->alphaPass.x8_c, idx), alpha_arg_reg(stage->alphaPass.xc_d, idx), - tev_op(stage->alphaOp.x4_op), tev_bias(stage->alphaOp.x8_bias), - tev_scale(stage->alphaOp.xc_scale)); + std::string op = + fmt::format(FMT_STRING("({3} {4} ((1.0 - {2}) * {0} + {2} * {1}){5}){6}"), + alpha_arg_reg(stage->alphaPass.x0_a, idx, *stage, info), + alpha_arg_reg(stage->alphaPass.x4_b, idx, *stage, info), + alpha_arg_reg(stage->alphaPass.x8_c, idx, *stage, info), + alpha_arg_reg(stage->alphaPass.xc_d, idx, *stage, info), tev_op(stage->alphaOp.x4_op), + tev_bias(stage->alphaOp.x8_bias), tev_scale(stage->alphaOp.xc_scale)); fragmentFn += fmt::format(FMT_STRING("\n {0} = {1};"), outReg, op); } idx++; } + for (int i = 0; i < info.sampledColorChannels.size(); ++i) { + if (!info.sampledColorChannels.test(i)) { + continue; + } + + uniBufAttrs += fmt::format(FMT_STRING("\n cc{0}_amb: vec4;"), i); + uniBufAttrs += fmt::format(FMT_STRING("\n cc{0}_mat: vec4;"), i); // TODO not needed for SRC_VTX + info.uniformSize += 32; + + if (config.channelMatSrcs[i] == GX::SRC_VTX) { + if (config.denormalizedVertexAttributes) { + if (!info.usesVtxColor) { + vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) clr: vec4;"), locIdx); + vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_clr: vec4"), ++locIdx); + vtxXfrAttrs += fmt::format(FMT_STRING("\n out.clr = in_clr;")); + } + fragmentFnPre += fmt::format(FMT_STRING("\n var rast{} = in.clr; // TODO lighting"), i); + } else { + Log.report(logvisor::Fatal, FMT_STRING("Don't know how to do this yet")); // TODO + } + info.usesVtxColor = true; + } else { + fragmentFnPre += fmt::format(FMT_STRING("\n var rast{0} = ubuf.cc{0}_mat; // TODO lighting"), i); + } + } + for (int i = 0; i < info.sampledKcolors.size(); ++i) { + if (!info.sampledKcolors.test(i)) { + continue; + } + uniBufAttrs += fmt::format(FMT_STRING("\n kcolor{}: vec4;"), i); + info.uniformSize += 16; + } + for (int i = 0; i < info.sampledTextures.size(); ++i) { + if (!info.sampledTextures.test(i)) { + continue; + } + uniBufAttrs += fmt::format(FMT_STRING("\n tex{}_lod: f32;"), i); + info.uniformSize += 4; + + sampBindings += fmt::format(FMT_STRING("\n@group(1) @binding({0})\n" + "var tex{0}_samp: sampler;"), + i); + texBindings += fmt::format(FMT_STRING("\n@group(2) @binding({0})\n" + "var tex{0}: texture_2d;"), + i); + + if (config.denormalizedVertexAttributes) { + vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex{}_uv: vec2;"), locIdx, i); + vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_tex{}_uv: vec2"), locIdx + 1, i); + vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex{0}_uv = in_tex{0}_uv;"), i); + } else { + Log.report(logvisor::Fatal, FMT_STRING("Don't know how to do this yet")); // TODO + } + fragmentFnPre += fmt::format( + FMT_STRING("\n var sampled{0} = textureSampleBias(tex{0}, tex{0}_samp, in.tex{0}_uv, ubuf.tex{0}_lod);"), i); + locIdx++; + } const auto shaderSource = fmt::format(FMT_STRING(R"""( @@ -226,13 +495,13 @@ fn vs_main( @stage(fragment) fn fs_main(in: VertexOutput) -> @location(0) vec4 {{ - var prev: vec4;{fragmentFn} + var prev: vec4;{fragmentFnPre}{fragmentFn} return prev; }} )"""), "uniBufAttrs"_a = uniBufAttrs, "sampBindings"_a = sampBindings, "texBindings"_a = texBindings, "vtxOutAttrs"_a = vtxOutAttrs, "vtxInAttrs"_a = vtxInAttrs, "vtxXfrAttrs"_a = vtxXfrAttrs, - "fragmentFn"_a = fragmentFn); + "fragmentFn"_a = fragmentFn, "fragmentFnPre"_a = fragmentFnPre); Log.report(logvisor::Info, FMT_STRING("Generated shader: {}"), shaderSource); wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{}; @@ -242,8 +511,11 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 {{ .label = "GX Shader", }; auto shader = gpu::g_device.CreateShaderModule(&shaderDescriptor); - g_gxCachedShaders.emplace(hash, shader); - return shader; + info.uniformSize = align_uniform(info.uniformSize); + const auto pair = std::make_pair(std::move(shader), info); + g_gxCachedShaders.emplace(hash, pair); + + return pair; } -} // namespace aurora::gfx +} // namespace aurora::gfx::gx diff --git a/aurora/lib/gfx/model/shader.hpp b/aurora/lib/gfx/model/shader.hpp index b84d9afac..fe79dba4c 100644 --- a/aurora/lib/gfx/model/shader.hpp +++ b/aurora/lib/gfx/model/shader.hpp @@ -3,6 +3,8 @@ #include "../common.hpp" #include "../gx.hpp" +#include + namespace aurora::gfx::model { struct DrawData { PipelineRef pipeline; @@ -14,7 +16,7 @@ struct DrawData { BindGroupRef textureBindGroup; }; -struct PipelineConfig : GXPipelineConfig { +struct PipelineConfig : gx::PipelineConfig { ShaderRef shader; uint32_t uniformSize; }; diff --git a/aurora/lib/gfx/stream.cpp b/aurora/lib/gfx/stream.cpp index 9a118bda6..3e590471a 100644 --- a/aurora/lib/gfx/stream.cpp +++ b/aurora/lib/gfx/stream.cpp @@ -4,6 +4,8 @@ #include "common.hpp" #include "gx.hpp" +#include + namespace aurora::gfx { static logvisor::Module Log("aurora::gfx::stream"); @@ -53,74 +55,19 @@ void stream_vertex(metaforce::EStreamFlags flags, const zeus::CVector3f& pos, co } void stream_end() noexcept { - const auto& tex = get_texture(GX::TEXMAP0); - if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture && !tex) { - Log.report(logvisor::Fatal, FMT_STRING("Stream has texture but no texture bound!")); - unreachable(); - } const auto vertRange = push_verts(sStreamState->vertexBuffer.data(), sStreamState->vertexBuffer.size()); - ByteBuffer uniBuf; - std::bitset usedColors; - { - const auto xf = get_combined_matrix(); - uniBuf.append(&xf, 64); - } - if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture) { - uniBuf.append(&tex.lod, 4); - } - const auto uniRange = push_uniform(uniBuf.data(), uniBuf.size()); - - const auto uniform_size = align_uniform(uniBuf.size()); - - stream::PipelineConfig config{ - .uniformSize = uniform_size, - .flags = sStreamState->flags, - }; + stream::PipelineConfig config{}; config.shaderConfig.denormalizedVertexAttributes = true; - config.shaderConfig.denormalizedNorm = sStreamState->flags.IsSet(metaforce::EStreamFlagBits::fHasNormal); - config.shaderConfig.denormalizedColor = sStreamState->flags.IsSet(metaforce::EStreamFlagBits::fHasColor); - populate_gx_pipeline_config(config, sStreamState->primitive, {1}); + const auto info = populate_pipeline_config(config, sStreamState->primitive); const auto pipeline = pipeline_ref(config); - BindGroupRef samplerBindGroup{}; - BindGroupRef textureBindGroup{}; - if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture) { - const auto& state = get_state(); - { - const std::array samplerEntries{wgpu::BindGroupEntry{ - .binding = 0, - .sampler = sampler_ref(tex.get_descriptor()), - }}; - samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ - .label = "Stream Sampler Bind Group", - .layout = state.samplerLayout, - .entryCount = samplerEntries.size(), - .entries = samplerEntries.data(), - }); - } - { - const std::array textureEntries{wgpu::BindGroupEntry{ - .binding = 0, - .textureView = tex.handle.ref->view, - }}; - textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ - .label = "Stream Texture Bind Group", - .layout = state.textureLayout, - .entryCount = textureEntries.size(), - .entries = textureEntries.data(), - }); - } - } - push_draw_command(stream::DrawData{ .pipeline = pipeline, .vertRange = vertRange, - .uniformRange = uniRange, + .uniformRange = build_uniform(info), .vertexCount = sStreamState->vertexCount, - .uniformSize = uniform_size, - .samplerBindGroup = samplerBindGroup, - .textureBindGroup = textureBindGroup, + .bindGroups = build_bind_groups(info), }); sStreamState.reset(); diff --git a/aurora/lib/gfx/stream/shader.cpp b/aurora/lib/gfx/stream/shader.cpp index 209f6ec19..61be8fbf4 100644 --- a/aurora/lib/gfx/stream/shader.cpp +++ b/aurora/lib/gfx/stream/shader.cpp @@ -14,10 +14,10 @@ namespace aurora::gfx::stream { static logvisor::Module Log("aurora::gfx::stream"); using gpu::g_device; -using gpu::g_graphicsConfig; -using gpu::utils::make_vertex_state; wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) { + const auto [shader, info] = build_shader(config.shaderConfig); + std::array attributes{}; attributes[0] = wgpu::VertexAttribute{ .format = wgpu::VertexFormat::Float32x3, @@ -26,7 +26,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli }; uint64_t offset = 12; uint32_t shaderLocation = 1; - if (config.flags & metaforce::EStreamFlagBits::fHasNormal) { + if (info.usesNormal) { attributes[shaderLocation] = wgpu::VertexAttribute{ .format = wgpu::VertexFormat::Float32x3, .offset = offset, @@ -35,7 +35,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli offset += 12; shaderLocation++; } - if (config.flags & metaforce::EStreamFlagBits::fHasColor) { + if (info.usesVtxColor) { attributes[shaderLocation] = wgpu::VertexAttribute{ .format = wgpu::VertexFormat::Float32x4, .offset = offset, @@ -44,7 +44,11 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli offset += 16; shaderLocation++; } - if (config.flags & metaforce::EStreamFlagBits::fHasTexture) { + // TODO only sample 1? + for (int i = 0; i < info.sampledTextures.size(); ++i) { + if (!info.sampledTextures.test(i)) { + continue; + } attributes[shaderLocation] = wgpu::VertexAttribute{ .format = wgpu::VertexFormat::Float32x2, .offset = offset, @@ -59,57 +63,7 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli .attributes = attributes.data(), }}; - wgpu::BindGroupLayout uniformLayout; - if (state.uniform.contains(config.uniformSize)) { - uniformLayout = state.uniform.at(config.uniformSize).layout; - } else { - const std::array uniformLayoutEntries{wgpu::BindGroupLayoutEntry{ - .binding = 0, - .visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment, - .buffer = - wgpu::BufferBindingLayout{ - .type = wgpu::BufferBindingType::Uniform, - .hasDynamicOffset = true, - .minBindingSize = config.uniformSize, - }, - }}; - const auto uniformLayoutDescriptor = wgpu::BindGroupLayoutDescriptor{ - .label = "Stream Uniform Bind Group Layout", - .entryCount = uniformLayoutEntries.size(), - .entries = uniformLayoutEntries.data(), - }; - uniformLayout = g_device.CreateBindGroupLayout(&uniformLayoutDescriptor); - - const std::array uniformBindGroupEntries{wgpu::BindGroupEntry{ - .binding = 0, - .buffer = g_uniformBuffer, - .size = config.uniformSize, - }}; - const auto uniformBindGroupDescriptor = wgpu::BindGroupDescriptor{ - .label = "Stream Quad Uniform Bind Group", - .layout = uniformLayout, - .entryCount = uniformBindGroupEntries.size(), - .entries = uniformBindGroupEntries.data(), - }; - auto uniformBindGroup = g_device.CreateBindGroup(&uniformBindGroupDescriptor); - - state.uniform.try_emplace(config.uniformSize, uniformLayout, std::move(uniformBindGroup)); - } - - const std::array bindGroupLayouts{ - uniformLayout, - state.samplerLayout, - state.textureLayout, - }; - const auto pipelineLayoutDescriptor = wgpu::PipelineLayoutDescriptor{ - .label = "Stream Pipeline Layout", - .bindGroupLayoutCount = - static_cast(config.flags & metaforce::EStreamFlagBits::fHasTexture ? bindGroupLayouts.size() : 1), - .bindGroupLayouts = bindGroupLayouts.data(), - }; - auto pipelineLayout = g_device.CreatePipelineLayout(&pipelineLayoutDescriptor); - - return build_pipeline(pipelineLayout, vertexBuffers, config, "Stream Pipeline"); + return build_pipeline(config, info, vertexBuffers, shader, "Stream Pipeline"); } State construct_state() { @@ -160,10 +114,10 @@ void render(const State& state, const DrawData& data, const wgpu::RenderPassEnco } const std::array offsets{data.uniformRange.first}; - pass.SetBindGroup(0, state.uniform.at(data.uniformSize).bindGroup, offsets.size(), offsets.data()); - if (data.samplerBindGroup && data.textureBindGroup) { - pass.SetBindGroup(1, find_bind_group(data.samplerBindGroup)); - pass.SetBindGroup(2, find_bind_group(data.textureBindGroup)); + pass.SetBindGroup(0, find_bind_group(data.bindGroups.uniformBindGroup), offsets.size(), offsets.data()); + if (data.bindGroups.samplerBindGroup && data.bindGroups.textureBindGroup) { + pass.SetBindGroup(1, find_bind_group(data.bindGroups.samplerBindGroup)); + pass.SetBindGroup(2, find_bind_group(data.bindGroups.textureBindGroup)); } pass.SetVertexBuffer(0, g_vertexBuffer, data.vertRange.first, data.vertRange.second); pass.Draw(data.vertexCount); diff --git a/aurora/lib/gfx/stream/shader.hpp b/aurora/lib/gfx/stream/shader.hpp index b67850444..ba9e7a2a7 100644 --- a/aurora/lib/gfx/stream/shader.hpp +++ b/aurora/lib/gfx/stream/shader.hpp @@ -3,21 +3,18 @@ #include "../common.hpp" #include "../gx.hpp" +#include + namespace aurora::gfx::stream { struct DrawData { PipelineRef pipeline; Range vertRange; Range uniformRange; uint32_t vertexCount; - uint32_t uniformSize; - BindGroupRef samplerBindGroup; - BindGroupRef textureBindGroup; + gx::GXBindGroups bindGroups; }; -struct PipelineConfig : public GXPipelineConfig { - uint32_t uniformSize; - metaforce::EStreamFlags flags; -}; +struct PipelineConfig : public gx::PipelineConfig {}; struct CachedBindGroup { wgpu::BindGroupLayout layout; @@ -30,6 +27,7 @@ struct State { wgpu::BindGroupLayout textureLayout; mutable std::unordered_map uniform; mutable std::unordered_map sampler; + mutable std::unordered_map shaderInfo; }; State construct_state(); diff --git a/aurora/lib/gfx/textured_quad/shader.cpp b/aurora/lib/gfx/textured_quad/shader.cpp index 42732ad8f..cdc24e3c9 100644 --- a/aurora/lib/gfx/textured_quad/shader.cpp +++ b/aurora/lib/gfx/textured_quad/shader.cpp @@ -348,7 +348,7 @@ DrawData make_draw_data_verts(const State& state, CameraFilterType filter_type, const auto vertRange = push_verts(ArrayRef{verts}); const auto uniform = Uniform{ - .xf = get_combined_matrix(), + .xf = gx::get_combined_matrix(), .color = color, .lod = lod, };