LibGfx: Don't create AnonymousBuffer for each bitmap in BitmapSequence

When decoding a BitmapSequence received over IPC, we were creating an
AnonymousBuffer for each bitmap and then making a Gfx::Bitmap wrapper
around it.

This was unnecessarily using up one file descriptor per bitmap, and also
wasting a lot of memory for small bitmaps since we always allocated at
least one VM page.

This patch changes the BitmapSequence decoder to use malloc memory
instead, saving file descriptors and using less memory overall.
This commit is contained in:
Andreas Kling 2025-10-23 21:47:16 +02:00 committed by Andreas Kling
parent 3593c3b687
commit 4f684bb4c9
3 changed files with 25 additions and 11 deletions

View File

@ -127,6 +127,16 @@ ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_with_anonymous_buffer(BitmapFormat
return adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, alpha_type, move(buffer), size));
}
ErrorOr<NonnullRefPtr<Bitmap>> Bitmap::create_with_raw_data(BitmapFormat format, AlphaType alpha_type, ReadonlyBytes raw_data, IntSize size)
{
if (size_would_overflow(format, size))
return Error::from_string_literal("Gfx::Bitmap::create_with_raw_data size overflow");
auto backing_store = TRY(Bitmap::allocate_backing_store(format, size, InitializeBackingStore::No));
raw_data.copy_to(Bytes { backing_store.data, backing_store.size_in_bytes });
return AK::adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, alpha_type, size, backing_store));
}
Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, Core::AnonymousBuffer buffer, IntSize size)
: m_size(size)
, m_data(buffer.data<void>())
@ -242,7 +252,7 @@ Gfx::ShareableBitmap Bitmap::to_shareable_bitmap() const
return Gfx::ShareableBitmap { bitmap_or_error.release_value_but_fixme_should_propagate_errors(), Gfx::ShareableBitmap::ConstructWithKnownGoodBitmap };
}
ErrorOr<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, IntSize size)
ErrorOr<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, IntSize size, InitializeBackingStore initialize_backing_store)
{
if (size.is_empty())
return Error::from_string_literal("Gfx::Bitmap backing store size is empty");
@ -253,7 +263,11 @@ ErrorOr<BackingStore> Bitmap::allocate_backing_store(BitmapFormat format, IntSiz
auto const pitch = minimum_pitch(size.width(), format);
auto const data_size_in_bytes = size_in_bytes(pitch, size.height());
void* data = kcalloc(1, data_size_in_bytes);
void* data;
if (initialize_backing_store == InitializeBackingStore::Yes)
data = kcalloc(1, data_size_in_bytes);
else
data = kmalloc(data_size_in_bytes);
if (data == nullptr)
return Error::from_errno(errno);
return BackingStore { data, pitch, data_size_in_bytes };

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2018-2025, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -70,6 +70,7 @@ public:
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create(BitmapFormat, AlphaType, IntSize);
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_shareable(BitmapFormat, AlphaType, IntSize);
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_wrapper(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function<void()>&& destruction_callback = {});
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_raw_data(BitmapFormat, AlphaType, ReadonlyBytes, IntSize);
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_anonymous_buffer(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize);
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> clone() const;
@ -172,7 +173,11 @@ private:
Bitmap(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function<void()>&& destruction_callback);
Bitmap(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize);
static ErrorOr<BackingStore> allocate_backing_store(BitmapFormat format, IntSize size);
enum class InitializeBackingStore {
No,
Yes,
};
static ErrorOr<BackingStore> allocate_backing_store(BitmapFormat format, IntSize size, InitializeBackingStore = InitializeBackingStore::Yes);
IntSize m_size;
void* m_data { nullptr };

View File

@ -128,14 +128,9 @@ ErrorOr<Gfx::BitmapSequence> decode(Decoder& decoder)
if (size_check.has_overflow() || size_check.value() > bytes.size())
return Error::from_string_literal("IPC: Invalid Gfx::BitmapSequence buffer data");
auto buffer = TRY(Core::AnonymousBuffer::create_with_size(size_in_bytes));
auto buffer_bytes = Bytes { buffer.data<u8>(), buffer.size() };
bytes.slice(bytes_read, size_in_bytes).copy_to(buffer_bytes);
auto slice = bytes.slice(bytes_read, size_in_bytes);
bytes_read += size_in_bytes;
bitmap = TRY(Gfx::Bitmap::create_with_anonymous_buffer(metadata.format, metadata.alpha_type, move(buffer), metadata.size));
bitmap = TRY(Gfx::Bitmap::create_with_raw_data(metadata.format, metadata.alpha_type, slice, metadata.size));
}
bitmaps.append(bitmap);