Files
ladybird/Libraries/LibGfx/SharedImage.cpp
Aliaksandr Kalenik 9a4e731746 LibGfx+LibWeb: Add Linux dmabuf backing stores to SharedImage
On Linux builds with Vulkan, WebContent already paints into GPU-backed
Skia surfaces, but the backing store shared with the UI process was
still a CPU ShareableBitmap. That forced every flush to read the GPU
image back into a bitmap so the UI could sample it, defeating most of
the benefit of GPU painting.

Teach SharedImage to carry a LinuxDmaBufHandle alongside ShareableBitmap
as a Variant, with a tagged IPC encoding and an fd clone on encode so
both processes own an independent handle. When USE_VULKAN_DMABUF_IMAGES
is enabled, BackingStoreManager now allocates the front/back buffers as
linear-modifier Vulkan images and publishes their dmabuf fds to the UI;
the Skia painting surfaces wrap those Vulkan images directly, so no
readback is needed. The old shareable-bitmap path is preserved as a
fallback for the non-Vulkan case and when image creation fails.

On the receive side, SharedImageBuffer::import_from_shared_image mmaps
a linear dmabuf to reconstruct a CPU Bitmap, keeping existing consumers
that expect CPU access working unchanged.

VulkanImage memory type selection is factored into a small helper, and
linear images now request host-visible (cached if available) memory
rather than device-local, since a linear dmabuf has to be CPU-mappable
on the importer side.
2026-04-14 21:49:08 +02:00

148 lines
4.4 KiB
C++

/*
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/SharedImage.h>
#include <LibIPC/Attachment.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#ifdef USE_VULKAN_DMABUF_IMAGES
# include <LibGfx/VulkanImage.h>
#endif
#ifdef AK_OS_MACOS
static Core::MachPort copy_send_right(Core::MachPort const& port)
{
auto result = mach_port_mod_refs(mach_task_self(), port.port(), MACH_PORT_RIGHT_SEND, +1);
VERIFY(result == KERN_SUCCESS);
return Core::MachPort::adopt_right(port.port(), Core::MachPort::PortRight::Send);
}
#endif
namespace Gfx {
#ifdef AK_OS_MACOS
SharedImage::SharedImage(Core::MachPort&& port)
: m_port(move(port))
{
}
#else
SharedImage::SharedImage(ShareableBitmap shareable_bitmap)
: m_data(move(shareable_bitmap))
{
}
SharedImage::SharedImage(LinuxDmaBufHandle&& dmabuf)
: m_data(move(dmabuf))
{
}
# ifdef USE_VULKAN_DMABUF_IMAGES
static constexpr auto shared_image_bitmap_format = BitmapFormat::BGRA8888;
static constexpr auto shared_image_alpha_type = AlphaType::Premultiplied;
SharedImage duplicate_shared_image(VulkanImage const& vulkan_image)
{
return SharedImage { duplicate_linux_dmabuf_handle(vulkan_image) };
}
LinuxDmaBufHandle duplicate_linux_dmabuf_handle(VulkanImage const& vulkan_image)
{
VERIFY(vulkan_image.info.format == VK_FORMAT_B8G8R8A8_UNORM);
auto fd = vulkan_image.get_dma_buf_fd();
VERIFY(fd >= 0);
return LinuxDmaBufHandle {
.bitmap_format = shared_image_bitmap_format,
.alpha_type = shared_image_alpha_type,
.size = IntSize(static_cast<int>(vulkan_image.info.extent.width), static_cast<int>(vulkan_image.info.extent.height)),
.drm_format = vk_format_to_drm_format(vulkan_image.info.format),
.pitch = static_cast<size_t>(vulkan_image.info.row_pitch),
.modifier = vulkan_image.info.modifier,
.file = IPC::File::adopt_fd(fd),
};
}
# endif
#endif
}
namespace IPC {
#ifndef AK_OS_MACOS
enum class SharedImageBackingType : u8 {
ShareableBitmap,
LinuxDmaBuf,
};
template<>
ErrorOr<void> encode(Encoder& encoder, Gfx::LinuxDmaBufHandle const& dmabuf)
{
TRY(encoder.encode(dmabuf.bitmap_format));
TRY(encoder.encode(dmabuf.alpha_type));
TRY(encoder.encode(dmabuf.size));
TRY(encoder.encode(dmabuf.drm_format));
TRY(encoder.encode(dmabuf.pitch));
TRY(encoder.encode(dmabuf.modifier));
TRY(encoder.encode(TRY(IPC::File::clone_fd(dmabuf.file.fd()))));
return {};
}
template<>
ErrorOr<Gfx::LinuxDmaBufHandle> decode(Decoder& decoder)
{
return Gfx::LinuxDmaBufHandle {
.bitmap_format = TRY(decoder.decode<Gfx::BitmapFormat>()),
.alpha_type = TRY(decoder.decode<Gfx::AlphaType>()),
.size = TRY(decoder.decode<Gfx::IntSize>()),
.drm_format = TRY(decoder.decode<u32>()),
.pitch = TRY(decoder.decode<size_t>()),
.modifier = TRY(decoder.decode<u64>()),
.file = TRY(decoder.decode<IPC::File>()),
};
}
#endif
template<>
ErrorOr<void> encode(Encoder& encoder, Gfx::SharedImage const& shared_image)
{
#ifdef AK_OS_MACOS
TRY(encoder.append_attachment(Attachment::from_mach_port(copy_send_right(shared_image.m_port), Core::MachPort::MessageRight::MoveSend)));
#else
return shared_image.m_data.visit(
[&](Gfx::ShareableBitmap const& shareable_bitmap) -> ErrorOr<void> {
TRY(encoder.encode(SharedImageBackingType::ShareableBitmap));
TRY(encoder.encode(shareable_bitmap));
return {};
},
[&](Gfx::LinuxDmaBufHandle const& dmabuf) -> ErrorOr<void> {
TRY(encoder.encode(SharedImageBackingType::LinuxDmaBuf));
TRY(encoder.encode(dmabuf));
return {};
});
#endif
return {};
}
template<>
ErrorOr<Gfx::SharedImage> decode(Decoder& decoder)
{
#ifdef AK_OS_MACOS
auto attachment = decoder.attachments().dequeue();
VERIFY(attachment.message_right() == Core::MachPort::MessageRight::MoveSend);
return Gfx::SharedImage { attachment.release_mach_port() };
#else
switch (TRY(decoder.decode<SharedImageBackingType>())) {
case SharedImageBackingType::ShareableBitmap:
return Gfx::SharedImage { TRY(decoder.decode<Gfx::ShareableBitmap>()) };
case SharedImageBackingType::LinuxDmaBuf:
return Gfx::SharedImage { TRY(decoder.decode<Gfx::LinuxDmaBufHandle>()) };
default:
VERIFY_NOT_REACHED();
}
#endif
}
}