mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 16:43:05 -05:00
perf(recompiler): scan image-table key buffers in bulk
Narrowing a table column read its key buffer one key at a time, with a page lookup and a single-word read per key; PPSA05684 scans about 20,000 keys per table snapshot. The scan now checks each page once and reads the keys with the bulk reader, and maps a key to its record without dividing when the multiplication cannot wrap. Gameplay, image-table time per snapshot: about 6.5 ms before, 5 ms after. AI-assisted: yes
This commit is contained in:
1 parent
20a84154ff
commit
bf0c51e4c1
2 files changed
+64
-10
No files matched your search
@@ -1240,6 +1240,43 @@ void verifyImageTableBulkReads() {
|
||||
require(cutByRun->snapshot.tables == cutByWord->snapshot.tables && cutWordReads.wordReads == 12u && cutRunReads.wordReads == 0u && cutRunReads.bulkWords == 12u, "image table bulk reads: a record cut by an unmapped page read other words in runs than word by word");
|
||||
}
|
||||
|
||||
void verifyImageTableBulkKeyScan() {
|
||||
using namespace ShaderRecompiler;
|
||||
namespace Abi = ImageTableAbi;
|
||||
static TableTexture textures[2];
|
||||
alignas(4096) static TableGuest guest;
|
||||
std::fill(guest.heap.begin(), guest.heap.end(), std::array<std::uint32_t, 8>{});
|
||||
guest.heap[0] = TableT(textures[0].bytes.data(), TableFormat8888UNorm, TableType2D);
|
||||
guest.heap[1] = TableT(textures[1].bytes.data(), TableFormat32UInt, TableType2D);
|
||||
guest.heap[3] = guest.heap[0];
|
||||
guest.materials = {{{0u, 1u, 0u, 0u}, {0u, 0x08000003u, 0u, 0u}, {0u, 1u, 0u, 0u}}};
|
||||
FillTableSrt(guest, guest.heap.data(), 32u, 4u);
|
||||
const auto userData = TableUserData(guest);
|
||||
const auto request = TableRequest(TableMaterialCode, userData);
|
||||
const auto capture = [&](CountingMemory& memory, bool bulk) {
|
||||
SrtRuntime runtime;
|
||||
runtime.userData = request.context.userData;
|
||||
runtime.shaderBase = request.shader.codeAddress;
|
||||
runtime.userContext = &memory;
|
||||
runtime.readMemory = &CountingRead;
|
||||
runtime.readSpecializationMemory = &CountingRead;
|
||||
runtime.accessible = &CountingAccessible;
|
||||
if (bulk) runtime.readWords = &CountingReadWords;
|
||||
return CaptureResources(request, runtime);
|
||||
};
|
||||
const auto begin = reinterpret_cast<std::uintptr_t>(guest.materials.data());
|
||||
CountingMemory byWordReads{begin, begin + sizeof(guest.materials)};
|
||||
CountingMemory byRunReads = byWordReads;
|
||||
const auto byWord = capture(byWordReads, false);
|
||||
const auto byRun = capture(byRunReads, true);
|
||||
require(byWordReads.wordReads == guest.materials.size(), "image table bulk key scan: without a bulk reader the keys were not read one word each");
|
||||
require(byRunReads.wordReads == 0u, "image table bulk key scan: the keys were read word by word although a bulk reader exists");
|
||||
require(byRun->snapshot.tables == byWord->snapshot.tables, "image table bulk key scan: scanning the keys in bulk changed the snapshot");
|
||||
const auto& snapshot = byRun->snapshot.tables.tables.at(0);
|
||||
const auto outside = Abi::PoisonCode(static_cast<std::uint32_t>(Abi::PoisonReason::OutsideDomain));
|
||||
require(snapshot.keys == 4u && snapshot.codes[0] == snapshot.codes[3] && snapshot.codes[0] != snapshot.codes[1] && snapshot.codes[2] == outside, "image table bulk key scan: the narrowed codes are wrong");
|
||||
}
|
||||
|
||||
void verifyImageTables() {
|
||||
using namespace ShaderRecompiler;
|
||||
namespace Abi = ImageTableAbi;
|
||||
@@ -5212,6 +5249,7 @@ int main(int argc, char** argv) {
|
||||
verifyImageTableKeys();
|
||||
verifyImageTables();
|
||||
verifyImageTableBulkReads();
|
||||
verifyImageTableBulkKeyScan();
|
||||
verifyImageTableSrtKey();
|
||||
verifyImageTableMaskedOffset();
|
||||
verifyImageTableKeyUnion();
|
||||
|
||||
@@ -505,27 +505,42 @@ private:
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
for (std::uint64_t index = 0; index < positions; index++) {
|
||||
const auto address = base + first + index * domain.stride;
|
||||
if (!PageAccessible(address, base, base + size)) {
|
||||
skip();
|
||||
return;
|
||||
if (runtime.readWords != nullptr && positions != 0u && domain.stride % 4u == 0u && domain.stride <= 64u) {
|
||||
const auto words = static_cast<std::size_t>(((positions - 1u) * domain.stride) / 4u + 1u);
|
||||
for (std::uint64_t page = (base + first) & ~(PageBytes - 1u); page < base + first + words * 4u; page += PageBytes) {
|
||||
if (!PageAccessible(std::max(page, base + first), base, base + size)) {
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (keyWords.size() < words) keyWords.resize(words);
|
||||
if (!runtime.readWords(runtime.userContext, base + first, keyWords.data(), words)) throw std::runtime_error("image table: failed to read guest memory at 0x" + hexText(base + first));
|
||||
const auto step = static_cast<std::size_t>(domain.stride / 4u);
|
||||
values.reserve(values.size() + positions);
|
||||
for (std::size_t index = 0; index < words; index += step) values.push_back(keyWords[index]);
|
||||
} else {
|
||||
for (std::uint64_t index = 0; index < positions; index++) {
|
||||
const auto address = base + first + index * domain.stride;
|
||||
if (!PageAccessible(address, base, base + size)) {
|
||||
skip();
|
||||
return;
|
||||
}
|
||||
values.push_back(Read(address));
|
||||
}
|
||||
values.push_back(Read(address));
|
||||
}
|
||||
if (positions != 0u) scanned.emplace_back(base, size);
|
||||
}
|
||||
records.states.assign(records.keys, RecordState::OutsideDomain);
|
||||
records.outside = false;
|
||||
const std::uint32_t unwrapped = std::numeric_limits<std::uint32_t>::max() / column.stride;
|
||||
for (const auto value : values) {
|
||||
const std::uint32_t relative = value * column.stride;
|
||||
const bool aligned = relative % column.stride == 0u;
|
||||
const auto record = relative / column.stride;
|
||||
if (aligned && record < records.keys) {
|
||||
const auto record = value <= unwrapped ? value : relative % column.stride == 0u ? relative / column.stride : records.keys;
|
||||
if (record < records.keys) {
|
||||
records.states[record] = RecordState::Read;
|
||||
continue;
|
||||
}
|
||||
if (column.address || ImageTableAbi::ScalarBufferDword(relative + column.addend, column.offset, records.size).has_value()) records.outside = true;
|
||||
if (!records.outside && (column.address || ImageTableAbi::ScalarBufferDword(relative + column.addend, column.offset, records.size).has_value())) records.outside = true;
|
||||
}
|
||||
records.narrowed = true;
|
||||
for (const auto& [base, size] : scanned) tables.ranges.emplace_back(base, size);
|
||||
@@ -774,6 +789,7 @@ private:
|
||||
std::unordered_map<WordsHash::Key, std::uint32_t, WordsHash> wordsIndex;
|
||||
std::unordered_map<std::uint64_t, bool> pages;
|
||||
std::unordered_map<std::uint64_t, bool> wholePages;
|
||||
std::vector<std::uint32_t> keyWords;
|
||||
};
|
||||
|
||||
void materializeSnapshot(const IrResourcePlan& plan, const SrtRuntime& runtime, SrtWalker& walker, ResourceSnapshot& snapshot, std::vector<std::uint8_t>& activeSources) {
|
||||
|
||||
Reference in new issue
Block a user