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
This commit is contained in:
Aliaksandr Kalenik 2026-06-06 12:07:47 +02:00 committed by Alexander Kalenik
parent 570ea59277
commit e9fe045de6
4 changed files with 13 additions and 19 deletions

View file

@ -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; }

View file

@ -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&)

View file

@ -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<PaintNestedDisplayList> 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)

View file

@ -109,11 +109,11 @@ void DisplayListResourceStorage::collect_referenced_resources(
ReadonlyBytes command_bytes,
DisplayListResourceSet& referenced_resources) const
{
auto add_display_list_resource = [&](DisplayListResourceId id, Optional<ReadonlyBytes> 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);
}
});
});