diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-03-17 04:23:54 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-03-17 04:23:54 +0100 |
commit | 4e451c1e92b36e57f3dd5cfe55517074da7e8e20 (patch) | |
tree | 041b466ee4b241eff10c470561139c5f66eb7d83 /LibGUI | |
parent | 2ac4f547240e0fc397ba71766643f5d228012130 (diff) | |
download | serenity-4e451c1e92b36e57f3dd5cfe55517074da7e8e20.zip |
Add client-side double buffering of window backing stores.
This prevents flicker and looks rather good. The main downside is that
resizing gets even more sluggish. That's the price we pay for now.
Diffstat (limited to 'LibGUI')
-rw-r--r-- | LibGUI/GEventLoop.h | 2 | ||||
-rw-r--r-- | LibGUI/GWindow.cpp | 87 | ||||
-rw-r--r-- | LibGUI/GWindow.h | 12 |
3 files changed, 72 insertions, 29 deletions
diff --git a/LibGUI/GEventLoop.h b/LibGUI/GEventLoop.h index 8133ef471a..325115e70a 100644 --- a/LibGUI/GEventLoop.h +++ b/LibGUI/GEventLoop.h @@ -39,7 +39,7 @@ public: WSAPI_ServerMessage sync_request(const WSAPI_ClientMessage& request, WSAPI_ServerMessage::Type response_type); - pid_t server_pid() const { return s_server_pid; } + static pid_t server_pid() { return s_server_pid; } private: void wait_for_event(); diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 5e246ac342..8486489f16 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -3,6 +3,7 @@ #include "GEventLoop.h" #include "GWidget.h" #include <SharedGraphics/GraphicsBitmap.h> +#include <SharedGraphics/Painter.h> #include <LibC/stdio.h> #include <LibC/stdlib.h> #include <LibC/unistd.h> @@ -166,33 +167,19 @@ void GWindow::event(GEvent& event) return; auto& paint_event = static_cast<GPaintEvent&>(event); auto rect = paint_event.rect(); - bool created_new_backing_store = !m_backing; - if (!m_backing) { - ASSERT(GEventLoop::main().server_pid()); - ASSERT(!paint_event.window_size().is_empty()); - Size new_backing_store_size = paint_event.window_size(); - size_t size_in_bytes = new_backing_store_size.area() * sizeof(RGBA32); - auto shared_buffer = SharedBuffer::create(GEventLoop::main().server_pid(), size_in_bytes); - ASSERT(shared_buffer); - m_backing = GraphicsBitmap::create_with_shared_buffer( - m_has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32, - *shared_buffer, - new_backing_store_size); - } + bool created_new_backing_store = !m_back_bitmap; + if (!m_back_bitmap) + m_back_bitmap = create_backing_bitmap(paint_event.window_size()); if (rect.is_empty() || created_new_backing_store) rect = m_main_widget->rect(); + m_main_widget->event(*make<GPaintEvent>(rect)); - if (created_new_backing_store) { - WSAPI_ClientMessage message; - message.type = WSAPI_ClientMessage::Type::SetWindowBackingStore; - message.window_id = m_window_id; - message.backing.bpp = 32; - message.backing.pitch = m_backing->pitch(); - message.backing.shared_buffer_id = m_backing->shared_buffer_id(); - message.backing.has_alpha_channel = m_backing->has_alpha_channel(); - message.backing.size = m_backing->size(); - GEventLoop::main().post_message_to_server(message); - } + + if (m_double_buffering_enabled) + flip(rect); + else if (created_new_backing_store) + set_current_backing_bitmap(*m_back_bitmap, true); + if (m_window_id) { WSAPI_ClientMessage message; message.type = WSAPI_ClientMessage::Type::DidFinishPainting; @@ -232,8 +219,8 @@ void GWindow::event(GEvent& event) if (event.type() == GEvent::Resize) { auto new_size = static_cast<GResizeEvent&>(event).size(); - if (m_backing && m_backing->size() != new_size) - m_backing = nullptr; + if (m_back_bitmap && m_back_bitmap->size() != new_size) + m_back_bitmap = nullptr; m_pending_paint_event_rects.clear(); m_rect_when_windowless = { { }, new_size }; m_main_widget->set_relative_rect({ { }, new_size }); @@ -328,6 +315,12 @@ void GWindow::set_has_alpha_channel(bool value) m_has_alpha_channel = value; } +void GWindow::set_double_buffering_enabled(bool value) +{ + ASSERT(!m_window_id); + m_double_buffering_enabled = value; +} + void GWindow::set_opacity(float opacity) { m_opacity_when_windowless = opacity; @@ -354,3 +347,45 @@ void GWindow::set_hovered_widget(GWidget* widget) if (m_hovered_widget) GEventLoop::main().post_event(*m_hovered_widget, make<GEvent>(GEvent::Enter)); } + +void GWindow::set_current_backing_bitmap(GraphicsBitmap& bitmap, bool flush_immediately) +{ + WSAPI_ClientMessage message; + message.type = WSAPI_ClientMessage::Type::SetWindowBackingStore; + message.window_id = m_window_id; + message.backing.bpp = 32; + message.backing.pitch = bitmap.pitch(); + message.backing.shared_buffer_id = bitmap.shared_buffer_id(); + message.backing.has_alpha_channel = bitmap.has_alpha_channel(); + message.backing.size = bitmap.size(); + message.backing.flush_immediately = flush_immediately; + GEventLoop::main().sync_request(message, WSAPI_ServerMessage::Type::DidSetWindowBackingStore); +} + +void GWindow::flip(const Rect& dirty_rect) +{ + swap(m_front_bitmap, m_back_bitmap); + + set_current_backing_bitmap(*m_front_bitmap); + + if (!m_back_bitmap || m_back_bitmap->size() != m_front_bitmap->size()) { + m_back_bitmap = create_backing_bitmap(m_front_bitmap->size()); + memcpy(m_back_bitmap->scanline(0), m_front_bitmap->scanline(0), m_front_bitmap->size().area() * sizeof(RGBA32)); + return; + } + + // Copy whatever was painted from the front to the back. + Painter painter(*m_back_bitmap); + painter.blit(dirty_rect.location(), *m_front_bitmap, dirty_rect); +} + +Retained<GraphicsBitmap> GWindow::create_backing_bitmap(const Size& size) +{ + ASSERT(GEventLoop::server_pid()); + ASSERT(!size.is_empty()); + size_t size_in_bytes = size.area() * sizeof(RGBA32); + auto shared_buffer = SharedBuffer::create(GEventLoop::server_pid(), size_in_bytes); + ASSERT(shared_buffer); + auto format = m_has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32; + return GraphicsBitmap::create_with_shared_buffer(format, *shared_buffer, size); +} diff --git a/LibGUI/GWindow.h b/LibGUI/GWindow.h index dfe69cb296..16abab6cbf 100644 --- a/LibGUI/GWindow.h +++ b/LibGUI/GWindow.h @@ -15,6 +15,7 @@ public: static GWindow* from_window_id(int); + void set_double_buffering_enabled(bool); void set_has_alpha_channel(bool); void set_opacity(float); @@ -68,7 +69,8 @@ public: const GWidget* hovered_widget() const { return m_hovered_widget.ptr(); } void set_hovered_widget(GWidget*); - GraphicsBitmap* backing() { return m_backing.ptr(); } + GraphicsBitmap* front_bitmap() { return m_front_bitmap.ptr(); } + GraphicsBitmap* back_bitmap() { return m_back_bitmap.ptr(); } Size size_increment() const { return m_size_increment; } void set_size_increment(const Size& increment) { m_size_increment = increment; } @@ -78,7 +80,12 @@ public: private: virtual const char* class_name() const override { return "GWindow"; } - RetainPtr<GraphicsBitmap> m_backing; + Retained<GraphicsBitmap> create_backing_bitmap(const Size&); + void set_current_backing_bitmap(GraphicsBitmap&, bool flush_immediately = false); + void flip(const Rect& dirty_rect); + + RetainPtr<GraphicsBitmap> m_front_bitmap; + RetainPtr<GraphicsBitmap> m_back_bitmap; int m_window_id { 0 }; float m_opacity_when_windowless { 1.0f }; GWidget* m_main_widget { nullptr }; @@ -93,5 +100,6 @@ private: bool m_is_active { false }; bool m_should_exit_app_on_close { false }; bool m_has_alpha_channel { false }; + bool m_double_buffering_enabled { true }; }; |