Implement syncpoints and nvhost events and fix an nvmap bug

These are used to allow the CPU to synchronise with the GPU as it
reaches specific points in its command stream.

Also fixes an nvmap bug where a struct was incorrect.
This commit is contained in:
Billy Laws
2020-08-09 14:36:47 +01:00
committed by ◱ PixelyIon
parent ed3ff862f6
commit 68d5a48df1
12 changed files with 582 additions and 106 deletions

View File

@ -0,0 +1,62 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common.h>
#include "syncpoint.h"
namespace skyline::gpu {
u64 Syncpoint::RegisterWaiter(u32 threshold, const std::function<void()> &callback) {
if (value >= threshold) {
callback();
return 0;
}
std::lock_guard guard(waiterLock);
waiterMap.insert({nextWaiterId, Waiter{threshold, callback}});
return nextWaiterId++;
}
void Syncpoint::DeregisterWaiter(u64 id) {
std::lock_guard guard(waiterLock);
waiterMap.erase(id);
}
u32 Syncpoint::Increment() {
value++;
std::lock_guard guard(waiterLock);
std::erase_if(waiterMap, [this](const auto &entry) {
if (value >= entry.second.threshold) {
entry.second.callback();
return true;
} else {
return false;
}
});
return value;
}
bool Syncpoint::Wait(u32 threshold, std::chrono::steady_clock::duration timeout) {
std::mutex mtx;
std::condition_variable cv;
bool flag{};
if (timeout == timeout.max())
timeout = std::chrono::seconds(1);
if (!RegisterWaiter(threshold, [&cv, &mtx, &flag] {
std::unique_lock lock(mtx);
flag = true;
lock.unlock();
cv.notify_all();
})) {
return true;
}
std::unique_lock lock(mtx);
return cv.wait_for(lock, timeout, [&flag] { return flag; });
}
};

View File

@ -0,0 +1,56 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
namespace skyline {
namespace constant {
constexpr size_t MaxHwSyncpointCount = 192;
}
namespace gpu {
class Syncpoint {
private:
/**
* @brief This holds information about a single waiter on a syncpoint
*/
struct Waiter {
u32 threshold;
std::function<void()> callback;
};
Mutex waiterLock{}; //!< Locks insertions and deletions of waiters
std::map<u64, Waiter> waiterMap{};
u64 nextWaiterId{1};
public:
std::atomic<u32> value{};
/**
* @brief Registers a new waiter with a callback that will be called when the syncpoint reaches the target threshold
* @note The callback will be called immediately if the syncpoint has already reached the given threshold
* @return A persistent identifier that can be used to refer to the waiter, or 0 if the threshold has already been reached
*/
u64 RegisterWaiter(u32 threshold, const std::function<void()> &callback);
/**
* @brief Removes a waiter given by 'id' from the pending waiter map
*/
void DeregisterWaiter(u64 id);
/**
* @brief Increments the syncpoint by 1
* @return The new value of the syncpoint
*/
u32 Increment();
/**
* @brief Waits for the syncpoint to reach given threshold
* @return false if the timeout was reached, otherwise true
*/
bool Wait(u32 threshold, std::chrono::steady_clock::duration timeout);
};
}
}