Skip to content

Commit

Permalink
Everywhere: Use IOSurface as backing store on macOS
Browse files Browse the repository at this point in the history
Using mmap-allocated memory for backing stores does not allow us to
benefit from using GPU-accelerated painting, because all the performance
increase we get is mostly negated by reading the GPU-allocated texture
back into RAM, so it can be shared with the browser process.

With IOSurface, we get a framebuffer that is both shareable between
processes and can be used as underlying memory for an OpenGL/Metal
texture.

This change does not yet benefit from using IOSurface and merely wraps
them into Gfx::Bitmap to be used by the CPU painter.
  • Loading branch information
kalenikaliaksandr authored and awesomekling committed Jun 24, 2024
1 parent e37071a commit c92f8ab
Show file tree
Hide file tree
Showing 15 changed files with 266 additions and 56 deletions.
6 changes: 6 additions & 0 deletions Ladybird/AppKit/main.mm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <LibWebView/Database.h>
#include <LibWebView/ProcessManager.h>
#include <LibWebView/URL.h>
#include <LibWebView/ViewImplementation.h>
#include <LibWebView/WebContentClient.h>

#import <Application/Application.h>
#import <Application/ApplicationDelegate.h>
Expand Down Expand Up @@ -123,6 +125,10 @@ static void open_urls_from_client(Vector<ByteString> const& raw_urls, NewWindow
mach_port_server->on_receive_child_mach_port = [](auto pid, auto port) {
WebView::ProcessManager::the().add_process(pid, move(port));
};
mach_port_server->on_receive_backing_stores = [](Ladybird::MachPortServer::BackingStoresMessage message) {
auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id);
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
};

auto database = TRY(WebView::Database::create());
auto cookie_jar = TRY(WebView::CookieJar::create(*database));
Expand Down
41 changes: 28 additions & 13 deletions Ladybird/MachPortServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "MachPortServer.h"
#include <AK/Debug.h>
#include <LibCore/Platform/MachMessageTypes.h>
#include <LibCore/Platform/ProcessStatisticsMach.h>

namespace Ladybird {
Expand Down Expand Up @@ -55,8 +56,7 @@ ErrorOr<void> MachPortServer::allocate_server_port()
void MachPortServer::thread_loop()
{
while (!m_should_stop.load(MemoryOrder::memory_order_acquire)) {

Core::Platform::ParentPortMessage message {};
Core::Platform::ReceivedMachMessage message {};

// Get the pid of the child from the audit trailer so we can associate the port w/it
mach_msg_options_t const options = MACH_RCV_MSG | MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
Expand All @@ -68,23 +68,38 @@ void MachPortServer::thread_loop()
break;
}

if (message.header.msgh_id != Core::Platform::SELF_TASK_PORT_MESSAGE_ID) {
dbgln("Received message with id {}, ignoring", message.header.msgh_id);
if (message.header.msgh_id == Core::Platform::BACKING_STORE_IOSURFACES_MESSAGE_ID) {
auto pid = static_cast<pid_t>(message.body.parent_iosurface.trailer.msgh_audit.val[5]);
auto const& backing_stores_message = message.body.parent_iosurface;
auto front_child_port = Core::MachPort::adopt_right(backing_stores_message.front_descriptor.name, Core::MachPort::PortRight::Send);
auto back_child_port = Core::MachPort::adopt_right(backing_stores_message.back_descriptor.name, Core::MachPort::PortRight::Send);
auto const& metadata = backing_stores_message.metadata;
if (on_receive_backing_stores)
on_receive_backing_stores({ .pid = pid,
.page_id = metadata.page_id,
.front_backing_store_id = metadata.front_backing_store_id,
.back_backing_store_id = metadata.back_backing_store_id,
.front_backing_store_port = move(front_child_port),
.back_backing_store_port = move(back_child_port) });
continue;
}

if (MACH_MSGH_BITS_LOCAL(message.header.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND) {
dbgln("Received message with invalid local port rights {}, ignoring", MACH_MSGH_BITS_LOCAL(message.header.msgh_bits));
if (message.header.msgh_id == Core::Platform::SELF_TASK_PORT_MESSAGE_ID) {
if (MACH_MSGH_BITS_LOCAL(message.header.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND) {
dbgln("Received message with invalid local port rights {}, ignoring", MACH_MSGH_BITS_LOCAL(message.header.msgh_bits));
continue;
}

auto const& task_port_message = message.body.parent;
auto pid = static_cast<pid_t>(task_port_message.trailer.msgh_audit.val[5]);
auto child_port = Core::MachPort::adopt_right(task_port_message.port_descriptor.name, Core::MachPort::PortRight::Send);
dbgln_if(MACH_PORT_DEBUG, "Received child port {:x} from pid {}", child_port.port(), pid);
if (on_receive_child_mach_port)
on_receive_child_mach_port(pid, move(child_port));
continue;
}

auto pid = static_cast<pid_t>(message.trailer.msgh_audit.val[5]);
auto child_port = Core::MachPort::adopt_right(message.port_descriptor.name, Core::MachPort::PortRight::Send);
dbgln_if(MACH_PORT_DEBUG, "Received child port {:x} from pid {}", child_port.port(), pid);

if (on_receive_child_mach_port)
on_receive_child_mach_port(pid, move(child_port));
dbgln("Received message with id {}, ignoring", message.header.msgh_id);
}
}

}
9 changes: 9 additions & 0 deletions Ladybird/MachPortServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ class MachPortServer {
bool is_initialized();

Function<void(pid_t, Core::MachPort)> on_receive_child_mach_port;
struct BackingStoresMessage {
pid_t pid { -1 };
u64 page_id { 0 };
i32 front_backing_store_id { 0 };
i32 back_backing_store_id { 0 };
Core::MachPort front_backing_store_port;
Core::MachPort back_backing_store_port;
};
Function<void(BackingStoresMessage)> on_receive_backing_stores;

ByteString const& server_port_name() const { return m_server_port_name; }

Expand Down
4 changes: 4 additions & 0 deletions Ladybird/Qt/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
mach_port_server->on_receive_child_mach_port = [](auto pid, auto port) {
WebView::ProcessManager::the().add_process(pid, move(port));
};
mach_port_server->on_receive_backing_stores = [](Ladybird::MachPortServer::BackingStoresMessage message) {
auto view = WebView::WebContentClient::view_for_pid_and_page_id(message.pid, message.page_id);
view->did_allocate_iosurface_backing_stores(message.front_backing_store_id, move(message.front_backing_store_port), message.back_backing_store_id, move(message.back_backing_store_port));
};
#endif

RefPtr<WebView::Database> database;
Expand Down
3 changes: 2 additions & 1 deletion Ladybird/WebContent/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)

#if defined(AK_OS_MACOS)
if (!mach_server_name.is_empty()) {
Core::Platform::register_with_mach_server(mach_server_name);
auto server_port = Core::Platform::register_with_mach_server(mach_server_name);
WebContent::BackingStoreManager::set_browser_mach_port(move(server_port));
}
#endif

Expand Down
4 changes: 4 additions & 0 deletions Userland/Libraries/LibCore/Forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,8 @@ class UDPSocket;

enum class TimerShouldFireWhenNotVisible;

#ifdef AK_OS_MACH
class MachPort;
#endif

}
65 changes: 65 additions & 0 deletions Userland/Libraries/LibCore/Platform/MachMessageTypes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2024, Andrew Kaster <[email protected]>
* Copyright (c) 2024, Aliaksandr Kalenik <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <AK/Platform.h>

#if !defined(AK_OS_MACH)
# error "This file is only available on Mach platforms"
#endif

#include <mach/mach.h>

namespace Core::Platform {

struct MessageBodyWithSelfTaskPort {
mach_msg_body_t body;
mach_msg_port_descriptor_t port_descriptor;
mach_msg_audit_trailer_t trailer;
};

struct MessageWithSelfTaskPort {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t port_descriptor;
};

struct BackingStoreMetadata {
u64 page_id { 0 };
i32 back_backing_store_id { 0 };
i32 front_backing_store_id { 0 };
};

struct MessageBodyWithBackingStores {
mach_msg_body_t body;
mach_msg_port_descriptor_t front_descriptor;
mach_msg_port_descriptor_t back_descriptor;
BackingStoreMetadata metadata;
mach_msg_audit_trailer_t trailer;
};

struct MessageWithBackingStores {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t front_descriptor;
mach_msg_port_descriptor_t back_descriptor;
BackingStoreMetadata metadata;
};

struct ReceivedMachMessage {
mach_msg_header_t header;
union {
MessageBodyWithSelfTaskPort parent;
MessageBodyWithBackingStores parent_iosurface;
} body;
};

static constexpr mach_msg_id_t SELF_TASK_PORT_MESSAGE_ID = 0x1234CAFE;
static constexpr mach_msg_id_t BACKING_STORE_IOSURFACES_MESSAGE_ID = 0x1234CAFF;

}
11 changes: 7 additions & 4 deletions Userland/Libraries/LibCore/Platform/ProcessStatisticsMach.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <AK/ByteString.h>
#include <AK/Time.h>
#include <LibCore/MachPort.h>
#include <LibCore/Platform/MachMessageTypes.h>
#include <LibCore/Platform/ProcessStatisticsMach.h>

namespace Core::Platform {
Expand Down Expand Up @@ -75,17 +76,17 @@ ErrorOr<void> update_process_statistics(ProcessStatistics& statistics)
return {};
}

void register_with_mach_server(ByteString const& server_name)
MachPort register_with_mach_server(ByteString const& server_name)
{
auto server_port_or_error = Core::MachPort::look_up_from_bootstrap_server(server_name);
if (server_port_or_error.is_error()) {
dbgln("Failed to lookup server port: {}", server_port_or_error.error());
return;
VERIFY_NOT_REACHED();
}
auto server_port = server_port_or_error.release_value();

// Send our own task port to the server so they can query statistics about us
ChildPortMessage message {};
MessageWithSelfTaskPort message {};
message.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSGH_BITS_ZERO) | MACH_MSGH_BITS_COMPLEX;
message.header.msgh_size = sizeof(message);
message.header.msgh_remote_port = server_port.port();
Expand All @@ -101,8 +102,10 @@ void register_with_mach_server(ByteString const& server_name)
auto const send_result = mach_msg(&message.header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, message.header.msgh_size, 0, MACH_PORT_NULL, timeout, MACH_PORT_NULL);
if (send_result != KERN_SUCCESS) {
dbgln("Failed to send message to server: {}", mach_error_string(send_result));
return;
VERIFY_NOT_REACHED();
}

return server_port;
}

}
17 changes: 1 addition & 16 deletions Userland/Libraries/LibCore/Platform/ProcessStatisticsMach.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,6 @@

namespace Core::Platform {

struct ChildPortMessage {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t port_descriptor;
};

struct ParentPortMessage {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_port_descriptor_t port_descriptor;
mach_msg_audit_trailer_t trailer; // for the child's pid
};

static constexpr mach_msg_id_t SELF_TASK_PORT_MESSAGE_ID = 0x1234CAFE;

void register_with_mach_server(ByteString const& server_name);
MachPort register_with_mach_server(ByteString const& server_name);

}
31 changes: 31 additions & 0 deletions Userland/Libraries/LibWebView/ViewImplementation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
#include <LibWeb/Infra/Strings.h>
#include <LibWebView/ViewImplementation.h>

#ifdef AK_OS_MACOS
# include <LibCore/IOSurface.h>
# include <LibCore/MachPort.h>
#endif

namespace WebView {

ViewImplementation::ViewImplementation()
Expand Down Expand Up @@ -397,6 +402,32 @@ void ViewImplementation::did_allocate_backing_stores(Badge<WebContentClient>, i3
m_client_state.back_bitmap.id = back_bitmap_id;
}

#ifdef AK_OS_MACOS
void ViewImplementation::did_allocate_iosurface_backing_stores(i32 front_id, Core::MachPort&& front_port, i32 back_id, Core::MachPort&& back_port)
{
if (m_client_state.has_usable_bitmap) {
// NOTE: We keep the outgoing front bitmap as a backup so we have something to paint until we get a new one.
m_backup_bitmap = m_client_state.front_bitmap.bitmap;
m_backup_bitmap_size = m_client_state.front_bitmap.last_painted_size;
}
m_client_state.has_usable_bitmap = false;

auto front_iosurface = Core::IOSurfaceHandle::from_mach_port(move(front_port));
auto back_iosurface = Core::IOSurfaceHandle::from_mach_port(move(back_port));

auto front_size = Gfx::IntSize { front_iosurface.width(), front_iosurface.height() };
auto back_size = Gfx::IntSize { back_iosurface.width(), back_iosurface.height() };

auto front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, front_size, front_size.width() * front_iosurface.bytes_per_element(), front_iosurface.data(), [handle = move(front_iosurface)] {});
auto back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, back_size, back_size.width() * back_iosurface.bytes_per_element(), back_iosurface.data(), [handle = move(back_iosurface)] {});

m_client_state.front_bitmap.bitmap = front_bitmap.release_value_but_fixme_should_propagate_errors();
m_client_state.front_bitmap.id = front_id;
m_client_state.back_bitmap.bitmap = back_bitmap.release_value_but_fixme_should_propagate_errors();
m_client_state.back_bitmap.id = back_id;
}
#endif

void ViewImplementation::handle_resize()
{
client().async_set_viewport_size(page_id(), this->viewport_size());
Expand Down
4 changes: 4 additions & 0 deletions Userland/Libraries/LibWebView/ViewImplementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <AK/LexicalPath.h>
#include <AK/Queue.h>
#include <AK/String.h>
#include <LibCore/Forward.h>
#include <LibCore/Promise.h>
#include <LibGfx/Forward.h>
#include <LibGfx/StandardCursor.h>
Expand Down Expand Up @@ -118,6 +119,9 @@ class ViewImplementation {
void did_update_navigation_buttons_state(Badge<WebContentClient>, bool back_enabled, bool forward_enabled) const;

void did_allocate_backing_stores(Badge<WebContentClient>, i32 front_bitmap_id, Gfx::ShareableBitmap const&, i32 back_bitmap_id, Gfx::ShareableBitmap const&);
#ifdef AK_OS_MACOS
void did_allocate_iosurface_backing_stores(i32 front_bitmap_id, Core::MachPort&&, i32 back_bitmap_id, Core::MachPort&&);
#endif

enum class ScreenshotType {
Visible,
Expand Down
17 changes: 17 additions & 0 deletions Userland/Libraries/LibWebView/WebContentClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,29 @@

namespace WebView {

static HashTable<WebContentClient*> s_clients;

Optional<ViewImplementation&> WebContentClient::view_for_pid_and_page_id(pid_t pid, u64 page_id)
{
for (auto* client : s_clients) {
if (client->m_process_handle.pid == pid)
return client->view_for_page_id(page_id);
}
return {};
}

WebContentClient::WebContentClient(NonnullOwnPtr<Core::LocalSocket> socket, ViewImplementation& view)
: IPC::ConnectionToServer<WebContentClientEndpoint, WebContentServerEndpoint>(*this, move(socket))
{
s_clients.set(this);
m_views.set(0, &view);
}

WebContentClient::~WebContentClient()
{
s_clients.remove(this);
}

void WebContentClient::die()
{
VERIFY(on_web_content_process_crash);
Expand Down
3 changes: 3 additions & 0 deletions Userland/Libraries/LibWebView/WebContentClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ class WebContentClient final
IPC_CLIENT_CONNECTION(WebContentClient, "/tmp/session/%sid/portal/webcontent"sv);

public:
static Optional<ViewImplementation&> view_for_pid_and_page_id(pid_t pid, u64 page_id);

WebContentClient(NonnullOwnPtr<Core::LocalSocket>, ViewImplementation&);
~WebContentClient();

void register_view(u64 page_id, ViewImplementation&);
void unregister_view(u64 page_id);
Expand Down
Loading

0 comments on commit c92f8ab

Please sign in to comment.