Skip to content

Commit

Permalink
WindowServer: Add an Overlay class for flicker-free overlay rendering
Browse files Browse the repository at this point in the history
An Overlay is similar to a transparent window, but has less overhead
and does not get rendered within the window stack. Basically, the area
that an Overlay occupies forces transparency rendering for any window
underneath, which allows us to render them flicker-free.

This also adds a new API that allows displaying the screen numbers,
e.g. while the user configures the screen layout in DisplaySettings

Because other things like drag&drop or the window-size label are not
yet converted to use this new mechanism, they will be drawn over the
screen-number currently.
  • Loading branch information
tomuta authored and awesomekling committed Jun 25, 2021
1 parent 42cb38b commit 41859ad
Show file tree
Hide file tree
Showing 14 changed files with 638 additions and 7 deletions.
3 changes: 3 additions & 0 deletions Base/etc/WindowServer.ini
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ Drag=/res/cursors/drag.png
Wait=/res/cursors/wait.f14t100.png
Crosshair=/res/cursors/crosshair.png

[Graphics]
OverlayRectShadow=/res/graphics/overlay-rect-shadow.png

[Input]
DoubleClickSpeed=250

Expand Down
Binary file added Base/res/graphics/overlay-rect-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions Userland/Services/WindowServer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(SOURCES
MenuItem.cpp
MenuManager.cpp
MultiScaleBitmaps.cpp
Overlays.cpp
Screen.cpp
ScreenLayout.cpp
Window.cpp
Expand Down
14 changes: 14 additions & 0 deletions Userland/Services/WindowServer/ClientConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ ClientConnection::~ClientConnection()
if (window.value->type() == WindowType::Applet)
AppletManager::the().remove_applet(window.value);
}

if (m_show_screen_number)
Compositor::the().decrement_show_screen_number({});
}

void ClientConnection::die()
Expand Down Expand Up @@ -316,6 +319,17 @@ Messages::WindowServer::SaveScreenLayoutResponse ClientConnection::save_screen_l
return { success, move(error_msg) };
}

void ClientConnection::show_screen_numbers(bool show)
{
if (m_show_screen_number == show)
return;
m_show_screen_number = show;
if (show)
Compositor::the().increment_show_screen_number({});
else
Compositor::the().decrement_show_screen_number({});
}

void ClientConnection::set_window_title(i32 window_id, String const& title)
{
auto it = m_windows.find(window_id);
Expand Down
2 changes: 2 additions & 0 deletions Userland/Services/WindowServer/ClientConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class ClientConnection final
virtual Messages::WindowServer::SetScreenLayoutResponse set_screen_layout(ScreenLayout const&, bool) override;
virtual Messages::WindowServer::GetScreenLayoutResponse get_screen_layout() override;
virtual Messages::WindowServer::SaveScreenLayoutResponse save_screen_layout() override;
virtual void show_screen_numbers(bool) override;
virtual void set_window_cursor(i32, i32) override;
virtual void set_window_custom_cursor(i32, Gfx::ShareableBitmap const&) override;
virtual void popup_menu(i32, Gfx::IntPoint const&) override;
Expand Down Expand Up @@ -168,6 +169,7 @@ class ClientConnection final
RefPtr<Core::Timer> m_ping_timer;

bool m_has_display_link { false };
bool m_show_screen_number { false };
bool m_unresponsive { false };

// Need this to get private client connection stuff
Expand Down
166 changes: 163 additions & 3 deletions Userland/Services/WindowServer/Compositor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "ClientConnection.h"
#include "Event.h"
#include "EventLoop.h"
#include "MultiScaleBitmaps.h"
#include "Screen.h"
#include "Window.h"
#include "WindowManager.h"
Expand Down Expand Up @@ -76,7 +77,7 @@ const Gfx::Bitmap& Compositor::front_bitmap_for_screenshot(Badge<ClientConnectio
return *m_screen_data[screen.index()].m_front_bitmap;
}

void Compositor::ScreenData::init_bitmaps(Screen& screen)
void Compositor::ScreenData::init_bitmaps(Compositor& compositor, Screen& screen)
{
auto size = screen.size();

Expand All @@ -97,13 +98,21 @@ void Compositor::ScreenData::init_bitmaps(Screen& screen)

m_buffers_are_flipped = false;
m_screen_can_set_buffer = screen.can_set_buffer();

// Recreate the screen-number overlay as the Screen instances may have changed, or get rid of it if we no longer need it
if (compositor.showing_screen_numbers()) {
m_screen_number_overlay = compositor.create_overlay<ScreenNumberOverlay>(screen);
m_screen_number_overlay->set_enabled(true);
} else {
m_screen_number_overlay = nullptr;
}
}

void Compositor::init_bitmaps()
{
m_screen_data.resize(Screen::count());
Screen::for_each([&](auto& screen) {
m_screen_data[screen.index()].init_bitmaps(screen);
m_screen_data[screen.index()].init_bitmaps(*this, screen);
return IterationDecision::Continue;
});

Expand Down Expand Up @@ -143,6 +152,9 @@ void Compositor::compose()
recompute_occlusions();
}

// We should have recomputed occlusions if any overlay rects were changed
VERIFY(!m_overlay_rects_changed);

auto dirty_screen_rects = move(m_dirty_screen_rects);
auto* dnd_client = wm.dnd_client();
if (!m_last_geometry_label_damage_rect.is_empty() || !m_last_dnd_rect.is_empty() || (m_invalidated_cursor && dnd_client)) {
Expand Down Expand Up @@ -517,6 +529,11 @@ void Compositor::compose()
return is_overlapping;
}());

if (!m_overlay_list.is_empty()) {
// Render everything to the temporary buffer before we copy it back
render_overlays();
}

// Copy anything rendered to the temporary buffer to the back buffer
Screen::for_each([&](auto& screen) {
auto screen_rect = screen.rect();
Expand Down Expand Up @@ -828,6 +845,7 @@ void Compositor::screen_resolution_changed()

init_bitmaps();
invalidate_occlusions();
overlay_rects_changed();
compose();
}

Expand Down Expand Up @@ -914,6 +932,54 @@ void Compositor::change_cursor(const Cursor* cursor)
}
}

void Compositor::render_overlays()
{
// NOTE: overlays should always be rendered to the temporary buffer!
for (auto& overlay : m_overlay_list) {
for (auto* screen : overlay.m_screens) {
auto& screen_data = m_screen_data[screen->index()];
auto& painter = screen_data.overlay_painter();
screen_data.for_each_intersected_flushing_rect(overlay.current_render_rect(), [&](auto& intersected_overlay_rect) {
Gfx::PainterStateSaver saver(painter);
painter.add_clip_rect(intersected_overlay_rect);
painter.translate(overlay.m_current_rect.location());
overlay.render(painter, *screen);
return IterationDecision::Continue;
});
}
}
}

void Compositor::add_overlay(Overlay& overlay)
{
VERIFY(!overlay.m_list_node.is_in_list());
auto zorder = overlay.zorder();
bool did_insert = false;
for (auto& other_overlay : m_overlay_list) {
if (other_overlay.zorder() > zorder) {
m_overlay_list.insert_before(other_overlay, overlay);
did_insert = true;
break;
}
}
if (!did_insert)
m_overlay_list.append(overlay);

overlay.clear_invalidated();
overlay_rects_changed();
auto& rect = overlay.rect();
if (!rect.is_empty())
invalidate_screen(rect);
}

void Compositor::remove_overlay(Overlay& overlay)
{
auto& current_render_rect = overlay.current_render_rect();
if (!current_render_rect.is_empty())
invalidate_screen(current_render_rect);
m_overlay_list.remove(overlay);
}

void Compositor::ScreenData::draw_cursor(Screen& screen, const Gfx::IntRect& cursor_rect)
{
auto& wm = WindowManager::the();
Expand Down Expand Up @@ -944,6 +1010,11 @@ bool Compositor::ScreenData::restore_cursor_back(Screen& screen, Gfx::IntRect& l
return true;
}

void Compositor::update_fonts()
{
ScreenNumberOverlay::pick_font();
}

void Compositor::notify_display_links()
{
ClientConnection::for_each_client([](auto& client) {
Expand All @@ -966,6 +1037,35 @@ void Compositor::decrement_display_link_count(Badge<ClientConnection>)
m_display_link_notify_timer->stop();
}

void Compositor::invalidate_current_screen_number_rects()
{
for (auto& screen_data : m_screen_data) {
if (screen_data.m_screen_number_overlay)
screen_data.m_screen_number_overlay->invalidate();
}
}

void Compositor::increment_show_screen_number(Badge<ClientConnection>)
{
if (m_show_screen_number_count++ == 0) {
Screen::for_each([&](auto& screen) {
auto& screen_data = m_screen_data[screen.index()];
VERIFY(!screen_data.m_screen_number_overlay);
screen_data.m_screen_number_overlay = create_overlay<ScreenNumberOverlay>(screen);
screen_data.m_screen_number_overlay->set_enabled(true);
return IterationDecision::Continue;
});
}
}
void Compositor::decrement_show_screen_number(Badge<ClientConnection>)
{
if (--m_show_screen_number_count == 0) {
invalidate_current_screen_number_rects();
for (auto& screen_data : m_screen_data)
screen_data.m_screen_number_overlay = nullptr;
}
}

bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_window, const Gfx::IntRect& rect)
{
bool found_containing_window = false;
Expand All @@ -992,6 +1092,50 @@ bool Compositor::any_opaque_window_above_this_one_contains_rect(const Window& a_
return found_containing_window;
};

void Compositor::overlays_theme_changed()
{
for (auto& overlay : m_overlay_list)
overlay.theme_changed();
overlay_rects_changed();
}

void Compositor::overlay_rects_changed()
{
if (m_overlay_rects_changed)
return;
m_overlay_rects_changed = true;
m_invalidated_any = true;
invalidate_occlusions();
for (auto& rect : m_overlay_rects.rects())
invalidate_screen(rect);
}

void Compositor::recompute_overlay_rects()
{
// The purpose of this is to gather all areas that we will render over
// regular window contents. This effectively just forces those areas to
// be rendered as transparency areas, which allows us to render these
// flicker-free.
m_overlay_rects.clear_with_capacity();
for (auto& overlay : m_overlay_list) {
auto& render_rect = overlay.rect();
m_overlay_rects.add(render_rect);

// Save the rectangle we are using for rendering from now on
overlay.did_recompute_occlusions();

// Cache which screens this overlay are rendered on
overlay.m_screens.clear_with_capacity();
Screen::for_each([&](auto& screen) {
if (render_rect.intersects(screen.rect()))
overlay.m_screens.append(&screen);
return IterationDecision::Continue;
});

invalidate_screen(render_rect);
}
}

void Compositor::recompute_occlusions()
{
auto& wm = WindowManager::the();
Expand All @@ -1007,7 +1151,16 @@ void Compositor::recompute_occlusions()
return IterationDecision::Continue;
});

dbgln_if(OCCLUSIONS_DEBUG, "OCCLUSIONS:");
if (m_overlay_rects_changed) {
m_overlay_rects_changed = false;
recompute_overlay_rects();
}

if constexpr (OCCLUSIONS_DEBUG) {
dbgln("OCCLUSIONS:");
for (auto& rect : m_overlay_rects.rects())
dbgln(" overlay: {}", rect);
}

auto& main_screen = Screen::main();
if (auto* fullscreen_window = wm.active_fullscreen_window()) {
Expand Down Expand Up @@ -1127,6 +1280,13 @@ void Compositor::recompute_occlusions()
return IterationDecision::Continue;
});

if (!m_overlay_rects.is_empty() && m_overlay_rects.intersects(visible_opaque)) {
// In order to render overlays flicker-free we need to force these area into the
// temporary transparency rendering buffer
transparency_rects.add(m_overlay_rects.intersected(visible_opaque));
visible_opaque = visible_opaque.shatter(m_overlay_rects);
}

bool have_opaque = !visible_opaque.is_empty();
if (!transparency_rects.is_empty())
have_transparent = true;
Expand Down
Loading

0 comments on commit 41859ad

Please sign in to comment.