Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ class ShaderMemory {
PendingWriteObserver observe = nullptr;
HookWaitCounter hookWaits = nullptr;
PendingWriteQuery pendingSpan = nullptr;
bool spanQuery = false;
std::size_t descriptorSpanBytes = 0;
std::uint64_t rawInvalidations = 0;
std::uint32_t forceSync = 0;
Expand Down
15 changes: 13 additions & 2 deletions core/libs/prx/libSceAgcDriver/Execution/src/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ void ShaderMemory::SetWaitedMsProvider(WaitedMsProvider provider) {
waitedMsProvider.store(provider, std::memory_order_release);
}

ShaderMemory::ShaderMemory(std::span<const ShaderRecompiler::MemoryRegion> regions, PendingWriteQuery pendingWrite, PendingWriteObserver observe, HookWaitCounter hookWaits, PendingWriteQuery pendingSpan) : pendingWrite(pendingWrite), observe(observe), hookWaits(hookWaits), pendingSpan(pendingSpan != nullptr ? pendingSpan : pendingWrite) {
ShaderMemory::ShaderMemory(std::span<const ShaderRecompiler::MemoryRegion> regions, PendingWriteQuery pendingWrite, PendingWriteObserver observe, HookWaitCounter hookWaits, PendingWriteQuery pendingSpan) : pendingWrite(pendingWrite), observe(observe), hookWaits(hookWaits), pendingSpan(pendingSpan != nullptr ? pendingSpan : pendingWrite), spanQuery(pendingSpan != nullptr) {
const ShaderRecompiler::RequestMemoryView validated(regions);
initial.reserve(regions.size());
for (const auto& region : regions) initial.emplace_back(region.guestAddress, region.bytes);
Expand Down Expand Up @@ -662,12 +662,23 @@ ShaderRecompiler::SrtWriteStamp ShaderMemory::stampWrites(void* context, std::ui
const auto& self = *static_cast<const ShaderMemory*>(context);
if (bytes == 0 || address > std::numeric_limits<std::uint64_t>::max() - bytes || self.overlapsInitial(address, address + bytes)) return {};
const auto fetched = self.pageAtOrAfter(address & ~static_cast<std::uint64_t>(PageBytes - 1));
if (fetched != self.pages.size() && self.pages[fetched].first < address + bytes) return {};
const bool anyFetched = fetched != self.pages.size() && self.pages[fetched].first < address + bytes;
if (anyFetched && !self.spanQuery) return {};
GuestMemory::FlushGpuWrites(address, static_cast<std::size_t>(bytes));
const auto layout = GuestAllocations::GuestAllocationsGeneration_nid_postfix();
const auto generation = GuestMemory::CollectWrites(address, static_cast<std::size_t>(bytes));
if (generation == 0) return {};
if (self.pendingSpan != nullptr && self.pendingSpan(address, static_cast<std::size_t>(bytes), {}) != PendingWrite::None) return {};
if (anyFetched) {
std::array<std::uint32_t, PageWords> current{};
for (auto index = fetched; index < self.pages.size() && self.pages[index].first < address + bytes; ++index) {
const auto& page = *self.pages[index].second;
if (page.wordwise || GuestMemory::CopyMapped(self.pages[index].first, std::as_writable_bytes(std::span(current))) != GuestMemory::Compare::Equal) return {};
for (std::size_t word = 0; word < PageWords; ++word) {
if (page.valid.Test(word) && page.words[word] != current[word]) return {};
}
}
}
return {generation, layout};
}

Expand Down
54 changes: 54 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3745,6 +3745,10 @@ std::uint32_t* AllocateWatchedTable(std::size_t bytes) {
return heap;
}

AgcDriver::ShaderMemory::PendingWrite NoPendingSpan(std::uint64_t, std::size_t, std::span<std::byte>) {
return AgcDriver::ShaderMemory::PendingWrite::None;
}

void ProtectTablePage(std::uintptr_t page, std::uint32_t protection) {
GuestArena::GuestArenaSetProtection_nid_postfix(page, 4096u, protection);
#ifdef _WIN32
Expand Down Expand Up @@ -5445,6 +5449,54 @@ void verifyScalarAddressRule() {
}
}

void verifyPointerTableUnwrittenReuse() {
using namespace ShaderRecompiler;
constexpr std::uint32_t Palette = 0x40;
constexpr std::size_t Bytes = 65536;
static TableTexture textures[2];
auto* root = AllocateWatchedTable(Bytes);
const auto base = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(root));
if (AgcDriver::GuestMemory::CollectWrites(base, Bytes) == 0) {
std::puts("pointer table unwritten reuse: guest memory has no write watch, not tested");
return;
}
static std::array<std::uint32_t, 8> keys{};
static std::array<std::uint32_t, 4> output{};
const auto keysV = TableV(keys.data(), 0u, static_cast<std::uint32_t>(sizeof(keys)));
const auto outputV = TableV(output.data(), 0u, static_cast<std::uint32_t>(sizeof(output)));
std::copy(keysV.begin(), keysV.end(), root);
std::copy(outputV.begin(), outputV.end(), root + 4);
const auto pointer = base + Palette + 0x100u;
root[12] = static_cast<std::uint32_t>(pointer);
root[13] = static_cast<std::uint32_t>(pointer >> 32u);
const auto entry = [&](std::uint32_t index, std::array<std::uint32_t, 8> words) {
std::copy(words.begin(), words.end(), root + (Palette + index * 32u) / 4u);
};
entry(0, TableT(textures[0].bytes.data(), TableFormat8888UNorm, TableType2D));
entry(1, TableT(textures[1].bytes.data(), TableFormat32UInt, TableType2D));
const std::array<std::uint32_t, 2> userData{static_cast<std::uint32_t>(base), static_cast<std::uint32_t>(base >> 32u)};
const std::vector<std::uint32_t> maskedCode{0xf4080200u, 0xfa000000u, 0xf4080700u, 0xfa000010u, 0xf4080800u, 0xfa000020u, 0x7e200500u, 0x8f108210u,
0xf4200444u, 0x20000000u, 0x8711ff11u, 0x000000ffu, 0x8f128511u, 0xf40c0900u, 0x24000040u, 0xf09c0f08u, 0x01090000u, 0xe0700000u, 0x80070000u, 0xbf810000u};
auto request = TableRequest(maskedCode, userData);
request.shader.codeAddress = 0x33000u;
const auto capture = [&] {
const auto before = ResourceMaterializer::TableReuse();
AgcDriver::ShaderMemory memory({}, nullptr, nullptr, nullptr, &NoPendingSpan);
const auto words = memory.Capture(request)->snapshot.tables.tables.at(0);
const auto after = ResourceMaterializer::TableReuse();
return std::tuple{after.unwritten - before.unwritten, after.compared - before.compared, words.words.at(words.codes.at(1)).dwords};
};
static_cast<void>(capture());
const auto [comparedUnwritten, compared, comparedWords] = capture();
require(comparedUnwritten == 0u && compared == 1u, "pointer table unwritten reuse: the second capture of a pointer-addressed table was not compared");
const auto [unwritten, recompared, unwrittenWords] = capture();
require(unwritten == 1u && recompared == 0u && unwrittenWords == comparedWords, "pointer table unwritten reuse: a pointer-addressed table nothing wrote was compared again instead of reused");
entry(1, TableT(textures[0].bytes.data(), TableFormat8888UNorm, TableType2D));
const auto [written, writtenCompared, writtenWords] = capture();
require(written == 0u && writtenWords[1] == root[(Palette + 32u) / 4u + 1u], "pointer table unwritten reuse: a pointer-addressed table the CPU wrote was reused without a read");
std::puts("pointer table unwritten reuse passed");
}

void verifyPointerImageTable() {
using namespace ShaderRecompiler;
namespace Abi = ImageTableAbi;
Expand Down Expand Up @@ -11409,6 +11461,7 @@ int main(int argc, char** argv) {
verifyImageTableRegistration();
verifyScalarAddressRule();
verifyPointerImageTable();
verifyPointerTableUnwrittenReuse();
verifyPointerTableOffsetCarry();
verifySelectedRecordFallbacks();
verifySelectedRecordOffsetCarry();
Expand Down Expand Up @@ -11505,6 +11558,7 @@ int main(int argc, char** argv) {
verifyImageTableRegistration();
verifyScalarAddressRule();
verifyPointerImageTable();
verifyPointerTableUnwrittenReuse();
verifyPointerTableOffsetCarry();
verifyChainedTableOffset();
verifyPointerChainImageTable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,10 @@ class TableSnapshotter {
if (memoized && !column.address && records.size != 0u && runtime.stampWrites != nullptr) {
const auto span = std::min<std::uint64_t>(records.size, static_cast<std::uint64_t>(column.addend) + column.offset + static_cast<std::uint64_t>(records.keys) * column.stride + column.dwordCount * 4u);
stamp = runtime.stampWrites(runtime.userContext, records.base, span);
} else if (memoized && column.address && runtime.stampWrites != nullptr) {
const auto& memos = ColumnMemos();
const auto found = std::find_if(memos.begin(), memos.end(), [&](const ColumnMemo& entry) { return entry.key == key; });
if (found != memos.end() && found->read->low < found->read->high) stamp = runtime.stampWrites(runtime.userContext, found->read->low, found->read->high - found->read->low);
}
if (memoized && Reuse(key, column, records, true)) return;
unread();
Expand Down
Loading