Fix Buffer::SynchronizeGuest Non-Blocking Behavior

The buffer's non-blocking behavior could lead to an invalid state where the dirty state doesn't adequately represent the buffer's true state, the check has now been moved inside the CAS loop as its behavior changes depending on the dirty state. In addition, `SynchronizeGuest` returns a boolean denoting if the synchronization was successful now to make code flows depending on non-blocking synchronization cleaner.
This commit is contained in:
PixelyIon 2022-07-13 22:19:37 +05:30
parent c1f2445772
commit 745d809e07
No known key found for this signature in database
GPG Key ID: 11BC6C3201BC2C05
2 changed files with 12 additions and 12 deletions

View File

@ -160,31 +160,32 @@ namespace skyline::gpu {
gpu.state.nce->RetrapRegions(*trapHandle, !rwTrap); // Trap any future CPU reads (optionally) + writes to this buffer gpu.state.nce->RetrapRegions(*trapHandle, !rwTrap); // Trap any future CPU reads (optionally) + writes to this buffer
} }
void Buffer::SynchronizeGuest(bool skipTrap, bool nonBlocking, bool setDirty) { bool Buffer::SynchronizeGuest(bool skipTrap, bool nonBlocking, bool setDirty) {
if (!guest) if (!guest)
return; return false;
auto currentState{dirtyState.load(std::memory_order_relaxed)}; auto currentState{dirtyState.load(std::memory_order_relaxed)};
do { do {
if (currentState == DirtyState::CpuDirty || (currentState == DirtyState::Clean && setDirty)) if (currentState == DirtyState::CpuDirty || (currentState == DirtyState::Clean && setDirty))
return; // If the buffer is synchronized (Clean/CpuDirty), there is no need to synchronize it return true; // If the buffer is synchronized (Clean/CpuDirty), there is no need to synchronize it
else if (currentState == DirtyState::GpuDirty && nonBlocking && !PollFence())
return false; // If the buffer is GPU dirty and the fence is not signalled then we can't block
} while (!dirtyState.compare_exchange_strong(currentState, setDirty ? DirtyState::CpuDirty : DirtyState::Clean, std::memory_order_relaxed)); } while (!dirtyState.compare_exchange_strong(currentState, setDirty ? DirtyState::CpuDirty : DirtyState::Clean, std::memory_order_relaxed));
if (nonBlocking && !PollFence())
return;
TRACE_EVENT("gpu", "Buffer::SynchronizeGuest"); TRACE_EVENT("gpu", "Buffer::SynchronizeGuest");
if (!skipTrap) if (!skipTrap)
gpu.state.nce->RetrapRegions(*trapHandle, true); gpu.state.nce->RetrapRegions(*trapHandle, true);
if (setDirty && currentState == DirtyState::Clean) if (setDirty && currentState == DirtyState::Clean)
return; // If the texture was simply transitioned from Clean to CpuDirty, there is no need to synchronize it return true; // If the texture was simply transitioned from Clean to CpuDirty, there is no need to synchronize it
if (!nonBlocking) if (!nonBlocking)
WaitOnFence(); WaitOnFence();
std::memcpy(mirror.data(), backing.data(), mirror.size()); std::memcpy(mirror.data(), backing.data(), mirror.size());
return true;
} }
void Buffer::SynchronizeGuestImmediate(bool isFirstUsage, const std::function<void()> &flushHostCallback) { void Buffer::SynchronizeGuestImmediate(bool isFirstUsage, const std::function<void()> &flushHostCallback) {
@ -231,10 +232,8 @@ namespace skyline::gpu {
} }
std::pair<u64, span<u8>> Buffer::AcquireCurrentSequence() { std::pair<u64, span<u8>> Buffer::AcquireCurrentSequence() {
SynchronizeGuest(false, true); // First try to remove GPU dirtiness by doing an immediate sync and taking a quick shower if (!SynchronizeGuest(false, true))
// Bail out if buffer cannot be synced, we don't know the contents ahead of time so the sequence is indeterminate
if (dirtyState == DirtyState::GpuDirty)
// Bail out if buffer is GPU dirty - since we don't know the contents ahead of time the sequence is indeterminate
return {}; return {};
SynchronizeHost(); // Ensure that the returned mirror is fully up-to-date by performing a CPU -> GPU sync SynchronizeHost(); // Ensure that the returned mirror is fully up-to-date by performing a CPU -> GPU sync

View File

@ -234,9 +234,10 @@ namespace skyline::gpu {
* @param skipTrap If true, setting up a CPU trap will be skipped and the dirty state will be Clean/CpuDirty * @param skipTrap If true, setting up a CPU trap will be skipped and the dirty state will be Clean/CpuDirty
* @param nonBlocking If true, the call will return immediately if the fence is not signalled, skipping the sync * @param nonBlocking If true, the call will return immediately if the fence is not signalled, skipping the sync
* @param setDirty If true, the buffer will be marked as CpuDirty rather than Clean * @param setDirty If true, the buffer will be marked as CpuDirty rather than Clean
* @return If the buffer's contents were successfully synchronized, this'll only be false on non-blocking operations or lack of a guest buffer
* @note The buffer **must** be locked prior to calling this * @note The buffer **must** be locked prior to calling this
*/ */
void SynchronizeGuest(bool skipTrap = false, bool nonBlocking = false, bool setDirty = false); bool SynchronizeGuest(bool skipTrap = false, bool nonBlocking = false, bool setDirty = false);
/** /**
* @brief Synchronizes the guest buffer with the host buffer immediately, flushing GPU work if necessary * @brief Synchronizes the guest buffer with the host buffer immediately, flushing GPU work if necessary