diff options
Diffstat (limited to 'Userland/Services')
-rw-r--r-- | Userland/Services/WindowServer/Compositor.cpp | 16 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Screen.cpp | 81 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Screen.h | 9 |
3 files changed, 90 insertions, 16 deletions
diff --git a/Userland/Services/WindowServer/Compositor.cpp b/Userland/Services/WindowServer/Compositor.cpp index 16cc37cf88..af5b9dc3c7 100644 --- a/Userland/Services/WindowServer/Compositor.cpp +++ b/Userland/Services/WindowServer/Compositor.cpp @@ -537,7 +537,7 @@ void Compositor::compose() if (need_to_draw_cursor) { auto& screen_data = m_screen_data[cursor_screen.index()]; screen_data.draw_cursor(cursor_screen, cursor_rect); - screen_data.m_flush_rects.add(cursor_rect); + screen_data.m_flush_rects.add(cursor_rect.intersected(cursor_screen.rect())); if (previous_cursor_screen && cursor_rect != previous_cursor_rect) m_screen_data[previous_cursor_screen->index()].m_flush_rects.add(previous_cursor_rect); } @@ -563,10 +563,9 @@ void Compositor::flush(Screen& screen) screen_data.flip_buffers(screen); auto screen_rect = screen.rect(); - auto do_flush = [&](const Gfx::IntRect& a_rect) { - auto rect = Gfx::IntRect::intersection(a_rect, screen_rect); - if (rect.is_empty()) - return; + bool device_can_flush_buffers = screen.can_device_flush_buffers(); + auto do_flush = [&](Gfx::IntRect rect) { + VERIFY(screen_rect.contains(rect)); rect.translate_by(-screen_rect.location()); // Almost everything in Compositor is in logical coordinates, with the painters having @@ -602,7 +601,8 @@ void Compositor::flush(Screen& screen) from_ptr = (const Gfx::RGBA32*)((const u8*)from_ptr + pitch); to_ptr = (Gfx::RGBA32*)((u8*)to_ptr + pitch); } - screen.flush_display(a_rect.intersected(screen.rect())); + if (device_can_flush_buffers) + screen.queue_flush_display_rect(rect); }; for (auto& rect : screen_data.m_flush_rects.rects()) do_flush(rect); @@ -610,6 +610,8 @@ void Compositor::flush(Screen& screen) do_flush(rect); for (auto& rect : screen_data.m_flush_special_rects.rects()) do_flush(rect); + if (device_can_flush_buffers) + screen.flush_display(); } void Compositor::invalidate_screen() @@ -738,7 +740,7 @@ bool Compositor::render_animation_frame(Screen& screen, Gfx::DisjointRectSet& fl dbgln_if(MINIMIZE_ANIMATION_DEBUG, "Minimize animation from {} to {} frame# {} {} on screen #{}", from_rect, to_rect, animation_index, rect, screen.index()); painter.draw_rect(rect, Color::Transparent); // Color doesn't matter, we draw inverted - flush_rects.add(rect); + flush_rects.add(rect.intersected(screen.rect())); invalidate_screen(rect); did_render_any = true; diff --git a/Userland/Services/WindowServer/Screen.cpp b/Userland/Services/WindowServer/Screen.cpp index f8bb2ad8dc..ca4f6f7f45 100644 --- a/Userland/Services/WindowServer/Screen.cpp +++ b/Userland/Services/WindowServer/Screen.cpp @@ -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(); } } diff --git a/Userland/Services/WindowServer/Screen.h b/Userland/Services/WindowServer/Screen.h index 3e653174d1..520b1fa5fe 100644 --- a/Userland/Services/WindowServer/Screen.h +++ b/Userland/Services/WindowServer/Screen.h @@ -8,6 +8,7 @@ #include "ScreenLayout.h" #include <AK/NonnullOwnPtrVector.h> +#include <AK/OwnPtr.h> #include <Kernel/API/KeyCode.h> #include <LibGfx/Bitmap.h> #include <LibGfx/Color.h> @@ -57,6 +58,8 @@ private: unsigned m_scroll_step_size { 1 }; }; +struct ScreenFBData; + class Screen { public: template<typename... Args> @@ -160,7 +163,9 @@ public: Gfx::IntSize size() const { return { m_virtual_rect.width(), m_virtual_rect.height() }; } Gfx::IntRect rect() const { return m_virtual_rect; } - void flush_display(const Gfx::IntRect& rect); + bool can_device_flush_buffers() const { return m_can_device_flush_buffers; } + void queue_flush_display_rect(Gfx::IntRect const& rect); + void flush_display(); private: Screen(ScreenLayout::Screen&); @@ -189,10 +194,12 @@ private: Gfx::RGBA32* m_framebuffer { nullptr }; bool m_can_set_buffer { false }; + bool m_can_device_flush_buffers { true }; // If the device can't do it we revert to false int m_pitch { 0 }; Gfx::IntRect m_virtual_rect; int m_framebuffer_fd { -1 }; + NonnullOwnPtr<ScreenFBData> m_framebuffer_data; ScreenLayout::Screen& m_info; }; |