From 4f684bb4c91c66bd96e2924fe1e10f9d2caa24b8 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 23 Oct 2025 21:47:16 +0200 Subject: [PATCH] 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. --- Libraries/LibGfx/Bitmap.cpp | 18 ++++++++++++++++-- Libraries/LibGfx/Bitmap.h | 9 +++++++-- Libraries/LibGfx/BitmapSequence.cpp | 9 ++------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Libraries/LibGfx/Bitmap.cpp b/Libraries/LibGfx/Bitmap.cpp index c5b0a98abcd..b33924f9810 100644 --- a/Libraries/LibGfx/Bitmap.cpp +++ b/Libraries/LibGfx/Bitmap.cpp @@ -127,6 +127,16 @@ ErrorOr> Bitmap::create_with_anonymous_buffer(BitmapFormat return adopt_nonnull_ref_or_enomem(new (nothrow) Bitmap(format, alpha_type, move(buffer), size)); } +ErrorOr> 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()) @@ -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 Bitmap::allocate_backing_store(BitmapFormat format, IntSize size) +ErrorOr 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 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 }; diff --git a/Libraries/LibGfx/Bitmap.h b/Libraries/LibGfx/Bitmap.h index 31c7aa5619d..d0d4871b7c7 100644 --- a/Libraries/LibGfx/Bitmap.h +++ b/Libraries/LibGfx/Bitmap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2024, Andreas Kling + * Copyright (c) 2018-2025, Andreas Kling * Copyright (c) 2022, Timothy Slater * * SPDX-License-Identifier: BSD-2-Clause @@ -70,6 +70,7 @@ public: [[nodiscard]] static ErrorOr> create(BitmapFormat, AlphaType, IntSize); [[nodiscard]] static ErrorOr> create_shareable(BitmapFormat, AlphaType, IntSize); [[nodiscard]] static ErrorOr> create_wrapper(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function&& destruction_callback = {}); + [[nodiscard]] static ErrorOr> create_with_raw_data(BitmapFormat, AlphaType, ReadonlyBytes, IntSize); [[nodiscard]] static ErrorOr> create_with_anonymous_buffer(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize); ErrorOr> clone() const; @@ -172,7 +173,11 @@ private: Bitmap(BitmapFormat, AlphaType, IntSize, size_t pitch, void*, Function&& destruction_callback); Bitmap(BitmapFormat, AlphaType, Core::AnonymousBuffer, IntSize); - static ErrorOr allocate_backing_store(BitmapFormat format, IntSize size); + enum class InitializeBackingStore { + No, + Yes, + }; + static ErrorOr allocate_backing_store(BitmapFormat format, IntSize size, InitializeBackingStore = InitializeBackingStore::Yes); IntSize m_size; void* m_data { nullptr }; diff --git a/Libraries/LibGfx/BitmapSequence.cpp b/Libraries/LibGfx/BitmapSequence.cpp index ed3d6f4de9c..3d997d30f7f 100644 --- a/Libraries/LibGfx/BitmapSequence.cpp +++ b/Libraries/LibGfx/BitmapSequence.cpp @@ -128,14 +128,9 @@ ErrorOr 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(), 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);