mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 12:28:12 +00:00
WindowServer: Coalesce flushing buffers into one ioctl() call
We regularily need to flush many rectangles, so instead of making many expensive ioctl() calls to the framebuffer driver, collect the rectangles and only make one call. And if we have too many rectangles then it may be cheaper to just update the entire region, in which case we simply convert them all into a union and just flush that one rectangle instead.
This commit is contained in:
parent
56cd0f929e
commit
38af4c29e6
7 changed files with 123 additions and 31 deletions
|
@ -25,6 +25,11 @@ Gfx::IntRect Screen::s_bounding_screens_rect {};
|
|||
ScreenLayout Screen::s_layout;
|
||||
Vector<int, default_scale_factors_in_use_count> Screen::s_scale_factors_in_use;
|
||||
|
||||
struct ScreenFBData {
|
||||
Vector<FBRect, 32> pending_flush_rects;
|
||||
bool too_many_pending_flush_rects { false };
|
||||
};
|
||||
|
||||
ScreenInput& ScreenInput::the()
|
||||
{
|
||||
static ScreenInput s_the;
|
||||
|
@ -105,6 +110,7 @@ void Screen::update_scale_factors_in_use()
|
|||
|
||||
Screen::Screen(ScreenLayout::Screen& screen_info)
|
||||
: m_virtual_rect(screen_info.location, { screen_info.resolution.width() / screen_info.scale_factor, screen_info.resolution.height() / screen_info.scale_factor })
|
||||
, m_framebuffer_data(adopt_own(*new ScreenFBData()))
|
||||
, m_info(screen_info)
|
||||
{
|
||||
open_device();
|
||||
|
@ -130,6 +136,7 @@ bool Screen::open_device()
|
|||
}
|
||||
|
||||
m_can_set_buffer = (fb_set_buffer(m_framebuffer_fd, 0) == 0);
|
||||
m_can_device_flush_buffers = true; // If the device can't do it we revert to false
|
||||
set_resolution(true);
|
||||
return true;
|
||||
}
|
||||
|
@ -316,14 +323,72 @@ void ScreenInput::on_receive_keyboard_data(::KeyEvent kernel_event)
|
|||
Core::EventLoop::current().post_event(WindowManager::the(), move(message));
|
||||
}
|
||||
|
||||
void Screen::flush_display(const Gfx::IntRect& flush_region)
|
||||
void Screen::queue_flush_display_rect(Gfx::IntRect const& flush_region)
|
||||
{
|
||||
FBRect rect {
|
||||
.x = (static_cast<unsigned>(flush_region.x()) - m_virtual_rect.left()) * scale_factor(),
|
||||
.y = (static_cast<unsigned>(flush_region.y()) - m_virtual_rect.top()) * scale_factor(),
|
||||
.width = static_cast<unsigned>(flush_region.width()) * scale_factor(),
|
||||
.height = static_cast<unsigned>(flush_region.height() * scale_factor())
|
||||
};
|
||||
fb_flush_buffer(m_framebuffer_fd, &rect);
|
||||
// NOTE: we don't scale until in Screen::flush_display so that when
|
||||
// there are too many rectangles that we end up throwing away, we didn't
|
||||
// waste accounting for scale factor!
|
||||
auto& fb_data = *m_framebuffer_data;
|
||||
if (fb_data.too_many_pending_flush_rects) {
|
||||
// We already have too many, just make sure we extend it if needed
|
||||
VERIFY(!fb_data.pending_flush_rects.is_empty());
|
||||
if (fb_data.pending_flush_rects.size() == 1) {
|
||||
auto& union_rect = fb_data.pending_flush_rects[0];
|
||||
auto new_union = flush_region.united(Gfx::IntRect((int)union_rect.x, (int)union_rect.y, (int)union_rect.width, (int)union_rect.height));
|
||||
union_rect.x = new_union.left();
|
||||
union_rect.y = new_union.top();
|
||||
union_rect.width = new_union.width();
|
||||
union_rect.height = new_union.height();
|
||||
} else {
|
||||
// Convert all the rectangles into one union
|
||||
auto new_union = flush_region;
|
||||
for (auto& flush_rect : fb_data.pending_flush_rects)
|
||||
new_union = new_union.united(Gfx::IntRect((int)flush_rect.x, (int)flush_rect.y, (int)flush_rect.width, (int)flush_rect.height));
|
||||
fb_data.pending_flush_rects.resize(1, true);
|
||||
auto& union_rect = fb_data.pending_flush_rects[0];
|
||||
union_rect.x = new_union.left();
|
||||
union_rect.y = new_union.top();
|
||||
union_rect.width = new_union.width();
|
||||
union_rect.height = new_union.height();
|
||||
}
|
||||
return;
|
||||
}
|
||||
VERIFY(fb_data.pending_flush_rects.size() < fb_data.pending_flush_rects.capacity());
|
||||
fb_data.pending_flush_rects.append({ (unsigned)flush_region.left(),
|
||||
(unsigned)flush_region.top(),
|
||||
(unsigned)flush_region.width(),
|
||||
(unsigned)flush_region.height() });
|
||||
if (fb_data.pending_flush_rects.size() == fb_data.pending_flush_rects.capacity()) {
|
||||
// If we get one more rectangle then we need to convert it to a single union rectangle
|
||||
fb_data.too_many_pending_flush_rects = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::flush_display()
|
||||
{
|
||||
VERIFY(m_can_device_flush_buffers);
|
||||
auto& fb_data = *m_framebuffer_data;
|
||||
if (fb_data.pending_flush_rects.is_empty())
|
||||
return;
|
||||
|
||||
// Now that we have a final set of rects, apply the scale factor
|
||||
auto scale_factor = this->scale_factor();
|
||||
for (auto& flush_rect : fb_data.pending_flush_rects) {
|
||||
flush_rect.x *= scale_factor;
|
||||
flush_rect.y *= scale_factor;
|
||||
flush_rect.width *= scale_factor;
|
||||
flush_rect.height *= scale_factor;
|
||||
}
|
||||
|
||||
if (fb_flush_buffers(m_framebuffer_fd, fb_data.pending_flush_rects.data(), (unsigned)fb_data.pending_flush_rects.size()) < 0) {
|
||||
int err = errno;
|
||||
if (err == ENOTSUP)
|
||||
m_can_device_flush_buffers = false;
|
||||
else
|
||||
dbgln("Screen #{}: Error ({}) flushing display: {}", index(), err, strerror(err));
|
||||
}
|
||||
|
||||
fb_data.too_many_pending_flush_rects = false;
|
||||
fb_data.pending_flush_rects.clear_with_capacity();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue