diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 181d2e3a7..208cc95b7 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -41,44 +41,33 @@ namespace Libs::Graphics { -struct ShaderBinaryInfo { - uint8_t signature[7]; - uint8_t version; - uint32_t pssl_or_cg : 1; - uint32_t cached : 1; - uint32_t type : 4; - uint32_t source_type : 2; - uint32_t length : 24; - uint8_t chunk_usage_base_offset_dw; - uint8_t num_input_usage_slots; - uint8_t is_srt : 1; - uint8_t is_srt_used_info_valid : 1; - uint8_t is_extended_usage_info : 1; - uint8_t reserved2 : 5; - uint8_t reserved3; - uint32_t hash0; - uint32_t hash1; - uint32_t crc32; +struct ShaderMapEntry { + ShaderMappedData data; + uint64_t hash; }; -static std::unique_ptr> g_shader_map; +static std::unique_ptr> g_shader_map; static std::mutex g_shader_map_mutex; void ShaderInit() { EXIT_IF(g_shader_map != nullptr); - g_shader_map = std::make_unique>(); + g_shader_map = std::make_unique>(); } void ShaderMapUserData(uint64_t addr, const ShaderMappedData& data) { EXIT_IF(g_shader_map == nullptr); + if (addr == 0 || data.code_size_bytes == 0 || data.code_size_bytes % sizeof(uint32_t) != 0) { + EXIT("ShaderMapUserData shader=0x%016" PRIx64 " has invalid AGC shader_size=0x%08" PRIx32 "\n", + addr, data.code_size_bytes); + } + const auto hash = XXH3_64bits(reinterpret_cast(addr), data.code_size_bytes); std::scoped_lock lock(g_shader_map_mutex); - - (*g_shader_map)[addr] = data; + (*g_shader_map)[addr] = {data, hash}; } -static ShaderMappedData ShaderGetMappedData(uint64_t addr, const char* label) { +static ShaderMapEntry ShaderGetMappedData(uint64_t addr, const char* label) { EXIT_IF(g_shader_map == nullptr); std::scoped_lock lock(g_shader_map_mutex); @@ -90,37 +79,15 @@ static ShaderMappedData ShaderGetMappedData(uint64_t addr, const char* label) { EXIT("%s shader=0x%016" PRIx64 " is missing from ShaderMap\n", label, addr); } -static const ShaderBinaryInfo* GetBinaryInfo(const uint32_t* code) { - EXIT_IF(code == nullptr); - - if (code[0] == 0xBEEB03FF) { - return reinterpret_cast(code + - static_cast(code[1] + 1) * 2); - } - - return nullptr; -} - -static uint64_t GetDeclaredShaderHash(uint64_t shader_addr) { - const auto* header = GetBinaryInfo(reinterpret_cast(shader_addr)); - return header != nullptr ? (static_cast(header->hash1) << 32u) | header->hash0 : 0; -} - -static ShaderParams GetShaderParams(uint64_t shader_addr, const char* label, uint64_t declared_hash, +static ShaderParams GetShaderParams(uint64_t shader_addr, uint64_t hash, std::span user_data, const ShaderMappedData& data, uint32_t user_data_base = 0) { - if (data.code_size_bytes == 0 || data.code_size_bytes % sizeof(uint32_t) != 0) { - EXIT("%s hash=0x%016" PRIx64 " shader=0x%016" PRIx64 - " has invalid AGC shader_size=0x%08" PRIx32 "\n", - label, declared_hash, shader_addr, data.code_size_bytes); - } const auto code_words = data.code_size_bytes / sizeof(uint32_t); const auto code = std::span {reinterpret_cast(shader_addr), code_words}; ShaderParams params { .code = code, .user_data_count = static_cast(user_data.size()) + user_data_base, - .hash = declared_hash != 0 ? declared_hash - : XXH3_64bits(code.data(), code.size_bytes()), + .hash = hash, }; EXIT_IF(user_data.size() > HW::UserSgprInfo::SGPRS_MAX || params.user_data_count > params.user_data.size()); @@ -726,11 +693,10 @@ void BuildStageStaticKey(const ShaderComputeInputInfo& info, std::vector(regs.gs_user_sgpr.value, regs.gs_regs.rsrc2.user_sgpr), data, merged ? 8u : 0u); if (!merged) { @@ -755,10 +721,9 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& mesh.scratch_size_dwords = data.scratch_size_dwords; if (data.type == Prospero::ShaderBinaryType::kGsFront) { EXIT_IF(regs.gs_regs.data_addr == 0); - const auto back = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():"); + const auto [back, back_hash] = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():"); const auto back_params = - GetShaderParams(regs.gs_regs.data_addr, "ShaderRecompiler GS", - GetDeclaredShaderHash(regs.gs_regs.data_addr), {}, back); + GetShaderParams(regs.gs_regs.data_addr, back_hash, {}, back); params.back_code = back_params.code; params.user_data[0] = static_cast(regs.gs_regs.user_data_addr); params.user_data[1] = static_cast(regs.gs_regs.user_data_addr >> 32u); @@ -797,9 +762,9 @@ std::array PrepareTessellationPrograms(const HW::VertexShaderInfo& regs, const HW::Context& context, std::array& input_info) { const auto& sh = context.GetShaderRegisters(); - const auto local = ShaderGetMappedData(regs.ls_regs.data_addr, "ShaderGetInputInfoLS():"); - const auto control = ShaderGetMappedData(regs.hs_regs.data_addr, "ShaderGetInputInfoHS():"); - const auto evaluation = ShaderGetMappedData(regs.es_regs.data_addr, "ShaderGetInputInfoTES():"); + const auto [local, local_hash] = ShaderGetMappedData(regs.ls_regs.data_addr, "ShaderGetInputInfoLS():"); + const auto [control, control_hash] = ShaderGetMappedData(regs.hs_regs.data_addr, "ShaderGetInputInfoHS():"); + const auto [evaluation, evaluation_hash] = ShaderGetMappedData(regs.es_regs.data_addr, "ShaderGetInputInfoTES():"); EXIT_NOT_IMPLEMENTED(local.type != Prospero::ShaderBinaryType::kHsFront || control.type != Prospero::ShaderBinaryType::kHsBack || evaluation.type != Prospero::ShaderBinaryType::kGs); @@ -810,12 +775,9 @@ PrepareTessellationPrograms(const HW::VertexShaderInfo& regs, const HW::Context& const auto local_users = std::span(regs.hs_user_sgpr.value, regs.hs_regs.rsrc2.user_sgpr); const auto evaluation_users = std::span(regs.gs_user_sgpr.value, regs.gs_regs.rsrc2.user_sgpr); std::array params { - GetShaderParams(regs.ls_regs.data_addr, "ShaderRecompiler LS", - GetDeclaredShaderHash(regs.ls_regs.data_addr), local_users, local), - GetShaderParams(regs.hs_regs.data_addr, "ShaderRecompiler HS", - GetDeclaredShaderHash(regs.hs_regs.data_addr), local_users, control, 8u), - GetShaderParams(regs.es_regs.data_addr, "ShaderRecompiler TES", - GetDeclaredShaderHash(regs.es_regs.data_addr), evaluation_users, + GetShaderParams(regs.ls_regs.data_addr, local_hash, local_users, local), + GetShaderParams(regs.hs_regs.data_addr, control_hash, local_users, control, 8u), + GetShaderParams(regs.es_regs.data_addr, evaluation_hash, evaluation_users, evaluation), }; // The fused HS back half receives its separate user-data address in s0:s1. @@ -855,19 +817,19 @@ ShaderParams PrepareProgram( const HW::PixelShaderInfo& regs, const HW::ShaderRegisters& sh, std::span target_export_mapping, ShaderPixelInputInfo& ps_info) { - const auto data = ShaderGetMappedData(regs.ps_regs.data_addr, "ShaderGetInputInfoPS():"); + const auto [data, hash] = ShaderGetMappedData(regs.ps_regs.data_addr, "ShaderGetInputInfoPS():"); ShaderGetStaticInputInfoPS(regs, sh, target_export_mapping, data, ps_info); return GetShaderParams( - regs.ps_regs.data_addr, "ShaderRecompiler PS", GetDeclaredShaderHash(regs.ps_regs.data_addr), + regs.ps_regs.data_addr, hash, std::span(regs.ps_user_sgpr.value, regs.ps_regs.rsrc2.user_sgpr), data); } ShaderParams PrepareProgram(const HW::ComputeShaderInfo& regs, const HW::ShaderRegisters& sh, ShaderComputeInputInfo& info) { - const auto data = ShaderGetMappedData(regs.cs_regs.data_addr, "ShaderGetInputInfoCS():"); + const auto [data, hash] = ShaderGetMappedData(regs.cs_regs.data_addr, "ShaderGetInputInfoCS():"); ShaderGetStaticInputInfoCS(regs, sh, data, info); return GetShaderParams( - regs.cs_regs.data_addr, "ShaderRecompiler CS", GetDeclaredShaderHash(regs.cs_regs.data_addr), + regs.cs_regs.data_addr, hash, std::span(regs.cs_user_sgpr.value, regs.cs_regs.user_sgpr), data); } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 032c70a0f..c3e76cccf 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -13503,11 +13503,11 @@ void TestPixelProgramCacheBindingIdentity() { Check(MakeStageStaticKey(no_depth_export) != MakeStageStaticKey(depth_export), "pixel shader identity omitted depth-export semantics"); - auto check_program_identity = [&](const uint32_t *shader) { + auto check_program_identity = [&](std::span shader) { HW::PixelShaderInfo regs{}; - regs.ps_regs.data_addr = reinterpret_cast(shader); + regs.ps_regs.data_addr = reinterpret_cast(shader.data()); ShaderMappedData mapped{}; - mapped.code_size_bytes = sizeof(uint32_t); + mapped.code_size_bytes = static_cast(shader.size_bytes()); ShaderMapUserData(regs.ps_regs.data_addr, mapped); const std::array identity_mappings{}; @@ -13522,7 +13522,8 @@ void TestPixelProgramCacheBindingIdentity() { second_info); CompilePixelRuntime(second_params, second_info); Check( - first_params.hash == second_params.hash && + first_params.hash == XXH3_64bits(shader.data(), shader.size_bytes()) && + first_params.hash == second_params.hash && first_key == MakeStageStaticKey(second_info) && first_info.stage.program != nullptr && second_info.stage.program != nullptr, @@ -13535,6 +13536,22 @@ void TestPixelProgramCacheBindingIdentity() { Check(request_01 == request_10, "relocated identical pixel programs did not share their source identity"); + // Identical metadata after the scalar-literal prefix must not mask changes + // to the executable code when computing program identity. + std::array shader{}; + shader[0] = 0xbeeb03ffu; + shader[1] = 0x5fu; + shader[2] = EncodeSMovB32(0, 129); + shader[3] = EncodeSopp(0x01); + shader[192] = 0x65726162u; + shader[193] = 0x746f6f66u; + shader[196] = 0x12345678u; + shader[197] = 0x9abcdef0u; + const auto first = check_program_identity(shader); + shader[2] = EncodeSMovB32(0, 130); + Check(check_program_identity(shader).first != first.first, + "re-registering changed code at the same address reused its old identity"); + } void TestGraphicsPushConstantPlacement() {