Files
ladybird/Libraries/LibWeb/Painting/BackingStoreManager.cpp
Aliaksandr Kalenik 3cb644500e Everywhere: Send IOSurface backing stores via main IPC route on macOS
Now that LibIPC uses Mach ports for transport on macOS, IOSurface port
rights can be sent as regular IPC message attachments instead of through
a separate ad-hoc Mach message side-channel. Introduce
Web::SharedBackingStore that wraps either a MachPort (macOS) or
ShareableBitmap (other platforms) with IPC encode/decode support,
unifying backing store allocation into the existing
did_allocate_backing_stores IPC message.
2026-03-23 23:22:38 +01:00

156 lines
6.1 KiB
C++

/*
* Copyright (c) 2024-2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibCore/Timer.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/PaintingSurface.h>
#include <LibGfx/SkiaBackendContext.h>
#include <LibWeb/HTML/TraversableNavigable.h>
#include <LibWeb/Page/SharedBackingStore.h>
#include <LibWeb/Painting/BackingStoreManager.h>
#include <WebContent/PageClient.h>
#ifdef AK_OS_MACOS
# include <LibCore/IOSurface.h>
#endif
namespace Web::Painting {
GC_DEFINE_ALLOCATOR(BackingStoreManager);
BackingStoreManager::BackingStoreManager(HTML::Navigable& navigable)
: m_navigable(navigable)
{
m_backing_store_shrink_timer = Core::Timer::create_single_shot(3000, [this] {
resize_backing_stores_if_needed(WindowResizingInProgress::No);
});
}
void BackingStoreManager::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_navigable);
}
void BackingStoreManager::restart_resize_timer()
{
m_backing_store_shrink_timer->restart();
}
void BackingStoreManager::reallocate_backing_stores(Gfx::IntSize size)
{
auto skia_backend_context = Gfx::SkiaBackendContext::the();
RefPtr<Gfx::PaintingSurface> front_store;
RefPtr<Gfx::PaintingSurface> back_store;
#ifdef AK_OS_MACOS
auto back_iosurface = Core::IOSurfaceHandle::create(size.width(), size.height());
auto back_iosurface_port = back_iosurface.create_mach_port();
auto front_iosurface = Core::IOSurfaceHandle::create(size.width(), size.height());
auto front_iosurface_port = front_iosurface.create_mach_port();
m_front_bitmap_id = m_next_bitmap_id++;
m_back_bitmap_id = m_next_bitmap_id++;
if (m_navigable->is_top_level_traversable()) {
auto& page_client = m_navigable->top_level_traversable()->page().client();
page_client.page_did_allocate_backing_stores(
m_front_bitmap_id,
Web::SharedBackingStore(move(front_iosurface_port)),
m_back_bitmap_id,
Web::SharedBackingStore(move(back_iosurface_port)));
}
if (skia_backend_context) {
front_store = Gfx::PaintingSurface::create_from_iosurface(move(front_iosurface), *skia_backend_context);
back_store = Gfx::PaintingSurface::create_from_iosurface(move(back_iosurface), *skia_backend_context);
} else {
auto front_bytes_per_row = front_iosurface.bytes_per_row();
auto* front_data = front_iosurface.data();
auto back_bytes_per_row = back_iosurface.bytes_per_row();
auto* back_data = back_iosurface.data();
auto front_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size, front_bytes_per_row, front_data, [handle = move(front_iosurface)] { }).release_value();
auto back_bitmap = Gfx::Bitmap::create_wrapper(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size, back_bytes_per_row, back_data, [handle = move(back_iosurface)] { }).release_value();
front_store = Gfx::PaintingSurface::wrap_bitmap(*front_bitmap);
back_store = Gfx::PaintingSurface::wrap_bitmap(*back_bitmap);
}
m_allocated_size = size;
m_navigable->rendering_thread().update_backing_stores(front_store, back_store, m_front_bitmap_id, m_back_bitmap_id);
return;
#else
m_front_bitmap_id = m_next_bitmap_id++;
m_back_bitmap_id = m_next_bitmap_id++;
auto front_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size).release_value();
auto back_bitmap = Gfx::Bitmap::create_shareable(Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied, size).release_value();
# ifdef USE_VULKAN
if (skia_backend_context) {
front_store = Gfx::PaintingSurface::create_with_size(size, Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
front_store->on_flush = [front_bitmap](auto& surface) {
surface.read_into_bitmap(*front_bitmap);
};
back_store = Gfx::PaintingSurface::create_with_size(size, Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
back_store->on_flush = [back_bitmap](auto& surface) {
surface.read_into_bitmap(*back_bitmap);
};
}
# endif
if (!front_store)
front_store = Gfx::PaintingSurface::wrap_bitmap(*front_bitmap);
if (!back_store)
back_store = Gfx::PaintingSurface::wrap_bitmap(*back_bitmap);
if (m_navigable->is_top_level_traversable()) {
auto& page_client = m_navigable->top_level_traversable()->page().client();
page_client.page_did_allocate_backing_stores(
m_front_bitmap_id,
Web::SharedBackingStore(front_bitmap->to_shareable_bitmap()),
m_back_bitmap_id,
Web::SharedBackingStore(back_bitmap->to_shareable_bitmap()));
}
m_allocated_size = size;
m_navigable->rendering_thread().update_backing_stores(front_store, back_store, m_front_bitmap_id, m_back_bitmap_id);
#endif
}
void BackingStoreManager::resize_backing_stores_if_needed(WindowResizingInProgress window_resize_in_progress)
{
if (!m_navigable->is_top_level_traversable() || m_navigable->is_svg_page())
return;
auto viewport_size = m_navigable->page().css_to_device_rect(m_navigable->viewport_rect()).size();
if (viewport_size.is_empty())
return;
Web::DevicePixelSize minimum_needed_size;
bool force_reallocate = false;
if (window_resize_in_progress == WindowResizingInProgress::Yes) {
// Pad the minimum needed size so that we don't have to keep reallocating backing stores while the window is being resized.
minimum_needed_size = { viewport_size.width() + 256, viewport_size.height() + 256 };
} else {
// If we're not in the middle of a resize, we can shrink the backing store size to match the viewport size.
minimum_needed_size = viewport_size;
force_reallocate = m_allocated_size != minimum_needed_size.to_type<int>();
}
if (force_reallocate || m_allocated_size.is_empty() || !m_allocated_size.contains(minimum_needed_size.to_type<int>())) {
reallocate_backing_stores(minimum_needed_size.to_type<int>());
}
}
}