Explorar el Código

LibWeb: Only perform the requested form of intrinsic size calculation

Before, querying any of the four intrinsic sizes would cause us to
calculate all of them (the four being min-content width/height, and
max-content width/height).

Now, the helper functions only calculate the specific intrinsic size
requested. It's then cached at the root formatting context level,
so that it's never calculated twice within the same layout pass.
Andreas Kling hace 3 años
padre
commit
4f6fc3d3a6

+ 4 - 8
Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp

@@ -1463,8 +1463,7 @@ float FlexFormattingContext::calculate_main_min_content_contribution(FlexItem co
     // the larger of its outer min-content size and outer preferred size if that is not auto,
     // the larger of its outer min-content size and outer preferred size if that is not auto,
     // clamped by its min/max main size.
     // clamped by its min/max main size.
     auto outer_min_content_size = [&]() -> float {
     auto outer_min_content_size = [&]() -> float {
-        auto intrinsic_sizes = FormattingContext::calculate_intrinsic_sizes(item.box);
-        auto inner_main_size = is_row_layout() ? intrinsic_sizes.min_content_size.width() : intrinsic_sizes.min_content_size.height();
+        auto inner_main_size = is_row_layout() ? calculate_min_content_width(item.box) : calculate_min_content_height(item.box);
         auto outer_main_size = inner_main_size
         auto outer_main_size = inner_main_size
             + item.margins.main_before + item.margins.main_after
             + item.margins.main_before + item.margins.main_after
             + item.borders.main_before + item.borders.main_after
             + item.borders.main_before + item.borders.main_after
@@ -1488,8 +1487,7 @@ float FlexFormattingContext::calculate_main_max_content_contribution(FlexItem co
 {
 {
     // The main-size max-content contribution of a flex item is the larger of its outer max-content size and outer preferred size if that is not auto, clamped by its min/max main size.
     // The main-size max-content contribution of a flex item is the larger of its outer max-content size and outer preferred size if that is not auto, clamped by its min/max main size.
     auto outer_max_content_size = [&]() -> float {
     auto outer_max_content_size = [&]() -> float {
-        auto intrinsic_sizes = FormattingContext::calculate_intrinsic_sizes(item.box);
-        auto inner_main_size = is_row_layout() ? intrinsic_sizes.max_content_size.width() : intrinsic_sizes.max_content_size.height();
+        auto inner_main_size = is_row_layout() ? calculate_max_content_width(item.box) : calculate_max_content_height(item.box);
         auto outer_main_size = inner_main_size
         auto outer_main_size = inner_main_size
             + item.margins.main_before + item.margins.main_after
             + item.margins.main_before + item.margins.main_after
             + item.borders.main_before + item.borders.main_after
             + item.borders.main_before + item.borders.main_after
@@ -1510,8 +1508,7 @@ float FlexFormattingContext::calculate_main_max_content_contribution(FlexItem co
 
 
 float FlexFormattingContext::calculate_cross_min_content_contribution(FlexItem const& flex_item) const
 float FlexFormattingContext::calculate_cross_min_content_contribution(FlexItem const& flex_item) const
 {
 {
-    auto intrinsic_sizes = FormattingContext::calculate_intrinsic_sizes(flex_item.box);
-    auto inner_cross_size = is_row_layout() ? intrinsic_sizes.min_content_size.height() : intrinsic_sizes.min_content_size.width();
+    auto inner_cross_size = is_row_layout() ? calculate_min_content_height(flex_item.box) : calculate_min_content_width(flex_item.box);
     auto outer_cross_size = inner_cross_size
     auto outer_cross_size = inner_cross_size
         + flex_item.margins.cross_before + flex_item.margins.cross_after
         + flex_item.margins.cross_before + flex_item.margins.cross_after
         + flex_item.borders.cross_before + flex_item.borders.cross_after
         + flex_item.borders.cross_before + flex_item.borders.cross_after
@@ -1521,8 +1518,7 @@ float FlexFormattingContext::calculate_cross_min_content_contribution(FlexItem c
 
 
 float FlexFormattingContext::calculate_cross_max_content_contribution(FlexItem const& flex_item) const
 float FlexFormattingContext::calculate_cross_max_content_contribution(FlexItem const& flex_item) const
 {
 {
-    auto intrinsic_sizes = FormattingContext::calculate_intrinsic_sizes(flex_item.box);
-    auto inner_cross_size = is_row_layout() ? intrinsic_sizes.max_content_size.height() : intrinsic_sizes.max_content_size.width();
+    auto inner_cross_size = is_row_layout() ? calculate_max_content_height(flex_item.box) : calculate_max_content_width(flex_item.box);
     auto outer_cross_size = inner_cross_size
     auto outer_cross_size = inner_cross_size
         + flex_item.margins.cross_before + flex_item.margins.cross_after
         + flex_item.margins.cross_before + flex_item.margins.cross_after
         + flex_item.borders.cross_before + flex_item.borders.cross_after
         + flex_item.borders.cross_before + flex_item.borders.cross_after

+ 95 - 80
Userland/Libraries/LibWeb/Layout/FormattingContext.cpp

@@ -812,82 +812,6 @@ void FormattingContext::compute_inset(Box const& box)
     resolve_two_opposing_insets(computed_values.inset().top, computed_values.inset().bottom, box_state.inset_top, box_state.inset_bottom, containing_block_state.content_height);
     resolve_two_opposing_insets(computed_values.inset().top, computed_values.inset().bottom, box_state.inset_top, box_state.inset_bottom, containing_block_state.content_height);
 }
 }
 
 
-FormattingState::IntrinsicSizes FormattingContext::calculate_intrinsic_sizes(Layout::Box const& box) const
-{
-    // FIXME: This should handle replaced elements with "native" intrinsic size properly!
-
-    if (box.has_intrinsic_width() && box.has_intrinsic_height()) {
-        auto const& replaced_box = static_cast<ReplacedBox const&>(box);
-        Gfx::FloatSize size { replaced_box.intrinsic_width().value_or(0), replaced_box.intrinsic_height().value_or(0) };
-        return FormattingState::IntrinsicSizes {
-            .min_content_size = size,
-            .max_content_size = size,
-        };
-    }
-
-    auto& root_state = m_state.m_root;
-
-    // If we have cached intrinsic sizes for this box, use them.
-    auto it = root_state.intrinsic_sizes.find(&box);
-    if (it != root_state.intrinsic_sizes.end())
-        return it->value;
-
-    // Nothing cached, perform two throwaway layouts to determine the intrinsic sizes.
-
-    FormattingState::IntrinsicSizes cached_box_sizes;
-    auto const& containing_block = *box.containing_block();
-    {
-        FormattingState throwaway_state(&m_state);
-        auto& containing_block_state = throwaway_state.get_mutable(containing_block);
-        containing_block_state.content_width = INFINITY;
-        containing_block_state.content_height = INFINITY;
-        auto independent_formatting_context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
-        VERIFY(independent_formatting_context);
-
-        independent_formatting_context->run(box, LayoutMode::MaxContent);
-
-        if (independent_formatting_context->type() == FormattingContext::Type::Flex) {
-            auto const& box_state = throwaway_state.get(box);
-            cached_box_sizes.max_content_size = { box_state.content_width, box_state.content_height };
-        } else {
-            cached_box_sizes.max_content_size.set_width(independent_formatting_context->greatest_child_width(box));
-            cached_box_sizes.max_content_size.set_height(calculate_auto_height(throwaway_state, box));
-        }
-    }
-
-    {
-        FormattingState throwaway_state(&m_state);
-        auto& containing_block_state = throwaway_state.get_mutable(containing_block);
-        containing_block_state.content_width = 0;
-        containing_block_state.content_height = 0;
-        auto independent_formatting_context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
-        VERIFY(independent_formatting_context);
-        independent_formatting_context->run(box, LayoutMode::MinContent);
-        if (independent_formatting_context->type() == FormattingContext::Type::Flex) {
-            auto const& box_state = throwaway_state.get(box);
-            cached_box_sizes.min_content_size = { box_state.content_width, box_state.content_height };
-        } else {
-            cached_box_sizes.min_content_size.set_width(independent_formatting_context->greatest_child_width(box));
-            cached_box_sizes.min_content_size.set_height(calculate_auto_height(throwaway_state, box));
-        }
-    }
-
-    if (cached_box_sizes.min_content_size.width() > cached_box_sizes.max_content_size.width()) {
-        float tmp = cached_box_sizes.min_content_size.width();
-        cached_box_sizes.min_content_size.set_width(cached_box_sizes.max_content_size.width());
-        cached_box_sizes.max_content_size.set_width(tmp);
-    }
-
-    if (cached_box_sizes.min_content_size.height() > cached_box_sizes.max_content_size.height()) {
-        float tmp = cached_box_sizes.min_content_size.height();
-        cached_box_sizes.min_content_size.set_height(cached_box_sizes.max_content_size.height());
-        cached_box_sizes.max_content_size.set_height(tmp);
-    }
-
-    root_state.intrinsic_sizes.set(&box, cached_box_sizes);
-    return cached_box_sizes;
-}
-
 float FormattingContext::calculate_fit_content_size(float min_content_size, float max_content_size, Optional<float> available_space) const
 float FormattingContext::calculate_fit_content_size(float min_content_size, float max_content_size, Optional<float> available_space) const
 {
 {
     // If the available space in a given axis is definite, equal to clamp(min-content size, stretch-fit size, max-content size)
     // If the available space in a given axis is definite, equal to clamp(min-content size, stretch-fit size, max-content size)
@@ -926,22 +850,113 @@ float FormattingContext::calculate_auto_height(FormattingState const& state, Box
 
 
 float FormattingContext::calculate_min_content_width(Layout::Box const& box) const
 float FormattingContext::calculate_min_content_width(Layout::Box const& box) const
 {
 {
-    return calculate_intrinsic_sizes(box).min_content_size.width();
+    if (box.has_intrinsic_width())
+        return *box.intrinsic_width();
+
+    auto& root_state = m_state.m_root;
+
+    auto& cache = *root_state.intrinsic_sizes.ensure(&box, [] { return adopt_own(*new FormattingState::IntrinsicSizes); });
+    if (cache.min_content_width.has_value())
+        return *cache.min_content_width;
+
+    FormattingState throwaway_state(&m_state);
+    auto const& containing_block = *box.containing_block();
+    auto& containing_block_state = throwaway_state.get_mutable(containing_block);
+    containing_block_state.content_width = 0;
+    auto context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
+    VERIFY(context);
+    context->run(box, LayoutMode::MinContent);
+    if (context->type() == FormattingContext::Type::Flex) {
+        auto const& box_state = throwaway_state.get(box);
+        cache.min_content_width = box_state.content_width;
+    } else {
+        cache.min_content_width = context->greatest_child_width(box);
+    }
+    return *cache.min_content_width;
 }
 }
 
 
 float FormattingContext::calculate_max_content_width(Layout::Box const& box) const
 float FormattingContext::calculate_max_content_width(Layout::Box const& box) const
 {
 {
-    return calculate_intrinsic_sizes(box).max_content_size.width();
+    if (box.has_intrinsic_width())
+        return *box.intrinsic_width();
+
+    auto& root_state = m_state.m_root;
+
+    auto& cache = *root_state.intrinsic_sizes.ensure(&box, [] { return adopt_own(*new FormattingState::IntrinsicSizes); });
+    if (cache.max_content_width.has_value())
+        return *cache.max_content_width;
+
+    FormattingState throwaway_state(&m_state);
+    auto const& containing_block = *box.containing_block();
+    auto& containing_block_state = throwaway_state.get_mutable(containing_block);
+    containing_block_state.content_width = INFINITY;
+    auto context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
+    VERIFY(context);
+    context->run(box, LayoutMode::MaxContent);
+    if (context->type() == FormattingContext::Type::Flex) {
+        auto const& box_state = throwaway_state.get(box);
+        cache.max_content_width = box_state.content_width;
+    } else {
+        cache.max_content_width = context->greatest_child_width(box);
+    }
+
+    return *cache.max_content_width;
 }
 }
 
 
 float FormattingContext::calculate_min_content_height(Layout::Box const& box) const
 float FormattingContext::calculate_min_content_height(Layout::Box const& box) const
 {
 {
-    return calculate_intrinsic_sizes(box).min_content_size.height();
+    if (box.has_intrinsic_height())
+        return *box.intrinsic_height();
+
+    auto& root_state = m_state.m_root;
+
+    auto& cache = *root_state.intrinsic_sizes.ensure(&box, [] { return adopt_own(*new FormattingState::IntrinsicSizes); });
+    if (cache.min_content_height.has_value())
+        return *cache.min_content_height;
+
+    FormattingState throwaway_state(&m_state);
+    auto const& containing_block = *box.containing_block();
+    auto& containing_block_state = throwaway_state.get_mutable(containing_block);
+    containing_block_state.content_height = 0;
+    auto context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
+    VERIFY(context);
+    context->run(box, LayoutMode::MinContent);
+    if (context->type() == FormattingContext::Type::Flex) {
+        auto const& box_state = throwaway_state.get(box);
+        cache.min_content_height = box_state.content_height;
+    } else {
+        cache.min_content_height = calculate_auto_height(throwaway_state, box);
+    }
+
+    return *cache.min_content_height;
 }
 }
 
 
 float FormattingContext::calculate_max_content_height(Layout::Box const& box) const
 float FormattingContext::calculate_max_content_height(Layout::Box const& box) const
 {
 {
-    return calculate_intrinsic_sizes(box).max_content_size.height();
+    if (box.has_intrinsic_height())
+        return *box.intrinsic_height();
+
+    auto& root_state = m_state.m_root;
+
+    auto& cache = *root_state.intrinsic_sizes.ensure(&box, [] { return adopt_own(*new FormattingState::IntrinsicSizes); });
+    if (cache.max_content_height.has_value())
+        return *cache.max_content_height;
+
+    FormattingState throwaway_state(&m_state);
+    auto const& containing_block = *box.containing_block();
+    auto& containing_block_state = throwaway_state.get_mutable(containing_block);
+    containing_block_state.content_height = INFINITY;
+    auto context = const_cast<FormattingContext*>(this)->create_independent_formatting_context_if_needed(throwaway_state, box);
+    VERIFY(context);
+    context->run(box, LayoutMode::MaxContent);
+    if (context->type() == FormattingContext::Type::Flex) {
+        auto const& box_state = throwaway_state.get(box);
+        cache.max_content_height = box_state.content_height;
+    } else {
+        cache.max_content_height = calculate_auto_height(throwaway_state, box);
+    }
+
+    return *cache.max_content_height;
 }
 }
 
 
 }
 }

+ 0 - 1
Userland/Libraries/LibWeb/Layout/FormattingContext.h

@@ -59,7 +59,6 @@ protected:
     FormattingContext(Type, FormattingState&, Box const&, FormattingContext* parent = nullptr);
     FormattingContext(Type, FormattingState&, Box const&, FormattingContext* parent = nullptr);
 
 
     float calculate_fit_content_size(float min_content_size, float max_content_size, Optional<float> available_space) const;
     float calculate_fit_content_size(float min_content_size, float max_content_size, Optional<float> available_space) const;
-    FormattingState::IntrinsicSizes calculate_intrinsic_sizes(Layout::Box const&) const;
 
 
     OwnPtr<FormattingContext> layout_inside(Box const&, LayoutMode);
     OwnPtr<FormattingContext> layout_inside(Box const&, LayoutMode);
     void compute_inset(Box const& box);
     void compute_inset(Box const& box);

+ 6 - 3
Userland/Libraries/LibWeb/Layout/FormattingState.h

@@ -102,10 +102,13 @@ struct FormattingState {
     // We cache intrinsic sizes once determined, as they will not change over the course of a full layout.
     // We cache intrinsic sizes once determined, as they will not change over the course of a full layout.
     // This avoids computing them several times while performing flex layout.
     // This avoids computing them several times while performing flex layout.
     struct IntrinsicSizes {
     struct IntrinsicSizes {
-        Gfx::FloatSize min_content_size;
-        Gfx::FloatSize max_content_size;
+        Optional<float> min_content_width;
+        Optional<float> max_content_width;
+        Optional<float> min_content_height;
+        Optional<float> max_content_height;
     };
     };
-    HashMap<NodeWithStyleAndBoxModelMetrics const*, IntrinsicSizes> mutable intrinsic_sizes;
+
+    HashMap<NodeWithStyleAndBoxModelMetrics const*, NonnullOwnPtr<IntrinsicSizes>> mutable intrinsic_sizes;
 
 
     HashMap<Box const*, float> mutable flex_item_size_cache;
     HashMap<Box const*, float> mutable flex_item_size_cache;