LibWeb: Preserve compatible visual context tree versions
Let accumulated visual context updates keep the previous tree version when rebuilt with the same shape. Display lists reference visual context tree versions, so keep compositor-only updates on the old version unless the tree structure changes. Add coverage for version reuse and incompatible tree shapes.
This commit is contained in:
parent
be5320b67c
commit
1b8072371b
5 changed files with 95 additions and 4 deletions
|
|
@ -482,6 +482,34 @@ void AccumulatedVisualContextTree::set_visual_viewport_transform(TransformData t
|
|||
m_nodes[VISUAL_VIEWPORT_NODE_INDEX.value()].data = move(transform);
|
||||
}
|
||||
|
||||
bool AccumulatedVisualContextTree::is_compatible_with(AccumulatedVisualContextTree const& other) const
|
||||
{
|
||||
if (m_nodes.size() != other.m_nodes.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < m_nodes.size(); ++i) {
|
||||
auto const& node = m_nodes[i];
|
||||
auto const& other_node = other.m_nodes[i];
|
||||
if (node.parent_index != other_node.parent_index)
|
||||
return false;
|
||||
if (node.has_empty_effective_clip != other_node.has_empty_effective_clip)
|
||||
return false;
|
||||
if (!node.data.visit([&](auto const& data) {
|
||||
using DataType = RemoveCVReference<decltype(data)>;
|
||||
return other_node.data.has<DataType>();
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AccumulatedVisualContextTree::reuse_version_from(AccumulatedVisualContextTree const& other)
|
||||
{
|
||||
VERIFY(is_compatible_with(other));
|
||||
m_version = other.m_version;
|
||||
}
|
||||
|
||||
VisualContextIndex AccumulatedVisualContextTree::find_common_ancestor(VisualContextIndex a, VisualContextIndex b) const
|
||||
{
|
||||
VERIFY(a.value() < m_nodes.size());
|
||||
|
|
|
|||
|
|
@ -98,8 +98,8 @@ public:
|
|||
Yes,
|
||||
};
|
||||
|
||||
static AccumulatedVisualContextTree create();
|
||||
static AccumulatedVisualContextTree create(TransformData visual_viewport_transform);
|
||||
static WEB_API AccumulatedVisualContextTree create();
|
||||
static WEB_API AccumulatedVisualContextTree create(TransformData visual_viewport_transform);
|
||||
|
||||
AccumulatedVisualContextTree(AccumulatedVisualContextTree const&) = default;
|
||||
AccumulatedVisualContextTree& operator=(AccumulatedVisualContextTree const&) = default;
|
||||
|
|
@ -109,8 +109,10 @@ public:
|
|||
|
||||
u64 version() const { return m_version; }
|
||||
|
||||
VisualContextIndex append(VisualContextData data, VisualContextIndex parent_index);
|
||||
WEB_API VisualContextIndex append(VisualContextData data, VisualContextIndex parent_index);
|
||||
WEB_API void set_visual_viewport_transform(TransformData);
|
||||
WEB_API bool is_compatible_with(AccumulatedVisualContextTree const&) const;
|
||||
WEB_API void reuse_version_from(AccumulatedVisualContextTree const&);
|
||||
|
||||
AccumulatedVisualContextNode const& node_at(VisualContextIndex index) const { return m_nodes[index.value()]; }
|
||||
ReadonlySpan<AccumulatedVisualContextNode> nodes() const { return m_nodes.span(); }
|
||||
|
|
|
|||
|
|
@ -218,7 +218,10 @@ void ViewportPaintable::assign_scroll_frames()
|
|||
|
||||
void ViewportPaintable::assign_accumulated_visual_contexts()
|
||||
{
|
||||
m_visual_context_tree = build_accumulated_visual_context_tree(*this);
|
||||
auto visual_context_tree = build_accumulated_visual_context_tree(*this);
|
||||
if (m_visual_context_tree.has_value() && visual_context_tree.is_compatible_with(*m_visual_context_tree))
|
||||
visual_context_tree.reuse_version_from(*m_visual_context_tree);
|
||||
m_visual_context_tree = move(visual_context_tree);
|
||||
m_visual_context_tree_needs_compositor_update = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
set(TEST_SOURCES
|
||||
TestCSSIDSpeed.cpp
|
||||
TestAccumulatedVisualContext.cpp
|
||||
TestContentBlocker.cpp
|
||||
TestControlMessageQueue.cpp
|
||||
TestCSSInheritedProperty.cpp
|
||||
|
|
@ -32,6 +33,7 @@ target_link_libraries(TestContentBlocker PRIVATE LibURL)
|
|||
target_link_libraries(TestControlMessageQueue PRIVATE LibSync)
|
||||
target_link_libraries(TestFetchResponse PRIVATE LibGC LibHTTP LibJS)
|
||||
target_link_libraries(TestFetchURL PRIVATE LibURL)
|
||||
target_link_libraries(TestAccumulatedVisualContext PRIVATE LibGfx)
|
||||
target_link_libraries(TestPage PRIVATE LibGC LibJS)
|
||||
target_link_libraries(TestSecureContexts PRIVATE LibURL)
|
||||
target_link_libraries(TestSessionHistoryEntry PRIVATE LibJS LibURL)
|
||||
|
|
|
|||
56
Tests/LibWeb/TestAccumulatedVisualContext.cpp
Normal file
56
Tests/LibWeb/TestAccumulatedVisualContext.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <LibWeb/Painting/AccumulatedVisualContext.h>
|
||||
|
||||
using namespace Web::Painting;
|
||||
|
||||
static TransformData make_transform(float translation)
|
||||
{
|
||||
auto matrix = Gfx::FloatMatrix4x4::identity();
|
||||
matrix[0, 3] = translation;
|
||||
matrix[1, 3] = translation;
|
||||
return { matrix, { translation, translation } };
|
||||
}
|
||||
|
||||
TEST_CASE(compatible_trees_can_reuse_versions)
|
||||
{
|
||||
auto tree = AccumulatedVisualContextTree::create(make_transform(1));
|
||||
tree.append(make_transform(2), VISUAL_VIEWPORT_NODE_INDEX);
|
||||
|
||||
auto updated_tree = AccumulatedVisualContextTree::create(make_transform(3));
|
||||
updated_tree.append(make_transform(4), VISUAL_VIEWPORT_NODE_INDEX);
|
||||
|
||||
EXPECT_NE(tree.version(), updated_tree.version());
|
||||
EXPECT(updated_tree.is_compatible_with(tree));
|
||||
|
||||
updated_tree.reuse_version_from(tree);
|
||||
EXPECT_EQ(updated_tree.version(), tree.version());
|
||||
}
|
||||
|
||||
TEST_CASE(compatibility_requires_same_shape)
|
||||
{
|
||||
auto tree = AccumulatedVisualContextTree::create();
|
||||
tree.append(make_transform(1), VISUAL_VIEWPORT_NODE_INDEX);
|
||||
|
||||
auto shorter_tree = AccumulatedVisualContextTree::create();
|
||||
EXPECT(!shorter_tree.is_compatible_with(tree));
|
||||
|
||||
auto different_type_tree = AccumulatedVisualContextTree::create();
|
||||
different_type_tree.append(EffectsData {}, VISUAL_VIEWPORT_NODE_INDEX);
|
||||
EXPECT(!different_type_tree.is_compatible_with(tree));
|
||||
|
||||
auto different_parent_tree = AccumulatedVisualContextTree::create();
|
||||
auto parent = different_parent_tree.append(make_transform(1), VISUAL_VIEWPORT_NODE_INDEX);
|
||||
different_parent_tree.append(make_transform(2), parent);
|
||||
|
||||
auto same_node_count_tree = AccumulatedVisualContextTree::create();
|
||||
same_node_count_tree.append(make_transform(1), VISUAL_VIEWPORT_NODE_INDEX);
|
||||
same_node_count_tree.append(make_transform(2), VISUAL_VIEWPORT_NODE_INDEX);
|
||||
|
||||
EXPECT(!different_parent_tree.is_compatible_with(same_node_count_tree));
|
||||
}
|
||||
Loading…
Reference in a new issue