Compare commits

...

7 Commits

Author SHA1 Message Date
Bananymous 36d07065fb WindowServer: Optimize msync calls to synchronize less bytes
This *probably* makes framebuffer much faster :D Window server does not
have to do page aligned and page sized syncs which used way too many
pixels.
2024-11-17 20:15:28 +02:00
Bananymous f206e72447 Kernel: Don't require msync addresses to be page aligned
Inodes implementing msync can require page aligned addresses, but are
also allowed to not :) This reduces sizes of framebuffer msyncs!
2024-11-17 20:15:28 +02:00
Bananymous 58e45fb394 Kernel: Print process name on exceptions if it is available 2024-11-17 20:15:28 +02:00
Bananymous 411f32c766 WindowServer: Make select timeout more precise to hit target FPS
Previously window server was only running at around 40 FPS while
targetting 60. This makes the select timeout properly sleep so that
target FPS is reached!
2024-11-17 20:15:28 +02:00
Bananymous e1b82e4e43 WindowServer: Don't allow focusing unfocusable windows after closing 2024-11-17 20:15:28 +02:00
Bananymous df613775b6 WindowServer: Alpha blend background image in fullscreen mode 2024-11-17 20:15:28 +02:00
Bananymous 5e8fdc997a Kernel: Prevent userspace from overwriting CPU load memory 2024-11-17 20:15:28 +02:00
8 changed files with 273 additions and 85 deletions

View File

@ -60,6 +60,7 @@ namespace Kernel
static void wait_until_processors_ready();
static void toggle_should_print_cpu_load() { s_should_print_cpu_load = !s_should_print_cpu_load; }
static bool get_should_print_cpu_load() { return s_should_print_cpu_load; }
static ProcessorID bsb_id() { return s_bsb_id; }
static bool current_is_bsb() { return current_id() == bsb_id(); }

View File

@ -3,11 +3,14 @@
#include <kernel/Device/FramebufferDevice.h>
#include <kernel/FS/DevFS/FileSystem.h>
#include <kernel/Memory/Heap.h>
#include <kernel/Terminal/TerminalDriver.h>
#include <sys/framebuffer.h>
#include <sys/mman.h>
#include <sys/sysmacros.h>
extern Kernel::TerminalDriver* g_terminal_driver;
namespace Kernel
{
@ -250,16 +253,21 @@ namespace Kernel
{
if (flags != MS_SYNC)
return BAN::Error::from_errno(ENOTSUP);
if (vaddr % (BANAN_FB_BPP / 8))
return BAN::Error::from_errno(EINVAL);
if (vaddr < m_vaddr)
vaddr = m_vaddr;
if (vaddr + size > m_vaddr + m_size)
size = (vaddr - m_vaddr) + m_size;
if (auto rem = size % (BANAN_FB_BPP / 8))
size += (BANAN_FB_BPP / 8) - rem;
m_framebuffer->sync_pixels_linear(
(vaddr - m_vaddr) / (BANAN_FB_BPP / 8),
BAN::Math::div_round_up<uint32_t>((vaddr % (BANAN_FB_BPP / 8)) + size, (BANAN_FB_BPP / 8))
);
const vaddr_t start = BAN::Math::max(vaddr, m_vaddr);
const size_t end = BAN::Math::min(vaddr + size, m_vaddr + m_size);
if (start < end)
{
do_msync(
(start - m_vaddr) / (BANAN_FB_BPP / 8),
(end - start) / (BANAN_FB_BPP / 8)
);
}
return {};
}
@ -300,6 +308,53 @@ namespace Kernel
, m_framebuffer(framebuffer)
{ }
void do_msync(uint32_t first_pixel, uint32_t pixel_count)
{
if (!Processor::get_should_print_cpu_load())
return m_framebuffer->sync_pixels_linear(first_pixel, pixel_count);
const uint32_t fb_width = m_framebuffer->width();
const auto& font = g_terminal_driver->font();
const uint32_t x = first_pixel % fb_width;
const uint32_t y = first_pixel / fb_width;
const uint32_t load_w = 16 * font.width();
const uint32_t load_h = Processor::count() * font.height();
if (y >= load_h || x + pixel_count <= fb_width - load_w)
return m_framebuffer->sync_pixels_linear(first_pixel, pixel_count);
if (x >= fb_width - load_w && x + pixel_count <= fb_width)
return;
if (x < fb_width - load_w)
m_framebuffer->sync_pixels_linear(first_pixel, fb_width - load_w - x);
if (x + pixel_count > fb_width)
{
const uint32_t past_last_pixel = first_pixel + pixel_count;
first_pixel = (y + 1) * fb_width;
pixel_count = past_last_pixel - first_pixel;
const uint32_t cpu_load_end = load_h * fb_width;
while (pixel_count && first_pixel < cpu_load_end)
{
m_framebuffer->sync_pixels_linear(first_pixel, BAN::Math::min(pixel_count, fb_width - load_w));
const uint32_t advance = BAN::Math::min(pixel_count, fb_width);
pixel_count -= advance;
first_pixel += advance;
}
if (pixel_count)
m_framebuffer->sync_pixels_linear(first_pixel, pixel_count);
}
}
private:
BAN::RefPtr<FramebufferDevice> m_framebuffer;
};

View File

@ -179,6 +179,8 @@ namespace Kernel
const pid_t tid = Thread::current_tid();
const pid_t pid = (tid && Thread::current().has_process()) ? Process::current().pid() : 0;
const char* process_name = "";
if (tid)
{
auto& thread = Thread::current();
@ -245,15 +247,22 @@ namespace Kernel
);
}
if (Thread::current().has_process() && Process::current().is_userspace())
{
const char* const* argv = Process::current().userspace_info().argv;
if (argv && *argv)
process_name = *argv;
}
#if ARCH(x86_64)
dwarnln(
"CPU {}: {} (error code: 0x{8H}), pid {}, tid {}\r\n"
"CPU {}: {} (error code: 0x{8H}), pid {}, tid {}: {}\r\n"
"Register dump\r\n"
"rax=0x{16H}, rbx=0x{16H}, rcx=0x{16H}, rdx=0x{16H}\r\n"
"rsp=0x{16H}, rbp=0x{16H}, rdi=0x{16H}, rsi=0x{16H}\r\n"
"rip=0x{16H}, rflags=0x{16H}\r\n"
"cr0=0x{16H}, cr2=0x{16H}, cr3=0x{16H}, cr4=0x{16H}",
Processor::current_id(), isr_exceptions[isr], error, pid, tid,
Processor::current_id(), isr_exceptions[isr], error, pid, tid, process_name,
regs->rax, regs->rbx, regs->rcx, regs->rdx,
interrupt_stack->sp, regs->rbp, regs->rdi, regs->rsi,
interrupt_stack->ip, interrupt_stack->flags,
@ -261,13 +270,13 @@ namespace Kernel
);
#elif ARCH(i686)
dwarnln(
"CPU {}: {} (error code: 0x{8H}), pid {}, tid {}\r\n"
"CPU {}: {} (error code: 0x{8H}), pid {}, tid {}: {}\r\n"
"Register dump\r\n"
"eax=0x{8H}, ebx=0x{8H}, ecx=0x{8H}, edx=0x{8H}\r\n"
"esp=0x{8H}, ebp=0x{8H}, edi=0x{8H}, esi=0x{8H}\r\n"
"eip=0x{8H}, eflags=0x{8H}\r\n"
"cr0=0x{8H}, cr2=0x{8H}, cr3=0x{8H}, cr4=0x{8H}",
Processor::current_id(), isr_exceptions[isr], error, pid, tid,
Processor::current_id(), isr_exceptions[isr], error, pid, tid, process_name,
regs->eax, regs->ebx, regs->ecx, regs->edx,
interrupt_stack->sp, regs->ebp, regs->edi, regs->esi,
interrupt_stack->ip, interrupt_stack->flags,

View File

@ -1682,12 +1682,9 @@ namespace Kernel
if (flags != MS_SYNC && flags != MS_ASYNC && flags != MS_INVALIDATE)
return BAN::Error::from_errno(EINVAL);
vaddr_t vaddr = (vaddr_t)addr;
if (vaddr % PAGE_SIZE != 0)
return BAN::Error::from_errno(EINVAL);
LockGuard _(m_process_lock);
const vaddr_t vaddr = reinterpret_cast<vaddr_t>(addr);
for (auto& mapped_region : m_mapped_regions)
if (mapped_region->overlaps(vaddr, len))
TRY(mapped_region->msync(vaddr, len, flags));

View File

@ -80,3 +80,32 @@ struct Circle
}
};
struct Range
{
uint32_t start { 0 };
uint32_t count { 0 };
bool is_continuous_with(const Range& range) const
{
return start <= range.start + range.count && range.start <= start + count;
}
uint32_t distance_between(const Range& range) const
{
if (is_continuous_with(range))
return 0;
if (start < range.start)
return range.start - (start + count);
return start - (range.start + range.count);
}
void merge_with(const Range& range)
{
const uint32_t new_start = BAN::Math::min(start, range.start);
const uint32_t new_end = BAN::Math::max(start + count, range.start + range.count);
start = new_start;
count = new_end - new_start;
}
};

View File

@ -20,7 +20,12 @@ WindowServer::WindowServer(Framebuffer& framebuffer, int32_t corner_radius)
, 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));
BAN::Vector<LibImage::Image::Color> bitmap;
MUST(bitmap.resize(m_framebuffer.width * m_framebuffer.height, { 0x10, 0x10, 0x10, 0xFF }));
m_background_image = MUST(BAN::UniqPtr<LibImage::Image>::create(m_framebuffer.width, m_framebuffer.height, BAN::move(bitmap)));
MUST(m_pending_syncs.resize(m_framebuffer.height));
invalidate(m_framebuffer.area());
}
@ -535,6 +540,7 @@ void WindowServer::on_mouse_scroll(LibInput::MouseScrollEvent event)
void WindowServer::set_focused_window(BAN::RefPtr<Window> window)
{
ASSERT(window->get_attributes().focusable);
if (m_focused_window == window)
return;
@ -574,6 +580,9 @@ static uint32_t alpha_blend(uint32_t color_a, uint32_t color_b)
void WindowServer::invalidate(Rectangle area)
{
ASSERT(m_background_image->width() == (uint64_t)m_framebuffer.width);
ASSERT(m_background_image->height() == (uint64_t)m_framebuffer.height);
const auto get_cursor_pixel =
[](int32_t rel_x, int32_t rel_y) -> BAN::Optional<uint32_t>
{
@ -605,11 +614,28 @@ void WindowServer::invalidate(Rectangle area)
return;
area = focused_overlap.release_value();
const bool should_alpha_blend = m_focused_window->get_attributes().alpha_channel;
if (client_area == m_framebuffer.area())
{
for (int32_t y = area.y; y < area.y + area.height; y++)
for (int32_t x = area.x; x < area.x + area.width; x++)
m_framebuffer.mmap[y * m_framebuffer.width + x] = m_focused_window->framebuffer()[y * m_focused_window->client_width() + x];
if (!should_alpha_blend)
{
for (int32_t y = area.y; y < area.y + area.height; y++)
for (int32_t x = area.x; x < area.x + area.width; x++)
m_framebuffer.mmap[y * m_framebuffer.width + x] = m_focused_window->framebuffer()[y * m_focused_window->client_width() + x];
}
else
{
for (int32_t y = area.y; y < area.y + area.height; y++)
{
for (int32_t x = area.x; x < area.x + area.width; x++)
{
const uint32_t src_pixel = m_focused_window->framebuffer()[y * m_focused_window->client_width() + x];
const uint32_t bg_pixel = m_background_image->get_color(x, y).as_argb();
m_framebuffer.mmap[y * m_framebuffer.width + x] = alpha_blend(src_pixel, bg_pixel);
}
}
}
mark_pending_sync(area);
}
else
@ -630,7 +656,12 @@ void WindowServer::invalidate(Rectangle area)
{
const int32_t src_x = BAN::Math::clamp<int32_t>(dst_x * m_focused_window->client_width() / m_framebuffer.width, 0, m_focused_window->client_width());
const int32_t src_y = BAN::Math::clamp<int32_t>(dst_y * m_focused_window->client_height() / m_framebuffer.height, 0, m_focused_window->client_height());
m_framebuffer.mmap[dst_y * m_framebuffer.width + dst_x] = m_focused_window->framebuffer()[src_y * m_focused_window->client_width() + src_x];
const uint32_t src_pixel = m_focused_window->framebuffer()[src_y * m_focused_window->client_width() + src_x];
const uint32_t bg_pixel = m_background_image->get_color(dst_x, dst_y).as_argb();
uint32_t& dst_pixel = m_framebuffer.mmap[dst_y * m_framebuffer.width + dst_x];
dst_pixel = should_alpha_blend ? alpha_blend(src_pixel, bg_pixel) : src_pixel;
}
}
@ -682,20 +713,9 @@ void WindowServer::invalidate(Rectangle area)
return;
area = fb_overlap.release_value();
if (m_background_image)
{
ASSERT(m_background_image->width() == (uint64_t)m_framebuffer.width);
ASSERT(m_background_image->height() == (uint64_t)m_framebuffer.height);
for (int32_t y = area.y; y < area.y + area.height; y++)
for (int32_t x = area.x; x < area.x + area.width; x++)
m_framebuffer.mmap[y * m_framebuffer.width + x] = m_background_image->get_color(x, y).as_argb();
}
else
{
for (int32_t y = area.y; y < area.y + area.height; y++)
for (int32_t x = area.x; x < area.x + area.width; x++)
m_framebuffer.mmap[y * m_framebuffer.width + x] = 0xFF101010;
}
for (int32_t y = area.y; y < area.y + area.height; y++)
for (int32_t x = area.x; x < area.x + area.width; x++)
m_framebuffer.mmap[y * m_framebuffer.width + x] = m_background_image->get_color(x, y).as_argb();
// FIXME: this loop should be inverse order and terminate
// after window without alpha channel is found
@ -892,24 +912,71 @@ void WindowServer::invalidate(Rectangle area)
mark_pending_sync(area);
}
void WindowServer::mark_pending_sync(Rectangle area)
void WindowServer::RangeList::add_range(const Range& range)
{
// FIXME: this marks too many pages
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)
if (range_count == 0)
{
size_t index = (mmap_addr - reinterpret_cast<uintptr_t>(m_framebuffer.mmap)) / 4096;
size_t byte = index / 8;
size_t bit = index % 8;
//dprintln("{}/{}", byte, m_pages_to_sync_bitmap.size());
if (byte < m_pages_to_sync_bitmap.size())
m_pages_to_sync_bitmap[byte] |= 1 << bit;
mmap_addr += 4096;
ranges[0] = range;
range_count++;
return;
}
size_t min_distance_value = SIZE_MAX;
size_t min_distance_index = 0;
for (size_t i = 0; i < range_count; i++)
{
if (ranges[i].is_continuous_with(range))
{
ranges[i].merge_with(range);
size_t last_continuous = i;
for (size_t j = i + 1; j < range_count; j++)
{
if (!ranges[i].is_continuous_with(ranges[j]))
break;
last_continuous = j;
}
if (last_continuous != i)
{
ranges[i].merge_with(ranges[last_continuous]);
for (size_t j = 1; last_continuous + j < range_count; j++)
ranges[i + j] = ranges[last_continuous + j];
range_count -= last_continuous - i;
}
return;
}
const auto distance = ranges[i].distance_between(range);
if (distance < min_distance_value)
{
min_distance_value = distance;
min_distance_index = i;
}
}
if (range_count >= ranges.size())
{
ranges[min_distance_index].merge_with(range);
return;
}
size_t insert_idx = 0;
for (; insert_idx < range_count; insert_idx++)
if (range.start < ranges[insert_idx].start)
break;
for (size_t i = range_count; i > insert_idx; i--)
ranges[i] = ranges[i - 1];
ranges[insert_idx] = range;
range_count++;
}
void WindowServer::mark_pending_sync(Rectangle to_sync)
{
ASSERT(to_sync == to_sync.get_overlap(m_framebuffer.area()).value());
for (int32_t y_off = 0; y_off < to_sync.height; y_off++)
m_pending_syncs[to_sync.y + y_off].add_range({ static_cast<uint32_t>(to_sync.x), static_cast<uint32_t>(to_sync.width) });
}
void WindowServer::sync()
@ -933,33 +1000,44 @@ void WindowServer::sync()
dir_y = -dir_y;
}
for (size_t i = 0; i < m_pages_to_sync_bitmap.size() * 8; i++)
size_t range_start = 0;
size_t range_count = 0;
for (int32_t y = 0; y < m_framebuffer.height; y++)
{
size_t byte = i / 8;
size_t bit = i % 8;
if (!(m_pages_to_sync_bitmap[byte] & (1 << bit)))
continue;
auto& range_list = m_pending_syncs[y];
size_t len = 1;
while (i + len < m_pages_to_sync_bitmap.size() * 8)
for (size_t i = 0; i < range_list.range_count; i++)
{
size_t byte = (i + len) / 8;
size_t bit = (i + len) % 8;
if (!(m_pages_to_sync_bitmap[byte] & (1 << bit)))
break;
len++;
const size_t cur_start = y * m_framebuffer.width + range_list.ranges[i].start;
const size_t cur_count = range_list.ranges[i].count;
if (range_count == 0)
{
range_start = cur_start;
range_count = cur_count;
}
else
{
const size_t distance = cur_start - (range_start + range_count);
// combine nearby ranges to reduce msync calls
// NOTE: value of 128 is an arbitary constant that *just* felt nice
if (distance <= 128)
range_count = (cur_start + cur_count) - range_start;
else
{
msync(m_framebuffer.mmap + range_start, range_count * 4, MS_SYNC);
range_start = cur_start;
range_count = cur_count;
}
}
}
msync(
reinterpret_cast<uint8_t*>(m_framebuffer.mmap) + i * 4096,
len * 4096,
MS_SYNC
);
i += len;
range_list.range_count = 0;
}
memset(m_pages_to_sync_bitmap.data(), 0, m_pages_to_sync_bitmap.size());
if (range_count)
msync(m_framebuffer.mmap + range_start, range_count * 4, MS_SYNC);
}
Rectangle WindowServer::cursor_area() const
@ -1002,8 +1080,14 @@ void WindowServer::remove_client_fd(int fd)
if (window == m_focused_window)
{
m_focused_window = nullptr;
if (!m_client_windows.empty())
set_focused_window(m_client_windows.back());
for (size_t j = m_client_windows.size(); j > 0; j--)
{
auto& client_window = m_client_windows[j - 1];
if (!client_window->get_attributes().focusable)
continue;
set_focused_window(client_window);
break;
}
}
break;

View File

@ -3,6 +3,7 @@
#include "Framebuffer.h"
#include "Window.h"
#include <BAN/Array.h>
#include <BAN/Function.h>
#include <BAN/Iteration.h>
#include <BAN/Vector.h>
@ -59,6 +60,14 @@ public:
private:
void mark_pending_sync(Rectangle area);
private:
struct RangeList
{
size_t range_count { 0 };
BAN::Array<Range, 32> ranges;
void add_range(const Range& range);
};
private:
Framebuffer& m_framebuffer;
BAN::Vector<BAN::RefPtr<Window>> m_client_windows;
@ -67,7 +76,7 @@ private:
const int32_t m_corner_radius;
BAN::Vector<uint8_t> m_pages_to_sync_bitmap;
BAN::Vector<RangeList> m_pending_syncs;
BAN::UniqPtr<LibImage::Image> m_background_image;

View File

@ -198,19 +198,23 @@ 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());
const auto get_current_us =
[]() -> uint64_t
{
timespec current_ts;
clock_gettime(CLOCK_MONOTONIC, &current_ts);
return (current_ts.tv_sec * 1'000'000) + (current_ts.tv_nsec / 1'000);
};
constexpr uint64_t sync_interval_us = 1'000'000 / 60;
timespec last_sync { .tv_sec = 0, .tv_nsec = 0 };
uint64_t last_sync_us = 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)
const auto current_us = get_current_us();
if (current_us - last_sync_us > sync_interval_us)
{
window_server.sync();
us_since_last_sync = 0;
last_sync = current_ts;
last_sync_us += sync_interval_us;
}
int max_fd = server_fd;
@ -230,10 +234,10 @@ int main()
}
max_fd = BAN::Math::max(max_fd, window_server.get_client_fds(fds));
timeval select_timeout {
.tv_sec = 0,
.tv_usec = static_cast<long>(sync_interval_us - us_since_last_sync)
};
timeval select_timeout {};
if (auto current_us = get_current_us(); current_us - last_sync_us < sync_interval_us)
select_timeout.tv_usec = sync_interval_us - (current_us - last_sync_us);
int nselect = select(max_fd + 1, &fds, nullptr, nullptr, &select_timeout);
if (nselect == 0)
continue;