mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
fix(shader): reset nested table budgets on snapshot retries
This commit is contained in:
1 parent
11366202db
commit
7f36e41c45
4 files changed
+114
-2
No files matched your search
@@ -2803,6 +2803,8 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(agc_shader_selected_records_image_limit agc_shader_selected_records_pair_limit agc_shader_selected_records_direct_image_limit agc_shader_selected_records_direct_pair_limit PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||
add_test(NAME agc_shader_srt_read_patching COMMAND agc_shader_memory_tests --srt-read-patching)
|
||||
set_tests_properties(agc_shader_srt_read_patching PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||
add_test(NAME agc_shader_nested_snapshot_retry COMMAND agc_shader_memory_tests --nested-snapshot-retry)
|
||||
set_tests_properties(agc_shader_nested_snapshot_retry PROPERTIES TIMEOUT 20)
|
||||
add_test(NAME agc_shader_table_snapshot_reuse COMMAND agc_shader_memory_tests --table-snapshot-reuse)
|
||||
add_test(NAME agc_shader_region_merge COMMAND agc_shader_memory_tests --region-merge)
|
||||
set_tests_properties(agc_shader_region_merge PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" TIMEOUT 30 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
@@ -2656,6 +2656,106 @@ void verifyRegionMerge(bool strided = false, bool benchmark = false) {
|
||||
}
|
||||
|
||||
|
||||
void verifyNestedSnapshotRetry() {
|
||||
using namespace ShaderRecompiler;
|
||||
constexpr std::uint32_t Parents = ImageTableAbi::MaxParentKeys / 2u + 1u;
|
||||
constexpr std::uint32_t Children = ImageTableAbi::MaxNestedKeys / 2u + 1u;
|
||||
std::vector<std::array<std::uint32_t, 8>> leaves(Children, TableT(reinterpret_cast<const void*>(0x100000000ull), TableFormat8888UNorm, TableType2D, 1u));
|
||||
const auto child = TableV(leaves.data(), 32u, Children);
|
||||
std::vector<std::uint64_t> parents(Parents, reinterpret_cast<std::uintptr_t>(child.data()));
|
||||
const auto root = TableV(parents.data(), 8u, Parents);
|
||||
std::vector<std::unique_ptr<IrValue>> values;
|
||||
IrResourcePlan plan;
|
||||
plan.srtPlanComplete = true;
|
||||
plan.resourceTrackingComplete = true;
|
||||
plan.userDataCount = 0u;
|
||||
plan.materializationSources = {2u};
|
||||
plan.descriptorSources.resize(3u);
|
||||
for (std::uint32_t word = 0; word < 4u; ++word) {
|
||||
auto value = std::make_unique<IrValue>(IrOpcode::Void, IrType::U32, word);
|
||||
value->SetImmediateU32(root[word]);
|
||||
plan.descriptorSources[0].dwords[word] = value.get();
|
||||
values.push_back(std::move(value));
|
||||
}
|
||||
plan.descriptorSources[0].dwordCount = 4u;
|
||||
TableColumn parent;
|
||||
parent.heapSource = 0u;
|
||||
parent.stride = 8u;
|
||||
parent.dwordCount = 4u;
|
||||
parent.indirect = true;
|
||||
parent.buffer = true;
|
||||
plan.descriptorSources[1].tableColumn = parent;
|
||||
TableColumn nested;
|
||||
nested.heapSource = 1u;
|
||||
nested.stride = 32u;
|
||||
nested.dwordCount = 8u;
|
||||
plan.descriptorSources[2].tableColumn = nested;
|
||||
ImageResource image;
|
||||
image.source = 2u;
|
||||
image.table = 0u;
|
||||
image.resourceClass = ImageResourceClass::Sampled;
|
||||
image.dimension = RdnaImageDimension::Dim2D;
|
||||
image.read = true;
|
||||
plan.info.images.push_back(image);
|
||||
plan.info.runtimeImageModes.push_back(ResourceMaterializer::RuntimeImageModes(image));
|
||||
struct Memory {
|
||||
std::array<std::pair<std::uint64_t, std::size_t>, 3> ranges;
|
||||
std::uint64_t epoch = 0;
|
||||
bool invalidate = false;
|
||||
std::uint32_t firstReads = 0;
|
||||
std::uint32_t epochQueries = 0;
|
||||
std::vector<std::array<std::size_t, 2>> coverage;
|
||||
};
|
||||
const auto capture = [&](bool invalidate) {
|
||||
Memory memory{{{{reinterpret_cast<std::uintptr_t>(parents.data()), parents.size() * sizeof(parents[0])},
|
||||
{reinterpret_cast<std::uintptr_t>(child.data()), sizeof(child)},
|
||||
{reinterpret_cast<std::uintptr_t>(leaves.data()), leaves.size() * sizeof(leaves[0])}}}, 0u, invalidate};
|
||||
SrtRuntime runtime{};
|
||||
runtime.userContext = &memory;
|
||||
runtime.readMemory = +[](void* context, std::uint64_t address, std::uint32_t* value) {
|
||||
auto& memory = *static_cast<Memory*>(context);
|
||||
for (const auto& [base, size] : memory.ranges) {
|
||||
if (address >= base && address - base <= size - sizeof(*value)) {
|
||||
if (address == memory.ranges[0].first) {
|
||||
++memory.firstReads;
|
||||
memory.coverage.push_back({});
|
||||
}
|
||||
if (memory.invalidate && address == memory.ranges[2].first + memory.ranges[2].second - sizeof(*value)) {
|
||||
++memory.epoch;
|
||||
memory.invalidate = false;
|
||||
}
|
||||
if (!memory.coverage.empty()) {
|
||||
if (base == memory.ranges[0].first) memory.coverage.back()[0] = std::max(memory.coverage.back()[0], static_cast<std::size_t>(address - base + sizeof(*value)));
|
||||
if (base == memory.ranges[2].first) memory.coverage.back()[1] = std::max(memory.coverage.back()[1], static_cast<std::size_t>(address - base + sizeof(*value)));
|
||||
}
|
||||
std::memcpy(value, reinterpret_cast<const void*>(address), sizeof(*value));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
runtime.descriptorScope = +[](void* context, std::size_t bytes) -> std::uint64_t {
|
||||
auto& memory = *static_cast<Memory*>(context);
|
||||
if (bytes == 0u) ++memory.epochQueries;
|
||||
return memory.epoch;
|
||||
};
|
||||
ResourceSnapshot snapshot;
|
||||
ResourceMaterializer{}.Materialize(plan, runtime, snapshot);
|
||||
require(memory.epochQueries != 0u, "nested retry: a single nested table did not check the descriptor epoch");
|
||||
require(memory.firstReads == (invalidate ? 3u : 1u), "nested retry: expected inner retry and outer revalidation");
|
||||
for (const auto& covered : memory.coverage)
|
||||
require(covered[0] == memory.ranges[0].second && covered[1] == memory.ranges[2].second,
|
||||
"nested retry: a discarded attempt consumed the next attempt key budgets");
|
||||
require(snapshot.tables.tables.at(0).keys == Parents && snapshot.tables.nested.at(0).keys == Children,
|
||||
"nested retry: parent or child key coverage was truncated");
|
||||
return snapshot.tables;
|
||||
};
|
||||
const auto stable = capture(false);
|
||||
const auto retried = capture(true);
|
||||
require(stable.tables == retried.tables && stable.nested == retried.nested,
|
||||
"nested retry: an invalidation changed the captured keys, pointers, words or codes");
|
||||
}
|
||||
|
||||
void verifyTableSnapshotReuse() {
|
||||
using namespace ShaderRecompiler;
|
||||
namespace Abi = ImageTableAbi;
|
||||
@@ -9651,6 +9751,11 @@ int main(int argc, char** argv) {
|
||||
std::cout << "Image table keys, maps, records and registration passed\n";
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::string_view(argv[1]) == "--nested-snapshot-retry") {
|
||||
verifyNestedSnapshotRetry();
|
||||
std::cout << "Nested snapshot retry preserves parent and child coverage\n";
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::string_view(argv[1]) == "--table-snapshot-reuse") {
|
||||
verifyTableSnapshotReuse();
|
||||
verifyTableStampRace();
|
||||
|
||||
@@ -707,7 +707,10 @@ public:
|
||||
for (const auto& image : plan.info.images) {
|
||||
if (image.table != NoTable) {
|
||||
count = std::max(count, image.table + 1u);
|
||||
if (image.source < plan.descriptorSources.size() && plan.descriptorSources[image.source].tableColumn.has_value() && plan.descriptorSources[image.source].tableColumn->indirect) hasIndirect = true;
|
||||
if (image.source < plan.descriptorSources.size() && plan.descriptorSources[image.source].tableColumn.has_value()) {
|
||||
const auto& column = *plan.descriptorSources[image.source].tableColumn;
|
||||
hasIndirect |= column.indirect || plan.descriptorSources.at(column.heapSource).tableColumn.has_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto& sampler : plan.info.samplers) {
|
||||
@@ -726,6 +729,8 @@ public:
|
||||
const bool checkEpoch = (count > 1u || hasIndirect) && runtime.descriptorScope != nullptr;
|
||||
bool completed = false;
|
||||
for (std::uint32_t attempt = 0; attempt < 3u; ++attempt) {
|
||||
parentKeysRemaining = ImageTableAbi::MaxParentKeys;
|
||||
nestedKeysRemaining = ImageTableAbi::MaxNestedKeys;
|
||||
if (attempt != 0u) {
|
||||
columns.clear();
|
||||
pages.clear();
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
namespace ShaderRecompiler::ShaderDiskCache {
|
||||
|
||||
inline constexpr std::uint32_t FormatVersion = 50;
|
||||
inline constexpr std::uint32_t FormatVersion = 51;
|
||||
|
||||
enum class LoadStatus {
|
||||
Loaded,
|
||||
|
||||
Reference in new issue
Block a user