#pragma once #include "common.hpp" #include #include #include namespace aurora::gfx::gx { constexpr u32 MaxTextures = GX::MAX_TEXMAP; constexpr u32 MaxTevStages = GX::MAX_TEVSTAGE; constexpr u32 MaxColorChannels = 2; // COLOR0A0, COLOR1A1 constexpr u32 MaxTevRegs = 4; // TEVPREV, TEVREG0-2 constexpr u32 MaxKColors = GX::MAX_KCOLOR; constexpr u32 MaxTexMtx = 10; constexpr u32 MaxPTTexMtx = 20; constexpr u32 MaxTexCoord = GX::MAX_TEXCOORD; constexpr u32 MaxVtxAttr = GX::VA_MAX_ATTR; constexpr u32 MaxTevSwap = GX::MAX_TEVSWAP; constexpr u32 MaxIndStages = GX::MAX_INDTEXSTAGE; constexpr u32 MaxIndTexMtxs = 3; template struct TevPass { Arg a = Default; Arg b = Default; Arg c = Default; Arg d = Default; bool operator==(const TevPass& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } }; static_assert(std::has_unique_object_representations_v>); static_assert(std::has_unique_object_representations_v>); struct TevOp { GX::TevOp op = GX::TevOp::TEV_ADD; GX::TevBias bias = GX::TevBias::TB_ZERO; GX::TevScale scale = GX::TevScale::CS_SCALE_1; GX::TevRegID outReg = GX::TevRegID::TEVPREV; bool clamp = true; u8 _p1 = 0; u8 _p2 = 0; u8 _p3 = 0; bool operator==(const TevOp& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } }; static_assert(std::has_unique_object_representations_v); struct TevStage { TevPass colorPass; TevPass alphaPass; TevOp colorOp; TevOp alphaOp; GX::TevKColorSel kcSel = GX::TEV_KCSEL_1; GX::TevKAlphaSel kaSel = GX::TEV_KASEL_1; GX::TexCoordID texCoordId = GX::TEXCOORD_NULL; GX::TexMapID texMapId = GX::TEXMAP_NULL; GX::ChannelID channelId = GX::COLOR_NULL; GX::TevSwapSel tevSwapRas = GX::TEV_SWAP0; GX::TevSwapSel tevSwapTex = GX::TEV_SWAP0; GX::IndTexStageID indTexStage = GX::INDTEXSTAGE0; GX::IndTexFormat indTexFormat = GX::ITF_8; GX::IndTexBiasSel indTexBiasSel = GX::ITB_NONE; GX::IndTexAlphaSel indTexAlphaSel = GX::ITBA_OFF; GX::IndTexMtxID indTexMtxId = GX::ITM_OFF; GX::IndTexWrap indTexWrapS = GX::ITW_OFF; GX::IndTexWrap indTexWrapT = GX::ITW_OFF; bool indTexUseOrigLOD = false; bool indTexAddPrev = false; u8 _p1 = 0; u8 _p2 = 0; bool operator==(const TevStage& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } }; static_assert(std::has_unique_object_representations_v); struct IndStage { GX::TexCoordID texCoordId; GX::TexMapID texMapId; GX::IndTexScale scaleS; GX::IndTexScale scaleT; }; static_assert(std::has_unique_object_representations_v); struct TextureBind { GXTexObj texObj; TextureBind() noexcept = default; TextureBind(GXTexObj obj) noexcept : texObj(std::move(obj)) {} void reset() noexcept { texObj.ref.reset(); }; [[nodiscard]] wgpu::SamplerDescriptor get_descriptor() const noexcept; operator bool() const noexcept { return texObj.ref.operator bool(); } }; // For shader generation struct ColorChannelConfig { GX::ColorSrc matSrc = GX::SRC_REG; GX::ColorSrc ambSrc = GX::SRC_REG; GX::DiffuseFn diffFn = GX::DF_NONE; GX::AttnFn attnFn = GX::AF_NONE; bool lightingEnabled = false; u8 _p1 = 0; u8 _p2 = 0; u8 _p3 = 0; bool operator==(const ColorChannelConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } }; static_assert(std::has_unique_object_representations_v); // For uniform generation struct ColorChannelState { zeus::CColor matColor; zeus::CColor ambColor; GX::LightMask lightState; }; using LightVariant = std::variant; // Mat4x4 used instead of Mat4x3 for padding purposes using TexMtxVariant = std::variant, Mat4x4>; struct TcgConfig { GX::TexGenType type = GX::TG_MTX2x4; GX::TexGenSrc src = GX::MAX_TEXGENSRC; GX::TexMtx mtx = GX::IDENTITY; GX::PTTexMtx postMtx = GX::PTIDENTITY; bool normalize = false; u8 _p1 = 0; u8 _p2 = 0; u8 _p3 = 0; bool operator==(const TcgConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } }; static_assert(std::has_unique_object_representations_v); struct FogState { GX::FogType type = GX::FOG_NONE; float startZ = 0.f; float endZ = 0.f; float nearZ = 0.f; float farZ = 0.f; zeus::CColor color; bool operator==(const FogState& rhs) const { return type == rhs.type && startZ == rhs.startZ && endZ == rhs.endZ && nearZ == rhs.nearZ && farZ == rhs.farZ && color == rhs.color; } }; struct TevSwap { GX::TevColorChan red = GX::CH_RED; GX::TevColorChan green = GX::CH_GREEN; GX::TevColorChan blue = GX::CH_BLUE; GX::TevColorChan alpha = GX::CH_ALPHA; bool operator==(const TevSwap& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } explicit operator bool() const { return !(*this == TevSwap{}); } }; static_assert(std::has_unique_object_representations_v); struct AlphaCompare { GX::Compare comp0 = GX::ALWAYS; u32 ref0; // would be u8 but extended to avoid padding bytes GX::AlphaOp op = GX::AOP_AND; GX::Compare comp1 = GX::ALWAYS; u32 ref1; bool operator==(const AlphaCompare& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } explicit operator bool() const { return comp0 != GX::ALWAYS || comp1 != GX::ALWAYS; } }; static_assert(std::has_unique_object_representations_v); struct IndTexMtxInfo { aurora::Mat3x2 mtx; s8 scaleExp; }; struct GXState { zeus::CMatrix4f mv; zeus::CMatrix4f mvInv; zeus::CMatrix4f proj; FogState fog; GX::CullMode cullMode = GX::CULL_BACK; GX::BlendMode blendMode = GX::BM_NONE; GX::BlendFactor blendFacSrc = GX::BL_SRCALPHA; GX::BlendFactor blendFacDst = GX::BL_INVSRCALPHA; GX::LogicOp blendOp = GX::LO_CLEAR; GX::Compare depthFunc = GX::LEQUAL; zeus::CColor clearColor = zeus::skBlack; u32 dstAlpha; // u8; UINT32_MAX = disabled AlphaCompare alphaCompare; std::array colorRegs; std::array kcolors; std::array colorChannelConfig; std::array colorChannelState; std::array lights; std::array tevStages; std::array textures; std::array tluts; std::array texMtxs; std::array, MaxPTTexMtx> ptTexMtxs; std::array tcgs; std::array vtxDesc; std::array tevSwapTable{ TevSwap{}, TevSwap{GX::CH_RED, GX::CH_RED, GX::CH_RED, GX::CH_ALPHA}, TevSwap{GX::CH_GREEN, GX::CH_GREEN, GX::CH_GREEN, GX::CH_ALPHA}, TevSwap{GX::CH_BLUE, GX::CH_BLUE, GX::CH_BLUE, GX::CH_ALPHA}, }; std::array indStages; std::array indTexMtxs; bool depthCompare = true; bool depthUpdate = true; bool colorUpdate = true; bool alphaUpdate = true; u8 numChans = 0; u8 numIndStages = 0; u8 numTevStages = 0; u8 numTexGens = 0; }; extern GXState g_gxState; static inline Mat4x4 get_combined_matrix() noexcept { return g_gxState.proj * g_gxState.mv; } void shutdown() noexcept; const TextureBind& get_texture(GX::TexMapID id) noexcept; static inline bool requires_copy_conversion(const GXTexObj& obj) { if (!obj.ref) { return false; } if (obj.ref->isRenderTexture) { return true; } switch (obj.ref->gxFormat) { // case GX::TF_RGB565: // case GX::TF_I4: // case GX::TF_I8: case GX::TF_C4: case GX::TF_C8: case GX::TF_C14X2: return true; default: return false; } } static inline bool requires_load_conversion(const GXTexObj& obj) { if (!obj.ref) { return false; } switch (obj.fmt) { case GX::TF_I4: case GX::TF_I8: case GX::TF_C4: case GX::TF_C8: case GX::TF_C14X2: return true; default: return false; } } static inline bool is_palette_format(GX::TextureFormat fmt) { return fmt == GX::TF_C4 || fmt == GX::TF_C8 || fmt == GX::TF_C14X2; } struct TextureConfig { GX::TextureFormat copyFmt = InvalidTextureFormat; // Underlying texture format GX::TextureFormat loadFmt = InvalidTextureFormat; // Texture format being bound bool renderTex = false; // Perform conversion u8 _p1 = 0; u8 _p2 = 0; u8 _p3 = 0; bool operator==(const TextureConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } }; static_assert(std::has_unique_object_representations_v); struct ShaderConfig { GX::FogType fogType; std::array vtxAttrs; std::array tevSwapTable; std::array tevStages; u32 tevStageCount = 0; std::array colorChannels; std::array tcgs; AlphaCompare alphaCompare; u32 indexedAttributeCount = 0; std::array textureConfig; bool operator==(const ShaderConfig& rhs) const { return memcmp(this, &rhs, sizeof(*this)) == 0; } }; static_assert(std::has_unique_object_representations_v); constexpr u32 GXPipelineConfigVersion = 4; struct PipelineConfig { u32 version = GXPipelineConfigVersion; ShaderConfig shaderConfig; GX::Primitive primitive; GX::Compare depthFunc; GX::CullMode cullMode; GX::BlendMode blendMode; GX::BlendFactor blendFacSrc, blendFacDst; GX::LogicOp blendOp; u32 dstAlpha; bool depthCompare, depthUpdate, alphaUpdate, colorUpdate; }; static_assert(std::has_unique_object_representations_v); struct GXBindGroupLayouts { wgpu::BindGroupLayout uniformLayout; wgpu::BindGroupLayout samplerLayout; wgpu::BindGroupLayout textureLayout; }; struct GXBindGroups { BindGroupRef uniformBindGroup; BindGroupRef samplerBindGroup; BindGroupRef textureBindGroup; }; // Output info from shader generation struct ShaderInfo { std::bitset sampledTexCoords; std::bitset sampledTextures; std::bitset sampledKColors; std::bitset sampledColorChannels; std::bitset loadsTevReg; std::bitset writesTevReg; std::bitset usesTexMtx; std::bitset usesPTTexMtx; std::array texMtxTypes{}; u32 uniformSize = 0; bool usesFog : 1 = false; }; struct BindGroupRanges { Range vtxDataRange; Range nrmDataRange; Range tcDataRange; Range packedTcDataRange; }; void 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; ShaderInfo build_shader_info(const ShaderConfig& config) noexcept; wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& info) noexcept; // Range build_vertex_buffer(const GXShaderInfo& info) noexcept; Range build_uniform(const ShaderInfo& info) noexcept; GXBindGroupLayouts build_bind_group_layouts(const ShaderInfo& info, const ShaderConfig& config) noexcept; GXBindGroups build_bind_groups(const ShaderInfo& info, const ShaderConfig& config, const BindGroupRanges& ranges) noexcept; } // namespace aurora::gfx::gx