blob: 3f6f81b88edd05d91a2a07de5cb894e9e52ff6e9 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/service/layers/layer_context_impl.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/memory/ptr_util.h"
#include "base/notimplemented.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/trace_event/traced_value.h"
#include "base/trace_event/typed_macros.h"
#include "base/types/expected_macros.h"
#include "cc/animation/animation.h"
#include "cc/animation/animation_host.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/debug/layer_tree_debug_state.h"
#include "cc/debug/rendering_stats_instrumentation.h"
#include "cc/input/browser_controls_offset_manager.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/mirror_layer_impl.h"
#include "cc/layers/nine_patch_layer_impl.h"
#include "cc/layers/nine_patch_thumb_scrollbar_layer_impl.h"
#include "cc/layers/painted_scrollbar_layer_impl.h"
#include "cc/layers/solid_color_layer_impl.h"
#include "cc/layers/solid_color_scrollbar_layer_impl.h"
#include "cc/layers/surface_layer_impl.h"
#include "cc/layers/texture_layer_impl.h"
#include "cc/layers/tile_display_layer_impl.h"
#include "cc/layers/ui_resource_layer_impl.h"
#include "cc/layers/view_transition_content_layer_impl.h"
#include "cc/trees/layer_tree_host_impl.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/layer_tree_settings.h"
#include "cc/trees/property_tree.h"
#include "cc/trees/task_runner_provider.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h"
namespace viz {
namespace {
#define RETURN_IF_FALSE(expr, error) \
do { \
if (!(expr)) { \
return base::unexpected(error); \
} \
} while (false)
int GenerateNextDisplayTreeId() {
static int next_id = 1;
return next_id++;
}
cc::LayerTreeSettings GetDisplayTreeSettings(
mojom::LayerContextSettingsPtr remote_settings) {
cc::LayerTreeSettings settings;
settings.use_layer_lists = true;
settings.trees_in_viz_in_viz_process = true;
settings.display_tree_draw_mode_is_gpu = remote_settings->draw_mode_is_gpu;
settings.enable_early_damage_check =
remote_settings->enable_early_damage_check;
settings.damaged_frame_limit = remote_settings->damaged_frame_limit;
settings.scrollbar_animator = remote_settings->scrollbar_animator;
settings.scrollbar_fade_delay = remote_settings->scrollbar_fade_delay;
settings.scrollbar_fade_duration = remote_settings->scrollbar_fade_duration;
settings.scrollbar_thinning_duration =
remote_settings->scrollbar_thinning_duration;
settings.idle_thickness_scale = remote_settings->idle_thickness_scale;
settings.top_controls_show_threshold =
remote_settings->top_controls_show_threshold;
settings.top_controls_hide_threshold =
remote_settings->top_controls_hide_threshold;
settings.minimum_occlusion_tracking_size =
remote_settings->minimum_occlusion_tracking_size;
settings.enable_edge_anti_aliasing =
remote_settings->enable_edge_anti_aliasing;
settings.enable_backface_visibility_interop =
remote_settings->enable_backface_visibility_interop;
settings.enable_fluent_scrollbar = remote_settings->enable_fluent_scrollbar;
settings.enable_fluent_overlay_scrollbar =
remote_settings->enable_fluent_overlay_scrollbar;
return settings;
}
base::expected<void, std::string> CreateLayer(
cc::LayerTreeHostImpl& host_impl,
cc::LayerTreeImpl& tree,
const mojom::Layer& wire,
std::unique_ptr<cc::LayerImpl>& layer) {
cc::mojom::LayerType type = wire.type;
int id = wire.id;
switch (type) {
case cc::mojom::LayerType::kLayer:
layer = cc::LayerImpl::Create(&tree, id);
break;
case cc::mojom::LayerType::kMirror:
layer = cc::MirrorLayerImpl::Create(&tree, id);
break;
case cc::mojom::LayerType::kNinePatch:
layer = cc::NinePatchLayerImpl::Create(&tree, id);
break;
case cc::mojom::LayerType::kNinePatchThumbScrollbar: {
RETURN_IF_FALSE(
wire.layer_extra &&
wire.layer_extra->is_nine_patch_thumb_scrollbar_layer_extra(),
"Invalid layer_extra type for NinePatchThumbScrollbarLayerImpl");
auto& extra =
wire.layer_extra->get_nine_patch_thumb_scrollbar_layer_extra();
cc::ScrollbarOrientation orientation =
extra->scrollbar_base_extra->is_horizontal_orientation
? cc::ScrollbarOrientation::kHorizontal
: cc::ScrollbarOrientation::kVertical;
layer = cc::NinePatchThumbScrollbarLayerImpl::Create(
&tree, id, orientation,
extra->scrollbar_base_extra->is_left_side_vertical_scrollbar);
break;
}
case cc::mojom::LayerType::kPaintedScrollbar: {
RETURN_IF_FALSE(wire.layer_extra &&
wire.layer_extra->is_painted_scrollbar_layer_extra(),
"Invalid layer_extra type for PaintedScrollbarLayerImpl");
auto& extra = wire.layer_extra->get_painted_scrollbar_layer_extra();
cc::ScrollbarOrientation orientation =
extra->scrollbar_base_extra->is_horizontal_orientation
? cc::ScrollbarOrientation::kHorizontal
: cc::ScrollbarOrientation::kVertical;
layer = cc::PaintedScrollbarLayerImpl::Create(
&tree, id, orientation,
extra->scrollbar_base_extra->is_left_side_vertical_scrollbar,
extra->scrollbar_base_extra->is_overlay_scrollbar);
break;
}
case cc::mojom::LayerType::kTileDisplay:
layer = std::make_unique<cc::TileDisplayLayerImpl>(tree, id);
break;
case cc::mojom::LayerType::kSolidColorScrollbar: {
RETURN_IF_FALSE(
wire.layer_extra &&
wire.layer_extra->is_solid_color_scrollbar_layer_extra(),
"Invalid layer_extra type for SolidColorScrollbarLayerImpl");
auto& extra = wire.layer_extra->get_solid_color_scrollbar_layer_extra();
cc::ScrollbarOrientation orientation =
extra->scrollbar_base_extra->is_horizontal_orientation
? cc::ScrollbarOrientation::kHorizontal
: cc::ScrollbarOrientation::kVertical;
layer = cc::SolidColorScrollbarLayerImpl::Create(
&tree, id, orientation, extra->thumb_thickness, extra->track_start,
extra->scrollbar_base_extra->is_left_side_vertical_scrollbar);
break;
}
case cc::mojom::LayerType::kSurface:
// The callback is triggered in the renderer side during WillDraw(),
// and there is no need to do it in viz.
layer = cc::SurfaceLayerImpl::Create(&tree, id, base::NullCallback());
break;
case cc::mojom::LayerType::kSolidColor:
layer = cc::SolidColorLayerImpl::Create(&tree, id);
break;
case cc::mojom::LayerType::kTexture:
layer = cc::TextureLayerImpl::Create(&tree, id);
break;
case cc::mojom::LayerType::kUIResource:
layer = cc::UIResourceLayerImpl::Create(&tree, id);
break;
case cc::mojom::LayerType::kViewTransitionContent: {
RETURN_IF_FALSE(
wire.layer_extra &&
wire.layer_extra->is_view_transition_content_layer_extra(),
"Invalid layer_extra type for ViewTransitionContentLayerImpl");
auto& extra = wire.layer_extra->get_view_transition_content_layer_extra();
layer = cc::ViewTransitionContentLayerImpl::Create(
&tree, id, extra->resource_id, extra->is_live_content_layer,
extra->max_extents_rect);
break;
}
default:
// TODO(rockot): Support other layer types.
layer = cc::SolidColorLayerImpl::Create(&tree, id);
break;
}
return base::ok();
}
template <typename TreeType>
bool IsPropertyTreeIndexValid(const TreeType& tree, int32_t index) {
return index >= 0 && index < tree.next_available_id();
}
template <typename TreeType>
bool IsOptionalPropertyTreeIndexValid(const TreeType& tree, int32_t index) {
return index == cc::kInvalidPropertyNodeId ||
IsPropertyTreeIndexValid(tree, index);
}
base::expected<void, std::string> UpdatePropertyTreeNode(
cc::PropertyTrees& trees,
cc::TransformNode& node,
const mojom::TransformNode& wire) {
auto& tree = trees.transform_tree_mutable();
if (!IsOptionalPropertyTreeIndexValid(tree, wire.parent_frame_id)) {
return base::unexpected("Invalid parent_frame_id");
}
node.parent_frame_id = wire.parent_frame_id;
node.element_id = wire.element_id;
if (node.element_id) {
tree.SetElementIdForNodeId(node.id, node.element_id);
}
if (node.local != wire.local || node.origin != wire.origin ||
node.scroll_offset() != wire.scroll_offset) {
node.needs_local_transform_update = true;
}
node.local = wire.local;
node.origin = wire.origin;
node.post_translation = wire.post_translation;
node.set_to_parent(wire.to_parent);
node.SetScrollOffset(wire.scroll_offset, cc::DamageReason::kUntracked);
node.snap_amount = wire.snap_amount;
if (!wire.sticky_position_constraint_id) {
node.sticky_position_constraint_id = -1;
} else if (*wire.sticky_position_constraint_id >=
tree.sticky_position_data().size()) {
return base::unexpected("Invalid sticky_position_constraint_id");
} else {
node.sticky_position_constraint_id =
base::saturated_cast<int>(*wire.sticky_position_constraint_id);
}
if (!wire.anchor_position_scroll_data_id) {
} else if (*wire.anchor_position_scroll_data_id >=
tree.anchor_position_scroll_data().size()) {
return base::unexpected("Invalid anchor_position_scroll_data_id");
} else {
node.anchor_position_scroll_data_id =
base::saturated_cast<int>(*wire.anchor_position_scroll_data_id);
}
node.sorting_context_id = wire.sorting_context_id;
node.has_potential_animation = wire.has_potential_animation;
node.is_currently_animating = wire.is_currently_animating;
node.flattens_inherited_transform = wire.flattens_inherited_transform;
node.scrolls = wire.scrolls;
node.should_undo_overscroll = wire.should_undo_overscroll;
node.should_be_snapped = wire.should_be_snapped;
node.moved_by_outer_viewport_bounds_delta_y =
wire.moved_by_outer_viewport_bounds_delta_y;
node.in_subtree_of_page_scale_layer = wire.in_subtree_of_page_scale_layer;
node.delegates_to_parent_for_backface = wire.delegates_to_parent_for_backface;
node.will_change_transform = wire.will_change_transform;
node.maximum_animation_scale = wire.maximum_animation_scale;
node.node_and_ancestors_are_animated_or_invertible =
wire.node_and_ancestors_are_animated_or_invertible;
node.is_invertible = wire.is_invertible;
node.ancestors_are_invertible = wire.ancestors_are_invertible;
node.node_and_ancestors_are_flat = wire.node_and_ancestors_are_flat;
node.node_or_ancestors_will_change_transform =
wire.node_or_ancestors_will_change_transform;
node.visible_frame_element_id = wire.visible_frame_element_id;
node.SetTransformChanged(cc::DamageReason::kUntracked);
if (!node.SetDamageReasonsForDeserialization(
cc::DamageReasonSet::FromEnumBitmask(wire.damage_reasons_bit_mask))) {
// This error case shouldn't be reachable, since
// DamageReasonSet::FromEnumBitmask should already ignore any bits outside
// of the set's range.
return base::unexpected("Invalid damage_reasons_bit_mask");
}
node.moved_by_safe_area_bottom = wire.moved_by_safe_area_bottom;
return base::ok();
}
base::expected<void, std::string> UpdatePropertyTreeNode(
cc::PropertyTrees& trees,
cc::ClipNode& node,
const mojom::ClipNode& wire) {
if (!IsPropertyTreeIndexValid(trees.transform_tree(), wire.transform_id) ||
!IsOptionalPropertyTreeIndexValid(trees.effect_tree(),
wire.pixel_moving_filter_id)) {
return base::unexpected("Invalid transform_id for clip node");
}
node.transform_id = wire.transform_id;
node.clip = wire.clip;
node.pixel_moving_filter_id = wire.pixel_moving_filter_id;
return base::ok();
}
base::expected<void, std::string> UpdatePropertyTreeNode(
cc::PropertyTrees& trees,
cc::EffectNode& node,
const mojom::EffectNode& wire) {
if (!IsPropertyTreeIndexValid(trees.transform_tree(), wire.transform_id)) {
return base::unexpected("Invalid transform_id for effect node");
}
if (!IsPropertyTreeIndexValid(trees.clip_tree(), wire.clip_id)) {
return base::unexpected("Invalid clip_id for effect node");
}
if (!IsPropertyTreeIndexValid(trees.effect_tree(), wire.target_id)) {
return base::unexpected("Invalid target_id for effect node");
}
if (!IsOptionalPropertyTreeIndexValid(
trees.effect_tree(),
wire.closest_ancestor_with_cached_render_surface_id)) {
return base::unexpected(
"Invalid closest_ancestor_with_cached_render_surface_id for effect "
"node");
}
if (!IsOptionalPropertyTreeIndexValid(
trees.effect_tree(), wire.closest_ancestor_with_copy_request_id)) {
return base::unexpected(
"Invalid closest_ancestor_with_copy_request_id for effect node");
}
if (!IsOptionalPropertyTreeIndexValid(
trees.effect_tree(), wire.closest_ancestor_being_captured_id)) {
return base::unexpected(
"Invalid closest_ancestor_being_captured_id for effect node");
}
if (!IsOptionalPropertyTreeIndexValid(
trees.effect_tree(), wire.closest_ancestor_with_shared_element_id)) {
return base::unexpected(
"Invalid closest_ancestor_with_shared_element_id for effect node");
}
node.transform_id = wire.transform_id;
node.clip_id = wire.clip_id;
node.element_id = wire.element_id;
if (node.element_id) {
trees.effect_tree_mutable().SetElementIdForNodeId(node.id, node.element_id);
}
node.opacity = wire.opacity;
node.effect_changed = true;
node.render_surface_reason = wire.render_surface_reason;
node.surface_contents_scale = wire.surface_contents_scale;
node.subtree_capture_id = wire.subtree_capture_id;
node.subtree_size = wire.subtree_size;
if (wire.blend_mode > static_cast<uint32_t>(SkBlendMode::kLastMode)) {
return base::unexpected("Invalid blend_mode for effect node");
}
node.blend_mode = static_cast<SkBlendMode>(wire.blend_mode);
node.target_id = wire.target_id;
node.view_transition_target_id = wire.view_transition_target_id;
node.closest_ancestor_with_cached_render_surface_id =
wire.closest_ancestor_with_cached_render_surface_id;
node.closest_ancestor_with_copy_request_id =
wire.closest_ancestor_with_copy_request_id;
node.closest_ancestor_being_captured_id =
wire.closest_ancestor_being_captured_id;
node.closest_ancestor_with_shared_element_id =
wire.closest_ancestor_with_shared_element_id;
node.view_transition_element_resource_id =
wire.view_transition_element_resource_id;
node.filters = wire.filters;
node.backdrop_filters = wire.backdrop_filters;
node.backdrop_filter_bounds = wire.backdrop_filter_bounds;
node.backdrop_filter_quality = wire.backdrop_filter_quality;
node.backdrop_mask_element_id = wire.backdrop_mask_element_id;
node.mask_filter_info = wire.mask_filter_info;
node.cache_render_surface = wire.cache_render_surface;
node.double_sided = wire.double_sided;
node.trilinear_filtering = wire.trilinear_filtering;
node.subtree_hidden = wire.subtree_hidden;
node.has_potential_filter_animation = wire.has_potential_filter_animation;
node.has_potential_backdrop_filter_animation =
wire.has_potential_backdrop_filter_animation;
node.has_potential_opacity_animation = wire.has_potential_opacity_animation;
node.subtree_has_copy_request = wire.subtree_has_copy_request;
node.is_fast_rounded_corner = wire.is_fast_rounded_corner;
node.may_have_backdrop_effect = wire.may_have_backdrop_effect;
node.needs_effect_for_2d_scale_transform =
wire.needs_effect_for_2d_scale_transform;
return base::ok();
}
base::expected<void, std::string> UpdatePropertyTreeNode(
cc::PropertyTrees& trees,
cc::ScrollNode& node,
const mojom::ScrollNode& wire) {
if (wire.transform_id != cc::kInvalidPropertyNodeId &&
!IsPropertyTreeIndexValid(trees.transform_tree(), wire.transform_id)) {
return base::unexpected("Invalid transform_id for scroll node");
}
node.transform_id = wire.transform_id;
node.container_bounds = wire.container_bounds;
node.bounds = wire.bounds;
node.max_scroll_offset_affected_by_page_scale =
wire.max_scroll_offset_affected_by_page_scale;
node.scrolls_inner_viewport = wire.scrolls_inner_viewport;
node.scrolls_outer_viewport = wire.scrolls_outer_viewport;
node.prevent_viewport_scrolling_from_inner =
wire.prevent_viewport_scrolling_from_inner;
node.user_scrollable_horizontal = wire.user_scrollable_horizontal;
node.user_scrollable_vertical = wire.user_scrollable_vertical;
node.is_composited = wire.is_composited;
node.element_id = wire.element_id;
if (node.element_id) {
trees.scroll_tree_mutable().SetElementIdForNodeId(node.id, node.element_id);
}
return base::ok();
}
template <typename TreeType>
bool ResizePropertyTree(TreeType& tree, uint32_t num_nodes) {
if (num_nodes == tree.nodes().size()) {
return false;
}
if (num_nodes < tree.nodes().size()) {
tree.RemoveNodes(tree.nodes().size() - num_nodes);
return true;
}
for (size_t i = tree.nodes().size(); i < num_nodes; ++i) {
tree.Insert(typename TreeType::NodeType(), cc::kRootPropertyNodeId);
}
return true;
}
template <typename TreeType, typename WireContainerType>
base::expected<bool, std::string> UpdatePropertyTree(
cc::PropertyTrees& trees,
TreeType& tree,
const WireContainerType& wire_updates) {
if (wire_updates.empty()) {
return false;
}
for (const auto& wire : wire_updates) {
if (!IsPropertyTreeIndexValid(tree, wire->id)) {
return base::unexpected("Invalid property tree node ID");
}
if (!IsOptionalPropertyTreeIndexValid(tree, wire->parent_id)) {
return base::unexpected("Invalid property tree node parent_id");
}
if (wire->parent_id == cc::kInvalidPropertyNodeId &&
wire->id != cc::kRootPropertyNodeId &&
wire->id != cc::kSecondaryRootPropertyNodeId) {
return base::unexpected(
"Invalid parent_id for non-root property tree node");
}
auto& node = *tree.Node(wire->id);
node.id = wire->id;
node.parent_id = wire->parent_id;
RETURN_IF_ERROR(UpdatePropertyTreeNode(trees, node, *wire));
}
return true;
}
base::expected<std::vector<cc::StickyPositionNodeData>, std::string>
DeserializeStickyPositionData(
cc::PropertyTrees& trees,
std::vector<mojom::StickyPositionNodeDataPtr>& wire_data) {
std::vector<cc::StickyPositionNodeData> sticky_position_node_data;
sticky_position_node_data.reserve(wire_data.size());
for (auto& wire : wire_data) {
if (!IsPropertyTreeIndexValid(trees.scroll_tree(), wire->scroll_ancestor)) {
return base::unexpected("Invalid scroll ancestor ID");
}
cc::StickyPositionNodeData& data = sticky_position_node_data.emplace_back();
data.scroll_ancestor = wire->scroll_ancestor;
data.constraints.is_anchored_left = wire->is_anchored_left;
data.constraints.is_anchored_right = wire->is_anchored_right;
data.constraints.is_anchored_top = wire->is_anchored_top;
data.constraints.is_anchored_bottom = wire->is_anchored_bottom;
data.constraints.left_offset = wire->left_offset;
data.constraints.right_offset = wire->right_offset;
data.constraints.top_offset = wire->top_offset;
data.constraints.bottom_offset = wire->bottom_offset;
data.constraints.constraint_box_rect = wire->constraint_box_rect;
data.constraints.scroll_container_relative_sticky_box_rect =
wire->scroll_container_relative_sticky_box_rect;
data.constraints.scroll_container_relative_containing_block_rect =
wire->scroll_container_relative_containing_block_rect;
data.constraints.pixel_snap_offset = wire->pixel_snap_offset;
data.nearest_node_shifting_sticky_box =
wire->nearest_node_shifting_sticky_box;
data.nearest_node_shifting_containing_block =
wire->nearest_node_shifting_containing_block;
data.total_sticky_box_sticky_offset = wire->total_sticky_box_sticky_offset;
data.total_containing_block_sticky_offset =
wire->total_containing_block_sticky_offset;
}
return sticky_position_node_data;
}
base::expected<std::vector<cc::AnchorPositionScrollData>, std::string>
DeserializeAnchorPositionScrollData(
std::vector<mojom::AnchorPositionScrollDataPtr>& wire_data) {
std::vector<cc::AnchorPositionScrollData> anchor_position_scroll_data;
for (auto& wire : wire_data) {
cc::AnchorPositionScrollData& data =
anchor_position_scroll_data.emplace_back();
data.adjustment_container_ids = wire->adjustment_container_ids;
data.accumulated_scroll_origin = wire->accumulated_scroll_origin;
data.needs_scroll_adjustment_in_x = wire->needs_scroll_adjustment_in_x;
data.needs_scroll_adjustment_in_y = wire->needs_scroll_adjustment_in_y;
}
return anchor_position_scroll_data;
}
base::expected<void, std::string> UpdateTransformTreeProperties(
cc::PropertyTrees& trees,
cc::TransformTree& tree,
mojom::TransformTreeUpdate& update) {
tree.set_page_scale_factor(update.page_scale_factor);
tree.set_device_scale_factor(update.device_scale_factor);
tree.set_device_transform_scale_factor(update.device_transform_scale_factor);
tree.set_nodes_affected_by_outer_viewport_bounds_delta(
std::move(update.nodes_affected_by_outer_viewport_bounds_delta));
tree.set_nodes_affected_by_safe_area_bottom(
std::move(update.nodes_affected_by_safe_area_bottom));
ASSIGN_OR_RETURN(
tree.sticky_position_data(),
DeserializeStickyPositionData(trees, update.sticky_position_data));
ASSIGN_OR_RETURN(
tree.anchor_position_scroll_data(),
DeserializeAnchorPositionScrollData(update.anchor_position_scroll_data));
return base::ok();
}
base::expected<bool, std::string> UpdateScrollTreeProperties(
cc::PropertyTrees& trees,
cc::ScrollTree& tree,
const mojom::ScrollTreeUpdate& update) {
tree.synced_scroll_offset_map() = update.synced_scroll_offsets;
tree.scrolling_contents_cull_rects() = update.scrolling_contents_cull_rects;
bool elastic_overscroll_changed =
tree.elastic_overscroll() != update.elastic_overscroll;
tree.elastic_overscroll() = update.elastic_overscroll;
return elastic_overscroll_changed;
}
void UpdateMirrorLayerExtra(const mojom::MirrorLayerExtraPtr& extra,
cc::MirrorLayerImpl& layer) {
layer.SetMirroredLayerId(extra->mirrored_layer_id);
}
base::expected<void, std::string> UpdateNinePatchLayerExtra(
const mojom::NinePatchLayerExtraPtr& extra,
cc::NinePatchLayerImpl& layer) {
if (!extra->ui_resource_id) {
return base::unexpected("Invalid ui_resource_id for NinePatchLayerImpl");
}
layer.SetUIResourceId(extra->ui_resource_id);
layer.SetImageBounds(extra->image_bounds);
layer.SetLayout(extra->image_aperture, extra->border, extra->layer_occlusion,
extra->fill_center);
layer.SetUV(extra->uv_top_left, extra->uv_bottom_right);
return base::ok();
}
void UpdateTextureLayerExtra(const mojom::TextureLayerExtraPtr& extra,
cc::TextureLayerImpl& layer) {
layer.SetBlendBackgroundColor(extra->blend_background_color);
layer.SetForceTextureToOpaque(extra->force_texture_to_opaque);
layer.SetUVTopLeft(extra->uv_top_left);
layer.SetUVBottomRight(extra->uv_bottom_right);
if (extra->transferable_resource) {
ReleaseCallback release_callback;
if (!extra->transferable_resource->is_empty()) {
release_callback = base::BindOnce(
[](cc::LayerTreeHostImpl* host_impl, ResourceId id,
const gpu::SyncToken& sync_token, bool is_lost) {
host_impl->ReturnResource({id, sync_token,
/*release_fence=*/gfx::GpuFenceHandle(),
/*count=*/1, is_lost});
},
layer.layer_tree_impl()->host_impl(),
extra->transferable_resource->id);
}
layer.SetTransferableResource(extra->transferable_resource.value(),
std::move(release_callback));
}
}
base::expected<void, std::string> UpdateUIResourceLayerExtra(
const mojom::UIResourceLayerExtraPtr& extra,
cc::UIResourceLayerImpl& layer) {
if (!extra->ui_resource_id) {
return base::unexpected("Invalid ui_resource_id for UIResourceLayerImpl");
}
layer.SetUIResourceId(extra->ui_resource_id);
layer.SetImageBounds(extra->image_bounds);
layer.SetUV(extra->uv_top_left, extra->uv_bottom_right);
return base::ok();
}
void UpdateScrollbarLayerBaseExtra(
const mojom::ScrollbarLayerBaseExtraPtr& extra,
cc::ScrollbarLayerImplBase& layer) {
// ScrollbarLayerImplBase properties
layer.SetScrollElementId(extra->scroll_element_id);
layer.set_is_overlay_scrollbar(extra->is_overlay_scrollbar);
layer.set_is_web_test(extra->is_web_test);
layer.SetThumbThicknessScaleFactor(extra->thumb_thickness_scale_factor);
layer.SetCurrentPos(extra->current_pos);
layer.SetClipLayerLength(extra->clip_layer_length);
layer.SetScrollLayerLength(extra->scroll_layer_length);
layer.SetVerticalAdjust(extra->vertical_adjust);
layer.SetHasFindInPageTickmarks(extra->has_find_in_page_tickmarks);
}
void UpdateNinePatchThumbScrollbarLayerExtra(
const mojom::NinePatchThumbScrollbarLayerExtraPtr& extra,
cc::NinePatchThumbScrollbarLayerImpl& layer) {
UpdateScrollbarLayerBaseExtra(
extra->scrollbar_base_extra,
static_cast<cc::ScrollbarLayerImplBase&>(layer));
layer.SetThumbThickness(extra->thumb_thickness);
layer.SetThumbLength(extra->thumb_length);
layer.SetTrackStart(extra->track_start);
layer.SetTrackLength(extra->track_length);
layer.SetImageBounds(extra->image_bounds);
layer.SetAperture(extra->aperture);
layer.set_thumb_ui_resource_id(extra->thumb_ui_resource_id);
layer.set_track_and_buttons_ui_resource_id(
extra->track_and_buttons_ui_resource_id);
}
void UpdatePaintedScrollbarLayerExtra(
const mojom::PaintedScrollbarLayerExtraPtr& extra,
cc::PaintedScrollbarLayerImpl& layer) {
UpdateScrollbarLayerBaseExtra(
extra->scrollbar_base_extra,
static_cast<cc::ScrollbarLayerImplBase&>(layer));
layer.set_internal_contents_scale_and_bounds(extra->internal_contents_scale,
extra->internal_content_bounds);
layer.SetJumpOnTrackClick(extra->jump_on_track_click);
layer.SetSupportsDragSnapBack(extra->supports_drag_snap_back);
layer.SetThumbThickness(extra->thumb_thickness);
layer.SetThumbLength(extra->thumb_length);
layer.SetBackButtonRect(extra->back_button_rect);
layer.SetForwardButtonRect(extra->forward_button_rect);
layer.SetTrackRect(extra->track_rect);
layer.set_track_and_buttons_ui_resource_id(
extra->track_and_buttons_ui_resource_id);
layer.set_thumb_ui_resource_id(extra->thumb_ui_resource_id);
layer.set_uses_nine_patch_track_and_buttons(
extra->uses_nine_patch_track_and_buttons);
layer.SetScrollbarPaintedOpacity(extra->painted_opacity);
if (extra->thumb_color) {
layer.SetThumbColor(extra->thumb_color.value());
}
layer.SetTrackAndButtonsImageBounds(extra->track_and_buttons_image_bounds);
layer.SetTrackAndButtonsAperture(extra->track_and_buttons_aperture);
}
void UpdateSolidColorScrollbarLayerExtra(
const mojom::SolidColorScrollbarLayerExtraPtr& extra,
cc::SolidColorScrollbarLayerImpl& layer) {
UpdateScrollbarLayerBaseExtra(
extra->scrollbar_base_extra,
static_cast<cc::ScrollbarLayerImplBase&>(layer));
layer.set_color(extra->color);
// thumb_thickness has no update method in SolidColorScrollbarLayerImpl
// so it is intentionally ignored here.
}
void UpdateSurfaceLayerExtra(const mojom::SurfaceLayerExtraPtr& extra,
cc::SurfaceLayerImpl& layer) {
layer.SetRange(extra->surface_range, extra->deadline_in_frames);
layer.SetStretchContentToFillBounds(extra->stretch_content_to_fill_bounds);
layer.SetSurfaceHitTestable(extra->surface_hit_testable);
layer.SetHasPointerEventsNone(extra->has_pointer_events_none);
layer.SetIsReflection(extra->is_reflection);
if (extra->will_draw_needs_reset) {
layer.ResetStateForUpdateSubmissionStateCallback();
}
layer.SetOverrideChildPaintFlags(extra->override_child_paint_flags);
}
void UpdateViewTransitionContentLayerExtra(
const mojom::ViewTransitionContentLayerExtraPtr& extra,
cc::ViewTransitionContentLayerImpl& layer) {
layer.SetMaxExtentsRect(extra->max_extents_rect);
}
void UpdateTileDisplayLayerExtra(const mojom::TileDisplayLayerExtraPtr& extra,
cc::TileDisplayLayerImpl& layer) {
layer.SetSolidColor(extra->solid_color);
layer.SetIsBackdropFilterMask(extra->is_backdrop_filter_mask);
layer.SetIsDirectlyCompositedImage(extra->is_directly_composited_image);
layer.SetNearestNeighbor(extra->nearest_neighbor);
layer.SetContentColorUsage(extra->content_color_usage);
layer.SetRecordedBounds(extra->recorded_bounds);
layer.SetProposedTilingScalesForDeletion(
extra->proposed_tiling_scales_for_deletion);
}
base::expected<void, std::string> UpdateLayer(const mojom::Layer& wire,
cc::LayerImpl& layer) {
if (wire.type != layer.GetLayerType()) {
return base::unexpected("Incorrect layer type used in Layer update.");
}
if (wire.contents_opaque && !wire.contents_opaque_for_text) {
return base::unexpected(
"Invalid contents_opaque_for_text: cannot be false if contents_opaque "
"is true.");
}
if (wire.safe_opaque_background_color.isOpaque() != wire.contents_opaque) {
return base::unexpected(
"Invalid safe_opaque_background_color: opaqueness must agree with "
"contents_opaque");
}
layer.SetBounds(wire.bounds);
layer.SetContentsOpaque(wire.contents_opaque);
layer.SetContentsOpaqueForText(wire.contents_opaque_for_text);
layer.SetDrawsContent(wire.is_drawable);
if (wire.layer_property_changed_not_from_property_trees) {
layer.NoteLayerPropertyChanged();
}
if (wire.layer_property_changed_from_property_trees) {
layer.NoteLayerPropertyChangedFromPropertyTrees();
}
layer.SetBackgroundColor(wire.background_color);
layer.SetSafeOpaqueBackgroundColor(wire.safe_opaque_background_color);
layer.SetHitTestOpaqueness(wire.hit_test_opaqueness);
layer.SetElementId(wire.element_id);
layer.UnionUpdateRect(wire.update_rect);
layer.SetOffsetToTransformParent(wire.offset_to_transform_parent);
layer.SetShouldCheckBackfaceVisibility(wire.should_check_backface_visibility);
if (wire.rare_properties) {
layer.SetFilterQuality(wire.rare_properties->filter_quality);
layer.SetDynamicRangeLimit(wire.rare_properties->dynamic_range_limit);
layer.SetCaptureBounds(wire.rare_properties->capture_bounds);
}
const cc::PropertyTrees& property_trees =
*layer.layer_tree_impl()->property_trees();
if (!IsPropertyTreeIndexValid(property_trees.transform_tree(),
wire.transform_tree_index)) {
return base::unexpected(
base::StrCat({"Invalid transform tree ID: ",
base::NumberToString(wire.transform_tree_index)}));
}
if (!IsPropertyTreeIndexValid(property_trees.clip_tree(),
wire.clip_tree_index)) {
return base::unexpected(
base::StrCat({"Invalid clip tree ID: ",
base::NumberToString(wire.clip_tree_index)}));
}
if (!IsPropertyTreeIndexValid(property_trees.effect_tree(),
wire.effect_tree_index)) {
return base::unexpected(
base::StrCat({"Invalid effect tree ID: ",
base::NumberToString(wire.effect_tree_index)}));
}
if (!IsPropertyTreeIndexValid(property_trees.scroll_tree(),
wire.scroll_tree_index)) {
return base::unexpected(
base::StrCat({"Invalid scroll tree ID: ",
base::NumberToString(wire.scroll_tree_index)}));
}
layer.SetTransformTreeIndex(wire.transform_tree_index);
layer.SetClipTreeIndex(wire.clip_tree_index);
layer.SetEffectTreeIndex(wire.effect_tree_index);
layer.SetScrollTreeIndex(wire.scroll_tree_index);
switch (wire.type) {
case cc::mojom::LayerType::kMirror:
RETURN_IF_FALSE(
wire.layer_extra && wire.layer_extra->is_mirror_layer_extra(),
"Invalid layer_extra type for MirrorLayerImpl");
UpdateMirrorLayerExtra(wire.layer_extra->get_mirror_layer_extra(),
static_cast<cc::MirrorLayerImpl&>(layer));
break;
case cc::mojom::LayerType::kNinePatch:
RETURN_IF_FALSE(
wire.layer_extra && wire.layer_extra->is_nine_patch_layer_extra(),
"Invalid layer_extra type for NinePatchLayerImpl");
RETURN_IF_ERROR(UpdateNinePatchLayerExtra(
wire.layer_extra->get_nine_patch_layer_extra(),
static_cast<cc::NinePatchLayerImpl&>(layer)));
break;
case cc::mojom::LayerType::kNinePatchThumbScrollbar:
RETURN_IF_FALSE(
wire.layer_extra &&
wire.layer_extra->is_nine_patch_thumb_scrollbar_layer_extra(),
"Invalid layer_extra type for NinePatchThumbScrollbarLayerImpl");
UpdateNinePatchThumbScrollbarLayerExtra(
wire.layer_extra->get_nine_patch_thumb_scrollbar_layer_extra(),
static_cast<cc::NinePatchThumbScrollbarLayerImpl&>(layer));
break;
case cc::mojom::LayerType::kPaintedScrollbar:
RETURN_IF_FALSE(wire.layer_extra &&
wire.layer_extra->is_painted_scrollbar_layer_extra(),
"Invalid layer_extra type for PaintedScrollbarLayerImpl");
UpdatePaintedScrollbarLayerExtra(
wire.layer_extra->get_painted_scrollbar_layer_extra(),
static_cast<cc::PaintedScrollbarLayerImpl&>(layer));
break;
case cc::mojom::LayerType::kSolidColorScrollbar:
RETURN_IF_FALSE(
wire.layer_extra &&
wire.layer_extra->is_solid_color_scrollbar_layer_extra(),
"Invalid layer_extra type for SolidColorScrollbarLayerImpl");
UpdateSolidColorScrollbarLayerExtra(
wire.layer_extra->get_solid_color_scrollbar_layer_extra(),
static_cast<cc::SolidColorScrollbarLayerImpl&>(layer));
break;
case cc::mojom::LayerType::kSurface:
RETURN_IF_FALSE(
wire.layer_extra && wire.layer_extra->is_surface_layer_extra(),
"Invalid layer_extra type for SurfaceLayerImpl");
UpdateSurfaceLayerExtra(wire.layer_extra->get_surface_layer_extra(),
static_cast<cc::SurfaceLayerImpl&>(layer));
break;
case cc::mojom::LayerType::kTexture:
RETURN_IF_FALSE(
wire.layer_extra && wire.layer_extra->is_texture_layer_extra(),
"Invalid layer_extra type for TextureLayerImpl");
UpdateTextureLayerExtra(wire.layer_extra->get_texture_layer_extra(),
static_cast<cc::TextureLayerImpl&>(layer));
break;
case cc::mojom::LayerType::kTileDisplay:
RETURN_IF_FALSE(
wire.layer_extra && wire.layer_extra->is_tile_display_layer_extra(),
"Invalid layer_extra type for TileDisplayLayerImpl");
UpdateTileDisplayLayerExtra(
wire.layer_extra->get_tile_display_layer_extra(),
static_cast<cc::TileDisplayLayerImpl&>(layer));
break;
case cc::mojom::LayerType::kUIResource:
RETURN_IF_FALSE(
wire.layer_extra && wire.layer_extra->is_ui_resource_layer_extra(),
"Invalid layer_extra type for UIResourceLayerImpl");
RETURN_IF_ERROR(UpdateUIResourceLayerExtra(
wire.layer_extra->get_ui_resource_layer_extra(),
static_cast<cc::UIResourceLayerImpl&>(layer)));
break;
case cc::mojom::LayerType::kViewTransitionContent:
RETURN_IF_FALSE(
wire.layer_extra &&
wire.layer_extra->is_view_transition_content_layer_extra(),
"Invalid layer_extra type for ViewTransitionContentLayerImpl");
UpdateViewTransitionContentLayerExtra(
wire.layer_extra->get_view_transition_content_layer_extra(),
static_cast<cc::ViewTransitionContentLayerImpl&>(layer));
break;
default:
// TODO(zmo): handle other types of LayerImpl.
break;
}
return base::ok();
}
base::expected<void, std::string> CreateOrUpdateLayers(
cc::LayerTreeHostImpl& host_impl,
const std::vector<mojom::LayerPtr>& updates,
std::optional<std::vector<int32_t>>& layer_order,
cc::LayerTreeImpl& layers) {
TRACE_EVENT1("viz", "CreateOrUpdateLayers", "LayerCount", updates.size());
if (!layer_order) {
// No layer list changes. Only update existing layers.
for (auto& wire : updates) {
cc::LayerImpl* layer = layers.LayerById(wire->id);
if (!layer) {
return base::unexpected("Invalid layer ID");
}
RETURN_IF_ERROR(UpdateLayer(*wire, *layer));
}
return base::ok();
}
// The layer list contents changed, so we need to rebuild the tree.
cc::OwnedLayerImplList old_layers = layers.DetachLayers();
cc::OwnedLayerImplMap layer_map;
for (auto& layer : old_layers) {
const int id = layer->id();
layer_map[id] = std::move(layer);
}
for (auto& wire : updates) {
auto& layer = layer_map[wire->id];
if (!layer) {
RETURN_IF_ERROR(CreateLayer(host_impl, layers, *wire, layer));
}
RETURN_IF_ERROR(UpdateLayer(*wire, *layer));
}
for (auto id : *layer_order) {
auto& layer = layer_map[id];
if (!layer) {
return base::unexpected("Invalid or duplicate layer ID");
}
layers.AddLayer(std::move(layer));
}
return base::ok();
}
base::expected<void, std::string> UpdateViewportPropertyIds(
cc::LayerTreeImpl& layers,
cc::PropertyTrees& trees,
mojom::LayerTreeUpdate& update) {
const auto& transform_tree = trees.transform_tree();
const auto& scroll_tree = trees.scroll_tree();
const auto& clip_tree = trees.clip_tree();
if (!IsOptionalPropertyTreeIndexValid(
transform_tree, update.overscroll_elasticity_transform)) {
return base::unexpected("Invalid overscroll_elasticity_transform");
}
if (!IsOptionalPropertyTreeIndexValid(transform_tree,
update.page_scale_transform)) {
return base::unexpected("Invalid page_scale_transform");
}
if (!IsOptionalPropertyTreeIndexValid(scroll_tree, update.inner_scroll)) {
return base::unexpected("Invalid inner_scroll");
}
if (update.inner_scroll == cc::kInvalidPropertyNodeId &&
(update.outer_clip != cc::kInvalidPropertyNodeId ||
update.outer_scroll != cc::kInvalidPropertyNodeId)) {
return base::unexpected(
"Cannot set outer_clip or outer_scroll without valid inner_scroll");
}
if (!IsOptionalPropertyTreeIndexValid(clip_tree, update.outer_clip)) {
return base::unexpected("Invalid outer_clip");
}
if (!IsOptionalPropertyTreeIndexValid(scroll_tree, update.outer_scroll)) {
return base::unexpected("Invalid outer_scroll");
}
layers.SetViewportPropertyIds(cc::ViewportPropertyIds{
.overscroll_elasticity_transform = update.overscroll_elasticity_transform,
.page_scale_transform = update.page_scale_transform,
.inner_scroll = update.inner_scroll,
.outer_clip = update.outer_clip,
.outer_scroll = update.outer_scroll,
});
return base::ok();
}
base::expected<cc::TileDisplayLayerImpl::TileResource, std::string>
DeserializeTileResource(cc::LayerTreeHostImpl* host_impl,
mojom::TileResource& wire) {
if (wire.resource.id == kInvalidResourceId) {
return base::unexpected("Invalid tile resource");
}
ReleaseCallback release_callback = base::BindOnce(
[](cc::LayerTreeHostImpl* host_impl, ResourceId id,
const gpu::SyncToken& sync_token, bool is_lost) {
host_impl->ReturnResource({id, sync_token,
/*release_fence=*/gfx::GpuFenceHandle(),
/*count=*/1, is_lost});
},
host_impl, wire.resource.id);
auto resource_id = host_impl->resource_provider()->ImportResource(
wire.resource,
/*impl_release_callback=*/std::move(release_callback),
/*main_thread_release_callback=*/base::NullCallback(),
/*evicted_callback=*/base::NullCallback());
return cc::TileDisplayLayerImpl::TileResource(
resource_id, wire.resource.GetSize(), wire.is_checkered);
}
base::expected<cc::TileDisplayLayerImpl::TileContents, std::string>
DeserializeTileContents(cc::LayerTreeHostImpl* host_impl,
mojom::TileContents& wire) {
switch (wire.which()) {
case mojom::TileContents::Tag::kMissingReason:
return cc::TileDisplayLayerImpl::TileContents(
cc::TileDisplayLayerImpl::NoContents(wire.get_missing_reason()));
case mojom::TileContents::Tag::kResource:
return DeserializeTileResource(host_impl, *wire.get_resource());
case mojom::TileContents::Tag::kSolidColor:
return cc::TileDisplayLayerImpl::TileContents(wire.get_solid_color());
}
}
base::expected<void, std::string> DeserializeTiling(
cc::LayerTreeHostImpl* host_impl,
cc::TileDisplayLayerImpl& layer,
mojom::Tiling& wire,
bool update_damage) {
if (wire.is_deleted) {
layer.RemoveTiling(wire.scale_key);
return base::ok();
}
if (wire.tile_size.width() <= 0 || wire.tile_size.height() <= 0) {
return base::unexpected("Invalid tile_size dimensions in Tiling");
}
const float scale_key =
std::max(wire.raster_scale.x(), wire.raster_scale.y());
auto& tiling = layer.GetOrCreateTilingFromScaleKey(scale_key);
tiling.SetRasterTransform(gfx::AxisTransform2d::FromScaleAndTranslation(
wire.raster_scale, wire.raster_translation));
tiling.SetTileSize(wire.tile_size);
tiling.SetTilingRect(wire.tiling_rect);
for (auto& wire_tile : wire.tiles) {
ASSIGN_OR_RETURN(auto contents,
DeserializeTileContents(host_impl, *wire_tile->contents));
tiling.SetTileContents(
cc::TileIndex{base::saturated_cast<int>(wire_tile->column_index),
base::saturated_cast<int>(wire_tile->row_index)},
std::move(contents), update_damage);
}
if (tiling.tiles().empty()) {
layer.RemoveTiling(tiling.contents_scale_key());
}
return base::ok();
}
void DeserializeViewTransitionRequests(
cc::LayerTreeImpl& layers,
std::vector<mojom::ViewTransitionRequestPtr>& wire_data) {
for (auto& wire : wire_data) {
std::unique_ptr<cc::ViewTransitionRequest> request;
switch (wire->type) {
case mojom::CompositorFrameTransitionDirectiveType::kSave:
// Callback is not used at all in
// ViewTransitionRequest::ConstructDirective, therefore it's not
// wired in mojom::ViewTransitionRequest to viz, and here we just use
// a placeholder.
request = cc::ViewTransitionRequest::CreateCapture(
wire->transition_token, wire->maybe_cross_frame_sink,
wire->capture_resource_ids,
cc::ViewTransitionRequest::ViewTransitionCaptureCallback());
break;
case mojom::CompositorFrameTransitionDirectiveType::kAnimateRenderer:
request = cc::ViewTransitionRequest::CreateAnimateRenderer(
wire->transition_token, wire->maybe_cross_frame_sink);
break;
case mojom::CompositorFrameTransitionDirectiveType::kRelease:
request = cc::ViewTransitionRequest::CreateRelease(
wire->transition_token, wire->maybe_cross_frame_sink);
break;
}
request->set_sequence_id(wire->sequence_id);
layers.AddViewTransitionRequest(std::move(request));
}
}
gfx::StepsTimingFunction::StepPosition DeserializeTimingStepPosition(
mojom::TimingStepPosition step_position) {
switch (step_position) {
case mojom::TimingStepPosition::kStart:
return gfx::StepsTimingFunction::StepPosition::START;
case mojom::TimingStepPosition::kEnd:
return gfx::StepsTimingFunction::StepPosition::END;
case mojom::TimingStepPosition::kJumpBoth:
return gfx::StepsTimingFunction::StepPosition::JUMP_BOTH;
case mojom::TimingStepPosition::kJumpEnd:
return gfx::StepsTimingFunction::StepPosition::JUMP_END;
case mojom::TimingStepPosition::kJumpNone:
return gfx::StepsTimingFunction::StepPosition::JUMP_NONE;
case mojom::TimingStepPosition::kJumpStart:
return gfx::StepsTimingFunction::StepPosition::JUMP_START;
}
}
std::unique_ptr<gfx::TimingFunction> DeserializeTimingFunction(
mojom::TimingFunction& wire) {
switch (wire.which()) {
case mojom::TimingFunction::Tag::kLinear: {
const auto& wire_points = wire.get_linear();
std::vector<gfx::LinearEasingPoint> points;
points.reserve(wire_points.size());
for (const auto& wire_point : wire_points) {
points.emplace_back(wire_point->in, wire_point->out);
}
if (points.empty()) {
return gfx::LinearTimingFunction::Create();
}
return gfx::LinearTimingFunction::Create(std::move(points));
}
case mojom::TimingFunction::Tag::kCubicBezier: {
const auto& bezier = *wire.get_cubic_bezier();
return gfx::CubicBezierTimingFunction::Create(bezier.x1, bezier.y1,
bezier.x2, bezier.y2);
}
case mojom::TimingFunction::Tag::kSteps: {
const auto& steps = *wire.get_steps();
return gfx::StepsTimingFunction::Create(
base::saturated_cast<int32_t>(steps.num_steps),
DeserializeTimingStepPosition(steps.step_position));
}
}
}
gfx::TransformOperations DeserializeTransformOperations(
const std::vector<mojom::TransformOperationPtr>& wire_ops) {
gfx::TransformOperations transform;
for (const auto& wire_op : wire_ops) {
switch (wire_op->which()) {
case mojom::TransformOperation::Tag::kIdentity:
transform.AppendIdentity();
break;
case mojom::TransformOperation::Tag::kPerspectiveDepth: {
std::optional<float> depth;
if (wire_op->get_perspective_depth()) {
depth = wire_op->get_perspective_depth();
}
transform.AppendPerspective(depth);
break;
}
case mojom::TransformOperation::Tag::kSkew: {
const auto& skew = wire_op->get_skew();
transform.AppendSkew(skew.x(), skew.y());
break;
}
case mojom::TransformOperation::Tag::kScale: {
const auto& scale = wire_op->get_scale();
transform.AppendScale(scale.x(), scale.y(), scale.z());
break;
}
case mojom::TransformOperation::Tag::kTranslate: {
const auto& translate = wire_op->get_translate();
transform.AppendTranslate(translate.x(), translate.y(), translate.z());
break;
}
case mojom::TransformOperation::Tag::kRotate: {
const auto& axis = wire_op->get_rotate()->axis;
const float angle = wire_op->get_rotate()->angle;
transform.AppendRotate(axis.x(), axis.y(), axis.z(), angle);
break;
}
case mojom::TransformOperation::Tag::kMatrix:
transform.AppendMatrix(wire_op->get_matrix());
break;
}
}
return transform;
}
template <typename CurveType>
using KeyframeType = CurveType::Keyframes::value_type::element_type;
template <typename CurveType>
base::expected<std::unique_ptr<KeyframeType<CurveType>>, std::string>
DeserializeKeyframe(const mojom::AnimationKeyframeValue& value,
base::TimeDelta start_time,
std::unique_ptr<gfx::TimingFunction> timing_function) {
using ValueType = std::remove_cvref_t<
decltype(std::declval<KeyframeType<CurveType>>().Value())>;
std::unique_ptr<KeyframeType<CurveType>> keyframe;
if constexpr (std::is_same_v<ValueType, float>) {
if (value.is_scalar()) {
keyframe = gfx::FloatKeyframe::Create(start_time, value.get_scalar(),
std::move(timing_function));
} else {
return base::unexpected("Invalid keyframe type");
}
} else if constexpr (std::is_same_v<ValueType, SkColor>) {
if (value.is_color()) {
keyframe = gfx::ColorKeyframe::Create(start_time, value.get_color(),
std::move(timing_function));
} else {
return base::unexpected("Invalid keyframe type");
}
} else if constexpr (std::is_same_v<ValueType, gfx::SizeF>) {
if (value.is_size()) {
keyframe = gfx::SizeKeyframe::Create(start_time, value.get_size(),
std::move(timing_function));
} else {
return base::unexpected("Invalid keyframe type");
}
} else if constexpr (std::is_same_v<ValueType, gfx::Rect>) {
if (value.is_rect()) {
keyframe = gfx::RectKeyframe::Create(start_time, value.get_rect(),
std::move(timing_function));
} else {
return base::unexpected("Invalid keyframe type");
}
} else if constexpr (std::is_same_v<ValueType, gfx::TransformOperations>) {
if (value.is_transform()) {
keyframe = gfx::TransformKeyframe::Create(
start_time, DeserializeTransformOperations(value.get_transform()),
std::move(timing_function));
} else {
return base::unexpected("Invalid keyframe type");
}
} else {
static_assert(false, "Unsupported curve type");
}
if (!keyframe) {
// This case handles failures from `gfx::Keyframe::Create` calls above
// if the value was of the correct type but otherwise invalid, or if a
// new `ValueType` is added to the system without a corresponding
// `if constexpr` block and `Create` method here.
return base::unexpected("Invalid keyframe value");
}
return keyframe;
}
cc::KeyframeModel::Direction DeserializeAnimationDirection(
mojom::AnimationDirection direction) {
switch (direction) {
case mojom::AnimationDirection::kNormal:
return cc::KeyframeModel::Direction::NORMAL;
case mojom::AnimationDirection::kReverse:
return cc::KeyframeModel::Direction::REVERSE;
case mojom::AnimationDirection::kAlternateNormal:
return cc::KeyframeModel::Direction::ALTERNATE_NORMAL;
case mojom::AnimationDirection::kAlternateReverse:
return cc::KeyframeModel::Direction::ALTERNATE_REVERSE;
}
}
cc::KeyframeModel::FillMode DeserializeAnimationFillMode(
mojom::AnimationFillMode fill_mode) {
switch (fill_mode) {
case mojom::AnimationFillMode::kNone:
return cc::KeyframeModel::FillMode::NONE;
case mojom::AnimationFillMode::kForwards:
return cc::KeyframeModel::FillMode::FORWARDS;
case mojom::AnimationFillMode::kBackwards:
return cc::KeyframeModel::FillMode::BACKWARDS;
case mojom::AnimationFillMode::kBoth:
return cc::KeyframeModel::FillMode::BOTH;
case mojom::AnimationFillMode::kAuto:
return cc::KeyframeModel::FillMode::AUTO;
}
}
template <typename CurveType>
base::expected<void, std::string> DeserializeAnimationCurve(
const mojom::AnimationKeyframeModel& wire,
cc::Animation& animation) {
auto curve = CurveType::Create();
if (wire.playback_rate == 0.0) {
return base::unexpected("Invalid playback_rate: cannot be 0");
}
curve->SetTimingFunction(DeserializeTimingFunction(*wire.timing_function));
curve->set_scaled_duration(wire.scaled_duration);
for (const auto& wire_keyframe : wire.keyframes) {
std::unique_ptr<gfx::TimingFunction> keyframe_timing_function;
if (wire_keyframe->timing_function) {
keyframe_timing_function =
DeserializeTimingFunction(*wire_keyframe->timing_function);
}
ASSIGN_OR_RETURN(auto keyframe,
DeserializeKeyframe<CurveType>(
*wire_keyframe->value, wire_keyframe->start_time,
std::move(keyframe_timing_function)));
curve->AddKeyframe(std::move(keyframe));
}
auto model = cc::KeyframeModel::Create(
std::move(curve), wire.id, wire.group_id,
cc::KeyframeModel::TargetPropertyId(wire.target_property_type));
model->set_direction(DeserializeAnimationDirection(wire.direction));
model->set_fill_mode(DeserializeAnimationFillMode(wire.fill_mode));
model->set_playback_rate(wire.playback_rate);
model->set_iterations(wire.iterations);
model->set_iteration_start(wire.iteration_start);
model->set_time_offset(wire.time_offset);
model->set_element_id(wire.element_id);
animation.keyframe_effect()->AddKeyframeModel(std::move(model));
return base::ok();
}
base::expected<void, std::string> DeserializeAnimation(
const mojom::Animation& wire,
cc::AnimationTimeline& timeline) {
if (timeline.GetAnimationById(wire.id)) {
return base::unexpected("Unexpected duplicate animation ID");
}
scoped_refptr<cc::Animation> animation = cc::Animation::Create(wire.id);
timeline.AttachAnimation(animation);
if (wire.element_id) {
animation->AttachElement(wire.element_id);
}
for (const auto& wire_model : wire.keyframe_models) {
if (wire_model->keyframes.empty()) {
return base::unexpected("Unexpected animation with no keyframes");
}
// We use the first keyframe to determine the curve type. All keyframes will
// be validated against this type.
switch (wire_model->keyframes[0]->value->which()) {
case mojom::AnimationKeyframeValue::Tag::kScalar:
RETURN_IF_ERROR(
DeserializeAnimationCurve<gfx::KeyframedFloatAnimationCurve>(
*wire_model, *animation));
break;
case mojom::AnimationKeyframeValue::Tag::kColor:
RETURN_IF_ERROR(
DeserializeAnimationCurve<gfx::KeyframedColorAnimationCurve>(
*wire_model, *animation));
break;
case mojom::AnimationKeyframeValue::Tag::kSize:
RETURN_IF_ERROR(
DeserializeAnimationCurve<gfx::KeyframedSizeAnimationCurve>(
*wire_model, *animation));
break;
case mojom::AnimationKeyframeValue::Tag::kRect:
RETURN_IF_ERROR(
DeserializeAnimationCurve<gfx::KeyframedRectAnimationCurve>(
*wire_model, *animation));
break;
case mojom::AnimationKeyframeValue::Tag::kTransform:
RETURN_IF_ERROR(
DeserializeAnimationCurve<gfx::KeyframedTransformAnimationCurve>(
*wire_model, *animation));
break;
}
}
animation->keyframe_effect()->UpdateTickingState();
return base::ok();
}
base::expected<void, std::string> DeserializeAnimationTimeline(
const mojom::AnimationTimeline& wire,
cc::AnimationHost& host) {
scoped_refptr<cc::AnimationTimeline> timeline = host.GetTimelineById(wire.id);
bool add_new_timeline = false;
if (!timeline) {
timeline = cc::AnimationTimeline::Create(wire.id);
add_new_timeline = true;
}
for (int32_t id : wire.removed_animations) {
if (auto* animation = timeline->GetAnimationById(id)) {
timeline->DetachAnimation(animation);
}
}
for (const auto& wire_animation : wire.new_animations) {
RETURN_IF_ERROR(DeserializeAnimation(*wire_animation, *timeline));
}
if (add_new_timeline) {
host.AddAnimationTimeline(timeline);
}
return base::ok();
}
base::expected<void, std::string> DeserializeAnimationUpdates(
const mojom::LayerTreeUpdate& update,
cc::AnimationHost& host) {
if (update.removed_animation_timelines) {
for (int32_t id : *update.removed_animation_timelines) {
if (auto* timeline = host.GetTimelineById(id)) {
host.RemoveAnimationTimeline(timeline);
}
}
}
if (update.animation_timelines) {
for (const auto& wire : *update.animation_timelines) {
RETURN_IF_ERROR(DeserializeAnimationTimeline(*wire, host));
}
}
return base::ok();
}
} // namespace
LayerContextImpl::LayerContextImpl(CompositorFrameSinkSupport* compositor_sink,
mojom::PendingLayerContext& context,
mojom::LayerContextSettingsPtr settings)
: LayerContextImpl(compositor_sink,
std::move(settings),
std::move(context.receiver),
std::move(context.client)) {
// Always expect valid context receiver & client to be passed to the
// public constructor.
CHECK(receiver_);
CHECK(client_);
}
// static
std::unique_ptr<LayerContextImpl> LayerContextImpl::CreateForTesting(
CompositorFrameSinkSupport* compositor_sink,
mojom::LayerContextSettingsPtr settings) {
return base::WrapUnique<LayerContextImpl>(new LayerContextImpl(
compositor_sink, std::move(settings),
mojo::PendingAssociatedReceiver<mojom::LayerContext>(),
mojo::PendingAssociatedRemote<mojom::LayerContextClient>()));
}
LayerContextImpl::LayerContextImpl(
CompositorFrameSinkSupport* compositor_sink,
mojom::LayerContextSettingsPtr settings,
mojo::PendingAssociatedReceiver<mojom::LayerContext> receiver_pipe,
mojo::PendingAssociatedRemote<mojom::LayerContextClient> client_pipe)
: compositor_sink_(compositor_sink),
task_runner_provider_(cc::TaskRunnerProvider::CreateForDisplayTree(
base::SingleThreadTaskRunner::GetCurrentDefault())),
rendering_stats_(cc::RenderingStatsInstrumentation::Create()),
host_impl_(cc::LayerTreeHostImpl::Create(
GetDisplayTreeSettings(std::move(settings)),
this,
task_runner_provider_.get(),
rendering_stats_.get(),
/*task_graph_runner=*/nullptr,
animation_host_->CreateImplInstance(),
/*dark_mode_filter=*/nullptr,
GenerateNextDisplayTreeId(),
/*image_worker_task_runner=*/nullptr,
/*scheduling_client=*/nullptr)) {
if (receiver_pipe.is_valid() && client_pipe.is_valid()) {
receiver_ = std::make_unique<mojo::AssociatedReceiver<mojom::LayerContext>>(
this, std::move(receiver_pipe));
client_ =
std::make_unique<mojo::AssociatedRemote<mojom::LayerContextClient>>(
std::move(client_pipe));
}
CHECK(host_impl_->InitializeFrameSink(this));
}
LayerContextImpl::~LayerContextImpl() {
DoReturnResources();
host_impl_->ReleaseLayerTreeFrameSink();
}
void LayerContextImpl::BeginFrame(const BeginFrameArgs& args) {
if (base::FeatureList::IsEnabled(features::kTreeAnimationsInViz)) {
compositor_sink_->SetLayerContextWantsBeginFrames(false);
// TODO(zmo): The stage breakdown var is |start_update_display_tree|.
// Consider using a difference name, so it works for TreeAnimationsInViz
// mode as well.
base::TimeTicks start_begin_frame = base::TimeTicks::Now();
DoDrawInternal(args, start_begin_frame);
}
}
void LayerContextImpl::ReceiveReturnsFromParent(
std::vector<ReturnedResource> resources) {
// Impl and Main thread task runners are the same. They bind to the viz
// thread.
auto* task_runner = task_runner_provider_->MainThreadTaskRunner();
if (!task_runner->BelongsToCurrentThread()) {
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&LayerContextImpl::ReceiveReturnsFromParent,
weak_factory_.GetWeakPtr(), std::move(resources)));
return;
}
host_impl_->resource_provider()->ReceiveReturnsFromParent(
std::move(resources));
DoReturnResources();
}
void LayerContextImpl::DoReturnResources() {
if (!resources_to_return_.empty()) {
compositor_sink_->DoReturnResources(std::move(resources_to_return_));
}
}
void LayerContextImpl::HandleBadMojoMessage(const std::string& function,
const std::string& error) {
receiver_->ReportBadMessage(function + "() : " + error);
}
void LayerContextImpl::DidLoseLayerTreeFrameSinkOnImplThread() {
NOTREACHED();
}
void LayerContextImpl::SetBeginFrameSource(BeginFrameSource* source) {}
void LayerContextImpl::DidReceiveCompositorFrameAckOnImplThread() {
NOTIMPLEMENTED();
}
void LayerContextImpl::OnCanDrawStateChanged(bool can_draw) {}
void LayerContextImpl::NotifyReadyToActivate() {}
bool LayerContextImpl::IsReadyToActivate() {
return false;
}
void LayerContextImpl::NotifyReadyToDraw() {}
void LayerContextImpl::SetNeedsRedrawOnImplThread() {
if (base::FeatureList::IsEnabled(features::kTreeAnimationsInViz)) {
compositor_sink_->SetLayerContextWantsBeginFrames(true);
}
}
void LayerContextImpl::SetNeedsOneBeginImplFrameOnImplThread() {
if (base::FeatureList::IsEnabled(features::kTreeAnimationsInViz)) {
compositor_sink_->SetLayerContextWantsBeginFrames(true);
}
}
void LayerContextImpl::SetNeedsPrepareTilesOnImplThread() {
NOTREACHED();
}
void LayerContextImpl::SetNeedsCommitOnImplThread(bool urgent) {
NOTIMPLEMENTED();
}
void LayerContextImpl::SetVideoNeedsBeginFrames(bool needs_begin_frames) {}
void LayerContextImpl::SetDeferBeginMainFrameFromImpl(
bool defer_begin_main_frame) {}
bool LayerContextImpl::IsInsideDraw() {
return false;
}
void LayerContextImpl::RenewTreePriority() {}
void LayerContextImpl::PostDelayedAnimationTaskOnImplThread(
base::OnceClosure task,
base::TimeDelta delay) {}
void LayerContextImpl::DidActivateSyncTree() {}
void LayerContextImpl::DidPrepareTiles() {}
void LayerContextImpl::DidCompletePageScaleAnimationOnImplThread() {}
void LayerContextImpl::OnDrawForLayerTreeFrameSink(
bool resourceless_software_draw,
bool skip_draw) {}
void LayerContextImpl::SetNeedsImplSideInvalidation(
bool needs_first_draw_on_activation) {}
void LayerContextImpl::NotifyImageDecodeRequestFinished(int request_id,
bool speculative,
bool decode_succeeded) {
}
void LayerContextImpl::NotifyTransitionRequestFinished(
uint32_t sequence_id,
const ViewTransitionElementResourceRects&) {}
void LayerContextImpl::DidPresentCompositorFrameOnImplThread(
uint32_t frame_token,
cc::PresentationTimeCallbackBuffer::PendingCallbacks callbacks,
const FrameTimingDetails& details) {
NOTIMPLEMENTED();
}
void LayerContextImpl::NotifyAnimationWorkletStateChange(
cc::AnimationWorkletMutationState state,
cc::ElementListType element_list_type) {}
void LayerContextImpl::NotifyPaintWorkletStateChange(
cc::Scheduler::PaintWorkletState state) {}
void LayerContextImpl::NotifyCompositorMetricsTrackerResults(
cc::CustomTrackerResults results) {}
bool LayerContextImpl::IsInSynchronousComposite() const {
return false;
}
void LayerContextImpl::FrameSinksToThrottleUpdated(
const base::flat_set<FrameSinkId>& ids) {}
void LayerContextImpl::ClearHistory() {}
void LayerContextImpl::SetHasActiveThreadedScroll(bool is_scrolling) {}
void LayerContextImpl::SetWaitingForScrollEvent(bool waiting_for_scroll_event) {
}
size_t LayerContextImpl::CommitDurationSampleCountForTesting() const {
return 0;
}
void LayerContextImpl::ReturnResource(ReturnedResource returned_resource) {
resources_to_return_.emplace_back(std::move(returned_resource));
}
void LayerContextImpl::DidObserveFirstScrollDelay(
int source_frame_number,
base::TimeDelta first_scroll_delay,
base::TimeTicks first_scroll_timestamp) {}
bool LayerContextImpl::BindToClient(cc::LayerTreeFrameSinkClient* client) {
frame_sink_client_ = client;
return true;
}
void LayerContextImpl::DetachFromClient() {
frame_sink_client_ = nullptr;
}
void LayerContextImpl::SetLocalSurfaceId(
const LocalSurfaceId& local_surface_id) {
// There are a few places that calls this. One is from LayerTreeHostImpl in
// TreesInViz mode in viz process, and it's unnecessary to call it. The
// others are from ui/aura/window.cc, and their frame_sink_ should not be
// LayerContextImpl.
NOTREACHED();
}
void LayerContextImpl::SubmitCompositorFrame(CompositorFrame frame,
bool hit_test_data_changed) {
if (!host_impl_->GetCurrentLocalSurfaceId().is_valid()) {
return;
}
std::optional<HitTestRegionList> hit_test_region_list =
host_impl_->BuildHitTestData();
// TODO(vmiura): Implement other functionality from
// AsyncLayerTreeFrameSink::SubmitCompositorFrame()
auto result = compositor_sink_->MaybeSubmitCompositorFrame(
host_impl_->GetCurrentLocalSurfaceId(), std::move(frame),
std::move(hit_test_region_list), 0);
if (result != SubmitResult::ACCEPTED) {
client_->ResetWithReason(
static_cast<uint32_t>(result),
CompositorFrameSinkSupport::GetSubmitResultAsString(result));
return;
}
if (base::FeatureList::IsEnabled(features::kTreeAnimationsInViz)) {
constexpr bool start_ready_animations = true;
host_impl_->UpdateAnimationState(start_ready_animations);
}
}
void LayerContextImpl::DidNotProduceFrame(const BeginFrameAck& ack,
cc::FrameSkippedReason reason) {
compositor_sink_->DidNotProduceFrame(ack);
}
void LayerContextImpl::NotifyNewLocalSurfaceIdExpectedWhilePaused() {
compositor_sink_->NotifyNewLocalSurfaceIdExpectedWhilePaused();
}
void LayerContextImpl::SetVisible(bool visible) {
host_impl_->SetVisible(visible);
}
void LayerContextImpl::UpdateDisplayTree(mojom::LayerTreeUpdatePtr update) {
CHECK(receiver_);
const BeginFrameArgs begin_frame_args = update->begin_frame_args;
auto start_update_display_tree = base::TimeTicks::Now();
auto result = DoUpdateDisplayTree(std::move(update));
if (!result.has_value()) {
HandleBadMojoMessage("UpdateDisplayTree", result.error());
return;
}
// After a tree update, either Draw or schedule animations.
DoDraw(begin_frame_args, start_update_display_tree);
// We may have resources to return after a tree update and draw.
DoReturnResources();
}
base::expected<void, std::string> LayerContextImpl::DoUpdateDisplayTree(
mojom::LayerTreeUpdatePtr update) {
TRACE_EVENT0("viz", "LayerContextImpl::DoUpdateDisplayTree");
cc::LayerTreeImpl& layers = *host_impl_->active_tree();
// We resize all property trees first, as layers and property tree nodes
// themselves may index one or more other property tree nodes. These indices
// need to be validated, and the dependency can be cyclic (e.g. scroll nodes
// may index transform nodes and transform nodes may index scroll nodes).
cc::PropertyTrees& property_trees = *layers.property_trees();
const bool transform_size_changed = ResizePropertyTree(
property_trees.transform_tree_mutable(), update->num_transform_nodes);
const bool clip_size_changed = ResizePropertyTree(
property_trees.clip_tree_mutable(), update->num_clip_nodes);
const bool effect_size_changed = ResizePropertyTree(
property_trees.effect_tree_mutable(), update->num_effect_nodes);
const bool scroll_size_changed = ResizePropertyTree(
property_trees.scroll_tree_mutable(), update->num_scroll_nodes);
// Transform tree properties need to update before its nodes are updated, as
// the nodes may index properties on the tree itself (e.g. scroll
// constraints). Note that these properties may also index scroll and
// transform nodes, so they must be deserialized after the trees are resized
// above.
bool transform_properties_changed = false;
if (update->transform_tree_update) {
transform_properties_changed = true;
RETURN_IF_ERROR(UpdateTransformTreeProperties(
property_trees, property_trees.transform_tree_mutable(),
*update->transform_tree_update));
}
if (update->scroll_tree_update) {
ASSIGN_OR_RETURN(const bool scroll_properties_changed,
UpdateScrollTreeProperties(
property_trees, property_trees.scroll_tree_mutable(),
*update->scroll_tree_update));
if (scroll_properties_changed) {
layers.set_needs_update_draw_properties();
}
}
ASSIGN_OR_RETURN(const bool transform_nodes_changed,
UpdatePropertyTree(property_trees,
property_trees.transform_tree_mutable(),
update->transform_nodes));
ASSIGN_OR_RETURN(
const bool clip_nodes_changed,
UpdatePropertyTree(property_trees, property_trees.clip_tree_mutable(),
update->clip_nodes));
ASSIGN_OR_RETURN(
const bool effect_nodes_changed,
UpdatePropertyTree(property_trees, property_trees.effect_tree_mutable(),
update->effect_nodes));
ASSIGN_OR_RETURN(
const bool scroll_nodes_changed,
UpdatePropertyTree(property_trees, property_trees.scroll_tree_mutable(),
update->scroll_nodes));
// Pull any copy output requests that came in over the wire.
for (const auto& wire : update->effect_nodes) {
for (auto&& copy_request : wire->copy_output_requests) {
property_trees.effect_tree_mutable().AddCopyRequest(
wire->id, std::move(copy_request));
}
}
if (update->surface_ranges) {
base::flat_set<SurfaceRange> surface_ranges;
for (auto& surface_range : *(update->surface_ranges)) {
surface_ranges.insert(surface_range);
}
layers.ClearSurfaceRanges();
layers.SetSurfaceRanges(surface_ranges);
}
if (update->view_transition_requests) {
DeserializeViewTransitionRequests(layers,
*(update->view_transition_requests));
}
RETURN_IF_ERROR(CreateOrUpdateLayers(
*(this->host_impl_.get()), update->layers, update->layer_order, layers));
// After layers are updated, validate backdrop_mask_element_id.
for (const auto& wire : update->effect_nodes) {
if (wire->backdrop_mask_element_id) {
if (auto* layer =
layers.LayerByElementId(wire->backdrop_mask_element_id)) {
if (layer->GetLayerType() != cc::mojom::LayerType::kTileDisplay) {
return base::unexpected(
"Invalid backdrop_mask_element_id: layer is not a "
"TileDisplayLayer");
}
} else {
return base::unexpected(
"Invalid backdrop_mask_element_id: layer not found");
}
}
}
if (update->local_surface_id_from_parent) {
layers.SetLocalSurfaceIdFromParent(*update->local_surface_id_from_parent);
}
host_impl_->set_current_local_surface_id_from_client(
update->current_local_surface_id);
if (update->target_local_surface_id) {
host_impl_->SetTargetLocalSurfaceId(*update->target_local_surface_id);
}
RETURN_IF_FALSE(update->next_frame_token > 0, "invalid frame token");
host_impl_->set_next_frame_token_from_client(update->next_frame_token);
host_impl_->set_send_frame_token_to_embedder(
update->send_frame_token_to_embedder);
{
TRACE_EVENT1("viz", "DeserializeTilings", "TilingCount",
update->tilings.size());
for (const auto& tiling : update->tilings) {
if (cc::LayerImpl* layer = layers.LayerById(tiling->layer_id)) {
if (layer->GetLayerType() != cc::mojom::LayerType::kTileDisplay) {
return base::unexpected("Invalid tile update");
}
RETURN_IF_ERROR(DeserializeTiling(
host_impl_.get(), static_cast<cc::TileDisplayLayerImpl&>(*layer),
*tiling, /*update_damage=*/false));
}
}
}
// This needs to happen before SetPageSvaleFactorAndLimitsForDisplayTree().
RETURN_IF_ERROR(UpdateViewportPropertyIds(layers, property_trees, *update));
layers.set_background_color(update->background_color);
layers.set_source_frame_number(update->source_frame_number);
layers.set_trace_id(
cc::BeginMainFrameTraceId::FromUnsafeValue(update->trace_id));
layers.set_primary_main_frame_item_sequence_number(
update->primary_main_frame_item_sequence_number);
layers.SetDeviceViewportRect(update->device_viewport);
layers.RegisterSelection(update->selection);
if (update->page_scale_factor <= 0 ||
!std::isfinite(update->page_scale_factor) ||
update->min_page_scale_factor <= 0 ||
!std::isfinite(update->min_page_scale_factor) ||
update->max_page_scale_factor <= 0 ||
!std::isfinite(update->max_page_scale_factor) ||
update->min_page_scale_factor > update->max_page_scale_factor) {
return base::unexpected("Invalid page scale factors");
}
layers.SetPageScaleFactorAndLimitsForDisplayTree(
update->page_scale_factor, update->min_page_scale_factor,
update->max_page_scale_factor);
if (update->external_page_scale_factor <= 0 ||
!std::isfinite(update->external_page_scale_factor)) {
return base::unexpected("Invalid external page scale factor");
}
layers.SetExternalPageScaleFactor(update->external_page_scale_factor);
if (update->device_scale_factor <= 0 ||
!std::isfinite(update->device_scale_factor)) {
return base::unexpected("Invalid device scale factor");
}
layers.SetDeviceScaleFactor(update->device_scale_factor);
if (update->painted_device_scale_factor <= 0 ||
!std::isfinite(update->painted_device_scale_factor)) {
return base::unexpected("Invalid painted device scale factor");
}
if (update->max_safe_area_inset_bottom < 0 ||
!std::isfinite(update->max_safe_area_inset_bottom)) {
return base::unexpected("Invalid max safe area inset bottom");
}
layers.SetBrowserControlsParams(update->browser_controls_params);
host_impl_->browser_controls_manager()->SetOffsetTagModifications(
update->browser_controls_offset_tag_modifications);
layers.set_display_transform_hint(update->display_transform_hint);
layers.SetMaxSafeAreaInsetBottom(update->max_safe_area_inset_bottom);
layers.set_painted_device_scale_factor(update->painted_device_scale_factor);
layers.SetDisplayColorSpaces(update->display_color_spaces);
if (!(update->top_controls_shown_ratio >= 0 &&
update->top_controls_shown_ratio <= 1 &&
update->bottom_controls_shown_ratio >= 0 &&
update->bottom_controls_shown_ratio <= 1)) {
return base::unexpected("Invalid top/bottom controls shown ratios");
}
host_impl_->SetCurrentBrowserControlsShownRatio(
update->top_controls_shown_ratio, update->bottom_controls_shown_ratio);
host_impl_->SetViewportDamage(update->viewport_damage_rect);
host_impl_->SetDebugState(update->debug_state);
for (auto& ui_resource_request : update->ui_resource_requests) {
if (ui_resource_request->type ==
mojom::TransferableUIResourceRequest::Type::kCreate) {
if (!ui_resource_request->transferable_resource ||
ui_resource_request->transferable_resource->is_empty()) {
return base::unexpected(
"Invalid transferable resource in UI resource creation");
}
if (ui_resource_request->transferable_resource->GetSize().width() <= 0 ||
ui_resource_request->transferable_resource->GetSize().height() <= 0) {
return base::unexpected(
"Invalid dimensions for transferable UI resource.");
}
ReleaseCallback release_callback = base::BindOnce(
[](cc::LayerTreeHostImpl* host_impl, ResourceId id,
const gpu::SyncToken& sync_token, bool is_lost) {
host_impl->ReturnResource({id, sync_token,
/*release_fence=*/gfx::GpuFenceHandle(),
/*count=*/1, is_lost});
},
host_impl_.get(), ui_resource_request->transferable_resource->id);
auto resource_id = host_impl_->resource_provider()->ImportResource(
ui_resource_request->transferable_resource.value(),
/*impl_release_callback=*/std::move(release_callback),
/*main_thread_release_callback=*/base::NullCallback(),
/*evicted_callback=*/base::NullCallback());
host_impl_->CreateUIResourceFromImportedResource(
ui_resource_request->uid, resource_id, ui_resource_request->opaque);
} else {
host_impl_->DeleteUIResource(ui_resource_request->uid);
}
}
property_trees.UpdateChangeTracking();
property_trees.transform_tree_mutable().set_needs_update(
transform_size_changed || transform_properties_changed ||
transform_nodes_changed ||
property_trees.transform_tree().needs_update());
property_trees.clip_tree_mutable().set_needs_update(
clip_size_changed || clip_nodes_changed ||
property_trees.clip_tree().needs_update());
property_trees.effect_tree_mutable().set_needs_update(
effect_size_changed || effect_nodes_changed ||
property_trees.effect_tree().needs_update());
const bool any_tree_changed =
transform_size_changed || transform_nodes_changed || clip_size_changed ||
clip_nodes_changed || effect_size_changed || effect_nodes_changed ||
scroll_size_changed || scroll_nodes_changed;
property_trees.set_changed(any_tree_changed);
if (any_tree_changed) {
property_trees.ResetCachedData();
layers.set_needs_update_draw_properties();
}
std::vector<std::unique_ptr<cc::RenderSurfaceImpl>> old_render_surfaces;
property_trees.effect_tree_mutable().TakeRenderSurfaces(&old_render_surfaces);
const bool render_surfaces_changed =
property_trees.effect_tree_mutable().CreateOrReuseRenderSurfaces(
&old_render_surfaces, &layers);
if (effect_size_changed || render_surfaces_changed) {
// TODO(rockot): Forcing draw property updates here isn't strictly necessary
// when `effect_size_changed` is true unless it's because we've removed at
// least one EffectNode that was inducing a render surface.
layers.set_needs_update_draw_properties();
}
// Set this last, making sure renderer side state isn't overwritten by other
// updates. As this is a transient property, we should set but not clear it.
if (update->full_tree_damaged) {
property_trees.set_full_tree_damaged(true);
}
// Safe down-cast: AnimationHost is the only subclass of MutatorHost.
auto* animation_host = static_cast<cc::AnimationHost*>(layers.mutator_host());
RETURN_IF_ERROR(DeserializeAnimationUpdates(*update, *animation_host));
host_impl_->ActivateAnimations();
return base::ok();
}
void LayerContextImpl::DoDraw(const BeginFrameArgs& begin_frame_args,
base::TimeTicks start_update_display_tree) {
if (base::FeatureList::IsEnabled(features::kTreeAnimationsInViz)) {
compositor_sink_->SetLayerContextWantsBeginFrames(true);
} else {
DoDrawInternal(begin_frame_args, start_update_display_tree);
}
}
void LayerContextImpl::DoDrawInternal(
const BeginFrameArgs& begin_frame_args,
base::TimeTicks start_update_display_tree) {
TRACE_EVENT0("viz", "LayerContextImpl::DoDrawInternal");
if (!host_impl_->CanDraw()) {
return;
}
host_impl_->WillBeginImplFrame(begin_frame_args);
cc::FrameData frame;
TreesInVizTiming stage_breakdown;
stage_breakdown.start_update_display_tree = start_update_display_tree;
// TODO(vmiura): Manage these flags properly.
const bool has_damage = true;
frame.begin_frame_ack = BeginFrameAck(begin_frame_args, has_damage);
frame.origin_begin_main_frame_args = begin_frame_args;
stage_breakdown.start_prepare_to_draw = base::TimeTicks::Now();
host_impl_->PrepareToDraw(&frame);
// Notifies the client which of the tilings it nominated for deletion are
// actually safe to delete. This is done after PrepareToDraw() so that we have
// the most up-to-date information on which tilings were used for the current
// frame.
SendTilingsCleanupNotificationToClient();
stage_breakdown.start_draw_layers = base::TimeTicks::Now();
frame.set_trees_in_viz_timestamps(std::move(stage_breakdown));
host_impl_->DrawLayers(&frame);
host_impl_->DidDrawAllLayers(frame);
host_impl_->DidFinishImplFrame(begin_frame_args);
}
void LayerContextImpl::SendTilingsCleanupNotificationToClient() {
for (cc::LayerImpl* layer : *host_impl_->active_tree()) {
if (layer->GetLayerType() == cc::mojom::LayerType::kTileDisplay) {
auto* tile_layer = static_cast<cc::TileDisplayLayerImpl*>(layer);
std::vector<float> scales_to_remove =
tile_layer->GetSafeToDeleteTilings();
if (!scales_to_remove.empty()) {
client_->get()->OnTilingsReadyForCleanup(tile_layer->id(),
scales_to_remove);
}
}
}
}
void LayerContextImpl::UpdateDisplayTiling(mojom::TilingPtr tiling,
bool update_damage) {
CHECK(receiver_);
auto result = DoUpdateDisplayTiling(std::move(tiling), update_damage);
if (!result.has_value()) {
HandleBadMojoMessage("UpdateDisplayTiling", result.error());
}
}
base::expected<void, std::string> LayerContextImpl::DoUpdateDisplayTiling(
mojom::TilingPtr tiling,
bool update_damage) {
cc::LayerTreeImpl& layers = *host_impl_->active_tree();
if (cc::LayerImpl* layer = layers.LayerById(tiling->layer_id)) {
if (layer->GetLayerType() != cc::mojom::LayerType::kTileDisplay) {
return base::unexpected("Invalid tile update");
}
return DeserializeTiling(host_impl_.get(),
static_cast<cc::TileDisplayLayerImpl&>(*layer),
*tiling, update_damage);
}
return base::ok();
}
} // namespace viz