diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index db11def7b2..beab29ec8a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -491,6 +491,9 @@ void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) { } void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) { + if (!ctx.runtime_info.active_color_outputs[index]) { + return; + } const Id component_id{ctx.Const(component)}; const AttributeType type{ctx.runtime_info.color_output_types[index]}; if (type == AttributeType::Float) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 68b9cad859..fb66a7962e 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1688,8 +1688,10 @@ void EmitContext::DefineOutputs(const IR::Program& program) { case Stage::Fragment: for (u32 index = 0; index < 8; ++index) { const bool need_dual_source = runtime_info.dual_source_blend && index <= 1; - if (!need_dual_source && !info.stores_frag_color[index] && - !profile.need_declared_frag_colors) { + const bool should_declare = runtime_info.active_color_outputs[index] && + (info.stores_frag_color[index] || + profile.need_declared_frag_colors); + if (!need_dual_source && !should_declare) { continue; } const Id type{GetAttributeType(*this, runtime_info.color_output_types[index])}; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index be10a9bb08..b8888504bb 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -111,6 +111,9 @@ struct RuntimeInfo { /// Output types for each color attachment std::array color_output_types{}; + /// Fragment color outputs that are active for the current pipeline. + std::array active_color_outputs{true, true, true, true, true, true, true, true}; + /// Dual source blending bool dual_source_blend{}; }; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 8cf02a959c..e838dc0b43 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -249,6 +249,17 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program dst_a == F::Source1Alpha_GL || dst_a == F::OneMinusSource1Alpha_GL; } + for (size_t i = 0; i < info.active_color_outputs.size(); ++i) { + const auto format = static_cast(key.state.color_formats[i]); + info.active_color_outputs[i] = format != Tegra::RenderTargetFormat::NONE; + } + if (info.dual_source_blend && info.active_color_outputs[0]) { + info.active_color_outputs[1] = true; + } + if (info.alpha_test_func && *info.alpha_test_func != Shader::CompareFunction::Always) { + info.active_color_outputs[0] = true; + } + if (device.IsMoltenVK()) { for (size_t i = 0; i < 8; ++i) { const auto format = static_cast(key.state.color_formats[i]);