diff --git a/Runtime/Graphics/CCubeMaterial.cpp b/Runtime/Graphics/CCubeMaterial.cpp index 1d9ad72b1..1fb7cf150 100644 --- a/Runtime/Graphics/CCubeMaterial.cpp +++ b/Runtime/Graphics/CCubeMaterial.cpp @@ -6,6 +6,8 @@ #include "Graphics/CCubeSurface.hpp" #include "Graphics/CModel.hpp" +#include + namespace metaforce { static u32 sReflectionType = 0; static u32 sLastMaterialUnique = UINT32_MAX; @@ -65,8 +67,8 @@ void CCubeMaterial::SetCurrent(const CModelFlags& flags, const CCubeSurface& sur } sLastMaterialUnique = groupIdx; - CCubeMaterialVatFlags vatFlags = SBig(*reinterpret_cast(materialDataCur)); - // SetVtxDescv_Compressed(vatFlags); + u32 vatFlags = SBig(*reinterpret_cast(materialDataCur)); + aurora::gfx::model::set_vtx_desc_compressed(vatFlags); materialDataCur += 8; bool packedLightMaps = matFlags.IsSet(CCubeMaterialFlagBits::fLightmapUvArray); diff --git a/Runtime/Graphics/CCubeModel.cpp b/Runtime/Graphics/CCubeModel.cpp index fa706707d..e67d742de 100644 --- a/Runtime/Graphics/CCubeModel.cpp +++ b/Runtime/Graphics/CCubeModel.cpp @@ -6,6 +6,8 @@ #include "Graphics/CGraphics.hpp" #include "Graphics/CModel.hpp" +#include + namespace metaforce { bool CCubeModel::sRenderModelBlack = false; bool CCubeModel::sRenderModelShadow = false; @@ -156,7 +158,8 @@ void CCubeModel::DrawFlat(TVectorRef positions, TVectorRef normals, ESurfaceSele const auto* surface = x38_firstUnsortedSurf; while (surface != nullptr) { const auto mat = GetMaterialByIndex(surface->GetMaterialIndex()); - // TODO draw + aurora::gfx::model::set_vtx_desc_compressed(mat.GetVertexDesc()); + aurora::gfx::model::queue_surface(surface->GetDisplayList(), surface->GetDisplayListSize()); surface = surface->GetNextSurface(); } } @@ -164,14 +167,19 @@ void CCubeModel::DrawFlat(TVectorRef positions, TVectorRef normals, ESurfaceSele const auto* surface = x3c_firstSortedSurf; while (surface != nullptr) { const auto mat = GetMaterialByIndex(surface->GetMaterialIndex()); - // TODO draw + aurora::gfx::model::set_vtx_desc_compressed(mat.GetVertexDesc()); + aurora::gfx::model::queue_surface(surface->GetDisplayList(), surface->GetDisplayListSize()); surface = surface->GetNextSurface(); } } } void CCubeModel::DrawNormal(TVectorRef positions, TVectorRef normals, ESurfaceSelection surfaces) { - // TODO bunch of CGX stuff, what is it doing? + CGraphics::SetDepthWriteMode(true, ERglEnum::LEqual, true); + CGraphics::SetTevOp(ERglTevStage::Stage0, CTevCombiners::skPassZero); + CGraphics::SetTevOp(ERglTevStage::Stage1, CTevCombiners::skPassThru); + // TODO update fog + CGraphics::SetBlendMode(ERglBlendMode::Blend, ERglBlendFactor::Zero, ERglBlendFactor::One, ERglLogicOp::Clear); DrawFlat(positions, normals, surfaces); } @@ -201,7 +209,7 @@ void CCubeModel::DrawSurface(const CCubeSurface& surface, const CModelFlags& fla auto mat = GetMaterialByIndex(surface.GetMaterialIndex()); if (!mat.GetFlags().IsSet(CCubeMaterialFlagBits::fShadowOccluderMesh) || sDrawingOccluders) { mat.SetCurrent(flags, surface, *this); - // TODO draw + aurora::gfx::model::queue_surface(surface.GetDisplayList(), surface.GetDisplayListSize()); } } @@ -249,10 +257,10 @@ void CCubeModel::DisableShadowMaps() { sRenderModelShadow = false; } void CCubeModel::SetArraysCurrent() { if (x0_modelInstance.GetVertexPointer() != nullptr) { - // TODO set vertices active + aurora::gfx::model::set_vertex_buffer(*x0_modelInstance.GetVertexPointer()); } if (x0_modelInstance.GetNormalPointer() != nullptr) { - // TODO set normals active + aurora::gfx::model::set_normal_buffer(*x0_modelInstance.GetNormalPointer()); } SetStaticArraysCurrent(); } @@ -273,26 +281,26 @@ void CCubeModel::SetRenderModelBlack(bool v) { } void CCubeModel::SetSkinningArraysCurrent(TVectorRef positions, TVectorRef normals) { - // TODO activate vertices, normals & colors + aurora::gfx::model::set_vertex_buffer(*positions); + aurora::gfx::model::set_normal_buffer(*normals); + // colors unused SetStaticArraysCurrent(); } void CCubeModel::SetStaticArraysCurrent() { - // TODO activate colors + // colors unused const auto* packedTexCoords = x0_modelInstance.GetPackedTCPointer(); const auto* texCoords = x0_modelInstance.GetTCPointer(); if (packedTexCoords == nullptr) { sUsingPackedLightmaps = false; } if (sUsingPackedLightmaps) { - // TODO activate packed TCs for texture 0 + aurora::gfx::model::set_tex0_tc_buffer(*packedTexCoords); } else if (texCoords != nullptr) { - // TODO activate TCs for texture 0 + aurora::gfx::model::set_tex0_tc_buffer(*packedTexCoords); } if (texCoords != nullptr) { - for (int i = 1; i < 8; ++i) { - // TODO activate TCs for textures 1-7 - } + aurora::gfx::model::set_tc_buffer(*texCoords); } CCubeMaterial::KillCachedViewDepState(); } @@ -300,9 +308,9 @@ void CCubeModel::SetStaticArraysCurrent() { void CCubeModel::SetUsingPackedLightmaps(bool v) { sUsingPackedLightmaps = v; if (v) { - // TODO activate packed TCs for texture 0 + aurora::gfx::model::set_tex0_tc_buffer(*x0_modelInstance.GetPackedTCPointer()); } else { - // TODO activate TCs for texture 0 + aurora::gfx::model::set_tex0_tc_buffer(*x0_modelInstance.GetTCPointer()); } } diff --git a/Runtime/Graphics/CTevCombiners.cpp b/Runtime/Graphics/CTevCombiners.cpp index 6e36b47e5..96941c250 100644 --- a/Runtime/Graphics/CTevCombiners.cpp +++ b/Runtime/Graphics/CTevCombiners.cpp @@ -19,6 +19,10 @@ const CTevPass skPassThru{ {GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_RASC}, {GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_RASA}, }; +const CTevPass skPassZero{ + {GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_ZERO}, + {GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_ZERO}, +}; const CTevPass sTevPass805a5698{ {GX::TevColorArg::CC_ZERO, GX::TevColorArg::CC_RASC, GX::TevColorArg::CC_C0, GX::TevColorArg::CC_ZERO}, {GX::TevAlphaArg::CA_ZERO, GX::TevAlphaArg::CA_RASA, GX::TevAlphaArg::CA_A0, GX::TevAlphaArg::CA_ZERO}, diff --git a/Runtime/Graphics/CTevCombiners.hpp b/Runtime/Graphics/CTevCombiners.hpp index 7c7e6aa24..b33945861 100644 --- a/Runtime/Graphics/CTevCombiners.hpp +++ b/Runtime/Graphics/CTevCombiners.hpp @@ -29,6 +29,7 @@ public: }; extern const CTevPass skPassThru; +extern const CTevPass skPassZero; extern const CTevPass sTevPass805a5698; extern const CTevPass sTevPass805a5e70; extern const CTevPass sTevPass805a5ebc; diff --git a/Runtime/Graphics/CTexture.cpp b/Runtime/Graphics/CTexture.cpp index cfa10382c..8f3738950 100644 --- a/Runtime/Graphics/CTexture.cpp +++ b/Runtime/Graphics/CTexture.cpp @@ -145,7 +145,7 @@ void CTexture::UnLock() { void CTexture::Load(GX::TexMapID id, EClampMode clamp) { if (sLoadedTextures[id] != this || xa_29_canLoadObj) { - auto* image_ptr = /*x44_aramToken.GetMRAMSafe() */ x44_aramToken_x4_buff.get(); + // auto* image_ptr = /*x44_aramToken.GetMRAMSafe() */ x44_aramToken_x4_buff.get(); CountMemory(); if (HasPalette()) { x10_graphicsPalette->Load(); @@ -202,8 +202,8 @@ void CTexture::MakeSwappable() { const u8* CTexture::GetConstBitMapData(s32 mip) const { u32 buffOffset = 0; - if (x8_mips > 0) { - for (u32 i = 0; i < x8_mips; ++i) { + if (x8_mips > mip) { + for (u32 i = 0; i < mip; ++i) { buffOffset += (x9_bitsPerPixel >> 3) * (x4_w >> (i & 0x3f)) * (x6_h >> (i & 0x3f)); } } diff --git a/aurora/CMakeLists.txt b/aurora/CMakeLists.txt index c155e08ca..1ea69bd26 100644 --- a/aurora/CMakeLists.txt +++ b/aurora/CMakeLists.txt @@ -26,11 +26,13 @@ add_library(aurora STATIC lib/gfx/texture.cpp lib/gfx/stream.cpp lib/gfx/gx.cpp + lib/gfx/gx_shader.cpp lib/gfx/texture_convert.cpp lib/gfx/movie_player/shader.cpp lib/gfx/textured_quad/shader.cpp lib/gfx/colored_quad/shader.cpp lib/gfx/stream/shader.cpp + lib/gfx/model/shader.cpp ) target_compile_definitions(aurora PRIVATE IMGUI_USER_CONFIG="imconfig_user.h") # IMGUI_USE_WCHAR32 target_include_directories(aurora PUBLIC include ../) diff --git a/aurora/include/aurora/model.hpp b/aurora/include/aurora/model.hpp index 0e4e06364..9d0c02f20 100644 --- a/aurora/include/aurora/model.hpp +++ b/aurora/include/aurora/model.hpp @@ -1,5 +1,13 @@ #pragma once -namespace aurora::gfx::model { +#include "common.hpp" -} +namespace aurora::gfx::model { +void set_vertex_buffer(const std::vector& data) noexcept; +void set_normal_buffer(const std::vector& norm) noexcept; +void set_tex0_tc_buffer(const std::vector& tcs) noexcept; // Tex coords for TEX0 +void set_tc_buffer(const std::vector& tcs) noexcept; // Tex coords for the TEX1-7 + +void set_vtx_desc_compressed(u32 vtxDesc) noexcept; +void queue_surface(const u8* dlStart, u32 dlSize) noexcept; +} // namespace aurora::gfx::model diff --git a/aurora/lib/gfx/gx.cpp b/aurora/lib/gfx/gx.cpp index 81f207f5d..bf2c104ce 100644 --- a/aurora/lib/gfx/gx.cpp +++ b/aurora/lib/gfx/gx.cpp @@ -25,14 +25,22 @@ std::array g_kcolors; bool g_alphaUpdate; std::optional g_dstAlpha; zeus::CColor g_clearColor = zeus::skClear; +bool g_alphaDiscard; std::array g_colorChannels; std::array g_lights; std::bitset g_lightState; std::array, maxTevStages> g_tevStages; +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)]; } + 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 { @@ -69,9 +77,7 @@ void set_dst_alpha(bool enabled, float value) noexcept { } } void set_clear_color(const zeus::CColor& color) noexcept { g_clearColor = color; } -void set_alpha_discard(bool v) { - // TODO -} +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; @@ -138,13 +144,217 @@ void set_chan_mat_color(GX::ChannelID id, const zeus::CColor& color) noexcept { g_colorChannels[id - GX::COLOR0A0].matColor = color; } -void load_light(GX::LightID id, const Light& light) noexcept { - g_lights[id] = light; +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: + return wgpu::BlendFactor::Zero; + case metaforce::ERglBlendFactor::One: + return wgpu::BlendFactor::One; + case metaforce::ERglBlendFactor::SrcColor: + return wgpu::BlendFactor::Src; + case metaforce::ERglBlendFactor::InvSrcColor: + return wgpu::BlendFactor::OneMinusSrc; + case metaforce::ERglBlendFactor::SrcAlpha: + return wgpu::BlendFactor::SrcAlpha; + case metaforce::ERglBlendFactor::InvSrcAlpha: + return wgpu::BlendFactor::OneMinusSrcAlpha; + case metaforce::ERglBlendFactor::DstAlpha: + return wgpu::BlendFactor::DstAlpha; + case metaforce::ERglBlendFactor::InvDstAlpha: + return wgpu::BlendFactor::OneMinusDstAlpha; + case metaforce::ERglBlendFactor::DstColor: + return wgpu::BlendFactor::Dst; + case metaforce::ERglBlendFactor::InvDstColor: + return wgpu::BlendFactor::OneMinusDst; + } } -void load_light_ambient(GX::LightID id, const zeus::CColor& ambient) noexcept { - g_lights[id] = ambient; + +static inline wgpu::CompareFunction to_compare_function(metaforce::ERglEnum func) { + switch (func) { + case metaforce::ERglEnum::Never: + return wgpu::CompareFunction::Never; + case metaforce::ERglEnum::Less: + return wgpu::CompareFunction::Less; + case metaforce::ERglEnum::Equal: + return wgpu::CompareFunction::Equal; + case metaforce::ERglEnum::LEqual: + return wgpu::CompareFunction::LessEqual; + case metaforce::ERglEnum::Greater: + return wgpu::CompareFunction::Greater; + case metaforce::ERglEnum::NEqual: + return wgpu::CompareFunction::NotEqual; + case metaforce::ERglEnum::GEqual: + return wgpu::CompareFunction::GreaterEqual; + case metaforce::ERglEnum::Always: + return wgpu::CompareFunction::Always; + } } -void set_light_state(std::bitset bits) noexcept { - g_lightState = bits; + +static inline wgpu::BlendState to_blend_state(metaforce::ERglBlendMode mode, metaforce::ERglBlendFactor srcFac, + metaforce::ERglBlendFactor dstFac, std::optional dstAlpha) { + if (mode != metaforce::ERglBlendMode::Blend) { + Log.report(logvisor::Fatal, FMT_STRING("How to {}?"), magic_enum::enum_name(mode)); + } + const auto colorBlendComponent = wgpu::BlendComponent{ + .operation = wgpu::BlendOperation::Add, + .srcFactor = to_blend_factor(srcFac), + .dstFactor = to_blend_factor(dstFac), + }; + auto alphaBlendComponent = colorBlendComponent; + if (dstAlpha) { + alphaBlendComponent = wgpu::BlendComponent{ + .operation = wgpu::BlendOperation::Add, + .srcFactor = wgpu::BlendFactor::Zero, + .dstFactor = wgpu::BlendFactor::Constant, + }; + } + return { + .color = colorBlendComponent, + .alpha = alphaBlendComponent, + }; +} + +static inline wgpu::ColorWriteMask to_write_mask(bool alphaUpdate) { + auto writeMask = wgpu::ColorWriteMask::Red | wgpu::ColorWriteMask::Green | wgpu::ColorWriteMask::Blue; + if (alphaUpdate) { + writeMask = writeMask | wgpu::ColorWriteMask::Alpha; + } + return writeMask; +} + +static inline wgpu::PrimitiveState to_primitive_state(GX::Primitive gx_prim, metaforce::ERglCullMode e_cullMode) { + wgpu::PrimitiveTopology primitive = wgpu::PrimitiveTopology::TriangleList; + switch (gx_prim) { + case GX::TRIANGLES: + break; + case GX::TRIANGLESTRIP: + primitive = wgpu::PrimitiveTopology::TriangleStrip; + break; + default: + Log.report(logvisor::Fatal, FMT_STRING("Unsupported primitive type {}"), gx_prim); + unreachable(); + } + wgpu::FrontFace frontFace = wgpu::FrontFace::CCW; + wgpu::CullMode cullMode = wgpu::CullMode::None; + switch (e_cullMode) { + case metaforce::ERglCullMode::Front: + frontFace = wgpu::FrontFace::CW; + cullMode = wgpu::CullMode::Front; + break; + case metaforce::ERglCullMode::Back: + cullMode = wgpu::CullMode::Back; + break; + default: + break; + } + return { + .topology = primitive, + .frontFace = frontFace, + .cullMode = cullMode, + }; +} + +wgpu::RenderPipeline build_pipeline(wgpu::PipelineLayout layout, ArrayRef vtxBuffers, + const GXPipelineConfig& config, zstring_view label) noexcept { + using gpu::g_graphicsConfig; + const auto depthStencil = wgpu::DepthStencilState{ + .format = g_graphicsConfig.depthFormat, + .depthWriteEnabled = config.depthUpdate, + .depthCompare = to_compare_function(config.depthFunc), + }; + const auto blendState = to_blend_state(config.blendMode, config.blendFacSrc, config.blendFacDst, config.dstAlpha); + const std::array colorTargets{wgpu::ColorTargetState{ + .format = g_graphicsConfig.colorFormat, + .blend = &blendState, + .writeMask = to_write_mask(config.alphaUpdate), + }}; + const auto shader = build_shader(config.shaderConfig); + const auto fragmentState = wgpu::FragmentState{ + .module = shader, + .entryPoint = "fs_main", + .targetCount = colorTargets.size(), + .targets = colorTargets.data(), + }; + const auto descriptor = wgpu::RenderPipelineDescriptor{ + .label = label.c_str(), + .layout = std::move(layout), + .vertex = + { + .module = shader, + .entryPoint = "vs_main", + .bufferCount = static_cast(vtxBuffers.size()), + .buffers = vtxBuffers.data(), + }, + .primitive = to_primitive_state(config.primitive, config.cullMode), + .depthStencil = &depthStencil, + .multisample = + wgpu::MultisampleState{ + .count = g_graphicsConfig.msaaSamples, + }, + .fragment = &fragmentState, + }; + return gpu::g_device.CreateRenderPipeline(&descriptor); +} + +void populate_gx_pipeline_config(GXPipelineConfig& config, GX::Primitive primitive, + std::bitset enabledTextures) noexcept { + for (size_t idx = 0; const auto& item : g_tevStages) { + // Copy until disabled TEV stage (indicating end) + if (!item) { + break; + } + config.shaderConfig.tevStages[idx++] = item; + } + 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, + .depthFunc = g_depthFunc, + .cullMode = g_cullMode, + .blendMode = g_blendMode, + .blendFacSrc = g_blendFacSrc, + .blendFacDst = g_blendFacDst, + .blendOp = g_blendOp, + .dstAlpha = g_dstAlpha, + .depthCompare = g_depthCompare, + .depthUpdate = g_depthUpdate, + .alphaUpdate = g_alphaUpdate, + }; +} + +wgpu::SamplerDescriptor STextureBind::get_descriptor() const noexcept { + wgpu::AddressMode mode; + switch (clampMode) { + case metaforce::EClampMode::Clamp: + mode = wgpu::AddressMode::ClampToEdge; + break; + case metaforce::EClampMode::Repeat: + mode = wgpu::AddressMode::Repeat; + break; + case metaforce::EClampMode::Mirror: + mode = wgpu::AddressMode::MirrorRepeat; + break; + } + return { + .label = "Generated Sampler", + .addressModeU = mode, + .addressModeV = mode, + .addressModeW = mode, + // TODO logic from CTexture? + .magFilter = wgpu::FilterMode::Linear, + .minFilter = wgpu::FilterMode::Linear, + .mipmapFilter = wgpu::FilterMode::Linear, + .maxAnisotropy = gpu::g_graphicsConfig.textureAnistropy, + }; } } // namespace aurora::gfx diff --git a/aurora/lib/gfx/gx.hpp b/aurora/lib/gfx/gx.hpp index f0d58057c..95dee9dce 100644 --- a/aurora/lib/gfx/gx.hpp +++ b/aurora/lib/gfx/gx.hpp @@ -22,6 +22,7 @@ extern std::array g_kcolors; extern bool g_alphaUpdate; extern std::optional g_dstAlpha; extern zeus::CColor g_clearColor; +extern bool g_alphaDiscard; struct SChannelState { zeus::CColor matColor; @@ -43,18 +44,56 @@ struct STevStage { GX::TexMapID texMapId = GX::TEXMAP_NULL; GX::ChannelID channelId = GX::COLOR_NULL; - STevStage(const metaforce::CTevCombiners::ColorPass& colPass, const metaforce::CTevCombiners::AlphaPass& alphaPass, - const metaforce::CTevCombiners::CTevOp& colorOp, const metaforce::CTevCombiners::CTevOp& alphaOp) + constexpr STevStage(const metaforce::CTevCombiners::ColorPass& colPass, + const metaforce::CTevCombiners::AlphaPass& alphaPass, + const metaforce::CTevCombiners::CTevOp& colorOp, + const metaforce::CTevCombiners::CTevOp& alphaOp) noexcept : colorPass(colPass), alphaPass(alphaPass), colorOp(colorOp), alphaOp(alphaOp) {} }; +struct STextureBind { + aurora::gfx::TextureHandle handle; + metaforce::EClampMode clampMode; + float lod; + + STextureBind() noexcept = default; + STextureBind(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; + operator bool() const noexcept { return handle; } +}; constexpr u32 maxTevStages = GX::MAX_TEVSTAGE; extern std::array, maxTevStages> g_tevStages; +constexpr u32 maxTextures = 8; +extern std::array g_textures; -static inline XXH64_hash_t hash_tev_stages(XXH64_hash_t seed = 0) { - const auto end = std::find_if(g_tevStages.cbegin(), g_tevStages.cend(), [](const auto& a) { return !a; }); - return xxh3_hash(g_tevStages.data(), (end - g_tevStages.begin()) * sizeof(STevStage), seed); -} +static inline Mat4x4 get_combined_matrix() noexcept { return g_proj * g_mv; } -static inline Mat4x4 get_combined_matrix() { return g_proj * g_mv; } +const STextureBind& get_texture(GX::TexMapID id) noexcept; + +struct GXShaderConfig { + std::array, maxTevStages> tevStages; + bool alphaDiscard; + bool denormalizedVertexAttributes; + bool denormalizedNorm; + bool denormalizedColor; + std::bitset boundTextures; +}; +struct GXPipelineConfig { + GXShaderConfig shaderConfig; + GX::Primitive primitive; + metaforce::ERglEnum depthFunc; + metaforce::ERglCullMode cullMode; + metaforce::ERglBlendMode blendMode; + metaforce::ERglBlendFactor blendFacSrc, blendFacDst; + metaforce::ERglLogicOp blendOp; + 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 diff --git a/aurora/lib/gfx/gx_shader.cpp b/aurora/lib/gfx/gx_shader.cpp new file mode 100644 index 000000000..001411e35 --- /dev/null +++ b/aurora/lib/gfx/gx_shader.cpp @@ -0,0 +1,249 @@ +#include "common.hpp" + +#include "../gpu.hpp" +#include "gx.hpp" + +namespace aurora::gfx { +using namespace fmt::literals; + +static logvisor::Module Log("aurora::gfx::gx"); + +std::unordered_map g_gxCachedShaders; + +static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) { + switch (arg) { + case GX::CC_CPREV: + return "prev.rgb"; + case GX::CC_APREV: + return "prev.a"; + case GX::CC_C0: + case GX::CC_A0: + case GX::CC_C1: + case GX::CC_A1: + case GX::CC_C2: + 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_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_ZERO: + return "0.0"; + } +} + +static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) { + switch (arg) { + case GX::CA_APREV: + return "prev.a"; + case GX::CA_A0: + case GX::CA_A1: + 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_ZERO: + return "0.0"; + } +} + +static std::string_view tev_op(GX::TevOp op) { + switch (op) { + case GX::TEV_ADD: + return "+"; + case GX::TEV_SUB: + return "-"; + default: + Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), op); + unreachable(); + } +} + +static std::string_view tev_bias(GX::TevBias bias) { + switch (bias) { + case GX::TB_ZERO: + return " + 0.0"; + case GX::TB_ADDHALF: + return " + 0.5"; + case GX::TB_SUBHALF: + return " - 0.5"; + } +} + +static std::string_view tev_scale(GX::TevScale scale) { + switch (scale) { + case GX::CS_SCALE_1: + return " * 1.0"; + case GX::CS_SCALE_2: + return " * 2.0"; + case GX::CS_SCALE_4: + return " * 4.0"; + case GX::CS_DIVIDE_2: + return " / 2.0"; + } +} + +wgpu::ShaderModule build_shader(const GXShaderConfig& config) { + const auto hash = xxh3_hash(config); + if (g_gxCachedShaders.contains(hash)) { + return g_gxCachedShaders[hash]; + } + + std::string uniBufAttrs; + std::string sampBindings; + std::string texBindings; + std::string vtxOutAttrs; + 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++; + } + + 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: + outReg = "prev"; + break; + 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)); + 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: + outReg = "prev.a"; + break; + 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)); + fragmentFn += fmt::format(FMT_STRING("\n {0} = {1};"), outReg, op); + } + idx++; + } + + const auto shaderSource = + fmt::format(FMT_STRING(R"""( +struct Uniform {{ + xf: mat4x4;{uniBufAttrs} +}}; +@group(0) @binding(0) +var ubuf: Uniform;{sampBindings}{texBindings} + +struct VertexOutput {{ + @builtin(position) pos: vec4;{vtxOutAttrs} +}}; + +@stage(vertex) +fn vs_main( + @location(0) in_pos: vec3{vtxInAttrs} +) -> VertexOutput {{ + var out: VertexOutput; + out.pos = ubuf.xf * vec4(in_pos, 1.0);{vtxXfrAttrs} + return out; +}} + +@stage(fragment) +fn fs_main(in: VertexOutput) -> @location(0) vec4 {{ + var prev: vec4;{fragmentFn} + return prev; +}} +)"""), + "uniBufAttrs"_a = uniBufAttrs, "sampBindings"_a = sampBindings, "texBindings"_a = texBindings, + "vtxOutAttrs"_a = vtxOutAttrs, "vtxInAttrs"_a = vtxInAttrs, "vtxXfrAttrs"_a = vtxXfrAttrs, + "fragmentFn"_a = fragmentFn); + Log.report(logvisor::Info, FMT_STRING("Generated shader: {}"), shaderSource); + + wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{}; + wgslDescriptor.source = shaderSource.c_str(); + const auto shaderDescriptor = wgpu::ShaderModuleDescriptor{ + .nextInChain = &wgslDescriptor, + .label = "GX Shader", + }; + auto shader = gpu::g_device.CreateShaderModule(&shaderDescriptor); + g_gxCachedShaders.emplace(hash, shader); + + return shader; +} +} // namespace aurora::gfx diff --git a/aurora/lib/gfx/model/shader.cpp b/aurora/lib/gfx/model/shader.cpp new file mode 100644 index 000000000..c3f11f971 --- /dev/null +++ b/aurora/lib/gfx/model/shader.cpp @@ -0,0 +1,180 @@ +#include "shader.hpp" + +#include "../../gpu.hpp" +#include "../common.hpp" + +#include +#include + +enum class VtxDescAttr : u8 { + Position = 0, + Normal = 2, + Color0 = 4, + Color1 = 8, + Tex0 = 10, + Tex1 = 12, + Tex2 = 14, + Tex3 = 16, + Tex4 = 18, + Tex5 = 20, + Tex6 = 22, + PnMatIdx = 24, + Tex0MatIdx = 25, + Tex1MatIdx = 26, + Tex2MatIdx = 27, + Tex3MatIdx = 28, + Tex4MatIdx = 29, + Tex5MatIdx = 30, + Tex6MatIdx = 31, +}; +enum class VtxDescAttrType : u8 { None = 0, Direct = 1, Index8 = 2, Index16 = 3 }; +class VtxDescFlags { + u32 m_flags = 0; + +public: + constexpr VtxDescFlags() noexcept = default; + constexpr VtxDescFlags(u32 flags) noexcept : m_flags(flags){}; + [[nodiscard]] constexpr VtxDescAttrType GetAttributeType(VtxDescAttr attribute) const noexcept { + return VtxDescAttrType((m_flags >> u32(attribute)) & 0x3); + } + [[nodiscard]] constexpr VtxDescAttrType GetDirectAttributeType(VtxDescAttr attribute) const noexcept { + return VtxDescAttrType((m_flags >> u32(attribute)) & 0x1); + } + constexpr void SetAttributeType(VtxDescAttr attribute, VtxDescAttrType type) noexcept { + m_flags &= ~(u32(0x3) << u32(attribute)); + m_flags |= u32(type) << u32(attribute); + } +}; + +namespace aurora::gfx::model { +static logvisor::Module Log("aurora::gfx::model"); + +static const std::vector* vtxData; +static const std::vector* nrmData; +static const std::vector* tex0TcData; +static const std::vector* tcData; + +void set_vertex_buffer(const std::vector& data) noexcept { vtxData = &data; } +void set_normal_buffer(const std::vector& norm) noexcept { nrmData = &norm; } +void set_tex0_tc_buffer(const std::vector& tcs) noexcept { tex0TcData = &tcs; } +void set_tc_buffer(const std::vector& tcs) noexcept { tcData = &tcs; } + +struct DlVert { + s16 pos; + s16 norm; + // colors ignored + std::array uvs; + // pn_mtx_idx ignored + // tex_mtx_idxs ignored + s16 _pad; +}; + +enum class VertexFormat : u8 { + F32F32, + S16F32, + S16S16, +}; +static VtxDescFlags sVtxDescFlags; +void set_vtx_desc_compressed(u32 vtxDesc) noexcept { sVtxDescFlags = vtxDesc; } + +static inline std::pair readVert(const u8* data) noexcept { + DlVert out{}; + size_t offset = 0; + const auto read8 = [data, &offset](VtxDescAttrType type) -> s8 { + if (type == VtxDescAttrType::Direct) { + s8 v = static_cast(data[offset]); + ++offset; + return v; + } + return 0; + }; + const auto read16 = [data, &offset](VtxDescAttrType type) -> s16 { + if (type == VtxDescAttrType::Index16) { + s16 v = metaforce::SBig(*reinterpret_cast(data + offset)); + offset += 2; + return v; + } + return 0; + }; + read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::PnMatIdx)); + read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex0MatIdx)); + read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex1MatIdx)); + read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex2MatIdx)); + read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex3MatIdx)); + read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex4MatIdx)); + read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex5MatIdx)); + read8(sVtxDescFlags.GetDirectAttributeType(VtxDescAttr::Tex6MatIdx)); + + out.pos = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Position)); + out.norm = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Normal)); + read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Color0)); + read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Color1)); + out.uvs[0] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex0)); + out.uvs[1] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex1)); + out.uvs[2] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex2)); + out.uvs[3] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex3)); + out.uvs[4] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex4)); + out.uvs[5] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex5)); + out.uvs[6] = read16(sVtxDescFlags.GetAttributeType(VtxDescAttr::Tex6)); + + return {out, offset}; +} + +void queue_surface(const u8* dlStart, u32 dlSize) noexcept { + // Log.report(logvisor::Info, FMT_STRING("DL size {}"), dlSize); + std::vector verts; + std::vector indices; + + size_t offset = 0; + while (offset < dlSize) { + const auto header = dlStart[offset]; + const auto primitive = static_cast(header & 0xF8); + const auto vtxFmt = static_cast(header & 0x3); + const auto vtxCount = metaforce::SBig(*reinterpret_cast(dlStart + offset + 1)); + // Log.report(logvisor::Info, FMT_STRING("DL header prim {}, fmt {}, vtx count {}"), primitive, + // magic_enum::enum_name(vtxFmt), vtxCount); + offset += 3; + + if (primitive == 0) { + break; + } + if (primitive != GX::TRIANGLES && primitive != GX::TRIANGLESTRIP && primitive != GX::TRIANGLEFAN) { + Log.report(logvisor::Fatal, FMT_STRING("queue_surface: unsupported primitive type {}"), primitive); + unreachable(); + } + + const u32 idxStart = indices.size(); + const u16 vertsStart = verts.size(); + verts.reserve(vertsStart + vtxCount); + if (vtxCount > 3 && (primitive == GX::TRIANGLEFAN || primitive == GX::TRIANGLESTRIP)) { + indices.reserve(idxStart + (u32(vtxCount) - 3) * 3 + 3); + } else { + indices.reserve(idxStart + vtxCount); + } + auto curVert = vertsStart; + for (int v = 0; v < vtxCount; ++v) { + const auto [vert, read] = readVert(dlStart + offset); + verts.push_back(vert); + offset += read; + if (primitive == GX::TRIANGLES || v < 3) { + // pass + } else if (primitive == GX::TRIANGLEFAN) { + indices.push_back(vertsStart); + indices.push_back(curVert - 1); + } else if (primitive == GX::TRIANGLESTRIP) { + if ((v & 1) == 0) { + indices.push_back(curVert - 2); + indices.push_back(curVert - 1); + } else { + indices.push_back(curVert - 1); + indices.push_back(curVert - 2); + } + } + indices.push_back(curVert); + ++curVert; + } + } + + // Log.report(logvisor::Info, FMT_STRING("Read {} verts, {} indices"), verts.size(), indices.size()); +} +} // namespace aurora::gfx::model diff --git a/aurora/lib/gfx/model/shader.hpp b/aurora/lib/gfx/model/shader.hpp new file mode 100644 index 000000000..b84d9afac --- /dev/null +++ b/aurora/lib/gfx/model/shader.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "../common.hpp" +#include "../gx.hpp" + +namespace aurora::gfx::model { +struct DrawData { + PipelineRef pipeline; + Range vertRange; + Range uniformRange; + uint32_t vertexCount; + uint32_t uniformSize; + BindGroupRef samplerBindGroup; + BindGroupRef textureBindGroup; +}; + +struct PipelineConfig : GXPipelineConfig { + ShaderRef shader; + uint32_t uniformSize; +}; + +struct CachedBindGroup { + wgpu::BindGroupLayout layout; + wgpu::BindGroup bindGroup; + CachedBindGroup(wgpu::BindGroupLayout layout, wgpu::BindGroup&& group) + : layout(std::move(layout)), bindGroup(std::move(group)) {} +}; +struct State { + wgpu::BindGroupLayout samplerLayout; + wgpu::BindGroupLayout textureLayout; + mutable std::unordered_map uniform; + mutable std::unordered_map sampler; +}; + +State construct_state(); +wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config); +void render(const State& state, const DrawData& data, const wgpu::RenderPassEncoder& pass); +} // namespace aurora::gfx::model diff --git a/aurora/lib/gfx/stream.cpp b/aurora/lib/gfx/stream.cpp index 78b8a7e68..9a118bda6 100644 --- a/aurora/lib/gfx/stream.cpp +++ b/aurora/lib/gfx/stream.cpp @@ -4,13 +4,7 @@ #include "common.hpp" #include "gx.hpp" -#include - -#include - namespace aurora::gfx { -using namespace fmt::literals; - static logvisor::Module Log("aurora::gfx::stream"); struct SStreamState { @@ -23,51 +17,6 @@ struct SStreamState { }; static std::optional sStreamState; -constexpr u32 maxTextures = 8; -struct STextureBind { - aurora::gfx::TextureHandle handle; - metaforce::EClampMode clampMode; - float lod; - - STextureBind() noexcept = default; - STextureBind(aurora::gfx::TextureHandle handle, metaforce::EClampMode clampMode, float lod) noexcept - : handle(std::move(handle)), clampMode(clampMode), lod(lod) {} - void reset() noexcept { handle.reset(); }; - wgpu::SamplerDescriptor get_descriptor() const noexcept { - wgpu::AddressMode mode; - switch (clampMode) { - case metaforce::EClampMode::Clamp: - mode = wgpu::AddressMode::ClampToEdge; - break; - case metaforce::EClampMode::Repeat: - mode = wgpu::AddressMode::Repeat; - break; - case metaforce::EClampMode::Mirror: - mode = wgpu::AddressMode::MirrorRepeat; - break; - } - return { - .label = "Generated Sampler", - .addressModeU = mode, - .addressModeV = mode, - .addressModeW = mode, - // TODO logic from CTexture? - .magFilter = wgpu::FilterMode::Linear, - .minFilter = wgpu::FilterMode::Linear, - .mipmapFilter = wgpu::FilterMode::Linear, - .maxAnisotropy = gpu::g_graphicsConfig.textureAnistropy, - }; - } - operator bool() const noexcept { return handle; } -}; -static std::array sTextures; - -void bind_texture(GX::TexMapID id, metaforce::EClampMode clamp, const TextureHandle& tex, float lod) noexcept { - sTextures[static_cast(id)] = {tex, clamp, lod}; -} - -void unbind_texture(GX::TexMapID id) noexcept { sTextures[static_cast(id)].reset(); } - void stream_begin(GX::Primitive primitive) noexcept { if (sStreamState) { Log.report(logvisor::Fatal, FMT_STRING("Stream began twice!")); @@ -89,7 +38,6 @@ void stream_vertex(metaforce::EStreamFlags flags, const zeus::CVector3f& pos, co } } else { sStreamState->flags = flags; - // TODO begin shader construction } sStreamState->vertexBuffer.append(&pos, 12); if (flags & metaforce::EStreamFlagBits::fHasNormal) { @@ -104,259 +52,9 @@ void stream_vertex(metaforce::EStreamFlags flags, const zeus::CVector3f& pos, co sStreamState->vertexCount++; } -static std::string color_arg_reg(GX::TevColorArg arg, size_t stageIdx) { - switch (arg) { - case GX::CC_CPREV: - return "prev.rgb"; - case GX::CC_APREV: - return "prev.a"; - case GX::CC_C0: - case GX::CC_A0: - case GX::CC_C1: - case GX::CC_A1: - case GX::CC_C2: - 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_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_ZERO: - return "0.0"; - } -} - -static std::string alpha_arg_reg(GX::TevAlphaArg arg, size_t stageIdx) { - switch (arg) { - case GX::CA_APREV: - return "prev.a"; - case GX::CA_A0: - case GX::CA_A1: - 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_ZERO: - return "0.0"; - } -} - -static std::string_view tev_op(GX::TevOp op) { - switch (op) { - case GX::TEV_ADD: - return "+"; - case GX::TEV_SUB: - return "-"; - default: - Log.report(logvisor::Fatal, FMT_STRING("TODO {}"), op); - unreachable(); - } -} - -static std::string_view tev_bias(GX::TevBias bias) { - switch (bias) { - case GX::TB_ZERO: - return " + 0.0"; - case GX::TB_ADDHALF: - return " + 0.5"; - case GX::TB_SUBHALF: - return " - 0.5"; - } -} - -static std::string_view tev_scale(GX::TevScale scale) { - switch (scale) { - case GX::CS_SCALE_1: - return " * 1.0"; - case GX::CS_SCALE_2: - return " * 2.0"; - case GX::CS_SCALE_4: - return " * 4.0"; - case GX::CS_DIVIDE_2: - return " / 2.0"; - } -} - -std::unordered_map g_streamCachedShaders; - -static ShaderRef generate_shader() { - auto flags = sStreamState->flags; - const auto hash = hash_tev_stages(static_cast(flags)); - if (g_streamCachedShaders.contains(hash)) { - return hash; - } - - std::string uniBufAttrs; - if (flags & metaforce::EStreamFlagBits::fHasTexture) { - uniBufAttrs += fmt::format(FMT_STRING("\n tex0_lod: f32;")); - } - std::string sampBindings; - if (flags & metaforce::EStreamFlagBits::fHasTexture) { - sampBindings += - "\n" - "@group(1) @binding(0)\n" - "var tex0_samp: sampler;"; - } - std::string texBindings; - if (flags & metaforce::EStreamFlagBits::fHasTexture) { - texBindings += - "\n" - "@group(2) @binding(0)\n" - "var tex0: texture_2d;"; - } - std::string vtxOutAttrs; - std::string vtxInAttrs; - std::string vtxXfrAttrs; - { - size_t idx = 0; - if (flags & metaforce::EStreamFlagBits::fHasNormal) { - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) nrm: vec3;"), idx); - vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_nrm: vec3"), ++idx); - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.nrm = in_nrm;")); - } - if (flags & metaforce::EStreamFlagBits::fHasColor) { - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) clr: vec4;"), idx); - vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_clr: vec4"), ++idx); - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.clr = in_clr;")); - } - if (flags & metaforce::EStreamFlagBits::fHasTexture) { - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) tex0_uv: vec2;"), idx); - vtxInAttrs += fmt::format(FMT_STRING("\n , @location({}) in_uv: vec2"), ++idx); - vtxXfrAttrs += fmt::format(FMT_STRING("\n out.tex0_uv = in_uv;")); - } - } - std::string fragmentFn; - bool hasRast = false; - for (size_t idx = 0; const auto& stage : g_tevStages) { - if (!stage) { - idx++; - continue; - } - 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 : g_tevStages) { - if (!stage) { - idx++; - continue; - } - { - std::string op; - std::string outReg; - switch (stage->colorOp.x10_regId) { - case GX::TevRegID::TEVPREV: - outReg = "prev"; - break; - default: - Log.report(logvisor::Fatal, FMT_STRING("TODO: colorOp outReg {}"), - magic_enum::enum_name(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)); - 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: - outReg = "prev.a"; - break; - default: - Log.report(logvisor::Fatal, FMT_STRING("TODO: alphaOp outReg {}"), - magic_enum::enum_name(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)); - fragmentFn += fmt::format(FMT_STRING("\n {0} = {1};"), outReg, op); - } - idx++; - } - - const auto shaderSource = - fmt::format(FMT_STRING(R"""( -struct Uniform {{ - xf: mat4x4;{uniBufAttrs} -}}; -@group(0) @binding(0) -var ubuf: Uniform;{sampBindings}{texBindings} - -struct VertexOutput {{ - @builtin(position) pos: vec4;{vtxOutAttrs} -}}; - -@stage(vertex) -fn vs_main( - @location(0) in_pos: vec3{vtxInAttrs} -) -> VertexOutput {{ - var out: VertexOutput; - out.pos = ubuf.xf * vec4(in_pos, 1.0);{vtxXfrAttrs} - return out; -}} - -@stage(fragment) -fn fs_main(in: VertexOutput) -> @location(0) vec4 {{ - var prev: vec4;{fragmentFn} - return prev; -}} -)"""), - "uniBufAttrs"_a = uniBufAttrs, "sampBindings"_a = sampBindings, "texBindings"_a = texBindings, - "vtxOutAttrs"_a = vtxOutAttrs, "vtxInAttrs"_a = vtxInAttrs, "vtxXfrAttrs"_a = vtxXfrAttrs, - "fragmentFn"_a = fragmentFn); - Log.report(logvisor::Info, FMT_STRING("Generated shader: {}"), shaderSource); - - wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{}; - wgslDescriptor.source = shaderSource.c_str(); - const auto shaderDescriptor = wgpu::ShaderModuleDescriptor{ - .nextInChain = &wgslDescriptor, - .label = "Generated Shader", - }; - auto shader = gpu::g_device.CreateShaderModule(&shaderDescriptor); - g_streamCachedShaders.emplace(hash, std::move(shader)); - - return hash; -} - void stream_end() noexcept { - if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture && !sTextures[0]) { + 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(); } @@ -369,28 +67,21 @@ void stream_end() noexcept { uniBuf.append(&xf, 64); } if (sStreamState->flags & metaforce::EStreamFlagBits::fHasTexture) { - uniBuf.append(&sTextures[0].lod, 4); + uniBuf.append(&tex.lod, 4); } const auto uniRange = push_uniform(uniBuf.data(), uniBuf.size()); - const auto shaderRef = generate_shader(); - const auto uniform_size = align_uniform(uniBuf.size()); - const auto pipeline = pipeline_ref(stream::PipelineConfig{ - .shader = shaderRef, + + stream::PipelineConfig config{ .uniformSize = uniform_size, - .primitive = sStreamState->primitive, .flags = sStreamState->flags, - .depthCompare = g_depthCompare, - .depthUpdate = g_depthUpdate, - .depthFunc = g_depthFunc, - .cullMode = g_cullMode, - .blendMode = g_blendMode, - .blendFacSrc = g_blendFacSrc, - .blendFacDst = g_blendFacDst, - .blendOp = g_blendOp, - .dstAlpha = g_dstAlpha, - }); + }; + 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 pipeline = pipeline_ref(config); BindGroupRef samplerBindGroup{}; BindGroupRef textureBindGroup{}; @@ -399,7 +90,7 @@ void stream_end() noexcept { { const std::array samplerEntries{wgpu::BindGroupEntry{ .binding = 0, - .sampler = sampler_ref(sTextures[0].get_descriptor()), + .sampler = sampler_ref(tex.get_descriptor()), }}; samplerBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ .label = "Stream Sampler Bind Group", @@ -411,7 +102,7 @@ void stream_end() noexcept { { const std::array textureEntries{wgpu::BindGroupEntry{ .binding = 0, - .textureView = sTextures[0].handle.ref->view, + .textureView = tex.handle.ref->view, }}; textureBindGroup = bind_group_ref(wgpu::BindGroupDescriptor{ .label = "Stream Texture Bind Group", diff --git a/aurora/lib/gfx/stream/shader.cpp b/aurora/lib/gfx/stream/shader.cpp index 641dfcd9d..209f6ec19 100644 --- a/aurora/lib/gfx/stream/shader.cpp +++ b/aurora/lib/gfx/stream/shader.cpp @@ -4,9 +4,10 @@ #include "../common.hpp" #include +#include namespace aurora::gfx { -extern std::unordered_map g_streamCachedShaders; +extern std::unordered_map g_gxCachedShaders; } // namespace aurora::gfx namespace aurora::gfx::stream { @@ -16,31 +17,6 @@ using gpu::g_device; using gpu::g_graphicsConfig; using gpu::utils::make_vertex_state; -static wgpu::BlendFactor to_blend_factor(metaforce::ERglBlendFactor fac) { - switch (fac) { - case metaforce::ERglBlendFactor::Zero: - return wgpu::BlendFactor::Zero; - case metaforce::ERglBlendFactor::One: - return wgpu::BlendFactor::One; - case metaforce::ERglBlendFactor::SrcColor: - return wgpu::BlendFactor::Src; - case metaforce::ERglBlendFactor::InvSrcColor: - return wgpu::BlendFactor::OneMinusSrc; - case metaforce::ERglBlendFactor::SrcAlpha: - return wgpu::BlendFactor::SrcAlpha; - case metaforce::ERglBlendFactor::InvSrcAlpha: - return wgpu::BlendFactor::OneMinusSrcAlpha; - case metaforce::ERglBlendFactor::DstAlpha: - return wgpu::BlendFactor::DstAlpha; - case metaforce::ERglBlendFactor::InvDstAlpha: - return wgpu::BlendFactor::OneMinusDstAlpha; - case metaforce::ERglBlendFactor::DstColor: - return wgpu::BlendFactor::Dst; - case metaforce::ERglBlendFactor::InvDstColor: - return wgpu::BlendFactor::OneMinusDst; - } -} - wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] PipelineConfig config) { std::array attributes{}; attributes[0] = wgpu::VertexAttribute{ @@ -83,115 +59,6 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli .attributes = attributes.data(), }}; - wgpu::CompareFunction depthCompare; - switch (config.depthFunc) { - case metaforce::ERglEnum::Never: - depthCompare = wgpu::CompareFunction::Never; - break; - case metaforce::ERglEnum::Less: - depthCompare = wgpu::CompareFunction::Less; - break; - case metaforce::ERglEnum::Equal: - depthCompare = wgpu::CompareFunction::Equal; - break; - case metaforce::ERglEnum::LEqual: - depthCompare = wgpu::CompareFunction::LessEqual; - break; - case metaforce::ERglEnum::Greater: - depthCompare = wgpu::CompareFunction::Greater; - break; - case metaforce::ERglEnum::NEqual: - depthCompare = wgpu::CompareFunction::NotEqual; - break; - case metaforce::ERglEnum::GEqual: - depthCompare = wgpu::CompareFunction::GreaterEqual; - break; - case metaforce::ERglEnum::Always: - depthCompare = wgpu::CompareFunction::Always; - break; - } - const auto depthStencil = wgpu::DepthStencilState{ - .format = g_graphicsConfig.depthFormat, - .depthWriteEnabled = config.depthUpdate, - .depthCompare = depthCompare, - }; - - if (config.blendMode != metaforce::ERglBlendMode::Blend) { - Log.report(logvisor::Fatal, FMT_STRING("How to {}?"), magic_enum::enum_name(config.blendMode)); - } - const auto colorBlendComponent = wgpu::BlendComponent{ - .operation = wgpu::BlendOperation::Add, - .srcFactor = to_blend_factor(config.blendFacSrc), - .dstFactor = to_blend_factor(config.blendFacDst), - }; - auto alphaBlendComponent = colorBlendComponent; - if (config.dstAlpha) { - alphaBlendComponent = wgpu::BlendComponent{ - .operation = wgpu::BlendOperation::Add, - .srcFactor = wgpu::BlendFactor::Zero, - .dstFactor = wgpu::BlendFactor::Constant, - }; - } - const auto blendState = wgpu::BlendState{ - .color = colorBlendComponent, - .alpha = alphaBlendComponent, - }; - auto writeMask = wgpu::ColorWriteMask::Red | wgpu::ColorWriteMask::Green | wgpu::ColorWriteMask::Blue; - if (config.alphaUpdate) { - writeMask = writeMask | wgpu::ColorWriteMask::Alpha; - } - const std::array colorTargets{wgpu::ColorTargetState{ - .format = g_graphicsConfig.colorFormat, - .blend = &blendState, - .writeMask = writeMask, - }}; - const auto& shader = g_streamCachedShaders[config.shader]; - const auto fragmentState = wgpu::FragmentState{ - .module = shader, - .entryPoint = "fs_main", - .targetCount = colorTargets.size(), - .targets = colorTargets.data(), - }; - - wgpu::PrimitiveTopology primitive; - switch (config.primitive) { - case GX::POINTS: - primitive = wgpu::PrimitiveTopology::PointList; - break; - case GX::LINES: - primitive = wgpu::PrimitiveTopology::LineList; - break; - case GX::LINESTRIP: - primitive = wgpu::PrimitiveTopology::LineStrip; - break; - case GX::TRIANGLES: - primitive = wgpu::PrimitiveTopology::TriangleList; - break; - case GX::TRIANGLESTRIP: - primitive = wgpu::PrimitiveTopology::TriangleStrip; - break; - default: - Log.report(logvisor::Fatal, FMT_STRING("Unsupported primitive type {}"), magic_enum::enum_name(config.primitive)); - unreachable(); - } - - wgpu::FrontFace frontFace; - wgpu::CullMode cullMode; - switch (config.cullMode) { - case metaforce::ERglCullMode::Front: - frontFace = wgpu::FrontFace::CW; - cullMode = wgpu::CullMode::Front; - break; - case metaforce::ERglCullMode::Back: - frontFace = wgpu::FrontFace::CCW; - cullMode = wgpu::CullMode::Back; - break; - default: - frontFace = wgpu::FrontFace::CCW; - cullMode = wgpu::CullMode::None; - break; - } - wgpu::BindGroupLayout uniformLayout; if (state.uniform.contains(config.uniformSize)) { uniformLayout = state.uniform.at(config.uniformSize).layout; @@ -237,30 +104,12 @@ wgpu::RenderPipeline create_pipeline(const State& state, [[maybe_unused]] Pipeli const auto pipelineLayoutDescriptor = wgpu::PipelineLayoutDescriptor{ .label = "Stream Pipeline Layout", .bindGroupLayoutCount = - // TODO avoid creating bind group layouts if no tex? - static_cast(config.flags & metaforce::EStreamFlagBits::fHasTexture ? bindGroupLayouts.size() : 1), + static_cast(config.flags & metaforce::EStreamFlagBits::fHasTexture ? bindGroupLayouts.size() : 1), .bindGroupLayouts = bindGroupLayouts.data(), }; auto pipelineLayout = g_device.CreatePipelineLayout(&pipelineLayoutDescriptor); - const auto pipelineDescriptor = wgpu::RenderPipelineDescriptor{ - .label = "Stream Pipeline", - .layout = pipelineLayout, - .vertex = make_vertex_state(shader, vertexBuffers), - .primitive = - wgpu::PrimitiveState{ - .topology = primitive, - .frontFace = frontFace, - .cullMode = cullMode, - }, - .depthStencil = &depthStencil, - .multisample = - wgpu::MultisampleState{ - .count = g_graphicsConfig.msaaSamples, - }, - .fragment = &fragmentState, - }; - return g_device.CreateRenderPipeline(&pipelineDescriptor); + return build_pipeline(pipelineLayout, vertexBuffers, config, "Stream Pipeline"); } State construct_state() { diff --git a/aurora/lib/gfx/stream/shader.hpp b/aurora/lib/gfx/stream/shader.hpp index c949c8f5e..b67850444 100644 --- a/aurora/lib/gfx/stream/shader.hpp +++ b/aurora/lib/gfx/stream/shader.hpp @@ -1,6 +1,7 @@ #pragma once #include "../common.hpp" +#include "../gx.hpp" namespace aurora::gfx::stream { struct DrawData { @@ -13,19 +14,9 @@ struct DrawData { BindGroupRef textureBindGroup; }; -struct PipelineConfig { - ShaderRef shader; +struct PipelineConfig : public GXPipelineConfig { uint32_t uniformSize; - - GX::Primitive primitive; metaforce::EStreamFlags flags; - bool depthCompare, depthUpdate, alphaUpdate; - metaforce::ERglEnum depthFunc; - metaforce::ERglCullMode cullMode; - metaforce::ERglBlendMode blendMode; - metaforce::ERglBlendFactor blendFacSrc, blendFacDst; - metaforce::ERglLogicOp blendOp; - std::optional dstAlpha; }; struct CachedBindGroup {