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
37 changes: 20 additions & 17 deletions core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <chrono>
#include <atomic>
#include <algorithm>
#include <bit>
#include <emmintrin.h>
#include <functional>
#include <cstdio>
#include <cstdlib>
Expand Down Expand Up @@ -1875,28 +1877,29 @@ void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std
const TimedAccess timed(CounterChangedWrite, current.size());
auto* destination = reinterpret_cast<std::byte*>(address);
CheckRange(destination, current.size(), 1, true);
constexpr std::size_t block = 256;
const auto size = current.size();
const auto differs = [&](std::size_t at) {
const auto length = std::min(block, size - at);
return std::memcmp(current.data() + at, original.data() + at, length) != 0;
};
// Stamped like a GPU write: a collect memoized for this packet would not see the page fault.
storeOwn(address, size, [&] {
std::size_t firstChanged = size;
std::size_t lastChanged = 0;
for (std::size_t at = 0; at < size; at += block) {
if (!differs(at)) continue;
const auto blockEnd = std::min(at + block, size);
for (std::size_t run = at; run < blockEnd;) {
if (current[run] == original[run]) { ++run; continue; }
auto runEnd = run + 1;
while (runEnd < blockEnd && current[runEnd] != original[runEnd]) ++runEnd;
std::memcpy(destination + run, current.data() + run, runEnd - run);
firstChanged = std::min(firstChanged, run);
lastChanged = std::max(lastChanged, runEnd);
run = runEnd;
}
std::size_t at = 0;
for (; at + 16 <= size; at += 16) {
const auto bytes = _mm_loadu_si128(reinterpret_cast<const __m128i*>(current.data() + at));
const auto same = _mm_cmpeq_epi8(bytes, _mm_loadu_si128(reinterpret_cast<const __m128i*>(original.data() + at)));
const auto equal = static_cast<std::uint32_t>(_mm_movemask_epi8(same));
if (equal == 0xffffu) continue;
if (equal == 0) _mm_storeu_si128(reinterpret_cast<__m128i*>(destination + at), bytes);
else _mm_maskmoveu_si128(bytes, _mm_xor_si128(same, _mm_set1_epi8(-1)), reinterpret_cast<char*>(destination + at));
const auto changed = ~equal & 0xffffu;
firstChanged = std::min(firstChanged, at + static_cast<std::size_t>(std::countr_zero(changed)));
lastChanged = std::max(lastChanged, at + 32 - static_cast<std::size_t>(std::countl_zero(changed)));
}
_mm_sfence();
for (; at < size; ++at) {
if (current[at] == original[at]) continue;
destination[at] = current[at];
firstChanged = std::min(firstChanged, at);
lastChanged = std::max(lastChanged, at + 1);
}
return firstChanged < lastChanged ? std::pair<std::uint64_t, std::uint64_t>{address + firstChanged, address + lastChanged} : std::pair<std::uint64_t, std::uint64_t>{0, 0};
});
Expand Down
23 changes: 23 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/Pm4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,28 @@ void testWriteChangedKeepsUntouchedBytes() {
check(guest[3] == 7 && guest[100] == 0x55, "write-back rolled back a byte the GPU did not change");
}

void testWriteChangedStoresExactlyTheChangedRuns() {
alignas(256) static std::uint8_t guest[1024 + 13];
std::vector<std::byte> original(sizeof(guest)), current(sizeof(guest));
std::uint32_t seed = 12345;
const auto next = [&] { return seed = seed * 1103515245u + 12345u, seed >> 16u; };
for (std::size_t i = 0; i < sizeof(guest);) {
const auto length = 1 + next() % 40;
const bool changed = next() % 2 == 0;
for (std::size_t j = i; j < std::min(sizeof(guest), i + length); ++j) {
original[j] = static_cast<std::byte>(next());
current[j] = changed && next() % 9 != 0 ? static_cast<std::byte>(static_cast<std::uint8_t>(original[j]) ^ (1u + next() % 255u)) : original[j];
}
i += length;
}
for (std::size_t i = 0; i < sizeof(guest); ++i) guest[i] = static_cast<std::uint8_t>(0xa0u ^ i);
AgcDriver::GuestMemory::WriteChanged(reinterpret_cast<std::uintptr_t>(guest), current, original);
for (std::size_t i = 0; i < sizeof(guest); ++i) {
const auto expected = current[i] != original[i] ? static_cast<std::uint8_t>(current[i]) : static_cast<std::uint8_t>(0xa0u ^ i);
check(guest[i] == expected, "write-back stored a byte the GPU did not change or missed one it did");
}
}

void testEventWrite() {
for (const auto eventType : {0x07u, 0x0fu, 0x10u}) {
AgcDriver::Pm4::Validate(makePacket(0x46, {0x400u | eventType}), 0);
Expand Down Expand Up @@ -1049,6 +1071,7 @@ int main(int argc, char** argv) {
testCatalog();
testNopPad();
testWriteChangedKeepsUntouchedBytes();
testWriteChangedStoresExactlyTheChangedRuns();
testRegisters();
testRegisterFile();
testContextAndBases();
Expand Down
Loading