LibWeb: Don't emit Push{Pop}StackingContext without visible effect

Before this change we would emit PushStackingContext/PopStackingContext
display list items regardless of whether the stacking context had any
transform/opacity/clip effects.

Display list size on https://x.com/ladybirdbrowser is reduced from ~2700
to ~800 items.
This commit is contained in:
Aliaksandr Kalenik 2025-09-23 16:56:43 +02:00 committed by Alexander Kalenik
parent 2cf10981af
commit 719a50c9bf
Notes: github-actions[bot] 2025-09-23 17:06:10 +00:00
7 changed files with 54 additions and 45 deletions

View file

@ -233,6 +233,18 @@ public:
return determinant() != static_cast<T>(0.0);
}
[[nodiscard]] bool is_identity() const
{
for (size_t i = 0; i < N; ++i) {
for (size_t j = 0; j < N; ++j) {
float expected = (i == j) ? 1.0f : 0.0f;
if ((*this)[i, j] != expected)
return false;
}
}
return true;
}
private:
T m_elements[N][N];
};

View file

@ -35,6 +35,8 @@ struct StackingContextTransform {
Gfx::FloatMatrix4x4 matrix;
StackingContextTransform(Gfx::FloatPoint origin, Gfx::FloatMatrix4x4 matrix, float scale);
[[nodiscard]] bool is_identity() const { return matrix.is_identity(); }
};
class WEB_API DisplayListRecorder {
@ -109,6 +111,8 @@ public:
bool isolate;
StackingContextTransform transform;
Optional<Gfx::Path> clip_path = {};
bool has_effect() const { return opacity != 1.0f || compositing_and_blending_operator != Gfx::CompositingAndBlendingOperator::Normal || isolate || clip_path.has_value() || !transform.is_identity(); }
};
void push_stacking_context(PushStackingContextParams params);
void pop_stacking_context();

View file

@ -331,18 +331,24 @@ void StackingContext::paint(DisplayListRecordingContext& context) const
paintable_box().apply_clip_overflow_rect(context, PaintPhase::Foreground);
}
paintable_box().apply_scroll_offset(context);
context.display_list_recorder().push_stacking_context(push_stacking_context_params);
auto const& filter = computed_values.filter();
auto filter_applied = false;
if (filter.has_filters()) {
if (auto resolved_filter = paintable_box().resolve_filter(filter); resolved_filter.has_value()) {
context.display_list_recorder().apply_filter(*resolved_filter);
filter_applied = true;
}
auto mask_image = computed_values.mask_image();
Optional<Gfx::Filter> resolved_filter;
if (computed_values.filter().has_filters())
resolved_filter = paintable_box().resolve_filter(computed_values.filter());
bool needs_to_save_state = mask_image || paintable_box().get_masking_area().has_value();
if (push_stacking_context_params.has_effect()) {
context.display_list_recorder().push_stacking_context(push_stacking_context_params);
} else if (needs_to_save_state) {
context.display_list_recorder().save();
}
if (auto mask_image = computed_values.mask_image()) {
if (resolved_filter.has_value())
context.display_list_recorder().apply_filter(*resolved_filter);
if (mask_image) {
auto mask_display_list = DisplayList::create(context.device_pixels_per_css_pixel());
DisplayListRecorder display_list_recorder(*mask_display_list);
auto mask_painting_context = context.clone(display_list_recorder);
@ -361,11 +367,14 @@ void StackingContext::paint(DisplayListRecordingContext& context) const
paint_internal(context);
if (filter_applied) {
if (resolved_filter.has_value())
context.display_list_recorder().restore();
if (push_stacking_context_params.has_effect()) {
context.display_list_recorder().pop_stacking_context();
} else if (needs_to_save_state) {
context.display_list_recorder().restore();
}
context.display_list_recorder().pop_stacking_context();
paintable_box().reset_scroll_offset(context);
if (has_css_transform)
paintable_box().clear_clip_overflow_rect(context, PaintPhase::Foreground);

View file

@ -1,12 +1,8 @@
SaveLayer
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
DrawGlyphRun rect=[35,8 8x18] translation=[35.15625,21.796875] color=rgb(0, 0, 0) scale=1
DrawGlyphRun rect=[8,8 28x18] translation=[8,21.796875] color=rgb(0, 0, 0) scale=1
DrawLine from=[8,24] to=[35,24] color=rgb(0, 0, 0) thickness=2
DrawGlyphRun rect=[43,8 28x18] translation=[43.15625,21.796875] color=rgb(0, 0, 0) scale=1
DrawLine from=[43,27] to=[71,27] color=rgb(0, 0, 0) thickness=2
PopStackingContext
PopStackingContext
DrawGlyphRun rect=[35,8 8x18] translation=[35.15625,21.796875] color=rgb(0, 0, 0) scale=1
DrawGlyphRun rect=[8,8 28x18] translation=[8,21.796875] color=rgb(0, 0, 0) scale=1
DrawLine from=[8,24] to=[35,24] color=rgb(0, 0, 0) thickness=2
DrawGlyphRun rect=[43,8 28x18] translation=[43.15625,21.796875] color=rgb(0, 0, 0) scale=1
DrawLine from=[43,27] to=[71,27] color=rgb(0, 0, 0) thickness=2
Restore

View file

@ -1,11 +1,7 @@
SaveLayer
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
FillRect rect=[8,8 106x22] color=rgb(212, 208, 200)
FillPath
DrawGlyphRun rect=[13,10 96x18] translation=[13,23.796875] color=rgb(0, 0, 0) scale=1
DrawLine from=[13,26] to=[109,26] color=rgb(0, 0, 0) thickness=2
PopStackingContext
PopStackingContext
FillRect rect=[8,8 106x22] color=rgb(212, 208, 200)
FillPath
DrawGlyphRun rect=[13,10 96x18] translation=[13,23.796875] color=rgb(0, 0, 0) scale=1
DrawLine from=[13,26] to=[109,26] color=rgb(0, 0, 0) thickness=2
Restore

View file

@ -1,12 +1,8 @@
SaveLayer
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
FillRect rect=[8,8 29x30] color=rgb(0, 0, 0)
FillRect rect=[38,8 28x30] color=rgb(0, 0, 0)
FillRect rect=[67,8 29x30] color=rgb(0, 0, 0)
FillRect rect=[97,8 28x30] color=rgb(0, 0, 0)
FillRect rect=[126,8 29x30] color=rgb(0, 0, 0)
PopStackingContext
PopStackingContext
FillRect rect=[8,8 29x30] color=rgb(0, 0, 0)
FillRect rect=[38,8 28x30] color=rgb(0, 0, 0)
FillRect rect=[67,8 29x30] color=rgb(0, 0, 0)
FillRect rect=[97,8 28x30] color=rgb(0, 0, 0)
FillRect rect=[126,8 29x30] color=rgb(0, 0, 0)
Restore

View file

@ -1,10 +1,6 @@
SaveLayer
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
PushStackingContext opacity=1 isolate=false has_clip_path=false transform=[1 0 0 1 0 0]
FillPath
FillRect rect=[10,10 300x150] color=rgb(240, 128, 128)
DrawGlyphRun rect=[10,10 38x18] translation=[10,23.796875] color=rgb(0, 0, 0) scale=1
PopStackingContext
PopStackingContext
FillPath
FillRect rect=[10,10 300x150] color=rgb(240, 128, 128)
DrawGlyphRun rect=[10,10 38x18] translation=[10,23.796875] color=rgb(0, 0, 0) scale=1
Restore