mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-22 11:56:58 +02:00
shader: cache registered code identities instead of parsing legacy footers
Co-authored-by: Mac <me@itsmemac.com> Co-authored-by: Senaxx <michelbeld@gmail.com>
This commit is contained in:
@@ -41,44 +41,33 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics {
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
struct ShaderBinaryInfo {
|
struct ShaderMapEntry {
|
||||||
uint8_t signature[7];
|
ShaderMappedData data;
|
||||||
uint8_t version;
|
uint64_t hash;
|
||||||
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unique_ptr<std::unordered_map<uint64_t, ShaderMappedData>> g_shader_map;
|
static std::unique_ptr<std::unordered_map<uint64_t, ShaderMapEntry>> g_shader_map;
|
||||||
static std::mutex g_shader_map_mutex;
|
static std::mutex g_shader_map_mutex;
|
||||||
|
|
||||||
void ShaderInit() {
|
void ShaderInit() {
|
||||||
EXIT_IF(g_shader_map != nullptr);
|
EXIT_IF(g_shader_map != nullptr);
|
||||||
|
|
||||||
g_shader_map = std::make_unique<std::unordered_map<uint64_t, ShaderMappedData>>();
|
g_shader_map = std::make_unique<std::unordered_map<uint64_t, ShaderMapEntry>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShaderMapUserData(uint64_t addr, const ShaderMappedData& data) {
|
void ShaderMapUserData(uint64_t addr, const ShaderMappedData& data) {
|
||||||
EXIT_IF(g_shader_map == nullptr);
|
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<const void*>(addr), data.code_size_bytes);
|
||||||
std::scoped_lock lock(g_shader_map_mutex);
|
std::scoped_lock lock(g_shader_map_mutex);
|
||||||
|
(*g_shader_map)[addr] = {data, hash};
|
||||||
(*g_shader_map)[addr] = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ShaderMappedData ShaderGetMappedData(uint64_t addr, const char* label) {
|
static ShaderMapEntry ShaderGetMappedData(uint64_t addr, const char* label) {
|
||||||
EXIT_IF(g_shader_map == nullptr);
|
EXIT_IF(g_shader_map == nullptr);
|
||||||
|
|
||||||
std::scoped_lock lock(g_shader_map_mutex);
|
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);
|
EXIT("%s shader=0x%016" PRIx64 " is missing from ShaderMap\n", label, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const ShaderBinaryInfo* GetBinaryInfo(const uint32_t* code) {
|
static ShaderParams GetShaderParams(uint64_t shader_addr, uint64_t hash,
|
||||||
EXIT_IF(code == nullptr);
|
|
||||||
|
|
||||||
if (code[0] == 0xBEEB03FF) {
|
|
||||||
return reinterpret_cast<const ShaderBinaryInfo*>(code +
|
|
||||||
static_cast<size_t>(code[1] + 1) * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t GetDeclaredShaderHash(uint64_t shader_addr) {
|
|
||||||
const auto* header = GetBinaryInfo(reinterpret_cast<const uint32_t*>(shader_addr));
|
|
||||||
return header != nullptr ? (static_cast<uint64_t>(header->hash1) << 32u) | header->hash0 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ShaderParams GetShaderParams(uint64_t shader_addr, const char* label, uint64_t declared_hash,
|
|
||||||
std::span<const uint32_t> user_data,
|
std::span<const uint32_t> user_data,
|
||||||
const ShaderMappedData& data, uint32_t user_data_base = 0) {
|
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_words = data.code_size_bytes / sizeof(uint32_t);
|
||||||
const auto code = std::span {reinterpret_cast<const uint32_t*>(shader_addr), code_words};
|
const auto code = std::span {reinterpret_cast<const uint32_t*>(shader_addr), code_words};
|
||||||
ShaderParams params {
|
ShaderParams params {
|
||||||
.code = code,
|
.code = code,
|
||||||
.user_data_count = static_cast<uint32_t>(user_data.size()) + user_data_base,
|
.user_data_count = static_cast<uint32_t>(user_data.size()) + user_data_base,
|
||||||
.hash = declared_hash != 0 ? declared_hash
|
.hash = hash,
|
||||||
: XXH3_64bits(code.data(), code.size_bytes()),
|
|
||||||
};
|
};
|
||||||
EXIT_IF(user_data.size() > HW::UserSgprInfo::SGPRS_MAX ||
|
EXIT_IF(user_data.size() > HW::UserSgprInfo::SGPRS_MAX ||
|
||||||
params.user_data_count > params.user_data.size());
|
params.user_data_count > params.user_data.size());
|
||||||
@@ -726,11 +693,10 @@ void BuildStageStaticKey(const ShaderComputeInputInfo& info, std::vector<uint32_
|
|||||||
ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& context,
|
ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& context,
|
||||||
const HW::UserConfig& user_config, ShaderVertexInputInfo& info) {
|
const HW::UserConfig& user_config, ShaderVertexInputInfo& info) {
|
||||||
const auto& sh = context.GetShaderRegisters();
|
const auto& sh = context.GetShaderRegisters();
|
||||||
const auto data = ShaderGetMappedData(regs.es_regs.data_addr, "ShaderGetInputInfoVS():");
|
const auto [data, hash] = ShaderGetMappedData(regs.es_regs.data_addr, "ShaderGetInputInfoVS():");
|
||||||
const bool merged = (context.GetShaderStages() & 0x20u) != 0;
|
const bool merged = (context.GetShaderStages() & 0x20u) != 0;
|
||||||
auto params = GetShaderParams(
|
auto params = GetShaderParams(
|
||||||
regs.es_regs.data_addr, "ShaderRecompiler VS",
|
regs.es_regs.data_addr, hash,
|
||||||
GetDeclaredShaderHash(regs.es_regs.data_addr),
|
|
||||||
std::span<const uint32_t>(regs.gs_user_sgpr.value, regs.gs_regs.rsrc2.user_sgpr), data,
|
std::span<const uint32_t>(regs.gs_user_sgpr.value, regs.gs_regs.rsrc2.user_sgpr), data,
|
||||||
merged ? 8u : 0u);
|
merged ? 8u : 0u);
|
||||||
if (!merged) {
|
if (!merged) {
|
||||||
@@ -755,10 +721,9 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context&
|
|||||||
mesh.scratch_size_dwords = data.scratch_size_dwords;
|
mesh.scratch_size_dwords = data.scratch_size_dwords;
|
||||||
if (data.type == Prospero::ShaderBinaryType::kGsFront) {
|
if (data.type == Prospero::ShaderBinaryType::kGsFront) {
|
||||||
EXIT_IF(regs.gs_regs.data_addr == 0);
|
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 =
|
const auto back_params =
|
||||||
GetShaderParams(regs.gs_regs.data_addr, "ShaderRecompiler GS",
|
GetShaderParams(regs.gs_regs.data_addr, back_hash, {}, back);
|
||||||
GetDeclaredShaderHash(regs.gs_regs.data_addr), {}, back);
|
|
||||||
params.back_code = back_params.code;
|
params.back_code = back_params.code;
|
||||||
params.user_data[0] = static_cast<uint32_t>(regs.gs_regs.user_data_addr);
|
params.user_data[0] = static_cast<uint32_t>(regs.gs_regs.user_data_addr);
|
||||||
params.user_data[1] = static_cast<uint32_t>(regs.gs_regs.user_data_addr >> 32u);
|
params.user_data[1] = static_cast<uint32_t>(regs.gs_regs.user_data_addr >> 32u);
|
||||||
@@ -797,9 +762,9 @@ std::array<ShaderParams, 3>
|
|||||||
PrepareTessellationPrograms(const HW::VertexShaderInfo& regs, const HW::Context& context,
|
PrepareTessellationPrograms(const HW::VertexShaderInfo& regs, const HW::Context& context,
|
||||||
std::array<ShaderVertexInputInfo, 3>& input_info) {
|
std::array<ShaderVertexInputInfo, 3>& input_info) {
|
||||||
const auto& sh = context.GetShaderRegisters();
|
const auto& sh = context.GetShaderRegisters();
|
||||||
const auto local = ShaderGetMappedData(regs.ls_regs.data_addr, "ShaderGetInputInfoLS():");
|
const auto [local, local_hash] = ShaderGetMappedData(regs.ls_regs.data_addr, "ShaderGetInputInfoLS():");
|
||||||
const auto control = ShaderGetMappedData(regs.hs_regs.data_addr, "ShaderGetInputInfoHS():");
|
const auto [control, control_hash] = ShaderGetMappedData(regs.hs_regs.data_addr, "ShaderGetInputInfoHS():");
|
||||||
const auto evaluation = ShaderGetMappedData(regs.es_regs.data_addr, "ShaderGetInputInfoTES():");
|
const auto [evaluation, evaluation_hash] = ShaderGetMappedData(regs.es_regs.data_addr, "ShaderGetInputInfoTES():");
|
||||||
EXIT_NOT_IMPLEMENTED(local.type != Prospero::ShaderBinaryType::kHsFront ||
|
EXIT_NOT_IMPLEMENTED(local.type != Prospero::ShaderBinaryType::kHsFront ||
|
||||||
control.type != Prospero::ShaderBinaryType::kHsBack ||
|
control.type != Prospero::ShaderBinaryType::kHsBack ||
|
||||||
evaluation.type != Prospero::ShaderBinaryType::kGs);
|
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 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);
|
const auto evaluation_users = std::span(regs.gs_user_sgpr.value, regs.gs_regs.rsrc2.user_sgpr);
|
||||||
std::array<ShaderParams, 3> params {
|
std::array<ShaderParams, 3> params {
|
||||||
GetShaderParams(regs.ls_regs.data_addr, "ShaderRecompiler LS",
|
GetShaderParams(regs.ls_regs.data_addr, local_hash, local_users, local),
|
||||||
GetDeclaredShaderHash(regs.ls_regs.data_addr), local_users, local),
|
GetShaderParams(regs.hs_regs.data_addr, control_hash, local_users, control, 8u),
|
||||||
GetShaderParams(regs.hs_regs.data_addr, "ShaderRecompiler HS",
|
GetShaderParams(regs.es_regs.data_addr, evaluation_hash, evaluation_users,
|
||||||
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,
|
|
||||||
evaluation),
|
evaluation),
|
||||||
};
|
};
|
||||||
// The fused HS back half receives its separate user-data address in s0:s1.
|
// 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,
|
const HW::PixelShaderInfo& regs, const HW::ShaderRegisters& sh,
|
||||||
std::span<const Prospero::ColorComponentMapping, 8> target_export_mapping,
|
std::span<const Prospero::ColorComponentMapping, 8> target_export_mapping,
|
||||||
ShaderPixelInputInfo& ps_info) {
|
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);
|
ShaderGetStaticInputInfoPS(regs, sh, target_export_mapping, data, ps_info);
|
||||||
return GetShaderParams(
|
return GetShaderParams(
|
||||||
regs.ps_regs.data_addr, "ShaderRecompiler PS", GetDeclaredShaderHash(regs.ps_regs.data_addr),
|
regs.ps_regs.data_addr, hash,
|
||||||
std::span<const uint32_t>(regs.ps_user_sgpr.value, regs.ps_regs.rsrc2.user_sgpr), data);
|
std::span<const uint32_t>(regs.ps_user_sgpr.value, regs.ps_regs.rsrc2.user_sgpr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderParams PrepareProgram(const HW::ComputeShaderInfo& regs, const HW::ShaderRegisters& sh,
|
ShaderParams PrepareProgram(const HW::ComputeShaderInfo& regs, const HW::ShaderRegisters& sh,
|
||||||
ShaderComputeInputInfo& info) {
|
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);
|
ShaderGetStaticInputInfoCS(regs, sh, data, info);
|
||||||
return GetShaderParams(
|
return GetShaderParams(
|
||||||
regs.cs_regs.data_addr, "ShaderRecompiler CS", GetDeclaredShaderHash(regs.cs_regs.data_addr),
|
regs.cs_regs.data_addr, hash,
|
||||||
std::span<const uint32_t>(regs.cs_user_sgpr.value, regs.cs_regs.user_sgpr), data);
|
std::span<const uint32_t>(regs.cs_user_sgpr.value, regs.cs_regs.user_sgpr), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13503,11 +13503,11 @@ void TestPixelProgramCacheBindingIdentity() {
|
|||||||
Check(MakeStageStaticKey(no_depth_export) != MakeStageStaticKey(depth_export),
|
Check(MakeStageStaticKey(no_depth_export) != MakeStageStaticKey(depth_export),
|
||||||
"pixel shader identity omitted depth-export semantics");
|
"pixel shader identity omitted depth-export semantics");
|
||||||
|
|
||||||
auto check_program_identity = [&](const uint32_t *shader) {
|
auto check_program_identity = [&](std::span<const uint32_t> shader) {
|
||||||
HW::PixelShaderInfo regs{};
|
HW::PixelShaderInfo regs{};
|
||||||
regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(shader);
|
regs.ps_regs.data_addr = reinterpret_cast<uint64_t>(shader.data());
|
||||||
ShaderMappedData mapped{};
|
ShaderMappedData mapped{};
|
||||||
mapped.code_size_bytes = sizeof(uint32_t);
|
mapped.code_size_bytes = static_cast<uint32_t>(shader.size_bytes());
|
||||||
ShaderMapUserData(regs.ps_regs.data_addr, mapped);
|
ShaderMapUserData(regs.ps_regs.data_addr, mapped);
|
||||||
|
|
||||||
const std::array<Prospero::ColorComponentMapping, 8> identity_mappings{};
|
const std::array<Prospero::ColorComponentMapping, 8> identity_mappings{};
|
||||||
@@ -13522,7 +13522,8 @@ void TestPixelProgramCacheBindingIdentity() {
|
|||||||
second_info);
|
second_info);
|
||||||
CompilePixelRuntime(second_params, second_info);
|
CompilePixelRuntime(second_params, second_info);
|
||||||
Check(
|
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_key == MakeStageStaticKey(second_info) &&
|
||||||
first_info.stage.program != nullptr &&
|
first_info.stage.program != nullptr &&
|
||||||
second_info.stage.program != nullptr,
|
second_info.stage.program != nullptr,
|
||||||
@@ -13535,6 +13536,22 @@ void TestPixelProgramCacheBindingIdentity() {
|
|||||||
Check(request_01 == request_10,
|
Check(request_01 == request_10,
|
||||||
"relocated identical pixel programs did not share their source identity");
|
"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<uint32_t, 200> 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() {
|
void TestGraphicsPushConstantPlacement() {
|
||||||
|
|||||||
Reference in New Issue
Block a user