mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05: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:
parent
2ac4f54724
commit
4e451c1e92
12 changed files with 95 additions and 39 deletions
|
@ -88,6 +88,7 @@ int main(int argc, char** argv)
|
|||
make_shell(ptm_fd);
|
||||
|
||||
auto* window = new GWindow;
|
||||
window->set_double_buffering_enabled(false);
|
||||
window->set_should_exit_app_on_close(true);
|
||||
|
||||
Terminal terminal(ptm_fd);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ GraphicsBitmap::GraphicsBitmap(Format format, const Size& size, RGBA32* data)
|
|||
{
|
||||
}
|
||||
|
||||
RetainPtr<GraphicsBitmap> GraphicsBitmap::create_with_shared_buffer(Format format, Retained<SharedBuffer>&& shared_buffer, const Size& size)
|
||||
Retained<GraphicsBitmap> GraphicsBitmap::create_with_shared_buffer(Format format, Retained<SharedBuffer>&& shared_buffer, const Size& size)
|
||||
{
|
||||
return adopt(*new GraphicsBitmap(format, move(shared_buffer), size));
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ public:
|
|||
static Retained<GraphicsBitmap> create(Format, const Size&);
|
||||
static Retained<GraphicsBitmap> create_wrapper(Format, const Size&, RGBA32*);
|
||||
static RetainPtr<GraphicsBitmap> load_from_file(Format, const String& path, const Size&);
|
||||
static RetainPtr<GraphicsBitmap> create_with_shared_buffer(Format, Retained<SharedBuffer>&&, const Size&);
|
||||
static Retained<GraphicsBitmap> create_with_shared_buffer(Format, Retained<SharedBuffer>&&, const Size&);
|
||||
~GraphicsBitmap();
|
||||
|
||||
RGBA32* scanline(int y);
|
||||
|
|
|
@ -28,7 +28,7 @@ Painter::Painter(GraphicsBitmap& bitmap)
|
|||
#ifdef LIBGUI
|
||||
Painter::Painter(GWidget& widget)
|
||||
: m_window(widget.window())
|
||||
, m_target(*m_window->backing())
|
||||
, m_target(*m_window->back_bitmap())
|
||||
{
|
||||
m_state_stack.append(State());
|
||||
state().font = &widget.font();
|
||||
|
|
|
@ -87,6 +87,7 @@ struct WSAPI_ServerMessage {
|
|||
Greeting,
|
||||
DidGetClipboardContents,
|
||||
DidSetClipboardContents,
|
||||
DidSetWindowBackingStore,
|
||||
};
|
||||
Type type { Invalid };
|
||||
int window_id { -1 };
|
||||
|
@ -191,6 +192,7 @@ struct WSAPI_ClientMessage {
|
|||
size_t pitch;
|
||||
int shared_buffer_id;
|
||||
bool has_alpha_channel;
|
||||
bool flush_immediately;
|
||||
} backing;
|
||||
struct {
|
||||
int shared_buffer_id;
|
||||
|
|
|
@ -357,6 +357,7 @@ void WSClientConnection::handle_request(WSAPICreateWindowRequest& request)
|
|||
window->set_opacity(request.opacity());
|
||||
window->set_size_increment(request.size_increment());
|
||||
window->set_base_size(request.base_size());
|
||||
window->invalidate();
|
||||
m_windows.set(window_id, move(window));
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidCreateWindow;
|
||||
|
@ -451,10 +452,16 @@ void WSClientConnection::handle_request(WSAPISetWindowBackingStoreRequest& reque
|
|||
request.has_alpha_channel() ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32,
|
||||
*shared_buffer,
|
||||
request.size());
|
||||
if (!backing_store)
|
||||
return;
|
||||
window.set_backing_store(move(backing_store));
|
||||
window.invalidate();
|
||||
|
||||
if (request.flush_immediately())
|
||||
window.invalidate();
|
||||
|
||||
WSAPI_ServerMessage response;
|
||||
response.type = WSAPI_ServerMessage::Type::DidSetWindowBackingStore;
|
||||
response.window_id = window_id;
|
||||
response.backing.shared_buffer_id = request.shared_buffer_id();
|
||||
post_message(response);
|
||||
}
|
||||
|
||||
void WSClientConnection::handle_request(WSAPISetGlobalCursorTrackingRequest& request)
|
||||
|
|
|
@ -320,7 +320,7 @@ private:
|
|||
|
||||
class WSAPISetWindowBackingStoreRequest final : public WSAPIClientRequest {
|
||||
public:
|
||||
explicit WSAPISetWindowBackingStoreRequest(int client_id, int window_id, int shared_buffer_id, const Size& size, size_t bpp, size_t pitch, bool has_alpha_channel)
|
||||
explicit WSAPISetWindowBackingStoreRequest(int client_id, int window_id, int shared_buffer_id, const Size& size, size_t bpp, size_t pitch, bool has_alpha_channel, bool flush_immediately)
|
||||
: WSAPIClientRequest(WSMessage::APISetWindowBackingStoreRequest, client_id)
|
||||
, m_client_id(client_id)
|
||||
, m_window_id(window_id)
|
||||
|
@ -329,6 +329,7 @@ public:
|
|||
, m_bpp(bpp)
|
||||
, m_pitch(pitch)
|
||||
, m_has_alpha_channel(has_alpha_channel)
|
||||
, m_flush_immediately(flush_immediately)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -339,6 +340,7 @@ public:
|
|||
size_t bpp() const { return m_bpp; }
|
||||
size_t pitch() const { return m_pitch; }
|
||||
bool has_alpha_channel() const { return m_has_alpha_channel; }
|
||||
bool flush_immediately() const { return m_flush_immediately; }
|
||||
|
||||
private:
|
||||
int m_client_id { 0 };
|
||||
|
@ -348,6 +350,7 @@ private:
|
|||
size_t m_bpp;
|
||||
size_t m_pitch;
|
||||
bool m_has_alpha_channel;
|
||||
bool m_flush_immediately;
|
||||
};
|
||||
|
||||
class WSAPISetWindowRectRequest final : public WSAPIClientRequest {
|
||||
|
|
|
@ -321,7 +321,7 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess
|
|||
post_message(client, make<WSAPIGetWindowBackingStoreRequest>(client_id, message.window_id));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::SetWindowBackingStore:
|
||||
post_message(client, make<WSAPISetWindowBackingStoreRequest>(client_id, message.window_id, message.backing.shared_buffer_id, message.backing.size, message.backing.bpp, message.backing.pitch, message.backing.has_alpha_channel));
|
||||
post_message(client, make<WSAPISetWindowBackingStoreRequest>(client_id, message.window_id, message.backing.shared_buffer_id, message.backing.size, message.backing.bpp, message.backing.pitch, message.backing.has_alpha_channel, message.backing.flush_immediately));
|
||||
break;
|
||||
case WSAPI_ClientMessage::Type::SetGlobalCursorTracking:
|
||||
post_message(client, make<WSAPISetGlobalCursorTrackingRequest>(client_id, message.window_id, message.value));
|
||||
|
|
|
@ -916,8 +916,6 @@ void WSWindowManager::compose()
|
|||
|
||||
for_each_visible_window_from_back_to_front([&] (WSWindow& window) {
|
||||
RetainPtr<GraphicsBitmap> backing_store = window.backing_store();
|
||||
if (!backing_store)
|
||||
return IterationDecision::Continue;
|
||||
if (!any_dirty_rect_intersects_window(window))
|
||||
return IterationDecision::Continue;
|
||||
PainterStateSaver saver(*m_back_painter);
|
||||
|
@ -928,6 +926,8 @@ void WSWindowManager::compose()
|
|||
PainterStateSaver saver(*m_back_painter);
|
||||
m_back_painter->set_clip_rect(dirty_rect);
|
||||
paint_window_frame(window);
|
||||
if (!backing_store)
|
||||
continue;
|
||||
Rect dirty_rect_in_window_coordinates = Rect::intersection(dirty_rect, window.rect());
|
||||
if (dirty_rect_in_window_coordinates.is_empty())
|
||||
continue;
|
||||
|
|
Loading…
Add table
Reference in a new issue