diff options
author | Andreas Kling <kling@serenityos.org> | 2022-07-08 12:44:49 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-07-11 18:57:44 +0200 |
commit | 4f6fc3d3a63ba14a8dc95d470a55a4dd137be9bf (patch) | |
tree | 863702847439661fef2c26192b5bf027ab400b35 | |
parent | 496cf39cf5c84544486d54eec9e76930c7debe7d (diff) | |
download | serenity-4f6fc3d3a63ba14a8dc95d470a55a4dd137be9bf.zip |
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.
4 files changed, 105 insertions, 92 deletions
diff --git a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp index 8a47cd7245..6952f367eb 100644 --- a/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.cpp +++ b/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, // clamped by its min/max main size. 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 + item.margins.main_before + item.margins.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. 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 + item.margins.main_before + item.margins.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 { - 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 + flex_item.margins.cross_before + flex_item.margins.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 { - 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 + flex_item.margins.cross_before + flex_item.margins.cross_after + flex_item.borders.cross_before + flex_item.borders.cross_after diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp index a15981361a..afae391bbe 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.cpp +++ b/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); } -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 { // 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 { - 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 { - 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 { - 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 { - 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; } } diff --git a/Userland/Libraries/LibWeb/Layout/FormattingContext.h b/Userland/Libraries/LibWeb/Layout/FormattingContext.h index 98cc417cc3..5ac22caa84 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingContext.h +++ b/Userland/Libraries/LibWeb/Layout/FormattingContext.h @@ -59,7 +59,6 @@ protected: 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; - FormattingState::IntrinsicSizes calculate_intrinsic_sizes(Layout::Box const&) const; OwnPtr<FormattingContext> layout_inside(Box const&, LayoutMode); void compute_inset(Box const& box); diff --git a/Userland/Libraries/LibWeb/Layout/FormattingState.h b/Userland/Libraries/LibWeb/Layout/FormattingState.h index 50b08e6062..3c9c13b40a 100644 --- a/Userland/Libraries/LibWeb/Layout/FormattingState.h +++ b/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. // This avoids computing them several times while performing flex layout. 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; |