diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index a39ee0ed0..baf9ced25 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -294,11 +294,11 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct ctx.AddCapability(spv::Capability::Geometry); } if (info.stage == Stage::Fragment) { - if (profile.supports_fragment_shader_barycentric) { + if (profile.supports_amd_shader_explicit_vertex_parameter) { + ctx.AddExtension("SPV_AMD_shader_explicit_vertex_parameter"); + } else if (profile.supports_fragment_shader_barycentric) { ctx.AddExtension("SPV_KHR_fragment_shader_barycentric"); ctx.AddCapability(spv::Capability::FragmentBarycentricKHR); - } else if (profile.supports_amd_shader_explicit_vertex_parameter) { - ctx.AddExtension("SPV_AMD_shader_explicit_vertex_parameter"); } if (info.loads.GetAny(IR::Attribute::BaryCoordSmoothSample) || info.loads.GetAny(IR::Attribute::BaryCoordNoPerspSample)) { 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 4342ea927..ead2a2825 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 @@ -68,7 +68,7 @@ Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { } if (IR::IsMrt(attr)) { const u32 index{u32(attr) - u32(IR::Attribute::RenderTarget0)}; - const auto& info{ctx.output_params.at(index)}; + const auto& info{ctx.frag_outputs.at(index)}; if (info.num_components == 1) { return info.id; } else { @@ -100,7 +100,7 @@ std::pair OutputAttrComponentType(EmitContext& ctx, IR::Attribute attr } if (IR::IsMrt(attr)) { const u32 index{u32(attr) - u32(IR::Attribute::RenderTarget0)}; - const auto& info{ctx.output_params.at(index)}; + const auto& info{ctx.frag_outputs.at(index)}; return {info.component_type, info.is_integer}; } switch (attr) { @@ -163,7 +163,7 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { if (IR::IsParam(attr)) { const u32 param_index{u32(attr) - u32(IR::Attribute::Param0)}; const auto& param{ctx.input_params.at(param_index)}; - const Id result = [&] { + const Id value = [&] { if (param.is_array) { ASSERT(param.num_components > 1); if (param.is_loaded) { @@ -185,7 +185,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { } } }(); - return param.is_integer ? ctx.OpBitcast(ctx.F32[1], result) : result; + return param.is_integer ? ctx.OpBitcast(ctx.F32[1], value) : value; + } + if (IR::IsBarycentricCoord(attr) && ctx.profile.supports_fragment_shader_barycentric) { + ++comp; } switch (attr) { case IR::Attribute::Position0: @@ -203,21 +206,12 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) { return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.ConstU32(1U))); case IR::Attribute::BaryCoordSmooth: - if (ctx.profile.supports_fragment_shader_barycentric) { - ++comp; - } return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.bary_coord_smooth, ctx.ConstU32(comp))); case IR::Attribute::BaryCoordSmoothSample: - if (ctx.profile.supports_fragment_shader_barycentric) { - ++comp; - } return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.bary_coord_smooth_sample, ctx.ConstU32(comp))); case IR::Attribute::BaryCoordNoPersp: - if (ctx.profile.supports_fragment_shader_barycentric) { - ++comp; - } return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.bary_coord_nopersp, ctx.ConstU32(comp))); default: diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 19fb39af7..f373808d9 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -305,7 +305,7 @@ void EmitContext::DefineAmdPerVertexAttribs() { } for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { const auto& input = runtime_info.fs_info.inputs[i]; - if (input.IsDefault() || info.fs_interp_qualifiers[i].primary != Qualifier::PerVertex) { + if (input.IsDefault() || info.fs_interpolation[i].primary != Qualifier::PerVertex) { continue; } auto& param = input_params[i]; @@ -316,6 +316,7 @@ void EmitContext::DefineAmdPerVertexAttribs() { OpInterpolateAtVertexAMD(F32[param.num_components], pointer, ConstU32(1U)); param.id_array[2] = OpInterpolateAtVertexAMD(F32[param.num_components], pointer, ConstU32(2U)); + param.is_loaded = true; } } @@ -378,28 +379,34 @@ void EmitContext::DefineInputs() { if (profile.supports_amd_shader_explicit_vertex_parameter) { bary_coord_smooth = DefineVariable(F32[2], spv::BuiltIn::BaryCoordSmoothAMD, spv::StorageClass::Input); - } else { + } else if (profile.supports_fragment_shader_barycentric) { bary_coord_smooth = DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input); + } else { + bary_coord_smooth = ConstF32(0.f, 0.f); } } if (info.loads.GetAny(IR::Attribute::BaryCoordSmoothSample)) { if (profile.supports_amd_shader_explicit_vertex_parameter) { - bary_coord_smooth = DefineVariable(F32[2], spv::BuiltIn::BaryCoordSmoothSampleAMD, - spv::StorageClass::Input); - } else { + bary_coord_smooth_sample = DefineVariable( + F32[2], spv::BuiltIn::BaryCoordSmoothSampleAMD, spv::StorageClass::Input); + } else if (profile.supports_fragment_shader_barycentric) { bary_coord_smooth_sample = DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input); // Decorate(bary_coord_smooth_sample, spv::Decoration::Sample); + } else { + bary_coord_smooth_sample = ConstF32(0.f, 0.f); } } if (info.loads.GetAny(IR::Attribute::BaryCoordNoPersp)) { if (profile.supports_amd_shader_explicit_vertex_parameter) { bary_coord_nopersp = DefineVariable(F32[2], spv::BuiltIn::BaryCoordNoPerspAMD, spv::StorageClass::Input); - } else { + } else if (profile.supports_fragment_shader_barycentric) { bary_coord_nopersp = DefineVariable(F32[3], spv::BuiltIn::BaryCoordNoPerspKHR, spv::StorageClass::Input); + } else { + bary_coord_nopersp = ConstF32(0.f, 0.f); } } for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { @@ -409,36 +416,30 @@ void EmitContext::DefineInputs() { } const IR::Attribute param = IR::Attribute::Param0 + i; const u32 num_components = info.loads.NumComponents(param); - const auto [primary, auxiliary] = info.fs_interp_qualifiers[i]; - const bool is_array = primary == Qualifier::PerVertex; - const Id type{F32[num_components]}; - Id attr_id{}; - // Declare primary qualifier (pervertex, smooth, noperspective, flat) - if (is_array) { - if (profile.supports_fragment_shader_barycentric) { - attr_id = DefineInput(TypeArray(type, ConstU32(3U)), input.param_index); - Decorate(attr_id, spv::Decoration::PerVertexKHR); - } else { - attr_id = DefineInput(type, input.param_index); - Decorate(attr_id, spv::Decoration::ExplicitInterpAMD); - } - Name(attr_id, fmt::format("fs_in_attr{}_p", i)); - } else { - attr_id = DefineInput(type, input.param_index); - Name(attr_id, fmt::format("fs_in_attr{}", i)); - if (primary != Qualifier::Smooth) { - Decorate(attr_id, primary == Qualifier::Flat ? spv::Decoration::Flat - : spv::Decoration::NoPerspective); + const auto [primary, auxiliary] = info.fs_interpolation[i]; + const Id type = F32[num_components]; + const Id attr_id = [&] { + if (primary == Qualifier::PerVertex && + profile.supports_fragment_shader_barycentric) { + return Name(DefineInput(TypeArray(type, ConstU32(3U)), input.param_index), + fmt::format("fs_in_attr{}_p", i)); } + return Name(DefineInput(type, input.param_index), fmt::format("fs_in_attr{}", i)); + }(); + if (primary == Qualifier::PerVertex) { + Decorate(attr_id, profile.supports_amd_shader_explicit_vertex_parameter + ? spv::Decoration::ExplicitInterpAMD + : spv::Decoration::PerVertexKHR); + } else if (primary != Qualifier::Smooth) { + Decorate(attr_id, primary == Qualifier::Flat ? spv::Decoration::Flat + : spv::Decoration::NoPerspective); } - // Declare auxiliary qualifier if present (centroid, sample) if (auxiliary != Qualifier::None) { Decorate(attr_id, auxiliary == Qualifier::Centroid ? spv::Decoration::Centroid : spv::Decoration::Sample); } - input_params[i] = GetAttributeInfo( - AmdGpu::NumberFormat::Float, attr_id, num_components, false, - is_array && profile.supports_amd_shader_explicit_vertex_parameter, is_array); + input_params[i] = GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, + false, false, primary == Qualifier::PerVertex); } break; case LogicalStage::Compute: @@ -650,7 +651,7 @@ void EmitContext::DefineOutputs() { id = DefineOutput(type, i); } Name(id, fmt::format("frag_color{}", i)); - output_params[i] = GetAttributeInfo(num_format, id, num_components, true); + 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, diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index bdd5fbf43..f57dbebd8 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -371,6 +371,7 @@ public: Id output_attr_array; std::array input_params{}; std::array output_params{}; + std::array frag_outputs{}; Id uf11_to_f32{}; Id f32_to_uf11{}; diff --git a/src/shader_recompiler/frontend/translate/vector_interpolation.cpp b/src/shader_recompiler/frontend/translate/vector_interpolation.cpp index 32ade6e9a..c32e80815 100644 --- a/src/shader_recompiler/frontend/translate/vector_interpolation.cpp +++ b/src/shader_recompiler/frontend/translate/vector_interpolation.cpp @@ -60,7 +60,7 @@ void Translator::V_INTERP_P2_F32(const GcnInst& inst) { const u32 attr_index = inst.control.vintrp.attr; const IR::Attribute attrib = IR::Attribute::Param0 + attr_index; const auto& attr = runtime_info.fs_info.inputs[attr_index]; - auto& interp = info.fs_interp_qualifiers[attr_index]; + auto& interp = info.fs_interpolation[attr_index]; ASSERT(!attr.IsDefault() && !attr.is_flat); if (!profile.needs_manual_interpolation) { interp = GetInterpolation(vgpr_to_interp[inst.src[0].code]); @@ -80,10 +80,11 @@ void Translator::V_INTERP_MOV_F32(const GcnInst& inst) { const u32 attr_index = inst.control.vintrp.attr; const IR::Attribute attrib = IR::Attribute::Param0 + attr_index; const auto& attr = runtime_info.fs_info.inputs[attr_index]; - auto& interp = info.fs_interp_qualifiers[attr_index]; + auto& interp = info.fs_interpolation[attr_index]; ASSERT(attr.is_flat); if (profile.supports_amd_shader_explicit_vertex_parameter || - profile.supports_fragment_shader_barycentric) { + (profile.supports_fragment_shader_barycentric && + !profile.has_incomplete_fragment_shader_barycentric)) { // VSRC 0=P10, 1=P20, 2=P0 interp.primary = Qualifier::PerVertex; SetDst(inst.dst[0], diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 7c833bfb7..bb5c88584 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -209,7 +209,7 @@ struct Info { Qualifier primary; Qualifier auxiliary; }; - std::array fs_interp_qualifiers{}; + std::array fs_interpolation{}; IR::ScalarReg tess_consts_ptr_base = IR::ScalarReg::Max; s32 tess_consts_dword_offset = -1; diff --git a/src/shader_recompiler/ir/attribute.h b/src/shader_recompiler/ir/attribute.h index 00559aaa8..00ec6c4b3 100644 --- a/src/shader_recompiler/ir/attribute.h +++ b/src/shader_recompiler/ir/attribute.h @@ -109,12 +109,7 @@ constexpr bool IsMrt(Attribute attribute) noexcept { return attribute >= Attribute::RenderTarget0 && attribute <= Attribute::RenderTarget7; } -constexpr bool IsLinear(Attribute attribute) noexcept { - return attribute >= Attribute::BaryCoordNoPersp && - attribute <= Attribute::BaryCoordNoPerspSample; -} - -constexpr bool IsPerspective(Attribute attribute) noexcept { +constexpr bool IsBarycentricCoord(Attribute attribute) noexcept { return attribute >= Attribute::BaryCoordSmooth && attribute <= Attribute::BaryCoordSmoothSample; } diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index cd6b88d49..d57e18ff0 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -29,6 +29,7 @@ struct Profile { bool supports_workgroup_explicit_memory_layout{}; bool supports_amd_shader_explicit_vertex_parameter{}; bool supports_fragment_shader_barycentric{}; + bool has_incomplete_fragment_shader_barycentric{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; bool needs_manual_interpolation{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index e09a5e457..5a4f44dcd 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -223,6 +223,9 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, .supports_amd_shader_explicit_vertex_parameter = instance_.IsAmdShaderExplicitVertexParameterSupported(), .supports_fragment_shader_barycentric = instance_.IsFragmentShaderBarycentricSupported(), + .has_incomplete_fragment_shader_barycentric = + instance_.IsFragmentShaderBarycentricSupported() && + instance.GetDriverID() == vk::DriverId::eMoltenvk, .needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() && instance.GetDriverID() == vk::DriverId::eNvidiaProprietary, .needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary ||