From e9fe045de62d6500a506714c1300209a95ade5a9 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Sat, 6 Jun 2026 12:07:47 +0200 Subject: [PATCH] LibWeb: Do not copy nested display list command bytes PaintNestedDisplayList carried both a display list resource id and an inline copy of that resource's command buffer. Repeated SVG image paints therefore duplicated the nested command stream in every parent display list even though resource storage already owns and deduplicates it. Drop the inline command byte span and replay nested display lists from the resource table. Resource collection now follows the referenced resource's command bytes as well, so cached paint data and compositor transactions keep the same nested resource retention behavior. Fixes https://github.com/LadybirdBrowser/ladybird/issues/9929 --- Libraries/LibWeb/Painting/DisplayListCommand.h | 1 - Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp | 7 +++++-- Libraries/LibWeb/Painting/DisplayListRecorder.cpp | 13 ++++--------- .../LibWeb/Painting/DisplayListResourceStorage.cpp | 11 ++++------- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/Libraries/LibWeb/Painting/DisplayListCommand.h b/Libraries/LibWeb/Painting/DisplayListCommand.h index 80d737438d..367f78d47f 100644 --- a/Libraries/LibWeb/Painting/DisplayListCommand.h +++ b/Libraries/LibWeb/Painting/DisplayListCommand.h @@ -512,7 +512,6 @@ struct PaintNestedDisplayList { static constexpr DisplayListCommandType command_type = DisplayListCommandType::PaintNestedDisplayList; DisplayListResourceId display_list_id; - DisplayListDataSpan command_bytes; Gfx::IntRect rect; [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } diff --git a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index 71cf534760..99f63a646c 100644 --- a/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -860,9 +860,12 @@ void DisplayListPlayerSkia::paint_nested_display_list(PaintNestedDisplayList con auto& canvas = surface().canvas(); canvas.translate(command.rect.x(), command.rect.y()); ScrollStateSnapshot scroll_state_snapshot; - auto command_bytes = inline_data(command.command_bytes); auto const& nested_display_list = resource_storage().display_list_resource(command.display_list_id); - execute_nested_display_list(*nested_display_list.display_list, nested_display_list.visual_context_tree, scroll_state_snapshot, command_bytes); + execute_nested_display_list( + *nested_display_list.display_list, + nested_display_list.visual_context_tree, + scroll_state_snapshot, + nested_display_list.display_list->command_bytes()); } void DisplayListPlayerSkia::compositor_scroll_node(CompositorScrollNode const&) diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index 938545289d..1760c24058 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -305,15 +305,10 @@ void DisplayListRecorder::replay_cached_commands(ReadonlyBytes command_bytes) void DisplayListRecorder::paint_nested_display_list(DisplayListResource const& display_list, Gfx::IntRect rect) { auto display_list_id = resource_storage().add_display_list(display_list.display_list, display_list.visual_context_tree); - CommandPayloadBuilder payload_builder(m_display_list); - auto command_bytes = payload_builder.append_data(display_list.display_list->command_bytes(), alignof(DisplayListCommandHeader)); - append_command( - PaintNestedDisplayList { - display_list_id, - command_bytes, - rect, - }, - payload_builder.inline_data()); + append_command(PaintNestedDisplayList { + display_list_id, + rect, + }); } void DisplayListRecorder::add_rounded_rect_clip(Gfx::CornerRadii corner_radii, Gfx::IntRect border_rect, Gfx::CornerClip corner_clip) diff --git a/Libraries/LibWeb/Painting/DisplayListResourceStorage.cpp b/Libraries/LibWeb/Painting/DisplayListResourceStorage.cpp index 492b577f6f..ae0f5c4e23 100644 --- a/Libraries/LibWeb/Painting/DisplayListResourceStorage.cpp +++ b/Libraries/LibWeb/Painting/DisplayListResourceStorage.cpp @@ -109,11 +109,11 @@ void DisplayListResourceStorage::collect_referenced_resources( ReadonlyBytes command_bytes, DisplayListResourceSet& referenced_resources) const { - auto add_display_list_resource = [&](DisplayListResourceId id, Optional command_bytes_to_collect) { + auto add_display_list_resource = [&](DisplayListResourceId id) { if (referenced_resources.display_lists.set(id, AK::HashSetExistingEntryBehavior::Keep) != HashSetResult::InsertedNewEntry) return; auto const& nested_display_list = display_list(id); - collect_referenced_resources(command_bytes_to_collect.value_or(nested_display_list.command_bytes()), referenced_resources); + collect_referenced_resources(nested_display_list.command_bytes(), referenced_resources); }; DisplayList::for_each_command_header(command_bytes, [&](DisplayListCommandHeader const& header, ReadonlyBytes payload) { @@ -127,7 +127,7 @@ void DisplayListResourceStorage::collect_referenced_resources( if constexpr (requires { command.paint_style; command.paint_kind; }) { if (command.paint_kind == decltype(command.paint_kind)::PaintStyle && command.paint_style.type == DisplayListPaintStyleType::Pattern) - add_display_list_resource(command.paint_style.pattern_tile_display_list_id, {}); + add_display_list_resource(command.paint_style.pattern_tile_display_list_id); } if constexpr (requires { command.backdrop_filter_data; }) { if (command.has_backdrop_filter) { @@ -146,10 +146,7 @@ void DisplayListResourceStorage::collect_referenced_resources( } } if constexpr (requires { command.display_list_id; }) { - if constexpr (requires { command.command_bytes; }) - add_display_list_resource(command.display_list_id, inline_data(payload, command.command_bytes)); - else - add_display_list_resource(command.display_list_id, {}); + add_display_list_resource(command.display_list_id); } }); });