1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-22 17:25:07 +00:00

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.
This commit is contained in:
Andreas Kling 2019-03-17 04:23:54 +01:00
parent 2ac4f54724
commit 4e451c1e92
12 changed files with 95 additions and 39 deletions

View file

@ -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);
}