video_core/amdgpu: heuristic for shader binary info

Games can strip the first shader instruction (meant for debugging) which we rely on for obtaining shader information (e.g. LittleBigPlanet 3). For this reason, we start a search through the code start until we arrive at the shader binary info.
This commit is contained in:
Daniel R. 2024-11-21 19:24:13 +01:00
parent 2a0629477b
commit e968b1c23f
No known key found for this signature in database
GPG key ID: B8ADC8F57BA18DBA
3 changed files with 37 additions and 13 deletions

View file

@ -32,7 +32,9 @@ IR::Program TranslateProgram(std::span<const u32> code, Pools& pools, Info& info
const RuntimeInfo& runtime_info, const Profile& profile) {
// Ensure first instruction is expected.
constexpr u32 token_mov_vcchi = 0xBEEB03FF;
ASSERT_MSG(code[0] == token_mov_vcchi, "First instruction is not s_mov_b32 vcc_hi, #imm");
if (code[0] != token_mov_vcchi) {
LOG_WARNING(Render_Recompiler, "First instruction is not s_mov_b32 vcc_hi, #imm");
}
Gcn::GcnCodeSlice slice(code.data(), code.data() + code.size());
Gcn::GcnDecodeContext decoder;

View file

@ -88,6 +88,32 @@ struct Liverpool {
}
};
static const BinaryInfo& SearchBinaryInfo(const u32* code, size_t search_limit = 0x1000) {
constexpr u32 token_mov_vcchi = 0xBEEB03FF;
if (code[0] == token_mov_vcchi) {
const auto* info = std::bit_cast<const BinaryInfo*>(code + (code[1] + 1) * 2);
if (info->Valid()) {
return *info;
}
}
// First instruction is not s_mov_b32 vcc_hi, #imm,
// which means we cannot get the binary info via said instruction.
// The easiest solution is to iterate through each dword and break
// on the first instance of the binary info.
constexpr size_t signature_size = sizeof(BinaryInfo::signature_ref) / sizeof(u8);
const u32* end = code + search_limit;
for (const u32* it = code; it < end; ++it) {
if (const BinaryInfo* info = std::bit_cast<const BinaryInfo*>(it); info->Valid()) {
return *info;
}
}
UNREACHABLE_MSG("Shader binary info not found.");
}
struct ShaderProgram {
u32 address_lo;
BitField<0, 8, u32> address_hi;
@ -113,8 +139,7 @@ struct Liverpool {
std::span<const u32> Code() const {
const u32* code = Address<u32*>();
BinaryInfo bininfo;
std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo));
const BinaryInfo& bininfo = SearchBinaryInfo(code);
const u32 num_dwords = bininfo.length / sizeof(u32);
return std::span{code, num_dwords};
}
@ -166,27 +191,24 @@ struct Liverpool {
std::span<const u32> Code() const {
const u32* code = Address<u32*>();
BinaryInfo bininfo;
std::memcpy(&bininfo, code + (code[1] + 1) * 2, sizeof(bininfo));
const BinaryInfo& bininfo = SearchBinaryInfo(code);
const u32 num_dwords = bininfo.length / sizeof(u32);
return std::span{code, num_dwords};
}
};
template <typename Shader>
static constexpr auto* GetBinaryInfo(const Shader& sh) {
static constexpr const BinaryInfo& GetBinaryInfo(const Shader& sh) {
const auto* code = sh.template Address<u32*>();
const auto* bininfo = std::bit_cast<const BinaryInfo*>(code + (code[1] + 1) * 2);
// ASSERT_MSG(bininfo->Valid(), "Invalid shader binary header");
return bininfo;
return SearchBinaryInfo(code);
}
static constexpr Shader::ShaderParams GetParams(const auto& sh) {
auto* bininfo = GetBinaryInfo(sh);
auto& bininfo = GetBinaryInfo(sh);
return {
.user_data = sh.user_data,
.code = sh.Code(),
.hash = bininfo->shader_hash,
.hash = bininfo.shader_hash,
};
}

View file

@ -292,8 +292,8 @@ bool PipelineCache::RefreshGraphicsKey() {
return false;
}
const auto* bininfo = Liverpool::GetBinaryInfo(*pgm);
if (!bininfo->Valid()) {
const auto& bininfo = Liverpool::GetBinaryInfo(*pgm);
if (!bininfo.Valid()) {
LOG_WARNING(Render_Vulkan, "Invalid binary info structure!");
key.stage_hashes[stage_out_idx] = 0;
infos[stage_out_idx] = nullptr;