mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
recognize verified pattern fills
Co-authored-by: Juan Pablo Farias <juanpablo.profissional.a.f@gmail.com>
This commit is contained in:
@@ -25,6 +25,7 @@ struct UniformFill {
|
||||
std::array<uint32_t, 3> group_stride {};
|
||||
uint32_t words = 0;
|
||||
uint32_t value = 0;
|
||||
uint32_t bounded_elements = 0;
|
||||
|
||||
bool operator==(const UniformFill&) const = default;
|
||||
};
|
||||
|
||||
@@ -622,6 +622,8 @@ struct CompiledShaderInfo {
|
||||
struct UniformFillPlan {
|
||||
UniformFill fill;
|
||||
std::array<Value, 4> values;
|
||||
Value pattern_size;
|
||||
Value element_count;
|
||||
};
|
||||
|
||||
// Resource analysis retained by the shader cache. It owns immutable descriptor/SRT,
|
||||
|
||||
@@ -1424,10 +1424,11 @@ ResourcePlan ExtractResourcePlan(const Program& program) {
|
||||
for (auto it = first; it != plan.value_storage.end(); ++it) it->Invalidate();
|
||||
plan.value_storage.erase(first, plan.value_storage.end());
|
||||
}
|
||||
plan.uniform_fill = AnalyzeUniformFill(program);
|
||||
for (uint32_t i = 0; i < plan.uniform_fill.fill.words; ++i) {
|
||||
plan.uniform_fill.values[i] = Clone(plan.uniform_fill.values[i]);
|
||||
}
|
||||
plan.uniform_fill = program.uniform_fill.fill.kind != UniformFillKind::None
|
||||
? program.uniform_fill : AnalyzeUniformFill(program);
|
||||
for (auto& value: plan.uniform_fill.values) value = Clone(value);
|
||||
plan.uniform_fill.pattern_size = Clone(plan.uniform_fill.pattern_size);
|
||||
plan.uniform_fill.element_count = Clone(plan.uniform_fill.element_count);
|
||||
plan.capture_specialization_reads |=
|
||||
capture_indirect_reads || capture_control_reads || !plan.source_reads.empty();
|
||||
if (plan.capture_specialization_reads) {
|
||||
@@ -1513,15 +1514,21 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
|
||||
}
|
||||
snapshot.uniform_fill = {};
|
||||
const auto& fill = program.uniform_fill;
|
||||
const auto words = fill.fill.words;
|
||||
auto words = fill.fill.words;
|
||||
std::array<uint32_t, 4> stored {};
|
||||
bool uniform_fill = words != 0;
|
||||
if (uniform_fill && !fill.pattern_size.IsEmpty())
|
||||
uniform_fill = clean.Evaluate(fill.pattern_size, words) && words >= 1 && words <= 4;
|
||||
uint32_t elements = 0;
|
||||
if (uniform_fill && !fill.element_count.IsEmpty())
|
||||
uniform_fill = clean.Evaluate(fill.element_count, elements) && elements != 0;
|
||||
for (uint32_t i = 0; i < words && uniform_fill; ++i) {
|
||||
uniform_fill = clean.Evaluate(fill.values[i], stored[i]) && stored[i] == stored[0];
|
||||
}
|
||||
if (uniform_fill) {
|
||||
snapshot.uniform_fill = fill.fill;
|
||||
snapshot.uniform_fill.value = stored[0];
|
||||
snapshot.uniform_fill.bounded_elements = elements;
|
||||
}
|
||||
const auto evaluate = [&](uint32_t source, DescriptorValue& value, bool written = false) {
|
||||
if (source >= program.descriptor_sources.size()) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#define XXH_INLINE_ALL
|
||||
#include <xxhash.h>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
namespace {
|
||||
@@ -4040,11 +4042,71 @@ private:
|
||||
bool m_shader_writes = false;
|
||||
};
|
||||
|
||||
UniformFillPlan RecognizePatternFill(Program& program, const Decoder::Program& decoded,
|
||||
ShaderStageInputInfo input) {
|
||||
struct Pattern {
|
||||
size_t code_words;
|
||||
uint64_t hash;
|
||||
uint32_t wave_size;
|
||||
bool bounded;
|
||||
};
|
||||
constexpr std::array patterns {
|
||||
// Bounded 64-lane variant.
|
||||
Pattern {.code_words = 69, .hash = 0x0704b9818e3cba06ull, .wave_size = 64, .bounded = true},
|
||||
// Dispatch thread dimensions, 64-lane variants.
|
||||
Pattern {.code_words = 42, .hash = 0x92ce57f21310ef02ull, .wave_size = 64, .bounded = false},
|
||||
Pattern {.code_words = 40, .hash = 0x374161027f58bee7ull, .wave_size = 64, .bounded = false},
|
||||
Pattern {.code_words = 40, .hash = 0x7d7172c56c2e296dull, .wave_size = 64, .bounded = false},
|
||||
// Dispatch thread dimensions, 32-lane variant.
|
||||
Pattern {.code_words = 38, .hash = 0xacbda1a1ccdd4198ull, .wave_size = 32, .bounded = false},
|
||||
};
|
||||
if (program.stage != ShaderType::Compute || input.compute == nullptr ||
|
||||
decoded.instructions.empty() || program.user_data_base != 0 || program.user_data_count < 10 ||
|
||||
program.info.buffers.size() != 1 || !program.info.images.empty()) return {};
|
||||
const auto& cs = *input.compute;
|
||||
const bool matching_inputs = cs.workgroup_register == 10 && cs.thread_ids_num == 1 &&
|
||||
cs.group_id[0] && !cs.group_id[1] && !cs.group_id[2] && cs.threads_num[0] == 64 &&
|
||||
cs.threads_num[1] == 1 && cs.threads_num[2] == 1 && cs.float_mode == 0xc0;
|
||||
if (!matching_inputs) return {};
|
||||
const auto& last = decoded.instructions.back();
|
||||
const size_t words = last.pc / sizeof(uint32_t) + last.word_count;
|
||||
if (words > decoded.code.size() ||
|
||||
std::ranges::find(patterns, words, &Pattern::code_words) == patterns.end()) return {};
|
||||
// Hash executable instructions through S_ENDPGM, excluding binary metadata.
|
||||
// Verified variants implement Destination[id] = value[id % pattern_size].
|
||||
const auto hash = XXH3_64bits(decoded.code.data(), words * sizeof(uint32_t));
|
||||
const auto pattern = std::ranges::find_if(patterns, [&](const Pattern& candidate) {
|
||||
return candidate.code_words == words && candidate.hash == hash;
|
||||
});
|
||||
if (pattern == patterns.end() || program.wave_size != pattern->wave_size ||
|
||||
cs.dispatch_thread_dimensions != !pattern->bounded) return {};
|
||||
std::array<Value, 10> userdata;
|
||||
for (auto* block: program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
if (inst.GetOpcode() != ValueOpcode::GetUserData) continue;
|
||||
const auto reg = RegIndex(inst.Arg(0).ScalarRegister());
|
||||
if (reg < userdata.size()) userdata[reg] = Value(&inst);
|
||||
}
|
||||
}
|
||||
for (uint32_t reg = 4; reg < 10; ++reg)
|
||||
if ((reg != 8 || pattern->bounded) && userdata[reg].IsEmpty()) return {};
|
||||
UniformFillPlan result;
|
||||
result.fill.kind = UniformFillKind::Buffer;
|
||||
result.fill.words = 1;
|
||||
result.fill.group_stride[0] = 64;
|
||||
std::copy_n(userdata.begin() + 4, 4, result.values.begin());
|
||||
result.pattern_size = userdata[9];
|
||||
// Only the bounded variant reads an explicit count; the other uses thread dimensions.
|
||||
if (pattern->bounded) result.element_count = userdata[8];
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void TrackResources(Program& program, const Decoder::Program& decoded, const CFG::Graph& native_cfg,
|
||||
ShaderStageInputInfo input) {
|
||||
Tracker(program, decoded, native_cfg, input).Run();
|
||||
program.uniform_fill = RecognizePatternFill(program, decoded, input);
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
|
||||
Reference in New Issue
Block a user