1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 08:47:34 +00:00

LibGUI: Add a WindowBackingStore class

Instead of storing the back/front buffers of a GUI::Window as just
Gfx::Bitmap, wrap them in a WindowBackingStore class.

This will allow us to add more information alongside the bitmaps while
keeping the back/front swapping logic simple.
This commit is contained in:
Andreas Kling 2021-01-15 09:38:12 +01:00
parent 7e71de8f1f
commit 96f8fcdcba
2 changed files with 71 additions and 44 deletions

View file

@ -48,6 +48,22 @@
namespace GUI { namespace GUI {
class WindowBackingStore {
public:
WindowBackingStore(NonnullRefPtr<Gfx::Bitmap> bitmap)
: m_bitmap(bitmap)
{
}
Gfx::Bitmap& bitmap() { return *m_bitmap; }
const Gfx::Bitmap& bitmap() const { return *m_bitmap; }
Gfx::IntSize size() const { return m_bitmap->size(); }
private:
NonnullRefPtr<Gfx::Bitmap> m_bitmap;
};
static NeverDestroyed<HashTable<Window*>> all_windows; static NeverDestroyed<HashTable<Window*>> all_windows;
static NeverDestroyed<HashMap<int, Window*>> reified_windows; static NeverDestroyed<HashMap<int, Window*>> reified_windows;
@ -156,8 +172,8 @@ void Window::server_did_destroy()
m_window_id = 0; m_window_id = 0;
m_visible = false; m_visible = false;
m_pending_paint_event_rects.clear(); m_pending_paint_event_rects.clear();
m_back_bitmap = nullptr; m_back_store = nullptr;
m_front_bitmap = nullptr; m_front_store = nullptr;
m_cursor = Gfx::StandardCursor::None; m_cursor = Gfx::StandardCursor::None;
} }
@ -226,10 +242,10 @@ void Window::set_rect(const Gfx::IntRect& a_rect)
return; return;
} }
auto window_rect = WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowRect>(m_window_id, a_rect)->rect(); auto window_rect = WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowRect>(m_window_id, a_rect)->rect();
if (m_back_bitmap && m_back_bitmap->size() != window_rect.size()) if (m_back_store && m_back_store->size() != window_rect.size())
m_back_bitmap = nullptr; m_back_store = nullptr;
if (m_front_bitmap && m_front_bitmap->size() != window_rect.size()) if (m_front_store && m_front_store->size() != window_rect.size())
m_front_bitmap = nullptr; m_front_store = nullptr;
if (m_main_widget) if (m_main_widget)
m_main_widget->resize(window_rect.size()); m_main_widget->resize(window_rect.size());
} }
@ -324,21 +340,21 @@ void Window::handle_multi_paint_event(MultiPaintEvent& event)
return; return;
auto rects = event.rects(); auto rects = event.rects();
ASSERT(!rects.is_empty()); ASSERT(!rects.is_empty());
if (m_back_bitmap && m_back_bitmap->size() != event.window_size()) { if (m_back_store && m_back_store->size() != event.window_size()) {
// Eagerly discard the backing store if we learn from this paint event that it needs to be bigger. // Eagerly discard the backing store if we learn from this paint event that it needs to be bigger.
// Otherwise we would have to wait for a resize event to tell us. This way we don't waste the // Otherwise we would have to wait for a resize event to tell us. This way we don't waste the
// effort on painting into an undersized bitmap that will be thrown away anyway. // effort on painting into an undersized bitmap that will be thrown away anyway.
m_back_bitmap = nullptr; m_back_store = nullptr;
} }
bool created_new_backing_store = !m_back_bitmap; bool created_new_backing_store = !m_back_store;
if (!m_back_bitmap) { if (!m_back_store) {
m_back_bitmap = create_backing_bitmap(event.window_size()); m_back_store = create_backing_store(event.window_size());
ASSERT(m_back_bitmap); ASSERT(m_back_store);
} else if (m_double_buffering_enabled) { } else if (m_double_buffering_enabled) {
bool still_has_pixels = m_back_bitmap->shared_buffer()->set_nonvolatile(); bool still_has_pixels = m_back_store->bitmap().shared_buffer()->set_nonvolatile();
if (!still_has_pixels) { if (!still_has_pixels) {
m_back_bitmap = create_backing_bitmap(event.window_size()); m_back_store = create_backing_store(event.window_size());
ASSERT(m_back_bitmap); ASSERT(m_back_store);
created_new_backing_store = true; created_new_backing_store = true;
} }
} }
@ -357,7 +373,7 @@ void Window::handle_multi_paint_event(MultiPaintEvent& event)
if (m_double_buffering_enabled) if (m_double_buffering_enabled)
flip(rects); flip(rects);
else if (created_new_backing_store) else if (created_new_backing_store)
set_current_backing_bitmap(*m_back_bitmap, true); set_current_backing_store(*m_back_store, true);
if (is_visible()) { if (is_visible()) {
Vector<Gfx::IntRect> rects_to_send; Vector<Gfx::IntRect> rects_to_send;
@ -383,8 +399,8 @@ void Window::handle_key_event(KeyEvent& event)
void Window::handle_resize_event(ResizeEvent& event) void Window::handle_resize_event(ResizeEvent& event)
{ {
auto new_size = event.size(); auto new_size = event.size();
if (m_back_bitmap && m_back_bitmap->size() != new_size) if (m_back_store && m_back_store->size() != new_size)
m_back_bitmap = nullptr; m_back_store = nullptr;
if (!m_pending_paint_event_rects.is_empty()) { if (!m_pending_paint_event_rects.is_empty()) {
m_pending_paint_event_rects.clear_with_capacity(); m_pending_paint_event_rects.clear_with_capacity();
m_pending_paint_event_rects.append({ {}, new_size }); m_pending_paint_event_rects.append({ {}, new_size });
@ -624,8 +640,8 @@ void Window::set_has_alpha_channel(bool value)
return; return;
m_pending_paint_event_rects.clear(); m_pending_paint_event_rects.clear();
m_back_bitmap = nullptr; m_back_store = nullptr;
m_front_bitmap = nullptr; m_front_store = nullptr;
WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowHasAlphaChannel>(m_window_id, value); WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowHasAlphaChannel>(m_window_id, value);
update(); update();
@ -659,31 +675,32 @@ void Window::set_hovered_widget(Widget* widget)
Core::EventLoop::current().post_event(*m_hovered_widget, make<Event>(Event::Enter)); Core::EventLoop::current().post_event(*m_hovered_widget, make<Event>(Event::Enter));
} }
void Window::set_current_backing_bitmap(Gfx::Bitmap& bitmap, bool flush_immediately) void Window::set_current_backing_store(WindowBackingStore& backing_store, bool flush_immediately)
{ {
auto& bitmap = backing_store.bitmap();
WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowBackingStore>(m_window_id, 32, bitmap.pitch(), bitmap.shbuf_id(), bitmap.has_alpha_channel(), bitmap.size(), flush_immediately); WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowBackingStore>(m_window_id, 32, bitmap.pitch(), bitmap.shbuf_id(), bitmap.has_alpha_channel(), bitmap.size(), flush_immediately);
} }
void Window::flip(const Vector<Gfx::IntRect, 32>& dirty_rects) void Window::flip(const Vector<Gfx::IntRect, 32>& dirty_rects)
{ {
swap(m_front_bitmap, m_back_bitmap); swap(m_front_store, m_back_store);
set_current_backing_bitmap(*m_front_bitmap); set_current_backing_store(*m_front_store);
if (!m_back_bitmap || m_back_bitmap->size() != m_front_bitmap->size()) { if (!m_back_store || m_back_store->size() != m_front_store->size()) {
m_back_bitmap = create_backing_bitmap(m_front_bitmap->size()); m_back_store = create_backing_store(m_front_store->size());
ASSERT(m_back_bitmap); ASSERT(m_back_store);
memcpy(m_back_bitmap->scanline(0), m_front_bitmap->scanline(0), m_front_bitmap->size_in_bytes()); memcpy(m_back_store->bitmap().scanline(0), m_front_store->bitmap().scanline(0), m_front_store->bitmap().size_in_bytes());
m_back_bitmap->shared_buffer()->set_volatile(); m_back_store->bitmap().shared_buffer()->set_volatile();
return; return;
} }
// Copy whatever was painted from the front to the back. // Copy whatever was painted from the front to the back.
Painter painter(*m_back_bitmap); Painter painter(m_back_store->bitmap());
for (auto& dirty_rect : dirty_rects) for (auto& dirty_rect : dirty_rects)
painter.blit(dirty_rect.location(), *m_front_bitmap, dirty_rect); painter.blit(dirty_rect.location(), m_front_store->bitmap(), dirty_rect);
m_back_bitmap->shared_buffer()->set_volatile(); m_back_store->bitmap().shared_buffer()->set_volatile();
} }
RefPtr<Gfx::Bitmap> Window::create_shared_bitmap(Gfx::BitmapFormat format, const Gfx::IntSize& size) RefPtr<Gfx::Bitmap> Window::create_shared_bitmap(Gfx::BitmapFormat format, const Gfx::IntSize& size)
@ -698,10 +715,13 @@ RefPtr<Gfx::Bitmap> Window::create_shared_bitmap(Gfx::BitmapFormat format, const
return Gfx::Bitmap::create_with_shared_buffer(format, *shared_buffer, size); return Gfx::Bitmap::create_with_shared_buffer(format, *shared_buffer, size);
} }
RefPtr<Gfx::Bitmap> Window::create_backing_bitmap(const Gfx::IntSize& size) OwnPtr<WindowBackingStore> Window::create_backing_store(const Gfx::IntSize& size)
{ {
auto format = m_has_alpha_channel ? Gfx::BitmapFormat::RGBA32 : Gfx::BitmapFormat::RGB32; auto format = m_has_alpha_channel ? Gfx::BitmapFormat::RGBA32 : Gfx::BitmapFormat::RGB32;
return create_shared_bitmap(format, size); auto bitmap = create_shared_bitmap(format, size);
if (!bitmap)
return {};
return make<WindowBackingStore>(bitmap.release_nonnull());
} }
void Window::set_modal(bool modal) void Window::set_modal(bool modal)
@ -846,14 +866,14 @@ void Window::notify_state_changed(Badge<WindowServerConnection>, bool minimized,
// When double buffering is enabled, minimization/occlusion means we can mark the front bitmap volatile (in addition to the back bitmap.) // When double buffering is enabled, minimization/occlusion means we can mark the front bitmap volatile (in addition to the back bitmap.)
// When double buffering is disabled, there is only the back bitmap (which we can now mark volatile!) // When double buffering is disabled, there is only the back bitmap (which we can now mark volatile!)
RefPtr<Gfx::Bitmap>& bitmap = m_double_buffering_enabled ? m_front_bitmap : m_back_bitmap; auto& store = m_double_buffering_enabled ? m_front_store : m_back_store;
if (!bitmap) if (!store)
return; return;
if (minimized || occluded) { if (minimized || occluded) {
bitmap->shared_buffer()->set_volatile(); store->bitmap().shared_buffer()->set_volatile();
} else { } else {
if (!bitmap->shared_buffer()->set_nonvolatile()) { if (!store->bitmap().shared_buffer()->set_nonvolatile()) {
bitmap = nullptr; store = nullptr;
update(); update();
} }
} }
@ -962,4 +982,9 @@ bool Window::is_active() const
return this == Application::the()->active_window(); return this == Application::the()->active_window();
} }
Gfx::Bitmap* Window::back_bitmap()
{
return m_back_store ? &m_back_store->bitmap() : nullptr;
}
} }

View file

@ -40,6 +40,8 @@
namespace GUI { namespace GUI {
class WindowBackingStore;
class Window : public Core::Object { class Window : public Core::Object {
C_OBJECT(Window) C_OBJECT(Window)
public: public:
@ -157,8 +159,7 @@ public:
const Widget* hovered_widget() const { return m_hovered_widget.ptr(); } const Widget* hovered_widget() const { return m_hovered_widget.ptr(); }
void set_hovered_widget(Widget*); void set_hovered_widget(Widget*);
Gfx::Bitmap* front_bitmap() { return m_front_bitmap.ptr(); } Gfx::Bitmap* back_bitmap();
Gfx::Bitmap* back_bitmap() { return m_back_bitmap.ptr(); }
Gfx::IntSize size_increment() const { return m_size_increment; } Gfx::IntSize size_increment() const { return m_size_increment; }
void set_size_increment(const Gfx::IntSize&); void set_size_increment(const Gfx::IntSize&);
@ -221,14 +222,15 @@ private:
void server_did_destroy(); void server_did_destroy();
RefPtr<Gfx::Bitmap> create_backing_bitmap(const Gfx::IntSize&); OwnPtr<WindowBackingStore> create_backing_store(const Gfx::IntSize&);
RefPtr<Gfx::Bitmap> create_shared_bitmap(Gfx::BitmapFormat, const Gfx::IntSize&); RefPtr<Gfx::Bitmap> create_shared_bitmap(Gfx::BitmapFormat, const Gfx::IntSize&);
void set_current_backing_bitmap(Gfx::Bitmap&, bool flush_immediately = false); void set_current_backing_store(WindowBackingStore&, bool flush_immediately = false);
void flip(const Vector<Gfx::IntRect, 32>& dirty_rects); void flip(const Vector<Gfx::IntRect, 32>& dirty_rects);
void force_update(); void force_update();
RefPtr<Gfx::Bitmap> m_front_bitmap; OwnPtr<WindowBackingStore> m_front_store;
RefPtr<Gfx::Bitmap> m_back_bitmap; OwnPtr<WindowBackingStore> m_back_store;
RefPtr<Gfx::Bitmap> m_icon; RefPtr<Gfx::Bitmap> m_icon;
RefPtr<Gfx::Bitmap> m_custom_cursor; RefPtr<Gfx::Bitmap> m_custom_cursor;
int m_window_id { 0 }; int m_window_id { 0 };