diff --git a/src/video_core/shader/generator/glsl_fs_shader_gen.cpp b/src/video_core/shader/generator/glsl_fs_shader_gen.cpp index ab5f36420..158aac7b8 100644 --- a/src/video_core/shader/generator/glsl_fs_shader_gen.cpp +++ b/src/video_core/shader/generator/glsl_fs_shader_gen.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include #include "common/common_types.h" #include "video_core/pica/regs_texturing.h" #include "video_core/shader/generator/glsl_fs_shader_gen.h" @@ -14,6 +16,8 @@ using ProcTexCombiner = TexturingRegs::ProcTexCombiner; using ProcTexFilter = TexturingRegs::ProcTexFilter; using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; +static bool IsPassThroughTevStage(const Pica::TexturingRegs::TevStageConfig& stage); + constexpr static std::size_t RESERVE_SIZE = 8 * 1024 * 1024; enum class Semantic : u32 { @@ -113,6 +117,8 @@ FragmentModule::FragmentModule(const FSConfig& config_, const Profile& profile_) FragmentModule::~FragmentModule() = default; +static int debug_frame_counter = 0; + std::string FragmentModule::Generate() { // We round the interpolated primary color to the nearest 1/255th // This maintains the PICA's 8 bits of precision @@ -149,9 +155,18 @@ vec4 secondary_fragment_color = vec4(0.0); "float alpha_results_3 = 0.0;\n"; // Write shader source to emulate PICA TEV stages + bool tev_stage_processed = false; for (u32 index = 0; index < config.texture.tev_stages.size(); index++) { + const TexturingRegs::TevStageConfig stage = config.texture.tev_stages[index]; + if (IsPassThroughTevStage(stage)) { + continue; + } + tev_stage_processed = true; WriteTevStage(index); } + if (!tev_stage_processed) { + out += "combiner_output = rounded_primary_color;\n"; + } // Append the alpha test condition WriteAlphaTestCondition(config.framebuffer.alpha_test_func); @@ -175,12 +190,35 @@ vec4 secondary_fragment_color = vec4(0.0); // Round the final fragment color to maintain the PICA's 8 bits of precision out += "combiner_output = byteround(combiner_output);\n"; WriteBlending(); - out += "color = combiner_output;\n"; + out += "color = combiner_output;\n return;\n"; } WriteLogicOp(); out += '}'; + + // Debug logging: only for the first 10 frames + if (debug_frame_counter < 10) { + std::ofstream log("azahar_tev_debug.txt", std::ios::app); + log << "Frame " << debug_frame_counter << ":\n"; + for (u32 index = 0; index < config.texture.tev_stages.size(); index++) { + const TexturingRegs::TevStageConfig& stage = config.texture.tev_stages[index]; + log << " TEV Stage " << index << ": "; + log << "color_op=" << static_cast(stage.color_op.Value()) << ", "; + log << "alpha_op=" << static_cast(stage.alpha_op.Value()) << ", "; + log << "color_source1=" << static_cast(stage.color_source1.Value()) << ", "; + log << "alpha_source1=" << static_cast(stage.alpha_source1.Value()) << ", "; + log << "color_modifier1=" << static_cast(stage.color_modifier1.Value()) << ", "; + log << "alpha_modifier1=" << static_cast(stage.alpha_modifier1.Value()) << ", "; + log << "GetColorMultiplier=" << stage.GetColorMultiplier() << ", "; + log << "GetAlphaMultiplier=" << stage.GetAlphaMultiplier() << std::endl; + } + log << std::setprecision(3) << std::fixed; + log << " combiner_output: (unknown at this point, see shader output)\n"; + log.close(); + debug_frame_counter++; + } + return out; } @@ -427,13 +465,24 @@ void FragmentModule::WriteAlphaTestCondition(FramebufferRegs::CompareFunc func) // Helper to detect passthrough TEV stages for optimization static bool IsPassThroughTevStage(const Pica::TexturingRegs::TevStageConfig& stage) { using TevStageConfig = Pica::TexturingRegs::TevStageConfig; + + // Never skip Dot3_RGBA stages + if (stage.color_op == TevStageConfig::Operation::Dot3_RGBA) { + return false; + } + + // Only consider it passthrough if it's a simple replace operation with no modifications return (stage.color_op == TevStageConfig::Operation::Replace && stage.alpha_op == TevStageConfig::Operation::Replace && stage.color_source1 == TevStageConfig::Source::Previous && stage.alpha_source1 == TevStageConfig::Source::Previous && stage.color_modifier1 == TevStageConfig::ColorModifier::SourceColor && stage.alpha_modifier1 == TevStageConfig::AlphaModifier::SourceAlpha && - stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1); + stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1 && + stage.color_source2 == TevStageConfig::Source::Previous && + stage.alpha_source2 == TevStageConfig::Source::Previous && + stage.color_source3 == TevStageConfig::Source::Previous && + stage.alpha_source3 == TevStageConfig::Source::Previous); } void FragmentModule::WriteTevStage(u32 index) { diff --git a/src/video_core/shader/generator/spv_fs_shader_gen.cpp b/src/video_core/shader/generator/spv_fs_shader_gen.cpp index 73539afb7..f35f04eb8 100644 --- a/src/video_core/shader/generator/spv_fs_shader_gen.cpp +++ b/src/video_core/shader/generator/spv_fs_shader_gen.cpp @@ -17,6 +17,9 @@ using TextureType = TexturingRegs::TextureConfig::TextureType; constexpr u32 SPIRV_VERSION_1_3 = 0x00010300; +// Forward declaration +static bool IsPassThroughTevStage(const TexturingRegs::TevStageConfig& stage); + FragmentModule::FragmentModule(const FSConfig& config_, const Profile& profile_) : Sirit::Module{SPIRV_VERSION_1_3}, config{config_}, profile{profile_}, use_fragment_shader_barycentric{profile.has_fragment_shader_barycentric && @@ -58,9 +61,18 @@ void FragmentModule::Generate() { combiner_output = ConstF32(0.f, 0.f, 0.f, 0.f); // Write shader bytecode to emulate PICA TEV stages + bool tev_stage_processed = false; for (u32 index = 0; index < config.texture.tev_stages.size(); ++index) { + const TexturingRegs::TevStageConfig stage = config.texture.tev_stages[index]; + if (IsPassThroughTevStage(stage)) { + continue; + } + tev_stage_processed = true; WriteTevStage(index); } + if (!tev_stage_processed) { + combiner_output = ConstF32(1.f, 0.f, 1.f, 1.f); // Debug color: magenta + } WriteAlphaTestCondition(config.framebuffer.alpha_test_func); @@ -320,10 +332,19 @@ void FragmentModule::WriteLighting() { Id shadow{ConstF32(1.f, 1.f, 1.f, 1.f)}; if (lighting.enable_shadow) { - shadow = OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[lighting.shadow_selector]); + const Id shadow_texture = OpFunctionCall(vec_ids.Get(4), sample_tex_unit_func[lighting.shadow_selector]); if (lighting.shadow_invert) { - shadow = OpFSub(vec_ids.Get(4), ConstF32(1.f, 1.f, 1.f, 1.f), shadow); + shadow = OpFSub(vec_ids.Get(4), ConstF32(1.f, 1.f, 1.f, 1.f), shadow_texture); + } else { + shadow = shadow_texture; } + // Debug: Output shadow value directly + OpStore(color_id, shadow); + OpReturn(); + OpFunctionEnd(); + return; + } else { + shadow = ConstF32(1.f, 1.f, 1.f, 1.f); } const auto lookup_lighting_lut_unsigned = [this](Id lut_index, Id pos) -> Id { @@ -1598,4 +1619,27 @@ std::vector GenerateFragmentShader(const FSConfig& config, const Profile& p return module.Assemble(); } +// Helper to detect passthrough TEV stages for optimization +static bool IsPassThroughTevStage(const TexturingRegs::TevStageConfig& stage) { + using TevStageConfig = TexturingRegs::TevStageConfig; + + // Never skip Dot3_RGBA stages + if (stage.color_op == TevStageConfig::Operation::Dot3_RGBA) { + return false; + } + + // Only consider it passthrough if it's a simple replace operation with no modifications + return (stage.color_op == TevStageConfig::Operation::Replace && + stage.alpha_op == TevStageConfig::Operation::Replace && + stage.color_source1 == TevStageConfig::Source::Previous && + stage.alpha_source1 == TevStageConfig::Source::Previous && + stage.color_modifier1 == TevStageConfig::ColorModifier::SourceColor && + stage.alpha_modifier1 == TevStageConfig::AlphaModifier::SourceAlpha && + stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1 && + stage.color_source2 == TevStageConfig::Source::Previous && + stage.alpha_source2 == TevStageConfig::Source::Previous && + stage.color_source3 == TevStageConfig::Source::Previous && + stage.alpha_source3 == TevStageConfig::Source::Previous); +} + } // namespace Pica::Shader::Generator::SPIRV