cc: Ensure correct render surface dependency tracking for transitions.
During a transition, each shared element induces a render surface to generate its content as an image. This image is then drawn using a pseudo element which can be animated for the duration of the transition. This pseudo element is drawn using a cc::DocumentTransitionContentLayer tagged with the same SharedElementResourceId that the shared element's render surface is tagged with. Since the shared element's render surface output is drawn where the DocumentTransitionContentLayer quad draws (instead of its actual render target), this results in incorrect render surface dependency tracking. This change fixes this by updating the shared element's target render surface to the render target of its corresponding DocumentTransitionContentLayer (or to the layer itself if it has a render surface). This ensures that render passes are created in correct dependency order. R=vasilyt@chromium.org, kylechar@chromium.org Bug: 1277772 Change-Id: I9972eff032cbf9d76a1f8c2770a0937322de8907 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3396605 Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org> Reviewed-by: Mike West <mkwst@chromium.org> Reviewed-by: Kyle Charbonneau <kylechar@chromium.org> Reviewed-by: Andrew Grieve <agrieve@chromium.org> Reviewed-by: Mason Freed <masonf@chromium.org> Reviewed-by: Chris Harrelson <chrishtr@chromium.org> Commit-Queue: Vladimir Levin <vmpstr@chromium.org> Cr-Commit-Position: refs/heads/main@{#961631}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
5a28972c44
commit
f1aa4e0d19
BUILD.gn
cc
layers
trees
content/web_test/renderer
third_party/blink
public
renderer
core
document_transition
web_test
platform
graphics
compositing
web_tests
1
BUILD.gn
1
BUILD.gn
@ -1264,6 +1264,7 @@ if (!is_ios) {
|
||||
"//third_party/blink/web_tests/custom-elements/",
|
||||
"//third_party/blink/web_tests/custom-properties/",
|
||||
"//third_party/blink/web_tests/dark-mode/",
|
||||
"//third_party/blink/web_tests/document-transition/",
|
||||
"//third_party/blink/web_tests/dom/",
|
||||
"//third_party/blink/web_tests/editing/",
|
||||
"//third_party/blink/web_tests/external/",
|
||||
|
@ -24,6 +24,11 @@ DocumentTransitionContentLayer::DocumentTransitionContentLayer(
|
||||
|
||||
DocumentTransitionContentLayer::~DocumentTransitionContentLayer() = default;
|
||||
|
||||
viz::SharedElementResourceId
|
||||
DocumentTransitionContentLayer::DocumentTransitionResourceId() const {
|
||||
return resource_id_;
|
||||
}
|
||||
|
||||
std::unique_ptr<LayerImpl> DocumentTransitionContentLayer::CreateLayerImpl(
|
||||
LayerTreeImpl* tree_impl) {
|
||||
return DocumentTransitionContentLayerImpl::Create(tree_impl, id(),
|
||||
|
@ -25,9 +25,7 @@ class CC_EXPORT DocumentTransitionContentLayer : public Layer {
|
||||
DocumentTransitionContentLayer& operator=(
|
||||
const DocumentTransitionContentLayer&) = delete;
|
||||
|
||||
const viz::SharedElementResourceId& resource_id() const {
|
||||
return resource_id_;
|
||||
}
|
||||
viz::SharedElementResourceId DocumentTransitionResourceId() const override;
|
||||
|
||||
// Layer overrides.
|
||||
std::unique_ptr<LayerImpl> CreateLayerImpl(LayerTreeImpl* tree_impl) override;
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "cc/trees/transform_node.h"
|
||||
#include "components/viz/common/frame_sinks/copy_output_request.h"
|
||||
#include "components/viz/common/frame_sinks/copy_output_result.h"
|
||||
#include "components/viz/common/shared_element_resource_id.h"
|
||||
#include "third_party/skia/include/core/SkImageFilter.h"
|
||||
#include "ui/gfx/geometry/rect_conversions.h"
|
||||
#include "ui/gfx/geometry/vector2d_conversions.h"
|
||||
@ -212,6 +213,10 @@ void Layer::SetDebugName(const std::string& name) {
|
||||
EnsureDebugInfo().name = name;
|
||||
}
|
||||
|
||||
viz::SharedElementResourceId Layer::DocumentTransitionResourceId() const {
|
||||
return viz::SharedElementResourceId();
|
||||
}
|
||||
|
||||
void Layer::SetNeedsFullTreeSync() {
|
||||
if (!IsAttached())
|
||||
return;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "cc/trees/effect_node.h"
|
||||
#include "cc/trees/property_tree.h"
|
||||
#include "cc/trees/target_property.h"
|
||||
#include "components/viz/common/shared_element_resource_id.h"
|
||||
#include "components/viz/common/surfaces/region_capture_bounds.h"
|
||||
#include "components/viz/common/surfaces/subtree_capture_id.h"
|
||||
#include "third_party/skia/include/core/SkColor.h"
|
||||
@ -780,6 +781,10 @@ class CC_EXPORT Layer : public base::RefCounted<Layer> {
|
||||
|
||||
void SetDebugName(const std::string& name);
|
||||
|
||||
// If the content of this layer is provided by a cached or live render
|
||||
// surface, returns the ID of that resource.
|
||||
virtual viz::SharedElementResourceId DocumentTransitionResourceId() const;
|
||||
|
||||
protected:
|
||||
friend class LayerImpl;
|
||||
friend class TreeSynchronizer;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/adapters.h"
|
||||
#include "base/containers/flat_map.h"
|
||||
#include "base/containers/stack.h"
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
@ -27,6 +28,7 @@
|
||||
#include "cc/trees/scroll_node.h"
|
||||
#include "cc/trees/transform_node.h"
|
||||
#include "components/viz/common/display/de_jelly.h"
|
||||
#include "components/viz/common/shared_element_resource_id.h"
|
||||
#include "ui/gfx/geometry/rect_conversions.h"
|
||||
|
||||
namespace cc {
|
||||
@ -777,6 +779,11 @@ std::pair<gfx::MaskFilterInfo, bool> GetMaskFilterInfoPair(
|
||||
|
||||
void UpdateRenderTarget(EffectTree* effect_tree) {
|
||||
int last_backdrop_filter = kInvalidNodeId;
|
||||
|
||||
// A list of EffectNodes which induce a render surface to generate and cache
|
||||
// content for a SharedElement.
|
||||
std::vector<EffectNode*> shared_element_render_pass_nodes;
|
||||
|
||||
for (int i = EffectTree::kContentsRootNodeId;
|
||||
i < static_cast<int>(effect_tree->size()); ++i) {
|
||||
EffectNode* node = effect_tree->Node(i);
|
||||
@ -792,6 +799,50 @@ void UpdateRenderTarget(EffectTree* effect_tree) {
|
||||
node->has_potential_backdrop_filter_animation)
|
||||
last_backdrop_filter = node->id;
|
||||
node->affected_by_backdrop_filter = false;
|
||||
|
||||
if (node->shared_element_resource_id.IsValid())
|
||||
shared_element_render_pass_nodes.push_back(node);
|
||||
}
|
||||
|
||||
// A SharedElement's render surface draws into the
|
||||
// DocumentTransitionContentLayer, not into its parent layer in the regular
|
||||
// effect tree. This is because during a shared element transition animation,
|
||||
// the SharedElement doesn't paint, but instead delegates its paint to the
|
||||
// DocumentTransitionContentLayer that paints on top of everything else.
|
||||
//
|
||||
// Update the target_id for each SharedElement to the render target where its
|
||||
// corresponding DocumentTransitionContentLayer generates quads. This is the
|
||||
// DocumentTransitionContentLayer's effect if it generates a render surface,
|
||||
// or its target_id if it doesn't. This must be done after a traversal of the
|
||||
// complete EffectTree to ensure render targets for nodes corresponding to the
|
||||
// DocumentTransitionContentLayer have been set up.
|
||||
//
|
||||
// TODO(vmpstr): there is bespoke viz code to draw the surface for the
|
||||
// SharedElement into the DocumentTransitionContentLayer target. Now that
|
||||
// we're also setting the target id here, some of that viz code can be
|
||||
// removed.
|
||||
const auto& document_transition_layer_to_effect_node_index =
|
||||
effect_tree->property_trees()
|
||||
->document_transition_layer_to_effect_node_index;
|
||||
for (auto* effect_node : shared_element_render_pass_nodes) {
|
||||
auto it = document_transition_layer_to_effect_node_index.find(
|
||||
effect_node->shared_element_resource_id);
|
||||
|
||||
// It's possible for a shared element to not have a corresponding document
|
||||
// transition layer. For example if the element which displays this layer is
|
||||
// marked display:none.
|
||||
if (it == document_transition_layer_to_effect_node_index.end())
|
||||
continue;
|
||||
|
||||
auto shared_element_layer_node_id = it->second;
|
||||
if (effect_tree->GetRenderSurface(shared_element_layer_node_id)) {
|
||||
effect_node->target_id = shared_element_layer_node_id;
|
||||
} else {
|
||||
auto* shared_element_layer_node =
|
||||
effect_tree->Node(shared_element_layer_node_id);
|
||||
DCHECK(shared_element_layer_node);
|
||||
effect_node->target_id = shared_element_layer_node->target_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_backdrop_filter == kInvalidNodeId)
|
||||
|
@ -1778,6 +1778,8 @@ bool PropertyTrees::operator==(const PropertyTrees& other) const {
|
||||
other.element_id_to_scroll_node_index &&
|
||||
element_id_to_transform_node_index ==
|
||||
other.element_id_to_transform_node_index &&
|
||||
document_transition_layer_to_effect_node_index ==
|
||||
other.document_transition_layer_to_effect_node_index &&
|
||||
needs_rebuild == other.needs_rebuild && changed == other.changed &&
|
||||
full_tree_damaged == other.full_tree_damaged &&
|
||||
is_main_thread == other.is_main_thread &&
|
||||
@ -1794,6 +1796,8 @@ PropertyTrees& PropertyTrees::operator=(const PropertyTrees& from) {
|
||||
element_id_to_effect_node_index = from.element_id_to_effect_node_index;
|
||||
element_id_to_scroll_node_index = from.element_id_to_scroll_node_index;
|
||||
element_id_to_transform_node_index = from.element_id_to_transform_node_index;
|
||||
document_transition_layer_to_effect_node_index =
|
||||
from.document_transition_layer_to_effect_node_index;
|
||||
needs_rebuild = from.needs_rebuild;
|
||||
changed = from.changed;
|
||||
full_tree_damaged = from.full_tree_damaged;
|
||||
@ -1820,6 +1824,7 @@ void PropertyTrees::clear() {
|
||||
element_id_to_effect_node_index.clear();
|
||||
element_id_to_scroll_node_index.clear();
|
||||
element_id_to_transform_node_index.clear();
|
||||
document_transition_layer_to_effect_node_index.clear();
|
||||
|
||||
needs_rebuild = true;
|
||||
full_tree_damaged = false;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "cc/paint/filter_operations.h"
|
||||
#include "cc/trees/mutator_host.h"
|
||||
#include "cc/trees/sticky_position_constraint.h"
|
||||
#include "components/viz/common/shared_element_resource_id.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
#include "ui/gfx/geometry/point_f.h"
|
||||
#include "ui/gfx/geometry/rect_f.h"
|
||||
@ -641,6 +642,12 @@ class CC_EXPORT PropertyTrees final {
|
||||
base::flat_map<ElementId, int> element_id_to_scroll_node_index;
|
||||
base::flat_map<ElementId, int> element_id_to_transform_node_index;
|
||||
|
||||
// If an element is being rendered as a "live snapshot" using a
|
||||
// DocumentTransitionLayer, this maps the layer's SharedElementResourceId to
|
||||
// the EffectNode id associated with that layer.
|
||||
base::flat_map<viz::SharedElementResourceId, int>
|
||||
document_transition_layer_to_effect_node_index;
|
||||
|
||||
TransformTree transform_tree;
|
||||
EffectTree effect_tree;
|
||||
ClipTree clip_tree;
|
||||
|
@ -414,6 +414,7 @@ class TestRunnerBindings : public gin::Wrappable<TestRunnerBindings> {
|
||||
void ZoomPageOut();
|
||||
void SetPageZoomFactor(double factor);
|
||||
std::string TooltipText();
|
||||
void DisableEndDocumentTransition();
|
||||
|
||||
int WebHistoryItemCount();
|
||||
int WindowCount();
|
||||
@ -843,7 +844,11 @@ gin::ObjectTemplateBuilder TestRunnerBindings::GetObjectTemplateBuilder(
|
||||
// webHistoryItemCount is used by tests in web_tests\http\tests\history
|
||||
.SetProperty("webHistoryItemCount",
|
||||
&TestRunnerBindings::WebHistoryItemCount)
|
||||
.SetMethod("windowCount", &TestRunnerBindings::WindowCount);
|
||||
.SetMethod("windowCount", &TestRunnerBindings::WindowCount)
|
||||
|
||||
// document-transition functionality to avoid ending the animation.
|
||||
.SetMethod("disableEndDocumentTransition",
|
||||
&TestRunnerBindings::DisableEndDocumentTransition);
|
||||
}
|
||||
|
||||
BoundV8Callback TestRunnerBindings::WrapV8Callback(
|
||||
@ -982,6 +987,12 @@ int TestRunnerBindings::WindowCount() {
|
||||
return runner_->InProcessWindowCount();
|
||||
}
|
||||
|
||||
void TestRunnerBindings::DisableEndDocumentTransition() {
|
||||
if (invalid_)
|
||||
return;
|
||||
frame_->GetLocalRootFrameWidgetTestHelper()->DisableEndDocumentTransition();
|
||||
}
|
||||
|
||||
void TestRunnerBindings::SetTabKeyCyclesThroughElements(
|
||||
bool tab_key_cycles_through_elements) {
|
||||
if (invalid_)
|
||||
|
@ -63,6 +63,10 @@ class FrameWidgetTestHelper {
|
||||
// to the user in the display compositor.
|
||||
virtual void UpdateAllLifecyclePhasesAndComposite(
|
||||
base::OnceClosure completion_callback) = 0;
|
||||
|
||||
// Retains the pseudo-element transition DOM generated for a
|
||||
// DocumentTransition after all animations have finished.
|
||||
virtual void DisableEndDocumentTransition() = 0;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
@ -363,6 +363,12 @@ void DocumentTransition::NotifyStartFinished(uint32_t sequence_id) {
|
||||
DCHECK(start_promise_resolver_);
|
||||
start_promise_resolver_->Resolve();
|
||||
start_promise_resolver_ = nullptr;
|
||||
|
||||
// Resolve the promise to notify script when animations finish but don't
|
||||
// remove the pseudo element tree.
|
||||
if (disable_end_transition_)
|
||||
return;
|
||||
|
||||
state_ = State::kIdle;
|
||||
SetActiveSharedElements({});
|
||||
|
||||
|
@ -98,6 +98,11 @@ class CORE_EXPORT DocumentTransition
|
||||
// transition.
|
||||
const String& UAStyleSheet() const;
|
||||
|
||||
// Used by web tests to retain the pseudo-element tree after a
|
||||
// DocumentTransition finishes. This is used to capture a static version of
|
||||
// the last rendered frame.
|
||||
void DisableEndTransition() { disable_end_transition_ = true; }
|
||||
|
||||
private:
|
||||
friend class DocumentTransitionTest;
|
||||
|
||||
@ -165,6 +170,7 @@ class CORE_EXPORT DocumentTransition
|
||||
|
||||
// Set only for tests.
|
||||
scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_testing_;
|
||||
bool disable_end_transition_ = false;
|
||||
};
|
||||
|
||||
} // namespace blink
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "third_party/blink/public/web/web_page_popup.h"
|
||||
#include "third_party/blink/public/web/web_view.h"
|
||||
#include "third_party/blink/public/web/web_widget.h"
|
||||
#include "third_party/blink/renderer/core/document_transition/document_transition_supplement.h"
|
||||
#include "third_party/blink/renderer/core/exported/web_view_impl.h"
|
||||
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
|
||||
|
||||
@ -117,6 +118,12 @@ void WebTestWebFrameWidgetImpl::UpdateAllLifecyclePhasesAndComposite(
|
||||
ScheduleAnimationForWebTests();
|
||||
}
|
||||
|
||||
void WebTestWebFrameWidgetImpl::DisableEndDocumentTransition() {
|
||||
DocumentTransitionSupplement::documentTransition(
|
||||
*LocalRootImpl()->GetFrame()->GetDocument())
|
||||
->DisableEndTransition();
|
||||
}
|
||||
|
||||
void WebTestWebFrameWidgetImpl::ScheduleAnimationInternal(bool do_raster) {
|
||||
// When using threaded compositing, have the WeFrameWidgetImpl normally
|
||||
// schedule a request for a frame, as we use the compositor's scheduler.
|
||||
|
@ -52,6 +52,7 @@ class WebTestWebFrameWidgetImpl : public WebFrameWidgetImpl,
|
||||
void SynchronouslyCompositeAfterTest() override;
|
||||
void UpdateAllLifecyclePhasesAndComposite(
|
||||
base::OnceClosure completion_callback) override;
|
||||
void DisableEndDocumentTransition() override;
|
||||
|
||||
// WebFrameWidget overrides.
|
||||
FrameWidgetTestHelper* GetFrameWidgetTestHelperForTesting() override;
|
||||
|
@ -782,6 +782,8 @@ void PaintArtifactCompositor::Update(
|
||||
for (auto& entry : synthesized_clip_cache_)
|
||||
entry.in_use = false;
|
||||
|
||||
host->property_trees()
|
||||
->document_transition_layer_to_effect_node_index.clear();
|
||||
cc::LayerSelection layer_selection;
|
||||
for (const auto& pending_layer : pending_layers_) {
|
||||
const auto& property_state = pending_layer.GetPropertyTreeState();
|
||||
@ -848,6 +850,13 @@ void PaintArtifactCompositor::Update(
|
||||
layer->SetSubtreePropertyChanged();
|
||||
root_layer_->SetNeedsCommit();
|
||||
}
|
||||
|
||||
auto shared_element_id = layer->DocumentTransitionResourceId();
|
||||
if (shared_element_id.IsValid()) {
|
||||
host->property_trees()
|
||||
->document_transition_layer_to_effect_node_index[shared_element_id] =
|
||||
effect_id;
|
||||
}
|
||||
}
|
||||
|
||||
root_layer_->layer_tree_host()->RegisterSelection(layer_selection);
|
||||
|
5
third_party/blink/web_tests/TestExpectations
vendored
5
third_party/blink/web_tests/TestExpectations
vendored
@ -186,6 +186,7 @@ crbug.com/949003 http/tests/printing/cross-site-frame.html [ Failure Pass ]
|
||||
# canvas, fast/canvas
|
||||
# transforms
|
||||
# display-lock
|
||||
# document-transition
|
||||
# Some additional bugs that are caused by painting problems are also within this section.
|
||||
|
||||
# --- CanvasFormattedText tests ---
|
||||
@ -265,6 +266,10 @@ crbug.com/1225630 fast/multicol/insane-column-count-and-padding-nested-crash.htm
|
||||
crbug.com/807395 fast/multicol/mixed-opacity-test.html [ Failure ]
|
||||
crbug.com/1225630 fast/multicol/nested-very-tall-inside-short-crash.html [ Skip ]
|
||||
|
||||
# Document transition tests
|
||||
crbug.com/1276999 document-transition/* [ Skip ]
|
||||
crbug.com/1276999 virtual/document-transition/* [ Pass ]
|
||||
|
||||
########## Ref tests can't be rebaselined ##########
|
||||
crbug.com/504613 crbug.com/524248 [ Mac ] paint/images/image-backgrounds-not-antialiased.html [ Failure ]
|
||||
|
||||
|
@ -956,9 +956,8 @@
|
||||
},
|
||||
{
|
||||
"prefix": "document-transition",
|
||||
"bases": ["wpt_internal/document-transition"],
|
||||
"args": ["--enable-blink-features=DocumentTransition",
|
||||
"--enable-features=DocumentTransitionRenderer",
|
||||
"bases": ["wpt_internal/document-transition", "document-transition"],
|
||||
"args": ["--enable-features=DocumentTransition,DocumentTransitionRenderer",
|
||||
"--enable-threaded-compositing",
|
||||
"--enable-gpu-rasterization"]
|
||||
},
|
||||
|
40
third_party/blink/web_tests/document-transition/paint-order-expected.html
vendored
Normal file
40
third_party/blink/web_tests/document-transition/paint-order-expected.html
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<html>
|
||||
<style>
|
||||
body {
|
||||
background: lightgrey;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
left: 100px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.new {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.old {
|
||||
background-color: blue;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="content new"></div>
|
||||
<div class="content old"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
49
third_party/blink/web_tests/document-transition/paint-order.html
vendored
Normal file
49
third_party/blink/web_tests/document-transition/paint-order.html
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<style>
|
||||
body { background: lightgrey; }
|
||||
div {
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
left: 100px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: blue;
|
||||
contain: paint;
|
||||
}
|
||||
html::transition-container(shared-0) { isolation: isolate; }
|
||||
html::transition-old-content(shared-0) {
|
||||
opacity: 1;
|
||||
}
|
||||
html::transition-new-content(shared-0) {
|
||||
opacity: 1;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
if (window.testRunner) {
|
||||
testRunner.waitUntilDone();
|
||||
testRunner.disableEndDocumentTransition();
|
||||
}
|
||||
|
||||
async function doTransition() {
|
||||
let elem = document.getElementsByTagName("div")[0];
|
||||
|
||||
await document.documentTransition.prepare({
|
||||
rootTransition: "none",
|
||||
sharedElements: [elem]
|
||||
});
|
||||
|
||||
elem.style.backgroundColor = "red";
|
||||
await document.documentTransition.start({
|
||||
sharedElements: [elem]
|
||||
});
|
||||
|
||||
if (window.testRunner) {
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => testRunner.notifyDone()));
|
||||
}
|
||||
}
|
||||
|
||||
onload = doTransition;
|
||||
</script>
|
Reference in New Issue
Block a user