diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 68bfcc0d0..bd10fd3df 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -634,7 +634,8 @@ void EmitContext::DefineOutputs() { } break; } - case LogicalStage::Fragment: + case LogicalStage::Fragment: { + u32 num_render_targets = 0; for (u32 i = 0; i < IR::NumRenderTargets; i++) { const IR::Attribute mrt{IR::Attribute::RenderTarget0 + i}; if (!info.stores.GetAny(mrt)) { @@ -643,11 +644,21 @@ void EmitContext::DefineOutputs() { const u32 num_components = info.stores.NumComponents(mrt); const AmdGpu::NumberFormat num_format{runtime_info.fs_info.color_buffers[i].num_format}; const Id type{GetAttributeType(*this, num_format)[num_components]}; - const Id id{DefineOutput(type, i)}; + Id id; + if (runtime_info.fs_info.dual_source_blending) { + id = DefineOutput(type, 0); + Decorate(id, spv::Decoration::Index, i); + } else { + id = DefineOutput(type, i); + } Name(id, fmt::format("frag_color{}", i)); frag_outputs[i] = GetAttributeInfo(num_format, id, num_components, true); + ++num_render_targets; } + ASSERT_MSG(!runtime_info.fs_info.dual_source_blending || num_render_targets == 2, + "Dual source blending enabled, there must be exactly two MRT exports"); break; + } case LogicalStage::Geometry: { output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index 0abef2e81..8a99f38a9 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -26,8 +26,11 @@ void Translator::ExportMrtValue(IR::Attribute attribute, u32 comp, const IR::F32 } void Translator::ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value) { - const u32 color_buffer_idx = + u32 color_buffer_idx = static_cast(attribute) - static_cast(IR::Attribute::RenderTarget0); + if (runtime_info.fs_info.dual_source_blending && attribute == IR::Attribute::RenderTarget1) { + color_buffer_idx = 0; + } const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx]; AmdGpu::NumberFormat num_format; @@ -68,8 +71,11 @@ void Translator::ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR: } void Translator::ExportMrtUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value) { - const u32 color_buffer_idx = + u32 color_buffer_idx = static_cast(attribute) - static_cast(IR::Attribute::RenderTarget0); + if (runtime_info.fs_info.dual_source_blending && attribute == IR::Attribute::RenderTarget1) { + color_buffer_idx = 0; + } const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx]; const auto swizzled_comp = SwizzleMrtComponent(color_buffer, comp); diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index b8ed42f5b..53d2d5303 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -196,11 +196,13 @@ struct FragmentRuntimeInfo { u32 num_inputs; std::array inputs; std::array color_buffers; + bool dual_source_blending; bool operator==(const FragmentRuntimeInfo& other) const noexcept { return std::ranges::equal(color_buffers, other.color_buffers) && en_flags.raw == other.en_flags.raw && addr_flags.raw == other.addr_flags.raw && num_inputs == other.num_inputs && + dual_source_blending == other.dual_source_blending && std::ranges::equal(inputs.begin(), inputs.begin() + num_inputs, other.inputs.begin(), other.inputs.begin() + num_inputs); } diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index a6ae0c304..5972296c0 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -214,6 +214,19 @@ vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor) { } } +bool IsDualSourceBlendFactor(Liverpool::BlendControl::BlendFactor factor) { + using BlendFactor = Liverpool::BlendControl::BlendFactor; + switch (factor) { + case BlendFactor::Src1Color: + case BlendFactor::Src1Alpha: + case BlendFactor::InvSrc1Color: + case BlendFactor::InvSrc1Alpha: + return true; + default: + return false; + } +} + vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func) { using BlendFunc = Liverpool::BlendControl::BlendFunc; switch (func) { diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index fca0a8378..61fd4a8c1 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -30,6 +30,8 @@ vk::FrontFace FrontFace(Liverpool::FrontFace mode); vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor); +bool IsDualSourceBlendFactor(Liverpool::BlendControl::BlendFactor factor); + vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func); vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index d7ad47a3c..b72f77e55 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -158,6 +158,15 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS info.fs_info.addr_flags = regs.ps_input_addr; const auto& ps_inputs = regs.ps_inputs; info.fs_info.num_inputs = regs.num_interp; + const auto& cb0_blend = regs.blend_control[0]; + info.fs_info.dual_source_blending = + LiverpoolToVK::IsDualSourceBlendFactor(cb0_blend.color_dst_factor) || + LiverpoolToVK::IsDualSourceBlendFactor(cb0_blend.color_src_factor); + if (cb0_blend.separate_alpha_blend) { + info.fs_info.dual_source_blending |= + LiverpoolToVK::IsDualSourceBlendFactor(cb0_blend.alpha_dst_factor) || + LiverpoolToVK::IsDualSourceBlendFactor(cb0_blend.alpha_src_factor); + } for (u32 i = 0; i < regs.num_interp; i++) { info.fs_info.inputs[i] = { .param_index = u8(ps_inputs[i].input_offset.Value()),