aurora: Support per-pixel lighting

This commit is contained in:
Luke Street 2022-05-25 01:45:42 -04:00
parent 05ad95bdd0
commit fe65258d91
4 changed files with 119 additions and 37 deletions

View File

@ -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 {

View File

@ -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];
}
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) {
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;

View File

@ -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<AlphaCompare>);
struct IndTexMtxInfo {
@ -202,6 +200,41 @@ static inline Mat4x4<float> 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<MaxTextures> sampledTextures;
std::bitset<MaxKColors> sampledKColors;
std::bitset<MaxColorChannels> sampledColorChannels;
std::bitset<MaxTevRegs> usesTevReg;
std::bitset<MaxTevRegs> loadsTevReg;
std::bitset<MaxTevRegs> writesTevReg;
std::bitset<MaxTexMtx> usesTexMtx;
std::bitset<MaxPTTexMtx> usesPTTexMtx;

View File

@ -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<f32>(0.0), vec3<f32>(1.0))"), op);
}
fragmentFn += fmt::format(FMT_STRING("\n // TEV stage {2}\n {0} = vec4<f32>({1}, {0}.a);"), outReg, op, idx);
fragmentFn +=
fmt::format(FMT_STRING("\n // TEV stage {2}\n {0} = vec4<f32>({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<f32>,";
fragmentFnPre += "\n var prev = ubuf.tevprev;";
} else {
fragmentFnPre += "\n var prev: vec4<f32>;";
}
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<f32>,"), 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<f32>,\n"
" dist_att: vec3<f32>,\n"
"};";
if (UsePerPixelLighting) {
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) mv_pos: vec3<f32>,"), vtxOutIdx++);
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) mv_nrm: vec3<f32>,"), 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<Light, {}>,"), i, GX::MaxLights);
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) cc{}: vec4<f32>,"), 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) {
if (UsePerPixelLighting) {
lightDiffFn = "dot(ldir, in.mv_nrm)";
} else {
lightDiffFn = "dot(ldir, mv_nrm)";
}
} else if (diffFn == GX::DF_CLAMP) {
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<f32>(0.0), vec4<f32>(1.0));
{6} = {4} * clamp(lighting, vec4<f32>(0.0), vec4<f32>(1.0));
}})"""),
i, GX::MaxLights, lightAttnFn, lightDiffFn, matSrc, ambSrc);
i, GX::MaxLights, lightAttnFn, lightDiffFn, matSrc, ambSrc, outVar, posVar);
if (UsePerPixelLighting) {
fragmentFnPre += fmt::format(FMT_STRING("\n var rast{}: vec4<f32>;"), i);
fragmentFnPre += lightFunc;
} else {
vtxOutAttrs += fmt::format(FMT_STRING("\n @location({}) cc{}: vec4<f32>,"), 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<f32>,"), vtxOutIdx++, i);
vtxXfrAttrs += fmt::format(FMT_STRING("\n out.cc{} = {};"), i, vtx_attr(config, GX::Attr(GX::VA_CLR0 + i)));