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.
This commit is contained in:
Bananymous 2024-06-29 19:00:58 +03:00
parent fd3cf5d2b1
commit d58a569660
3 changed files with 80 additions and 12 deletions

View File

@ -11,6 +11,15 @@
#include <sys/mman.h>
#include <sys/socket.h>
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<size_t>(m_framebuffer.width * m_framebuffer.height * sizeof(uint32_t), 4096 * 8), 0));
invalidate(m_framebuffer.area());
}
BAN::ErrorOr<void> WindowServer::set_background_image(BAN::UniqPtr<LibImage::Image> 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<uintptr_t>(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<void*>(mmap_start), mmap_end - mmap_start, MS_SYNC);
const uintptr_t mmap_start = reinterpret_cast<uintptr_t>(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<uintptr_t>(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<uint8_t*>(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

View File

@ -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<void> set_background_image(BAN::UniqPtr<LibImage::Image>);
@ -38,6 +32,7 @@ public:
void set_focused_window(BAN::RefPtr<Window> window);
void invalidate(Rectangle area);
void sync();
Rectangle cursor_area() const;
@ -53,6 +48,8 @@ private:
BAN::Vector<BAN::RefPtr<Window>> m_client_windows;
BAN::Vector<int> m_client_fds;
BAN::Vector<uint8_t> m_pages_to_sync_bitmap;
BAN::UniqPtr<LibImage::Image> m_background_image;
bool m_is_mod_key_held { false };

View File

@ -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, &current_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<long>(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;