summaryrefslogtreecommitdiff
path: root/Userland/Services
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Services')
-rw-r--r--Userland/Services/WindowServer/Compositor.cpp16
-rw-r--r--Userland/Services/WindowServer/Screen.cpp81
-rw-r--r--Userland/Services/WindowServer/Screen.h9
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;
};