From fe65258d917cad11bfc85c42c4de51ba038430cf Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 25 May 2022 01:45:42 -0400 Subject: [PATCH] aurora: Support per-pixel lighting --- aurora/lib/gfx/common.cpp | 3 +- aurora/lib/gfx/gx.cpp | 29 ++++++++++--- aurora/lib/gfx/gx.hpp | 43 ++++++++++++++++--- aurora/lib/gfx/gx_shader.cpp | 81 +++++++++++++++++++++++++----------- 4 files changed, 119 insertions(+), 37 deletions(-) diff --git a/aurora/lib/gfx/common.cpp b/aurora/lib/gfx/common.cpp index c0aa2f31f..d550e5774 100644 --- a/aurora/lib/gfx/common.cpp +++ b/aurora/lib/gfx/common.cpp @@ -754,8 +754,7 @@ const wgpu::Sampler& sampler_ref(const wgpu::SamplerDescriptor& descriptor) { } uint32_t align_uniform(uint32_t value) { - const auto uniform_alignment = g_cachedLimits.limits.minUniformBufferOffsetAlignment; - return ALIGN(value, uniform_alignment); + return ALIGN(value, g_cachedLimits.limits.minUniformBufferOffsetAlignment); } void push_debug_group(zstring_view label) noexcept { diff --git a/aurora/lib/gfx/gx.cpp b/aurora/lib/gfx/gx.cpp index b88a75bd1..693b09ea4 100644 --- a/aurora/lib/gfx/gx.cpp +++ b/aurora/lib/gfx/gx.cpp @@ -891,12 +891,22 @@ void populate_pipeline_config(PipelineConfig& config, GX::Primitive primitive) n } config.shaderConfig.tevStageCount = g_gxState.numTevStages; for (u8 i = 0; i < g_gxState.numChans; ++i) { - config.shaderConfig.colorChannels[i] = g_gxState.colorChannelConfig[i]; + const auto& cc = g_gxState.colorChannelConfig[i]; + if (g_gxState.colorChannelState[i].lightState.any() && cc.lightingEnabled) { + config.shaderConfig.colorChannels[i] = cc; + } else { + // Only matSrc matters when lighting disabled + config.shaderConfig.colorChannels[i] = { + .matSrc = cc.matSrc, + }; + } } for (u8 i = 0; i < g_gxState.numTexGens; ++i) { config.shaderConfig.tcgs[i] = g_gxState.tcgs[i]; } - config.shaderConfig.alphaCompare = g_gxState.alphaCompare; + if (g_gxState.alphaCompare) { + config.shaderConfig.alphaCompare = g_gxState.alphaCompare; + } config.shaderConfig.indexedAttributeCount = std::count_if(config.shaderConfig.vtxAttrs.begin(), config.shaderConfig.vtxAttrs.end(), [](const auto type) { return type == GX::INDEX8 || type == GX::INDEX16; }); @@ -904,8 +914,12 @@ void populate_pipeline_config(PipelineConfig& config, GX::Primitive primitive) n const auto& bind = g_gxState.textures[i]; TextureConfig texConfig{}; if (bind.texObj.ref) { - texConfig.copyFmt = bind.texObj.ref->gxFormat; - texConfig.loadFmt = bind.texObj.fmt; + if (requires_copy_conversion(bind.texObj)) { + texConfig.copyFmt = bind.texObj.ref->gxFormat; + } + if (requires_load_conversion(bind.texObj)) { + texConfig.loadFmt = bind.texObj.fmt; + } texConfig.renderTex = bind.texObj.ref->isRenderTexture; } config.shaderConfig.textureConfig[i] = texConfig; @@ -934,8 +948,8 @@ Range build_uniform(const ShaderInfo& info) noexcept { buf.append(&g_gxState.mvInv, 64); buf.append(&g_gxState.proj, 64); } - for (int i = 0; i < info.usesTevReg.size(); ++i) { - if (!info.usesTevReg.test(i)) { + for (int i = 0; i < info.loadsTevReg.size(); ++i) { + if (!info.loadsTevReg.test(i)) { continue; } buf.append(&g_gxState.colorRegs[i], 16); @@ -950,6 +964,9 @@ Range build_uniform(const ShaderInfo& info) noexcept { if (g_gxState.colorChannelConfig[i].lightingEnabled) { int addedLights = 0; const auto& lightState = g_gxState.colorChannelState[i].lightState; + u32 state = lightState.to_ulong(); + buf.append(&lightState, sizeof(u32)); + buf.append_zeroes(12); // alignment for (int li = 0; li < lightState.size(); ++li) { if (!lightState.test(li)) { continue; diff --git a/aurora/lib/gfx/gx.hpp b/aurora/lib/gfx/gx.hpp index a8cdcb9ea..ebe8275a4 100644 --- a/aurora/lib/gfx/gx.hpp +++ b/aurora/lib/gfx/gx.hpp @@ -80,9 +80,7 @@ struct TextureBind { TextureBind() noexcept = default; TextureBind(GXTexObj obj) noexcept : texObj(std::move(obj)) {} - void reset() noexcept { - texObj.ref.reset(); - }; + void reset() noexcept { texObj.ref.reset(); }; [[nodiscard]] wgpu::SamplerDescriptor get_descriptor() const noexcept; operator bool() const noexcept { return texObj.ref.operator bool(); } }; @@ -144,7 +142,7 @@ struct AlphaCompare { GX::Compare comp1 = GX::ALWAYS; u32 ref1; bool operator==(const AlphaCompare& other) const = default; - operator bool() const { return *this != AlphaCompare{}; } + operator bool() const { return comp0 != GX::ALWAYS || comp1 != GX::ALWAYS; } }; static_assert(std::has_unique_object_representations_v); struct IndTexMtxInfo { @@ -202,6 +200,41 @@ static inline Mat4x4 get_combined_matrix() noexcept { return g_gxState.pr 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; + } +} + struct TextureConfig { GX::TextureFormat copyFmt = InvalidTextureFormat; // Underlying texture format GX::TextureFormat loadFmt = InvalidTextureFormat; // Texture format being bound @@ -257,7 +290,7 @@ struct ShaderInfo { std::bitset sampledTextures; std::bitset sampledKColors; std::bitset sampledColorChannels; - std::bitset usesTevReg; + std::bitset loadsTevReg; std::bitset writesTevReg; std::bitset usesTexMtx; std::bitset usesPTTexMtx; diff --git a/aurora/lib/gfx/gx_shader.cpp b/aurora/lib/gfx/gx_shader.cpp index 7f4315670..08bbe7e94 100644 --- a/aurora/lib/gfx/gx_shader.cpp +++ b/aurora/lib/gfx/gx_shader.cpp @@ -7,6 +7,7 @@ constexpr bool EnableNormalVisualization = false; constexpr bool EnableDebugPrints = true; +constexpr bool UsePerPixelLighting = true; namespace aurora::gfx::gx { using namespace fmt::literals; @@ -36,25 +37,25 @@ static void color_arg_reg_info(GX::TevColorArg arg, const TevStage& stage, Shade case GX::CC_CPREV: case GX::CC_APREV: if (!info.writesTevReg.test(GX::TEVPREV)) { - info.usesTevReg.set(GX::TEVPREV); + info.loadsTevReg.set(GX::TEVPREV); } break; case GX::CC_C0: case GX::CC_A0: if (!info.writesTevReg.test(GX::TEVREG0)) { - info.usesTevReg.set(GX::TEVREG0); + info.loadsTevReg.set(GX::TEVREG0); } break; case GX::CC_C1: case GX::CC_A1: if (!info.writesTevReg.test(GX::TEVREG1)) { - info.usesTevReg.set(GX::TEVREG1); + info.loadsTevReg.set(GX::TEVREG1); } break; case GX::CC_C2: case GX::CC_A2: if (!info.writesTevReg.test(GX::TEVREG2)) { - info.usesTevReg.set(GX::TEVREG2); + info.loadsTevReg.set(GX::TEVREG2); } break; case GX::CC_TEXC: @@ -299,22 +300,22 @@ static void alpha_arg_reg_info(GX::TevAlphaArg arg, const TevStage& stage, Shade switch (arg) { case GX::CA_APREV: if (!info.writesTevReg.test(GX::TEVPREV)) { - info.usesTevReg.set(GX::TEVPREV); + info.loadsTevReg.set(GX::TEVPREV); } break; case GX::CA_A0: if (!info.writesTevReg.test(GX::TEVREG0)) { - info.usesTevReg.set(GX::TEVREG0); + info.loadsTevReg.set(GX::TEVREG0); } break; case GX::CA_A1: if (!info.writesTevReg.test(GX::TEVREG1)) { - info.usesTevReg.set(GX::TEVREG1); + info.loadsTevReg.set(GX::TEVREG1); } break; case GX::CA_A2: if (!info.writesTevReg.test(GX::TEVREG2)) { - info.usesTevReg.set(GX::TEVREG2); + info.loadsTevReg.set(GX::TEVREG2); } break; case GX::CA_TEXA: @@ -636,16 +637,16 @@ ShaderInfo build_shader_info(const ShaderConfig& config) noexcept { if (!info.writesTevReg.test(stage.alphaOp.outReg)) { // If we're writing alpha to a register that's not been // written to in the shader, load from uniform buffer - info.usesTevReg.set(stage.alphaOp.outReg); + info.loadsTevReg.set(stage.alphaOp.outReg); info.writesTevReg.set(stage.alphaOp.outReg); } } - info.uniformSize += info.usesTevReg.count() * 16; + info.uniformSize += info.loadsTevReg.count() * 16; for (int i = 0; i < info.sampledColorChannels.size(); ++i) { if (info.sampledColorChannels.test(i)) { info.uniformSize += 32; if (config.colorChannels[i].lightingEnabled) { - info.uniformSize += (80 * GX::MaxLights); + info.uniformSize += 16 + (80 * GX::MaxLights); } } } @@ -883,7 +884,8 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in if (stage.colorOp.clamp) { op = fmt::format(FMT_STRING("clamp({}, vec3(0.0), vec3(1.0))"), op); } - fragmentFn += fmt::format(FMT_STRING("\n // TEV stage {2}\n {0} = vec4({1}, {0}.a);"), outReg, op, idx); + fragmentFn += + fmt::format(FMT_STRING("\n // TEV stage {2}\n {0} = vec4({1}, {0}.a);"), outReg, op, idx); } { std::string outReg; @@ -914,14 +916,14 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in fragmentFn += fmt::format(FMT_STRING("\n {0} = {1};"), outReg, op); } } - if (info.usesTevReg.test(0)) { + if (info.loadsTevReg.test(0)) { uniBufAttrs += "\n tevprev: vec4,"; fragmentFnPre += "\n var prev = ubuf.tevprev;"; } else { fragmentFnPre += "\n var prev: vec4;"; } - for (int i = 1 /* Skip TEVPREV */; i < info.usesTevReg.size(); ++i) { - if (info.usesTevReg.test(i)) { + for (int i = 1 /* Skip TEVPREV */; i < info.loadsTevReg.size(); ++i) { + if (info.loadsTevReg.test(i)) { uniBufAttrs += fmt::format(FMT_STRING("\n tevreg{}: vec4,"), i - 1); fragmentFnPre += fmt::format(FMT_STRING("\n var tevreg{0} = ubuf.tevreg{0};"), i - 1); } else if (info.writesTevReg.test(i)) { @@ -949,11 +951,18 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in " cos_att: vec3,\n" " dist_att: vec3,\n" "};"; + if (UsePerPixelLighting) { + vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) mv_pos: vec3,"), vtxOutIdx++); + vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) mv_nrm: vec3,"), vtxOutIdx++); + vtxXfrAttrs += fmt::format(FMT_STRING(R"""( + out.mv_pos = mv_pos; + out.mv_nrm = mv_nrm;)""")); + } addedLightStruct = true; } + uniBufAttrs += fmt::format(FMT_STRING("\n lightState{}: u32,"), i); uniBufAttrs += fmt::format(FMT_STRING("\n lights{}: array,"), i, GX::MaxLights); - vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) cc{}: vec4,"), vtxOutIdx++, i); std::string ambSrc, matSrc, lightAttnFn, lightDiffFn; if (cc.ambSrc == GX::SRC_VTX) { @@ -982,16 +991,33 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in if (diffFn == GX::DF_NONE) { lightDiffFn = "1.0"; } else if (diffFn == GX::DF_SIGN) { - lightDiffFn = "dot(ldir, mv_nrm)"; + if (UsePerPixelLighting) { + lightDiffFn = "dot(ldir, in.mv_nrm)"; + } else { + lightDiffFn = "dot(ldir, mv_nrm)"; + } } else if (diffFn == GX::DF_CLAMP) { - lightDiffFn = "max(0.0, dot(ldir, mv_nrm))"; + if (UsePerPixelLighting) { + lightDiffFn = "max(0.0, dot(ldir, in.mv_nrm))"; + } else { + lightDiffFn = "max(0.0, dot(ldir, mv_nrm))"; + } } - vtxXfrAttrs += fmt::format(FMT_STRING(R"""( + std::string outVar, posVar; + if (UsePerPixelLighting) { + outVar = fmt::format(FMT_STRING("rast{}"), i); + posVar = "in.mv_pos"; + } else { + outVar = fmt::format(FMT_STRING("out.cc{}"), i); + posVar = "mv_pos"; + } + auto lightFunc = fmt::format(FMT_STRING(R"""( {{ var lighting = {5}; - for (var i = 0; i < {1}; i = i + 1) {{ + for (var i = 0u; i < {1}u; i++) {{ + if ((ubuf.lightState{0} & (1u << i)) == 0u) {{ continue; }} var light = ubuf.lights{0}[i]; - var ldir = light.pos - mv_pos; + var ldir = light.pos - {7}; var dist2 = dot(ldir, ldir); var dist = sqrt(dist2); ldir = ldir / dist; @@ -999,10 +1025,17 @@ wgpu::ShaderModule build_shader(const ShaderConfig& config, const ShaderInfo& in var diff = {3}; lighting = lighting + (attn * diff * light.color); }} - out.cc{0} = {4} * clamp(lighting, vec4(0.0), vec4(1.0)); + {6} = {4} * clamp(lighting, vec4(0.0), vec4(1.0)); }})"""), - i, GX::MaxLights, lightAttnFn, lightDiffFn, matSrc, ambSrc); - fragmentFnPre += fmt::format(FMT_STRING("\n var rast{0} = in.cc{0};"), i); + i, GX::MaxLights, lightAttnFn, lightDiffFn, matSrc, ambSrc, outVar, posVar); + if (UsePerPixelLighting) { + fragmentFnPre += fmt::format(FMT_STRING("\n var rast{}: vec4;"), i); + fragmentFnPre += lightFunc; + } else { + vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) cc{}: vec4,"), vtxOutIdx++, i); + vtxXfrAttrs += lightFunc; + fragmentFnPre += fmt::format(FMT_STRING("\n var rast{0} = in.cc{0};"), i); + } } else if (cc.matSrc == GX::SRC_VTX) { vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) cc{}: vec4,"), vtxOutIdx++, i); vtxXfrAttrs += fmt::format(FMT_STRING("\n out.cc{} = {};"), i, vtx_attr(config, GX::Attr(GX::VA_CLR0 + i)));