mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-12 19:41:10 +00:00
Performance: idle front end, proot stops, launch-time passes and log collection (#529)
* Front end: draw the capsule wall without clipping it every frame The tilted wall clipped each capsule to a rounded rect and recomposed its columns on every drift step, and a rotated rounded clip is rasterised in software on the render threads: the idle main page held about 1.7 cores. The art is now loaded at the capsule's size with its corners already rounded, each column draws its capsules itself in one layer moved by the drift, and the drift advances at most every 15 ms. The idle page takes about a third of a core. * libfakeinput: let calls on descriptors it does not serve skip its lock Every read, write, ioctl, close and poll of every process in a Proton session went through the controller lock and the three map lookups, Wine's wineserver pipes and the game's files alike. A bitmap of the descriptors the maps hold, set before one is handed out and cleared when it is closed, lets everything else go straight to the kernel. * Pad motion: ask the display for its rotation four times a second The rotation was looked up for every sensor reading, every 2 ms, and cost more than the rest of the reading; it is now refreshed every 250 ms, and the readings no longer allocate pairs. * Session: keep the library passes out of a game's launch The redist seeder stamped each prefix with the checksum of the whole set of names, which differs between the session's pass and the game launch's (the game's own names are added), so every launch re-appended the markers to every prefix in the library while the game loaded. Each prefix's marker now lists the names it was given and only the missing ones are added. The beat's seed and compatibility passes, and the Lossless Scaling ownership probe (which loads steamclient.so), also wait while a wineserver runs: a launch rewrites the manifest and made them due at exactly that moment. The game's own launch seeds its prefix regardless. * Agent: keep the focus watcher's X connection open while nothing changes The connect timeout stayed on the socket, so with no property change the read timed out, and the watcher dropped the connection, reconnected and took a fresh snapshot every few seconds. * Game environment: start Cyberpunk 2077 without REDlauncher The prelauncher opens REDlauncher, a Qt WebEngine window that hands over to the game half a minute later and then stays up beside it, well over a core busy for as long as the game runs; its own --launcher-skip started the game twice. The game's executable is started directly instead. * netif: answer Wine's network driver from a reading of the link Mortal Kombat X has Wine's network driver enumerate every interface, address and route table hundreds of times a second. Each enumeration read /etc/droiddeck-net several times, asked the kernel for its interface dump again (a netlink socket bound under proot, refused each time) and looked the real functions up with dlsym. The link file is now read at most once a second, a refused dump is not asked for again within that second, and the real functions are looked up once. * Fast path: create memfds without a stop in proot proot looks at a memfd's name only to refuse the few that are then executed or locked; every other memfd_create is now issued from the trampoline. The session's /proc/net and status copies made one per read, a stop each. Without memfd in the kernel the call still goes to proot's ashmem. * Logs: run each redaction rule only on a line that could match it A session's end copies some 70 MB of the Steam client's logs and puts every line through all twenty-odd rules, most of a minute of a core on the phone after every session. Each rule now runs only on a line holding what it cannot match without: its keyword, the five separators of a MAC, the four dashes of a GUID, a long enough hex or token run, the learned account or address text. Lines with non-ASCII text still meet every case-insensitive rule. Over 895,000 lines of real client logs, with the device's accounts and addresses learned, the output is identical and the pass takes a twentieth of the time.
This commit is contained in:
1 parent
96d7c66535
commit
eadebc46d5
11 files changed
+416
-125
No files matched your search
@@ -266,6 +266,31 @@ static std::unordered_map<int, struct ff_effect> &ff_effects() {
|
||||
return *map;
|
||||
}
|
||||
|
||||
// Every fd the maps hold, readable without controller_mutex(): the hooks see each read, write,
|
||||
// ioctl and poll a process makes, and almost none are on these. Set before an fd is handed out,
|
||||
// cleared once no map holds it; an fd past the bitmap takes the lock once any such fd exists.
|
||||
static constexpr int kWatchedFds = 1 << 16;
|
||||
static constexpr int kWatchedBits = sizeof(unsigned long) * 8;
|
||||
static std::atomic<unsigned long> watched_fds[kWatchedFds / kWatchedBits];
|
||||
static std::atomic<bool> watched_beyond{false};
|
||||
|
||||
static void watch_fd(int fd) {
|
||||
if (fd < 0) return;
|
||||
if (fd >= kWatchedFds) watched_beyond.store(true, std::memory_order_release);
|
||||
else watched_fds[fd / kWatchedBits].fetch_or(1UL << (fd % kWatchedBits), std::memory_order_release);
|
||||
}
|
||||
|
||||
static void unwatch_fd(int fd) {
|
||||
if (fd >= 0 && fd < kWatchedFds)
|
||||
watched_fds[fd / kWatchedBits].fetch_and(~(1UL << (fd % kWatchedBits)), std::memory_order_release);
|
||||
}
|
||||
|
||||
static bool watched(int fd) {
|
||||
if (fd < 0) return false;
|
||||
if (fd >= kWatchedFds) return watched_beyond.load(std::memory_order_acquire);
|
||||
return (watched_fds[fd / kWatchedBits].load(std::memory_order_acquire) >> (fd % kWatchedBits)) & 1;
|
||||
}
|
||||
|
||||
static const char *fake_hook_dir() { return config().hook_dir; }
|
||||
static const char *fake_udev_data_dir() { return config().udev_data_dir; }
|
||||
static bool fake_vibration_enabled() { return config().vibration_enabled; }
|
||||
@@ -692,6 +717,7 @@ open_fake_input_ring(const char *event, int flags) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
controller_map()[fd] = controller;
|
||||
watch_fd(fd);
|
||||
}
|
||||
|
||||
Logger::log("Adding ring-backed controller, fd %d event %s slot %d\n", fd,
|
||||
@@ -774,11 +800,13 @@ copy_slot_ioctl_string(int op, void *argp, const char *format, int event_number)
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden"))) static bool is_fake_input_fd(int fd) {
|
||||
if (!watched(fd)) return false;
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
return controller_map().find(fd) != controller_map().end();
|
||||
}
|
||||
|
||||
__attribute__((visibility("hidden"))) static bool fake_fd_is_stale(int fd) {
|
||||
if (!watched(fd)) return false;
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
auto controller = controller_map().find(fd);
|
||||
return controller != controller_map().end() &&
|
||||
@@ -892,6 +920,7 @@ __attribute__((visibility("hidden"))) static int open_uinput(int flags) {
|
||||
writer->owner = getpid();
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
uinput_map()[fd] = writer;
|
||||
watch_fd(fd);
|
||||
Logger::log("uinput: opened as fd %d\n", fd);
|
||||
return fd;
|
||||
}
|
||||
@@ -1556,6 +1585,7 @@ __attribute__((visibility("hidden"))) static int open_deck_hidraw(int flags) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
deck_map()[pair[0]] = deck;
|
||||
watch_fd(pair[0]);
|
||||
}
|
||||
pthread_t thread;
|
||||
auto *arg = new std::shared_ptr<DeckHidraw>(deck);
|
||||
@@ -1563,6 +1593,7 @@ __attribute__((visibility("hidden"))) static int open_deck_hidraw(int flags) {
|
||||
delete arg;
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
deck_map().erase(pair[0]);
|
||||
unwatch_fd(pair[0]);
|
||||
syscall(SYS_close, pair[0]);
|
||||
syscall(SYS_close, pair[1]);
|
||||
munmap(mapping, mapping_size);
|
||||
@@ -1898,6 +1929,7 @@ __attribute__((visibility("hidden"))) static int open_touch_hidraw(int flags) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
deck_map()[pair[0]] = touch;
|
||||
watch_fd(pair[0]);
|
||||
}
|
||||
touch_state([](TouchRingFile &r) { r.opened++; });
|
||||
pthread_t thread;
|
||||
@@ -1906,6 +1938,7 @@ __attribute__((visibility("hidden"))) static int open_touch_hidraw(int flags) {
|
||||
delete arg;
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
deck_map().erase(pair[0]);
|
||||
unwatch_fd(pair[0]);
|
||||
syscall(SYS_close, pair[0]);
|
||||
syscall(SYS_close, pair[1]);
|
||||
touch_state([](TouchRingFile &r) { if (r.opened) r.opened--; });
|
||||
@@ -2689,6 +2722,7 @@ static int fake_stat_path(const char *pathname, S *statbuf, Real real) {
|
||||
template <typename S, typename Real>
|
||||
static int fake_stat_fd(int fd, S *buf, Real real) {
|
||||
int ret = real(fd, buf);
|
||||
if (ret != 0 || !watched(fd)) return ret;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
if (ret == 0 && deck_map().count(fd)) {
|
||||
@@ -2898,6 +2932,7 @@ EXPORT int ioctl(int fd, ioctl_request_t op, ...) {
|
||||
// The lock must not be held across the passthrough: binder's transport is a
|
||||
// blocking ioctl(BINDER_WRITE_READ), so a parked binder pool thread would own
|
||||
// controller_mutex() for as long as it waits and deadlock every other ioctl.
|
||||
if (!watched(fd)) return syscall(SYS_ioctl, fd, op, argp);
|
||||
std::unique_lock<std::recursive_mutex> guard(controller_mutex());
|
||||
auto maker = uinput_map().find(fd);
|
||||
if (maker != uinput_map().end()) return ioctl_uinput(*maker->second, op, argp);
|
||||
@@ -3117,6 +3152,7 @@ EXPORT int ioctl(int fd, ioctl_request_t op, ...) {
|
||||
EXPORT int close(int fd) {
|
||||
static auto my_close = reinterpret_cast<decltype(&::close)>(dlsym(RTLD_NEXT, "close"));
|
||||
|
||||
if (!watched(fd)) return my_close(fd);
|
||||
close_uinput(fd);
|
||||
std::unique_lock<std::recursive_mutex> guard(controller_mutex());
|
||||
deck_map().erase(fd);
|
||||
@@ -3127,6 +3163,7 @@ EXPORT int close(int fd) {
|
||||
controller->second->closed = true;
|
||||
controller_map().erase(fd);
|
||||
}
|
||||
unwatch_fd(fd);
|
||||
guard.unlock();
|
||||
|
||||
return my_close(fd);
|
||||
@@ -3135,6 +3172,7 @@ EXPORT int close(int fd) {
|
||||
static constexpr int kMaxRingWaitMs = 2;
|
||||
|
||||
EXPORT ssize_t read(int fd, void *buf, size_t count) {
|
||||
if (!watched(fd)) return syscall(SYS_read, fd, buf, count);
|
||||
std::unique_lock<std::recursive_mutex> guard(controller_mutex());
|
||||
auto controller = controller_map().find(fd);
|
||||
if (controller == controller_map().end()) {
|
||||
@@ -3233,6 +3271,7 @@ EXPORT ssize_t read(int fd, void *buf, size_t count) {
|
||||
EXPORT ssize_t write(int fd, const void *buf, size_t count) {
|
||||
static auto my_write = reinterpret_cast<decltype(&::write)>(dlsym(RTLD_NEXT, "write"));
|
||||
|
||||
if (!watched(fd)) return my_write(fd, buf, count);
|
||||
std::unique_lock<std::recursive_mutex> guard(controller_mutex());
|
||||
auto made = uinput_map().find(fd);
|
||||
if (made != uinput_map().end()) return write_uinput(*made->second, buf, count);
|
||||
@@ -3262,6 +3301,7 @@ EXPORT ssize_t write(int fd, const void *buf, size_t count) {
|
||||
}
|
||||
|
||||
EXPORT ssize_t writev(int fd, const struct iovec *iov, int iovcnt) {
|
||||
if (!watched(fd)) return syscall(SYS_writev, fd, iov, iovcnt);
|
||||
std::unique_lock<std::recursive_mutex> guard(controller_mutex());
|
||||
if (deck_map().count(fd)) {
|
||||
ssize_t total = 0;
|
||||
@@ -3312,11 +3352,14 @@ static int poll_fake(struct pollfd *fds, nfds_t nfds, int timeout,
|
||||
static auto my_poll = reinterpret_cast<decltype(&::poll)>(dlsym(RTLD_NEXT, "poll"));
|
||||
static auto my_ppoll = reinterpret_cast<decltype(&::ppoll)>(dlsym(RTLD_NEXT, "ppoll"));
|
||||
|
||||
nfds_t first = 0;
|
||||
while (first < nfds && !watched(fds[first].fd)) first++;
|
||||
bool has_fake_fds = false;
|
||||
std::vector<struct pollfd> real_fds;
|
||||
real_fds.reserve(nfds);
|
||||
std::vector<std::shared_ptr<FakeController>> fake_fds(nfds);
|
||||
{
|
||||
std::vector<std::shared_ptr<FakeController>> fake_fds;
|
||||
if (first < nfds) {
|
||||
real_fds.reserve(nfds);
|
||||
fake_fds.resize(nfds);
|
||||
std::lock_guard<std::recursive_mutex> guard(controller_mutex());
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
real_fds.push_back(fds[i]);
|
||||
|
||||
Reference in new issue
Block a user