From d58a569660cea14dc56653a3051d3946dd6d1d99 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sat, 29 Jun 2024 19:00:58 +0300 Subject: [PATCH] WindowServer: Limit msync to 60 Hz and only sync necessary pages This speeds up GUI a lot. I can now run GUI on real hardware at almost 60 Hz. --- userspace/WindowServer/WindowServer.cpp | 59 +++++++++++++++++++++++-- userspace/WindowServer/WindowServer.h | 11 ++--- userspace/WindowServer/main.cpp | 22 ++++++++- 3 files changed, 80 insertions(+), 12 deletions(-) diff --git a/userspace/WindowServer/WindowServer.cpp b/userspace/WindowServer/WindowServer.cpp index 86d410d9..fb4397d4 100644 --- a/userspace/WindowServer/WindowServer.cpp +++ b/userspace/WindowServer/WindowServer.cpp @@ -11,6 +11,15 @@ #include #include +WindowServer::WindowServer(Framebuffer& framebuffer) + : m_framebuffer(framebuffer) + , m_cursor({ framebuffer.width / 2, framebuffer.height / 2 }) + , m_font(MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"_sv))) +{ + MUST(m_pages_to_sync_bitmap.resize(BAN::Math::div_round_up(m_framebuffer.width * m_framebuffer.height * sizeof(uint32_t), 4096 * 8), 0)); + invalidate(m_framebuffer.area()); +} + BAN::ErrorOr WindowServer::set_background_image(BAN::UniqPtr image) { if (image->width() != (uint64_t)m_framebuffer.width || image->height() != (uint64_t)m_framebuffer.height) @@ -350,10 +359,52 @@ void WindowServer::invalidate(Rectangle area) } } - uintptr_t mmap_start = reinterpret_cast(m_framebuffer.mmap) + area.y * m_framebuffer.width * 4; - uintptr_t mmap_end = mmap_start + (area.height + 1) * m_framebuffer.width * 4; - mmap_start &= ~(uintptr_t)0xFFF; - msync(reinterpret_cast(mmap_start), mmap_end - mmap_start, MS_SYNC); + const uintptr_t mmap_start = reinterpret_cast(m_framebuffer.mmap) + area.y * m_framebuffer.width * 4; + const uintptr_t mmap_end = mmap_start + (area.height + 1) * m_framebuffer.width * 4; + + uintptr_t mmap_addr = mmap_start & ~(uintptr_t)0xFFF; + while (mmap_addr < mmap_end) + { + size_t index = (mmap_addr - reinterpret_cast(m_framebuffer.mmap)) / 4096; + size_t byte = index / 8; + size_t bit = index % 8; + m_pages_to_sync_bitmap[byte] |= 1 << bit; + mmap_addr += 4096; + } +} + +void WindowServer::sync() +{ + size_t synced_pages = 0; + + for (size_t i = 0; i < m_pages_to_sync_bitmap.size() * 8; i++) + { + size_t byte = i / 8; + size_t bit = i % 8; + if (!(m_pages_to_sync_bitmap[byte] & (1 << bit))) + continue; + + size_t len = 1; + while (i + len < m_pages_to_sync_bitmap.size() * 8) + { + size_t byte = (i + len) / 8; + size_t bit = (i + len) % 8; + if (!(m_pages_to_sync_bitmap[byte] & (1 << bit))) + break; + len++; + } + + msync( + reinterpret_cast(m_framebuffer.mmap) + i * 4096, + len * 4096, + MS_SYNC + ); + synced_pages += len; + + i += len; + } + + memset(m_pages_to_sync_bitmap.data(), 0, m_pages_to_sync_bitmap.size()); } Rectangle WindowServer::cursor_area() const diff --git a/userspace/WindowServer/WindowServer.h b/userspace/WindowServer/WindowServer.h index cf3c8f1b..7365882d 100644 --- a/userspace/WindowServer/WindowServer.h +++ b/userspace/WindowServer/WindowServer.h @@ -19,13 +19,7 @@ class WindowServer { public: - WindowServer(Framebuffer& framebuffer) - : m_framebuffer(framebuffer) - , m_cursor({ framebuffer.width / 2, framebuffer.height / 2 }) - , m_font(MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"_sv))) - { - invalidate(m_framebuffer.area()); - } + WindowServer(Framebuffer& framebuffer); BAN::ErrorOr set_background_image(BAN::UniqPtr); @@ -38,6 +32,7 @@ public: void set_focused_window(BAN::RefPtr window); void invalidate(Rectangle area); + void sync(); Rectangle cursor_area() const; @@ -53,6 +48,8 @@ private: BAN::Vector> m_client_windows; BAN::Vector m_client_fds; + BAN::Vector m_pages_to_sync_bitmap; + BAN::UniqPtr m_background_image; bool m_is_mod_key_held { false }; diff --git a/userspace/WindowServer/main.cpp b/userspace/WindowServer/main.cpp index ffa50b66..2a63fb8d 100644 --- a/userspace/WindowServer/main.cpp +++ b/userspace/WindowServer/main.cpp @@ -181,8 +181,21 @@ int main() if (auto ret = window_server.set_background_image(BAN::move(config.background_image)); ret.is_error()) dwarnln("Could not set background image: {}", ret.error()); + constexpr uint64_t sync_interval_us = 1'000'000 / 60; + timespec last_sync { .tv_sec = 0, .tv_nsec = 0 }; while (!window_server.is_stopped()) { + timespec current_ts; + clock_gettime(CLOCK_MONOTONIC, ¤t_ts); + + uint64_t us_since_last_sync = (current_ts.tv_sec - last_sync.tv_sec) * 1'000'000 + (current_ts.tv_nsec - last_sync.tv_nsec) / 1000; + if (us_since_last_sync > sync_interval_us) + { + window_server.sync(); + us_since_last_sync = 0; + last_sync = current_ts; + } + int max_fd = server_fd; fd_set fds; @@ -200,7 +213,14 @@ int main() } max_fd = BAN::Math::max(max_fd, window_server.get_client_fds(fds)); - if (select(max_fd + 1, &fds, nullptr, nullptr, nullptr) == -1) + timeval select_timeout { + .tv_sec = 0, + .tv_usec = static_cast(sync_interval_us - us_since_last_sync) + }; + int nselect = select(max_fd + 1, &fds, nullptr, nullptr, &select_timeout); + if (nselect == 0) + continue; + if (nselect == -1) { dwarnln("select: {}", strerror(errno)); break;