0

Refactor visual overflow computation for view-transitions (part 1)

Up until now, blink attempts to compute the exact bounds of the paint
that generates the view transition capture, by applying the same logic
performed by the paint system. This is needed to achieve precise
geometric projection of the captured snapshot to the pseudo element's
content rect.

This parallel logic creates a brittle effect where every optimization in the paint system requires a parallel fix in the view transition overflow
computation, and it's easy to get them out of sync.

Instead, we do the following in this CL:

- We introduce the "max extents", which is a rect used to compute the CC
  layer "bounds". That rect is determined for all the layers before determining the surface content rect.

- After we've computed the actual capture rect (which is the
  surface content rect of the view-transition effect), the content
  layer maps this capture rect to its own coordinate system using the
  max extents rect, and the result (the actual extents) is used for
  the paint rect, as well as intersects with the content
  layer's drawable rect to avoid over-allocating surface.

- When the old capture is done, we record the actual content rects
  (adjusted by surface scale) and pass them to the style tracker.
  These style tracker would use those captured rects to map
  the contents of the old pseudo-elements, and no adjustment is needed
  as the content is no longer changing.

See stacked CL (https://chromium-review.googlesource.com/c/chromium/src/+/5920431/1?usp=related-change) for how this is used. This CL only introduces the CC parts, and the
minimum blink changes to make them compile without a behavior change.

Bug: 347947051
Change-Id: I18203b23e34e09f7c230da17bafd91eeff6aa383
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5823921
Reviewed-by: Khushal Sagar <khushalsagar@chromium.org>
Commit-Queue: Noam Rosenthal <nrosenthal@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1367386}
This commit is contained in:
Noam Rosenthal
2024-10-11 09:40:37 +00:00
committed by Chromium LUCI CQ
parent 617e3b7565
commit 47d08e8e10
34 changed files with 390 additions and 72 deletions

@ -270,6 +270,13 @@ void RenderSurfaceImpl::CalculateContentRectFromAccumulatedContentRect(
// Root render surface use viewport, and does not calculate content rect.
DCHECK_NE(render_target(), this);
for (LayerImpl* layer : deferred_contributing_layers_) {
DCHECK(layer->draws_content());
accumulated_content_rect_.Union(layer->visible_drawable_content_rect());
}
deferred_contributing_layers_.clear();
// Surface's content rect is the clipped accumulated content rect. By default
// use accumulated content rect, and then try to clip it.
gfx::Rect surface_content_rect = CalculateClippedAccumulatedContentRect();
@ -296,6 +303,7 @@ void RenderSurfaceImpl::CalculateContentRectFromAccumulatedContentRect(
void RenderSurfaceImpl::SetContentRectToViewport() {
// Only root render surface use viewport as content rect.
DCHECK_EQ(render_target(), this);
DCHECK(deferred_contributing_layers_.empty());
gfx::Rect viewport = gfx::ToEnclosingRect(
layer_tree_impl_->property_trees()->clip_tree().ViewportClip());
SetContentRect(viewport);
@ -315,7 +323,13 @@ void RenderSurfaceImpl::AccumulateContentRectFromContributingLayer(
if (render_target() == this)
return;
accumulated_content_rect_.Union(layer->visible_drawable_content_rect());
// Contributions from view-transition capture layers are deferred until
// their surface content rect is computed.
if (layer->ViewTransitionResourceId().IsValid()) {
deferred_contributing_layers_.push_back(layer);
} else {
accumulated_content_rect_.Union(layer->visible_drawable_content_rect());
}
}
void RenderSurfaceImpl::AccumulateContentRectFromContributingRenderSurface(

@ -351,6 +351,11 @@ class CC_EXPORT RenderSurfaceImpl {
nearest_occlusion_immune_ancestor_ = nullptr;
std::unique_ptr<DamageTracker> damage_tracker_;
// A ViewTransitionContentLayer only knows its final visible drawable rect
// once its originating surface's content rect has been computed. So we defer
// adding this contribution until that is complete.
std::vector<LayerImpl*> deferred_contributing_layers_;
};
} // namespace cc

@ -34,8 +34,27 @@ ViewTransitionContentLayer::ViewTransitionResourceId() const {
std::unique_ptr<LayerImpl> ViewTransitionContentLayer::CreateLayerImpl(
LayerTreeImpl* tree_impl) const {
return ViewTransitionContentLayerImpl::Create(tree_impl, id(), resource_id_,
is_live_content_layer_);
return ViewTransitionContentLayerImpl::Create(
tree_impl, id(), resource_id_, is_live_content_layer_, gfx::RectF());
}
void ViewTransitionContentLayer::SetMaxExtentsRectInOriginatingLayerSpace(
const gfx::RectF& max_extents_rect) {
if (max_extents_rect == max_extents_rect_.Read(*this)) {
return;
}
max_extents_rect_.Write(*this) = max_extents_rect;
SetNeedsCommit();
}
void ViewTransitionContentLayer::PushPropertiesTo(
LayerImpl* layer,
const CommitState& commit_state,
const ThreadUnsafeCommitState& unsafe_state) {
Layer::PushPropertiesTo(layer, commit_state, unsafe_state);
static_cast<ViewTransitionContentLayerImpl*>(layer)->SetMaxExtentsRect(
max_extents_rect_.Read(*this));
}
} // namespace cc

@ -36,16 +36,27 @@ class CC_EXPORT ViewTransitionContentLayer : public Layer {
return is_live_content_layer_;
}
// This is a superset of the captured rect, in the element's enclosing layer's
// coordinate space. If not set, it's assumed that the layer is already sized
// to match the originating element's painted contents (e.g. the root element,
// or displaying captured snapshots of the old content).
void SetMaxExtentsRectInOriginatingLayerSpace(
const gfx::RectF& max_extents_rect_in_originating_layer_space);
protected:
explicit ViewTransitionContentLayer(
const viz::ViewTransitionElementResourceId& resource_id,
bool is_live_content_layer);
void PushPropertiesTo(LayerImpl* layer,
const CommitState& commit_state,
const ThreadUnsafeCommitState& unsafe_state) override;
private:
~ViewTransitionContentLayer() override;
const viz::ViewTransitionElementResourceId resource_id_;
const bool is_live_content_layer_;
ProtectedSequenceReadable<gfx::RectF> max_extents_rect_;
};
} // namespace cc

@ -4,6 +4,7 @@
#include "cc/layers/view_transition_content_layer_impl.h"
#include "base/check.h"
#include "base/memory/ptr_util.h"
#include "cc/layers/append_quads_data.h"
#include "cc/layers/layer_impl.h"
@ -11,6 +12,8 @@
#include "components/viz/common/quads/shared_element_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/view_transition_element_resource_id.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
namespace cc {
@ -20,19 +23,23 @@ ViewTransitionContentLayerImpl::Create(
LayerTreeImpl* tree_impl,
int id,
const viz::ViewTransitionElementResourceId& resource_id,
bool is_live_content_layer) {
bool is_live_content_layer,
const gfx::RectF& max_extents_rect) {
return base::WrapUnique(new ViewTransitionContentLayerImpl(
tree_impl, id, resource_id, is_live_content_layer));
tree_impl, id, resource_id, is_live_content_layer, max_extents_rect));
}
ViewTransitionContentLayerImpl::ViewTransitionContentLayerImpl(
LayerTreeImpl* tree_impl,
int id,
const viz::ViewTransitionElementResourceId& resource_id,
bool is_live_content_layer)
bool is_live_content_layer,
const gfx::RectF& max_extents_rect)
: LayerImpl(tree_impl, id),
resource_id_(resource_id),
is_live_content_layer_(is_live_content_layer) {}
is_live_content_layer_(is_live_content_layer),
max_extents_rect_in_originating_layer_coordinate_space_(
max_extents_rect) {}
ViewTransitionContentLayerImpl::~ViewTransitionContentLayerImpl() = default;
@ -42,8 +49,9 @@ mojom::LayerType ViewTransitionContentLayerImpl::GetLayerType() const {
std::unique_ptr<LayerImpl> ViewTransitionContentLayerImpl::CreateLayerImpl(
LayerTreeImpl* tree_impl) const {
return ViewTransitionContentLayerImpl::Create(tree_impl, id(), resource_id_,
is_live_content_layer_);
return ViewTransitionContentLayerImpl::Create(
tree_impl, id(), resource_id_, is_live_content_layer_,
max_extents_rect_in_originating_layer_coordinate_space_);
}
void ViewTransitionContentLayerImpl::NotifyKnownResourceIdsBeforeAppendQuads(
@ -52,6 +60,49 @@ void ViewTransitionContentLayerImpl::NotifyKnownResourceIdsBeforeAppendQuads(
skip_unseen_resource_quads_ = known_resource_ids.count(resource_id_) == 0;
}
void ViewTransitionContentLayerImpl::PushPropertiesTo(LayerImpl* layer) {
LayerImpl::PushPropertiesTo(layer);
static_cast<ViewTransitionContentLayerImpl*>(layer)->SetMaxExtentsRect(
max_extents_rect_in_originating_layer_coordinate_space_);
}
// Compute the bounds that are actually going to be drawn, given that the
// bounds set for the layer are based on the max extents, and the actual drawn
// surface could be smaller.
void ViewTransitionContentLayerImpl::SetOriginatingSurfaceContentRect(
const gfx::Rect&
originating_surface_content_rect_in_layer_coordinate_space) {
// All these scenarios mean that no correction needs to be done, and we can
// paint the whole bounds.
if (originating_surface_content_rect_in_layer_coordinate_space.IsEmpty() ||
max_extents_rect_in_originating_layer_coordinate_space_.IsEmpty() ||
gfx::RectF(originating_surface_content_rect_in_layer_coordinate_space) ==
max_extents_rect_in_originating_layer_coordinate_space_) {
actual_extents_rect_ = gfx::Rect();
return;
}
DUMP_WILL_BE_CHECK(
max_extents_rect_in_originating_layer_coordinate_space_.Contains(
gfx::RectF(
originating_surface_content_rect_in_layer_coordinate_space)));
// The actual extents rect is a subset of the max extents rect. This
// projection maps this subset to the coordinate space of this layer (0, 0,
// bounds()).
actual_extents_rect_ = gfx::ToRoundedRect(gfx::MapRect(
gfx::RectF(originating_surface_content_rect_in_layer_coordinate_space),
max_extents_rect_in_originating_layer_coordinate_space_,
gfx::RectF(bounds())));
// Note that this is called late, when we compute the original layer's surface
// content rect. So the surface content rect chain that relies on this should
// also read this value later.
draw_properties().visible_drawable_content_rect.Intersect(
MathUtil::MapEnclosingClippedRect(
draw_properties().target_space_transform, actual_extents_rect_));
}
void ViewTransitionContentLayerImpl::AppendQuads(
viz::CompositorRenderPass* render_pass,
AppendQuadsData* append_quads_data) {
@ -60,20 +111,25 @@ void ViewTransitionContentLayerImpl::AppendQuads(
if (is_live_content_layer_ && skip_unseen_resource_quads_)
return;
auto bounds_rect = actual_extents_rect_.IsEmpty() ? gfx::Rect(bounds())
: actual_extents_rect_;
float device_scale_factor = layer_tree_impl()->device_scale_factor();
gfx::Rect quad_rect(
gfx::ScaleToEnclosingRect(gfx::Rect(bounds()), device_scale_factor));
gfx::ScaleToEnclosingRect(bounds_rect, device_scale_factor));
gfx::Rect visible_quad_rect =
draw_properties().occlusion_in_content_space.GetUnoccludedContentRect(
gfx::Rect(bounds()));
bounds_rect);
visible_quad_rect =
gfx::ScaleToEnclosingRect(visible_quad_rect, device_scale_factor);
visible_quad_rect = gfx::IntersectRects(quad_rect, visible_quad_rect);
if (visible_quad_rect.IsEmpty())
if (visible_quad_rect.IsEmpty()) {
return;
}
viz::SharedQuadState* shared_quad_state =
render_pass->CreateAndAppendSharedQuadState();
@ -82,6 +138,7 @@ void ViewTransitionContentLayerImpl::AppendQuads(
auto* quad =
render_pass->CreateAndAppendDrawQuad<viz::SharedElementDrawQuad>();
quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect, resource_id_);
append_quads_data->has_shared_element_resources = true;
}
@ -91,4 +148,15 @@ ViewTransitionContentLayerImpl::ViewTransitionResourceId() const {
return resource_id_;
}
void ViewTransitionContentLayerImpl::SetMaxExtentsRect(
const gfx::RectF& max_extents_rect) {
if (max_extents_rect_in_originating_layer_coordinate_space_ ==
max_extents_rect) {
return;
}
max_extents_rect_in_originating_layer_coordinate_space_ = max_extents_rect;
actual_extents_rect_ = gfx::Rect();
NoteLayerPropertyChanged();
}
} // namespace cc

@ -19,7 +19,8 @@ class CC_EXPORT ViewTransitionContentLayerImpl : public LayerImpl {
LayerTreeImpl* tree_impl,
int id,
const viz::ViewTransitionElementResourceId& resource_id,
bool is_live_content_layer);
bool is_live_content_layer,
const gfx::RectF& max_extents_rect);
ViewTransitionContentLayerImpl(const ViewTransitionContentLayerImpl&) =
delete;
@ -42,17 +43,31 @@ class CC_EXPORT ViewTransitionContentLayerImpl : public LayerImpl {
viz::ViewTransitionElementResourceId ViewTransitionResourceId()
const override;
void SetMaxExtentsRect(const gfx::RectF& max_extents_rect);
void PushPropertiesTo(LayerImpl* layer) override;
void SetOriginatingSurfaceContentRect(
const gfx::Rect&
originating_surface_content_rect_in_layer_coordinate_space);
protected:
ViewTransitionContentLayerImpl(
LayerTreeImpl* tree_impl,
int id,
const viz::ViewTransitionElementResourceId& resource_id,
bool is_live_content_layer);
bool is_live_content_layer,
const gfx::RectF& max_extents_rect);
private:
const viz::ViewTransitionElementResourceId resource_id_;
const bool is_live_content_layer_;
bool skip_unseen_resource_quads_ = false;
// max_extents_rect_ is the maximum possible size of the originating layer, as
// known to blink, in the originating layer's coordinate space.
gfx::RectF max_extents_rect_in_originating_layer_coordinate_space_;
// actual_extents_rect_ is the actual size of the surface, computed by CC, in
// this layer's coordinate space.
gfx::Rect actual_extents_rect_;
};
} // namespace cc

@ -43,7 +43,9 @@ class FakeLayerTreeHostImplClient : public LayerTreeHostImplClient {
bool needs_first_draw_on_activation) override;
void NotifyImageDecodeRequestFinished(int request_id,
bool decode_succeeded) override {}
void NotifyTransitionRequestFinished(uint32_t sequence_id) override {}
void NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects&) override {}
void DidPresentCompositorFrameOnImplThread(
uint32_t frame_token,
PresentationTimeCallbackBuffer::PendingCallbacks activated,

@ -113,7 +113,8 @@ TestViewTransitionContentLayerImpl::TestViewTransitionContentLayerImpl(
: ViewTransitionContentLayerImpl(tree_impl,
id,
resource_id,
is_live_content_layer) {}
is_live_content_layer,
gfx::RectF()) {}
void TestViewTransitionContentLayerImpl::AddDamageRect(
const gfx::Rect& damage_rect) {

@ -12,6 +12,7 @@
#include <stddef.h>
#include <algorithm>
#include <map>
#include <utility>
#include <vector>
@ -29,6 +30,7 @@
#include "cc/layers/layer.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/picture_layer.h"
#include "cc/layers/view_transition_content_layer_impl.h"
#include "cc/paint/filter_operation.h"
#include "cc/trees/clip_node.h"
#include "cc/trees/effect_node.h"
@ -1064,9 +1066,12 @@ bool SkipForInvertibility(const LayerImpl* layer,
: !transform_node->ancestors_are_invertible;
}
void ComputeInitialRenderSurfaceList(LayerTreeImpl* layer_tree_impl,
PropertyTrees* property_trees,
RenderSurfaceList* render_surface_list) {
void ComputeInitialRenderSurfaceList(
LayerTreeImpl* layer_tree_impl,
PropertyTrees* property_trees,
RenderSurfaceList* render_surface_list,
std::map<viz::ViewTransitionElementResourceId,
ViewTransitionContentLayerImpl*>& view_transition_content_layers) {
EffectTree& effect_tree = property_trees->effect_tree_mutable();
for (int i = kContentsRootPropertyNodeId;
i < static_cast<int>(effect_tree.size()); ++i) {
@ -1136,12 +1141,27 @@ void ComputeInitialRenderSurfaceList(LayerTreeImpl* layer_tree_impl,
// The layer contributes its drawable content rect to its render target.
render_target->AccumulateContentRectFromContributingLayer(layer);
render_target->increment_num_contributors();
if (!layer->ViewTransitionResourceId().IsValid()) {
continue;
}
auto* view_transition_content_layer =
static_cast<ViewTransitionContentLayerImpl*>(layer);
view_transition_content_layer->SetOriginatingSurfaceContentRect(
gfx::Rect());
view_transition_content_layers[layer->ViewTransitionResourceId()] =
view_transition_content_layer;
}
}
void ComputeSurfaceContentRects(PropertyTrees* property_trees,
RenderSurfaceList* render_surface_list,
int max_texture_size) {
void ComputeSurfaceContentRects(
PropertyTrees* property_trees,
RenderSurfaceList* render_surface_list,
int max_texture_size,
const std::map<viz::ViewTransitionElementResourceId,
ViewTransitionContentLayerImpl*>
view_transition_content_layers,
std::vector<std::pair<viz::ViewTransitionElementResourceId, gfx::RectF>>&
view_transition_content_rects) {
// Walk the list backwards, accumulating each surface's content rect into its
// target's content rect.
for (RenderSurfaceImpl* render_surface :
@ -1164,6 +1184,28 @@ void ComputeSurfaceContentRects(PropertyTrees* property_trees,
render_target->AccumulateContentRectFromContributingRenderSurface(
render_surface);
render_target->increment_num_contributors();
// Collect the content rects for view transition capture surfaces, and
// adjust the geometry for the corresponding content layer (the
// pseudo-element's layer).
const auto& view_transition_id =
render_surface->OwningEffectNode()->view_transition_element_resource_id;
if (!view_transition_id.IsValid()) {
continue;
}
auto unscaled_content_rect =
render_surface->SurfaceScale().InverseOrIdentity().MapRect(
render_surface->content_rect());
view_transition_content_rects.emplace_back(
view_transition_id, gfx::RectF(unscaled_content_rect));
if (view_transition_content_layers.contains(view_transition_id)) {
view_transition_content_layers.at(view_transition_id)
->SetOriginatingSurfaceContentRect(unscaled_content_rect);
}
}
}
@ -1210,17 +1252,28 @@ void CalculateRenderSurfaceLayerList(LayerTreeImpl* layer_tree_impl,
RenderSurfaceList* render_surface_list,
const int max_texture_size) {
RenderSurfaceList initial_render_surface_list;
std::vector<std::pair<viz::ViewTransitionElementResourceId, gfx::RectF>>
view_transition_content_rects;
std::map<viz::ViewTransitionElementResourceId,
ViewTransitionContentLayerImpl*>
view_transition_content_layers;
// First compute a list that might include surfaces that later turn out to
// have an empty content rect. After surface content rects are computed,
// produce a final list that omits empty surfaces.
ComputeInitialRenderSurfaceList(layer_tree_impl, property_trees,
&initial_render_surface_list);
&initial_render_surface_list,
view_transition_content_layers);
ComputeSurfaceContentRects(property_trees, &initial_render_surface_list,
max_texture_size);
max_texture_size, view_transition_content_layers,
view_transition_content_rects);
ComputeListOfNonEmptySurfaces(layer_tree_impl, property_trees,
&initial_render_surface_list,
render_surface_list);
for (const auto& [id, rect] : view_transition_content_rects) {
layer_tree_impl->SetViewTransitionContentRect(id, rect);
}
}
void RecordRenderSurfaceReasonsForTracing(

@ -527,17 +527,17 @@ void LayerTreeHost::NotifyImageDecodeFinished(int request_id,
}
void LayerTreeHost::NotifyTransitionRequestsFinished(
const std::vector<uint32_t>& sequence_ids) {
const uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects& rects) {
DCHECK(IsMainThread());
// TODO(vmpstr): This might also be a good spot to expire long standing
// requests if they were not finished.
for (auto& sequence_id : sequence_ids) {
auto it = view_transition_callbacks_.find(sequence_id);
if (it == view_transition_callbacks_.end())
continue;
std::move(it->second).Run();
view_transition_callbacks_.erase(it);
auto it = view_transition_callbacks_.find(sequence_id);
if (it == view_transition_callbacks_.end()) {
return;
}
std::move(it->second).Run(rects);
view_transition_callbacks_.erase(it);
}
void LayerTreeHost::SetLayerTreeFrameSink(
@ -2100,10 +2100,10 @@ void LayerTreeHost::SetDelegatedInkMetadata(
SetNeedsCommit();
}
std::vector<base::OnceClosure>
std::vector<ViewTransitionRequest::ViewTransitionCaptureCallback>
LayerTreeHost::TakeViewTransitionCallbacksForTesting() {
DCHECK(IsMainThread());
std::vector<base::OnceClosure> result;
std::vector<ViewTransitionRequest::ViewTransitionCaptureCallback> result;
for (auto& item : view_transition_callbacks_)
result.push_back(std::move(item.second));
view_transition_callbacks_.clear();

@ -789,7 +789,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
void NotifyThroughputTrackerResults(CustomTrackerResults results);
void NotifyImageDecodeFinished(int request_id, bool decode_succeeded);
void NotifyTransitionRequestsFinished(
const std::vector<uint32_t>& sequence_ids);
const uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects& rects);
LayerTreeHostClient* client() {
DCHECK(IsMainThread());
@ -907,7 +908,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
void AddViewTransitionRequest(std::unique_ptr<ViewTransitionRequest> request);
std::vector<base::OnceClosure> TakeViewTransitionCallbacksForTesting();
std::vector<ViewTransitionRequest::ViewTransitionCaptureCallback>
TakeViewTransitionCallbacksForTesting();
// Returns a percentage of dropped frames of the last second.
double GetPercentDroppedFrames() const;
@ -1114,7 +1116,8 @@ class CC_EXPORT LayerTreeHost : public MutatorHostClient {
EventsMetricsManager events_metrics_manager_;
// A list of callbacks that need to be invoked when they are processed.
base::flat_map<uint32_t, base::OnceClosure> view_transition_callbacks_;
base::flat_map<uint32_t, ViewTransitionRequest::ViewTransitionCaptureCallback>
view_transition_callbacks_;
// Set if WaitForCommitCompletion() was called before commit completes. Used
// for histograms.

@ -125,6 +125,7 @@
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "components/viz/common/traced_value.h"
#include "components/viz/common/transition_utils.h"
#include "components/viz/common/view_transition_element_resource_id.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/client/context_support.h"
@ -2374,7 +2375,29 @@ void LayerTreeHostImpl::OnDraw(const gfx::Transform& transform,
void LayerTreeHostImpl::OnCompositorFrameTransitionDirectiveProcessed(
uint32_t sequence_id) {
client_->NotifyTransitionRequestFinished(sequence_id);
// See ViewTransitionOverflowRectFromSurface
// We cache the computed content rect for each view-transition capture
// surface. This rect is then used to map the generated render pass or texture
// to the target coordinate space.
viz::ViewTransitionElementResourceRects rects_for_this_sequence;
auto it = view_transition_content_rects_.find(sequence_id);
// The map entry would exist only if there are actual rects (non-root
// elements).
if (it != view_transition_content_rects_.end()) {
it->second.swap(rects_for_this_sequence);
view_transition_content_rects_.erase(it);
}
client_->NotifyTransitionRequestFinished(sequence_id,
std::move(rects_for_this_sequence));
}
void LayerTreeHostImpl::SetViewTransitionContentRect(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceId& id,
const gfx::RectF& rect) {
view_transition_content_rects_[sequence_id].insert(std::make_pair(id, rect));
}
void LayerTreeHostImpl::OnSurfaceEvicted(

@ -72,6 +72,7 @@
#include "components/viz/common/surfaces/region_capture_bounds.h"
#include "components/viz/common/surfaces/surface_id.h"
#include "components/viz/common/surfaces/surface_range.h"
#include "components/viz/common/view_transition_element_resource_id.h"
#include "ui/gfx/geometry/rect.h"
namespace gfx {
@ -152,7 +153,9 @@ class LayerTreeHostImplClient {
virtual void NotifyImageDecodeRequestFinished(int request_id,
bool decode_succeeded) = 0;
virtual void NotifyTransitionRequestFinished(uint32_t sequence_id) = 0;
virtual void NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects&) = 0;
// Called when a presentation time is requested. |frame_token| identifies
// the frame that was presented. |callbacks| holds both impl side and main
@ -927,6 +930,11 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
}
const LayerTreeHostImplClient* client_for_testing() const { return client_; }
void SetViewTransitionContentRect(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceId& id,
const gfx::RectF& rect);
protected:
LayerTreeHostImpl(
const LayerTreeSettings& settings,
@ -1354,6 +1362,9 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
base::flat_set<ElementId> pending_invalidation_raster_inducing_scrolls_;
std::unordered_map<uint32_t, viz::ViewTransitionElementResourceRects>
view_transition_content_rects_;
// Must be the last member to ensure this is destroyed first in the
// destruction order and invalidates all weak pointers.
base::WeakPtrFactory<LayerTreeHostImpl> weak_factory_{this};

@ -314,7 +314,9 @@ class LayerTreeHostImplTestBase : public testing::Test,
void SetHasActiveThreadedScroll(bool is_scrolling) override {}
void SetWaitingForScrollEvent(bool waiting_for_scroll_event) override {}
size_t CommitDurationSampleCountForTesting() const override { return 0; }
void NotifyTransitionRequestFinished(uint32_t sequence_id) override {}
void NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects&) override {}
void set_reduce_memory_result(bool reduce_memory_result) {
reduce_memory_result_ = reduce_memory_result;
}

@ -9817,7 +9817,10 @@ class LayerTreeHostTestViewTransitionsPropagatedToMetadata
layer_tree_host()->AddViewTransitionRequest(
ViewTransitionRequest::CreateCapture(
blink::ViewTransitionToken(), /*maybe_cross_frame_sink=*/false, {},
base::BindLambdaForTesting([this]() { CommitLambdaCalled(); })));
base::BindLambdaForTesting(
[this](const viz::ViewTransitionElementResourceRects&) {
CommitLambdaCalled();
})));
}
void CommitLambdaCalled() { ++num_lambda_calls_; }
@ -9826,7 +9829,7 @@ class LayerTreeHostTestViewTransitionsPropagatedToMetadata
const viz::CompositorFrame& frame) override {
ASSERT_EQ(1u, frame.metadata.transition_directives.size());
const auto& save = frame.metadata.transition_directives[0];
submitted_sequence_ids_.push_back(save.sequence_id());
submitted_sequence_id_ = save.sequence_id();
EXPECT_EQ(save.type(),
viz::CompositorFrameTransitionDirective::Type::kSave);
@ -9835,14 +9838,14 @@ class LayerTreeHostTestViewTransitionsPropagatedToMetadata
void DidPresentCompositorFrame(
uint32_t frame_token,
const viz::FrameTimingDetails& frame_timing_details) override {
layer_tree_host()->NotifyTransitionRequestsFinished(
submitted_sequence_ids_);
layer_tree_host()->NotifyTransitionRequestsFinished(submitted_sequence_id_,
{});
EndTest();
}
void AfterTest() override { EXPECT_EQ(1, num_lambda_calls_); }
std::vector<uint32_t> submitted_sequence_ids_;
uint32_t submitted_sequence_id_;
int num_lambda_calls_ = 0;
};

@ -25,6 +25,7 @@
#include "base/metrics/histogram_macros.h"
#include "base/not_fatal_until.h"
#include "base/notreached.h"
#include "base/parameter_pack.h"
#include "base/ranges/algorithm.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
@ -58,6 +59,7 @@
#include "cc/trees/tree_synchronizer.h"
#include "cc/view_transition/view_transition_request.h"
#include "components/viz/common/traced_value.h"
#include "components/viz/common/view_transition_element_resource_id.h"
#include "ui/gfx/geometry/box_f.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/rect_conversions.h"
@ -3066,6 +3068,33 @@ bool LayerTreeImpl::HasViewTransitionSaveRequest() const {
return false;
}
bool LayerTreeImpl::MatchViewTransitionResource(
uint32_t sequence_id,
const blink::ViewTransitionToken& token) const {
for (const auto& request : view_transition_requests_) {
if (request->type() == ViewTransitionRequest::Type::kSave &&
request->sequence_id() == sequence_id && request->token() == token) {
return true;
}
}
return false;
}
void LayerTreeImpl::SetViewTransitionContentRect(
const viz::ViewTransitionElementResourceId& id,
const gfx::RectF& rect) {
// TODO(noamr) this could be more efficient, but usually there would be only 1
// or 2 anyway.
for (const auto& request : view_transition_requests_) {
if (request->type() == ViewTransitionRequest::Type::kSave &&
request->token() == id.transition_token()) {
host_impl_->SetViewTransitionContentRect(request->sequence_id(), id,
rect);
}
}
}
bool LayerTreeImpl::IsReadyToActivate() const {
return host_impl_->IsReadyToActivate();
}

@ -823,10 +823,17 @@ class CC_EXPORT LayerTreeImpl {
// output of the current frame.
bool HasViewTransitionSaveRequest() const;
bool MatchViewTransitionResource(
uint32_t sequence_id,
const blink::ViewTransitionToken& token) const;
void UpdateAllScrollbarGeometriesForTesting() {
UpdateAllScrollbarGeometries();
}
void SetViewTransitionContentRect(const viz::ViewTransitionElementResourceId&,
const gfx::RectF&);
protected:
float ClampPageScaleFactorToLimits(float page_scale_factor) const;
void PushPageScaleFactorAndLimits(const float* page_scale_factor,

@ -630,11 +630,13 @@ void ProxyImpl::NotifyImageDecodeRequestFinished(int request_id,
}
}
void ProxyImpl::NotifyTransitionRequestFinished(uint32_t sequence_id) {
void ProxyImpl::NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects& rects) {
DCHECK(IsImplThread());
MainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProxyMain::NotifyTransitionRequestFinished,
proxy_main_weak_ptr_, sequence_id));
proxy_main_weak_ptr_, sequence_id, rects));
}
void ProxyImpl::DidPresentCompositorFrameOnImplThread(

@ -133,7 +133,9 @@ class CC_EXPORT ProxyImpl : public LayerTreeHostImplClient,
bool needs_first_draw_on_activation) override;
void NotifyImageDecodeRequestFinished(int request_id,
bool decode_succeeded) override;
void NotifyTransitionRequestFinished(uint32_t sequence_id) override;
void NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects&) override;
void DidPresentCompositorFrameOnImplThread(
uint32_t frame_token,
PresentationTimeCallbackBuffer::PendingCallbacks activated,

@ -35,6 +35,7 @@
#include "cc/trees/scoped_abort_remaining_swap_promises.h"
#include "cc/trees/swap_promise.h"
#include "cc/trees/trace_utils.h"
#include "components/viz/common/view_transition_element_resource_id.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
namespace cc {
@ -530,8 +531,10 @@ void ProxyMain::NotifyImageDecodeRequestFinished(int request_id,
layer_tree_host_->NotifyImageDecodeFinished(request_id, decode_succeeded);
}
void ProxyMain::NotifyTransitionRequestFinished(uint32_t sequence_id) {
layer_tree_host_->NotifyTransitionRequestsFinished({sequence_id});
void ProxyMain::NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects& rects) {
layer_tree_host_->NotifyTransitionRequestsFinished(sequence_id, rects);
}
bool ProxyMain::IsStarted() const {

@ -74,7 +74,9 @@ class CC_EXPORT ProxyMain : public Proxy {
base::TimeDelta first_scroll_delay,
base::TimeTicks first_scroll_timestamp);
void NotifyImageDecodeRequestFinished(int request_id, bool decode_succeeded);
void NotifyTransitionRequestFinished(uint32_t sequence_id);
void NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects&);
CommitPipelineStage max_requested_pipeline_stage() const {
return max_requested_pipeline_stage_;

@ -672,12 +672,14 @@ void SingleThreadProxy::NotifyImageDecodeRequestFinished(
}
}
void SingleThreadProxy::NotifyTransitionRequestFinished(uint32_t sequence_id) {
void SingleThreadProxy::NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects& rects) {
DCHECK(!task_runner_provider_->HasImplThread() ||
task_runner_provider_->IsImplThread());
DebugScopedSetMainThread main_thread(task_runner_provider_);
layer_tree_host_->NotifyTransitionRequestsFinished({sequence_id});
layer_tree_host_->NotifyTransitionRequestsFinished(sequence_id, rects);
}
void SingleThreadProxy::DidPresentCompositorFrameOnImplThread(

@ -22,6 +22,7 @@
#include "cc/trees/task_runner_provider.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/surfaces/local_surface_id.h"
#include "components/viz/common/view_transition_element_resource_id.h"
namespace viz {
class BeginFrameSource;
@ -148,7 +149,9 @@ class CC_EXPORT SingleThreadProxy : public Proxy,
bool needs_first_draw_on_activation) override;
void NotifyImageDecodeRequestFinished(int request_id,
bool decode_succeeded) override;
void NotifyTransitionRequestFinished(uint32_t sequence_id) override;
void NotifyTransitionRequestFinished(
uint32_t sequence_id,
const viz::ViewTransitionElementResourceRects&) override;
void DidPresentCompositorFrameOnImplThread(
uint32_t frame_token,
PresentationTimeCallbackBuffer::PendingCallbacks callbacks,

@ -41,7 +41,7 @@ std::unique_ptr<ViewTransitionRequest> ViewTransitionRequest::CreateCapture(
const blink::ViewTransitionToken& transition_token,
bool maybe_cross_frame_sink,
std::vector<viz::ViewTransitionElementResourceId> capture_ids,
base::OnceClosure commit_callback) {
ViewTransitionCaptureCallback commit_callback) {
return base::WrapUnique(new ViewTransitionRequest(
Type::kSave, transition_token, maybe_cross_frame_sink,
std::move(capture_ids), std::move(commit_callback)));
@ -54,7 +54,7 @@ ViewTransitionRequest::CreateAnimateRenderer(
bool maybe_cross_frame_sink) {
return base::WrapUnique(new ViewTransitionRequest(
Type::kAnimateRenderer, transition_token, maybe_cross_frame_sink, {},
base::OnceClosure()));
ViewTransitionCaptureCallback()));
}
// static
@ -63,7 +63,7 @@ std::unique_ptr<ViewTransitionRequest> ViewTransitionRequest::CreateRelease(
bool maybe_cross_frame_sink) {
return base::WrapUnique(new ViewTransitionRequest(
Type::kRelease, transition_token, maybe_cross_frame_sink, {},
base::OnceClosure()));
ViewTransitionCaptureCallback()));
}
ViewTransitionRequest::ViewTransitionRequest(
@ -71,7 +71,7 @@ ViewTransitionRequest::ViewTransitionRequest(
const blink::ViewTransitionToken& transition_token,
bool maybe_cross_frame_sink,
std::vector<viz::ViewTransitionElementResourceId> capture_ids,
base::OnceClosure commit_callback)
ViewTransitionCaptureCallback commit_callback)
: type_(type),
transition_token_(transition_token),
maybe_cross_frame_sink_(maybe_cross_frame_sink),

@ -15,6 +15,7 @@
#include "cc/cc_export.h"
#include "components/viz/common/quads/compositor_frame_transition_directive.h"
#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/view_transition_element_resource_id.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "ui/gfx/display_color_spaces.h"
@ -30,6 +31,9 @@ class CC_EXPORT ViewTransitionRequest {
viz::CompositorRenderPassId>;
using Type = viz::CompositorFrameTransitionDirective::Type;
using ViewTransitionContentRectMap = viz::ViewTransitionElementResourceRects;
using ViewTransitionCaptureCallback =
base::OnceCallback<void(const ViewTransitionContentRectMap&)>;
// Creates a Type::kCapture type of request.
// `transition_token` is an identifier that uniquely identifies each
// transition.
@ -39,7 +43,7 @@ class CC_EXPORT ViewTransitionRequest {
const blink::ViewTransitionToken& transition_token,
bool maybe_cross_frame_sink,
std::vector<viz::ViewTransitionElementResourceId> capture_ids,
base::OnceClosure commit_callback);
ViewTransitionCaptureCallback commit_callback);
// Creates a Type::kAnimateRenderer type of request.
static std::unique_ptr<ViewTransitionRequest> CreateAnimateRenderer(
@ -60,7 +64,7 @@ class CC_EXPORT ViewTransitionRequest {
// able to begin the next step in the animation. In other words, when this
// callback is invoked it can resolve a script promise that is gating this
// step.
base::OnceClosure TakeFinishedCallback() {
ViewTransitionCaptureCallback TakeFinishedCallback() {
return std::move(commit_callback_);
}
@ -79,18 +83,20 @@ class CC_EXPORT ViewTransitionRequest {
// Testing / debugging functionality.
std::string ToString() const;
const blink::ViewTransitionToken& token() { return transition_token_; }
private:
ViewTransitionRequest(
Type type,
const blink::ViewTransitionToken& transition_token,
bool maybe_cross_frame_sink,
std::vector<viz::ViewTransitionElementResourceId> capture_ids,
base::OnceClosure commit_callback);
ViewTransitionCaptureCallback commit_callback);
const Type type_;
const blink::ViewTransitionToken transition_token_;
const bool maybe_cross_frame_sink_;
base::OnceClosure commit_callback_;
ViewTransitionCaptureCallback commit_callback_;
const uint32_t sequence_id_;
const std::vector<viz::ViewTransitionElementResourceId> capture_resource_ids_;

@ -13,14 +13,17 @@ namespace cc {
TEST(ViewTransitionRequestTest, PrepareRequest) {
bool called = false;
auto callback = base::BindLambdaForTesting([&called]() { called = true; });
auto callback = base::BindLambdaForTesting(
[&called](const viz::ViewTransitionElementResourceRects&) {
called = true;
});
auto request = ViewTransitionRequest::CreateCapture(
blink::ViewTransitionToken(),
/*maybe_cross_frame_sink=*/false, {}, std::move(callback));
EXPECT_FALSE(called);
request->TakeFinishedCallback().Run();
request->TakeFinishedCallback().Run({});
EXPECT_TRUE(called);
EXPECT_TRUE(request->TakeFinishedCallback().is_null());

@ -8,12 +8,15 @@
#include <stdint.h>
#include <cstdint>
#include <functional>
#include <string>
#include <tuple>
#include <vector>
#include <unordered_map>
#include <utility>
#include "components/viz/common/viz_common_export.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "ui/gfx/geometry/rect_f.h"
namespace viz {
@ -54,6 +57,21 @@ class VIZ_COMMON_EXPORT ViewTransitionElementResourceId {
uint32_t local_id_ = kInvalidLocalId;
};
using ViewTransitionElementResourceRects =
std::unordered_map<ViewTransitionElementResourceId, gfx::RectF>;
} // namespace viz
namespace std {
template <>
struct hash<viz::ViewTransitionElementResourceId> {
size_t operator()(
const viz::ViewTransitionElementResourceId& resource_id) const {
// Usually view transition resources in the same map would have the same
// transition token, so only using the local ID for hashing.
return resource_id.local_id();
}
};
} // namespace std
#endif // COMPONENTS_VIZ_COMMON_VIEW_TRANSITION_ELEMENT_RESOURCE_ID_H_

@ -169,8 +169,9 @@ gfx::Transform GetViewTransitionTransform(
shared_element_quad.height() /
static_cast<SkScalar>(view_transition_content_output.height()));
view_transition_transform.Translate(-view_transition_content_output.x(),
-view_transition_content_output.y());
view_transition_transform.Translate(
shared_element_quad.x() - view_transition_content_output.x(),
shared_element_quad.y() - view_transition_content_output.y());
return view_transition_transform;
}

@ -934,7 +934,9 @@ void LayerContextImpl::NotifyImageDecodeRequestFinished(int request_id,
bool decode_succeeded) {
}
void LayerContextImpl::NotifyTransitionRequestFinished(uint32_t sequence_id) {}
void LayerContextImpl::NotifyTransitionRequestFinished(
uint32_t sequence_id,
const ViewTransitionElementResourceRects&) {}
void LayerContextImpl::DidPresentCompositorFrameOnImplThread(
uint32_t frame_token,

@ -76,7 +76,9 @@ class LayerContextImpl : public cc::LayerTreeHostImplClient,
bool needs_first_draw_on_activation) override;
void NotifyImageDecodeRequestFinished(int request_id,
bool decode_succeeded) override;
void NotifyTransitionRequestFinished(uint32_t sequence_id) override;
void NotifyTransitionRequestFinished(
uint32_t sequence_id,
const ViewTransitionElementResourceRects&) override;
void DidPresentCompositorFrameOnImplThread(
uint32_t frame_token,
cc::PresentationTimeCallbackBuffer::PendingCallbacks callbacks,

@ -11,6 +11,7 @@
#include "base/trace_event/trace_event.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/paint_holding_reason.h"
#include "components/viz/common/view_transition_element_resource_id.h"
#include "third_party/blink/public/platform/web_content_settings_client.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_view_transition_type_set.h"
#include "third_party/blink/renderer/core/css/css_rule.h"
@ -686,7 +687,9 @@ void ViewTransition::ContextDestroyed() {
SkipTransition(PromiseResponse::kRejectAbort);
}
void ViewTransition::NotifyCaptureFinished() {
void ViewTransition::NotifyCaptureFinished(
const std::unordered_map<viz::ViewTransitionElementResourceId,
gfx::RectF>&) {
if (state_ != State::kCapturing) {
DCHECK(IsTerminalState(state_));
return;

@ -6,6 +6,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_VIEW_TRANSITION_VIEW_TRANSITION_H_
#include <memory>
#include <unordered_map>
#include "base/memory/scoped_refptr.h"
#include "base/task/single_thread_task_runner.h"
@ -324,7 +325,9 @@ class CORE_EXPORT ViewTransition : public GarbageCollected<ViewTransition>,
void ProcessCurrentState();
void NotifyCaptureFinished();
void NotifyCaptureFinished(
const std::unordered_map<viz::ViewTransitionElementResourceId,
gfx::RectF>&);
// Used to defer visual updates between transition prepare dispatching and
// transition start to allow the page to set up the final scene

@ -94,7 +94,7 @@ class ViewTransitionTest : public testing::Test,
UpdateAllLifecyclePhasesForTest();
for (auto& callback :
LayerTreeHost()->TakeViewTransitionCallbacksForTesting()) {
std::move(callback).Run();
std::move(callback).Run({});
}
}

@ -238,7 +238,7 @@ class AXViewTransitionTest : public testing::Test {
UpdateAllLifecyclePhasesForTest();
for (auto& callback :
LayerTreeHost()->TakeViewTransitionCallbacksForTesting()) {
std::move(callback).Run();
std::move(callback).Run({});
}
}