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
Notes: github-actions[bot] 2025-10-24 06:54:18 +00:00
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)); 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) Bitmap::Bitmap(BitmapFormat format, AlphaType alpha_type, Core::AnonymousBuffer buffer, IntSize size)
: m_size(size) : m_size(size)
, m_data(buffer.data<void>()) , 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 }; 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()) if (size.is_empty())
return Error::from_string_literal("Gfx::Bitmap backing store 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 pitch = minimum_pitch(size.width(), format);
auto const data_size_in_bytes = size_in_bytes(pitch, size.height()); 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) if (data == nullptr)
return Error::from_errno(errno); return Error::from_errno(errno);
return BackingStore { data, pitch, data_size_in_bytes }; 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> * Copyright (c) 2022, Timothy Slater <tslater2006@gmail.com>
* *
* SPDX-License-Identifier: BSD-2-Clause * 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(BitmapFormat, AlphaType, IntSize);
[[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_shareable(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_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); [[nodiscard]] static ErrorOr<NonnullRefPtr<Bitmap>> create_with_anonymous_buffer(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize);
ErrorOr<NonnullRefPtr<Gfx::Bitmap>> clone() const; 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, IntSize, size_t pitch, void*, Function<void()>&& destruction_callback);
Bitmap(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize); 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; IntSize m_size;
void* m_data { nullptr }; 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()) if (size_check.has_overflow() || size_check.value() > bytes.size())
return Error::from_string_literal("IPC: Invalid Gfx::BitmapSequence buffer data"); return Error::from_string_literal("IPC: Invalid Gfx::BitmapSequence buffer data");
auto buffer = TRY(Core::AnonymousBuffer::create_with_size(size_in_bytes)); auto slice = bytes.slice(bytes_read, size_in_bytes);
auto buffer_bytes = Bytes { buffer.data<u8>(), buffer.size() };
bytes.slice(bytes_read, size_in_bytes).copy_to(buffer_bytes);
bytes_read += size_in_bytes; bytes_read += size_in_bytes;
bitmap = TRY(Gfx::Bitmap::create_with_raw_data(metadata.format, metadata.alpha_type, slice, metadata.size));
bitmap = TRY(Gfx::Bitmap::create_with_anonymous_buffer(metadata.format, metadata.alpha_type, move(buffer), metadata.size));
} }
bitmaps.append(bitmap); bitmaps.append(bitmap);