| // 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 <memory> |
| #include <utility> |
| |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/time/time.h" |
| #include "base/unguessable_token.h" |
| #include "cc/slim/features.h" |
| #include "cc/slim/layer.h" |
| #include "cc/slim/nine_patch_layer.h" |
| #include "cc/slim/solid_color_layer.h" |
| #include "cc/slim/surface_layer.h" |
| #include "cc/slim/test_frame_sink_impl.h" |
| #include "cc/slim/test_layer_tree_client.h" |
| #include "cc/slim/test_layer_tree_impl.h" |
| #include "cc/slim/ui_resource_layer.h" |
| #include "components/viz/common/frame_sinks/begin_frame_args.h" |
| #include "components/viz/common/frame_sinks/copy_output_request.h" |
| #include "components/viz/common/quads/compositor_frame.h" |
| #include "components/viz/common/quads/solid_color_draw_quad.h" |
| #include "components/viz/common/quads/surface_draw_quad.h" |
| #include "components/viz/common/quads/texture_draw_quad.h" |
| #include "components/viz/common/resources/transferable_resource.h" |
| #include "components/viz/common/surfaces/local_surface_id.h" |
| #include "components/viz/test/draw_quad_matchers.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/test/geometry_util.h" |
| #include "ui/gfx/presentation_feedback.h" |
| |
| namespace cc::slim { |
| |
| namespace { |
| |
| using testing::AllOf; |
| using testing::ElementsAre; |
| |
| class SlimLayerTreeCompositorFrameTest : public testing::Test { |
| public: |
| void SetUp() override { |
| scoped_feature_list_.InitAndEnableFeature(features::kSlimCompositor); |
| layer_tree_ = std::make_unique<TestLayerTreeImpl>(&client_); |
| layer_tree_->SetVisible(true); |
| |
| auto frame_sink = TestFrameSinkImpl::Create(); |
| frame_sink_ = frame_sink->GetWeakPtr(); |
| layer_tree_->SetFrameSink(std::move(frame_sink)); |
| |
| viewport_ = gfx::Rect(100, 100); |
| base::UnguessableToken token = base::UnguessableToken::Create(); |
| local_surface_id_ = viz::LocalSurfaceId(1u, 2u, token); |
| EXPECT_TRUE(local_surface_id_.is_valid()); |
| layer_tree_->SetViewportRectAndScale( |
| viewport_, /*device_scale_factor=*/1.0f, local_surface_id_); |
| } |
| |
| void IncrementLocalSurfaceId() { |
| DCHECK(local_surface_id_.is_valid()); |
| local_surface_id_ = |
| viz::LocalSurfaceId(local_surface_id_.parent_sequence_number(), |
| local_surface_id_.child_sequence_number() + 1, |
| local_surface_id_.embed_token()); |
| DCHECK(local_surface_id_.is_valid()); |
| } |
| |
| viz::CompositorFrame ProduceFrame( |
| absl::optional<viz::HitTestRegionList>* out_list = nullptr) { |
| layer_tree_->SetNeedsRedraw(); |
| EXPECT_TRUE(layer_tree_->NeedsBeginFrames()); |
| base::TimeTicks frame_time = base::TimeTicks::Now(); |
| base::TimeDelta interval = viz::BeginFrameArgs::DefaultInterval(); |
| viz::BeginFrameArgs begin_frame_args = viz::BeginFrameArgs::Create( |
| BEGINFRAME_FROM_HERE, |
| /*source_id=*/1, ++sequence_id_, frame_time, frame_time + interval, |
| interval, viz::BeginFrameArgs::NORMAL); |
| frame_sink_->OnBeginFrame(begin_frame_args, std::move(next_timing_details_), |
| /*frame_ack=*/false, {}); |
| next_timing_details_.clear(); |
| viz::CompositorFrame frame = frame_sink_->TakeLastFrame(); |
| if (out_list) { |
| *out_list = frame_sink_->GetLastHitTestRegionList(); |
| } |
| frame_sink_->DidReceiveCompositorFrameAck({}); |
| return frame; |
| } |
| |
| scoped_refptr<SolidColorLayer> CreateSolidColorLayer(const gfx::Size& bounds, |
| SkColor4f color) { |
| auto solid_color_layer = SolidColorLayer::Create(); |
| solid_color_layer->SetBounds(bounds); |
| solid_color_layer->SetBackgroundColor(color); |
| solid_color_layer->SetIsDrawable(true); |
| return solid_color_layer; |
| } |
| |
| void SetNextFrameTimingDetailsMap(viz::FrameTimingDetailsMap timing_map) { |
| next_timing_details_ = std::move(timing_map); |
| } |
| |
| viz::FrameTimingDetails BuildFrameTimingDetails(uint32_t flags = 0) { |
| viz::FrameTimingDetails details; |
| base::TimeTicks timestamp = base::TimeTicks::Now(); |
| base::TimeDelta interval = base::Milliseconds(16.6); |
| gfx::PresentationFeedback feedback(timestamp, interval, flags); |
| details.presentation_feedback = feedback; |
| return details; |
| } |
| |
| protected: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| TestLayerTreeClient client_; |
| std::unique_ptr<TestLayerTreeImpl> layer_tree_; |
| base::WeakPtr<TestFrameSinkImpl> frame_sink_; |
| |
| uint64_t sequence_id_ = 0; |
| viz::FrameTimingDetailsMap next_timing_details_; |
| |
| gfx::Rect viewport_; |
| viz::LocalSurfaceId local_surface_id_; |
| }; |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, CompositorFrameMetadataBasics) { |
| auto solid_color_layer = |
| CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(solid_color_layer); |
| |
| uint32_t first_frame_token = 0u; |
| { |
| viz::CompositorFrame frame = ProduceFrame(); |
| viz::CompositorFrameMetadata& metadata = frame.metadata; |
| EXPECT_NE(0u, metadata.frame_token); |
| first_frame_token = metadata.frame_token; |
| EXPECT_EQ(sequence_id_, metadata.begin_frame_ack.frame_id.sequence_number); |
| EXPECT_EQ(1.0f, metadata.device_scale_factor); |
| EXPECT_EQ(SkColors::kWhite, metadata.root_background_color); |
| EXPECT_EQ(gfx::OVERLAY_TRANSFORM_NONE, metadata.display_transform_hint); |
| EXPECT_EQ(absl::nullopt, metadata.top_controls_visible_height); |
| } |
| |
| IncrementLocalSurfaceId(); |
| layer_tree_->SetViewportRectAndScale(viewport_, /*device_scale_factor=*/2.0f, |
| local_surface_id_); |
| layer_tree_->set_background_color(SkColors::kBlue); |
| layer_tree_->set_display_transform_hint(gfx::OVERLAY_TRANSFORM_ROTATE_90); |
| layer_tree_->UpdateTopControlsVisibleHeight(5.0f); |
| { |
| viz::CompositorFrame frame = ProduceFrame(); |
| viz::CompositorFrameMetadata& metadata = frame.metadata; |
| EXPECT_NE(0u, metadata.frame_token); |
| EXPECT_NE(first_frame_token, metadata.frame_token); |
| EXPECT_EQ(sequence_id_, metadata.begin_frame_ack.frame_id.sequence_number); |
| EXPECT_EQ(2.0f, metadata.device_scale_factor); |
| EXPECT_EQ(SkColors::kBlue, metadata.root_background_color); |
| EXPECT_EQ(gfx::OVERLAY_TRANSFORM_ROTATE_90, |
| metadata.display_transform_hint); |
| EXPECT_EQ(5.0f, metadata.top_controls_visible_height); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, OneSolidColorQuad) { |
| auto solid_color_layer = |
| CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(solid_color_layer); |
| |
| viz::CompositorFrame frame = ProduceFrame(); |
| |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| EXPECT_EQ(pass->output_rect, viewport_); |
| EXPECT_EQ(pass->damage_rect, viewport_); |
| EXPECT_EQ(pass->transform_to_root_target, gfx::Transform()); |
| |
| ASSERT_THAT( |
| pass->quad_list, |
| ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kGray), |
| viz::HasRect(viewport_), viz::HasVisibleRect(viewport_), |
| viz::HasTransform(gfx::Transform())))); |
| auto* quad = pass->quad_list.back(); |
| auto* shared_quad_state = quad->shared_quad_state; |
| |
| EXPECT_EQ(shared_quad_state->quad_layer_rect, viewport_); |
| EXPECT_EQ(shared_quad_state->visible_quad_layer_rect, viewport_); |
| EXPECT_EQ(shared_quad_state->clip_rect, absl::nullopt); |
| EXPECT_EQ(shared_quad_state->are_contents_opaque, true); |
| EXPECT_EQ(shared_quad_state->opacity, 1.0f); |
| EXPECT_EQ(shared_quad_state->blend_mode, SkBlendMode::kSrcOver); |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, LayerTransform) { |
| auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(root_layer); |
| |
| auto child = CreateSolidColorLayer(gfx::Size(10, 20), SkColors::kGreen); |
| root_layer->AddChild(child); |
| |
| auto check_child_quad = [&](gfx::Rect expected_rect_in_root) { |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT(pass->quad_list, |
| ElementsAre(AllOf(viz::IsSolidColorQuad(SkColors::kGreen), |
| viz::HasRect(gfx::Rect(10, 20)), |
| viz::HasVisibleRect(gfx::Rect(10, 20))), |
| AllOf(viz::IsSolidColorQuad(SkColors::kGray), |
| viz::HasRect(viewport_), |
| viz::HasVisibleRect(viewport_)))); |
| |
| auto* quad = pass->quad_list.front(); |
| auto* shared_quad_state = quad->shared_quad_state; |
| |
| EXPECT_EQ(shared_quad_state->quad_layer_rect, gfx::Rect(10, 20)); |
| EXPECT_EQ(shared_quad_state->visible_quad_layer_rect, gfx::Rect(10, 20)); |
| |
| gfx::Rect rect_in_root = |
| shared_quad_state->quad_to_target_transform.MapRect(quad->rect); |
| EXPECT_EQ(expected_rect_in_root, rect_in_root); |
| }; |
| |
| child->SetPosition(gfx::PointF(30.0f, 30.0f)); |
| check_child_quad(gfx::Rect(30, 30, 10, 20)); |
| |
| child->SetTransform(gfx::Transform::MakeTranslation(10.0f, 10.0f)); |
| check_child_quad(gfx::Rect(40, 40, 10, 20)); |
| |
| // Rotate about top left corner. |
| child->SetTransform(gfx::Transform::Make90degRotation()); |
| check_child_quad(gfx::Rect(10, 30, 20, 10)); |
| |
| // Rotate about the center. |
| child->SetTransformOrigin(gfx::Point3F(5.0f, 10.0f, 0.0f)); |
| check_child_quad(gfx::Rect(25, 35, 20, 10)); |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, ChildOrder) { |
| auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(root_layer); |
| |
| scoped_refptr<SolidColorLayer> children[] = { |
| CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kBlue), |
| CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kGreen), |
| CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kMagenta), |
| CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kRed), |
| CreateSolidColorLayer(gfx::Size(10, 10), SkColors::kYellow)}; |
| |
| // Build tree such that quads appear in child order. |
| // Quads are appended post order depth first, in reverse child order. |
| // root <- child4 <- child3 |
| // <- child2 |
| // <- child1 <- child0 |
| root_layer->AddChild(children[4]); |
| root_layer->AddChild(children[1]); |
| children[4]->AddChild(children[3]); |
| children[4]->AddChild(children[2]); |
| children[1]->AddChild(children[0]); |
| |
| // Add offsets so they do not cover each other. |
| children[3]->SetPosition(gfx::PointF(10.0f, 10.0f)); |
| children[2]->SetPosition(gfx::PointF(20.0f, 20.0f)); |
| children[1]->SetPosition(gfx::PointF(30.0f, 30.0f)); |
| children[0]->SetPosition(gfx::PointF(10.0f, 10.0f)); |
| |
| gfx::Point expected_origins[] = { |
| gfx::Point(40.0f, 40.0f), gfx::Point(30.0f, 30.0f), |
| gfx::Point(20.0f, 20.0f), gfx::Point(10.0f, 10.0f), |
| gfx::Point(00.0f, 00.0f)}; |
| |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT(pass->quad_list, |
| ElementsAre(viz::IsSolidColorQuad(SkColors::kBlue), |
| viz::IsSolidColorQuad(SkColors::kGreen), |
| viz::IsSolidColorQuad(SkColors::kMagenta), |
| viz::IsSolidColorQuad(SkColors::kRed), |
| viz::IsSolidColorQuad(SkColors::kYellow), |
| viz::IsSolidColorQuad(SkColors::kGray))); |
| |
| for (size_t i = 0; i < std::size(expected_origins); ++i) { |
| auto* quad = pass->quad_list.ElementAt(i); |
| EXPECT_EQ(quad->shared_quad_state->quad_to_target_transform.MapPoint( |
| gfx::Point()), |
| expected_origins[i]); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, AxisAlignedClip) { |
| auto root_layer = CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(root_layer); |
| |
| auto clip_layer = Layer::Create(); |
| clip_layer->SetBounds(gfx::Size(10, 20)); |
| clip_layer->SetMasksToBounds(true); |
| |
| auto draw_layer = CreateSolidColorLayer(gfx::Size(30, 30), SkColors::kRed); |
| |
| root_layer->AddChild(clip_layer); |
| clip_layer->AddChild(draw_layer); |
| |
| { |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT(pass->quad_list, |
| ElementsAre(viz::IsSolidColorQuad(SkColors::kRed), |
| viz::IsSolidColorQuad(SkColors::kGray))); |
| |
| auto* quad = pass->quad_list.front(); |
| ASSERT_TRUE(quad->shared_quad_state->clip_rect); |
| EXPECT_EQ(quad->shared_quad_state->clip_rect.value(), gfx::Rect(10, 20)); |
| } |
| |
| clip_layer->SetPosition(gfx::PointF(5, 5)); |
| { |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT(pass->quad_list, |
| ElementsAre(viz::IsSolidColorQuad(SkColors::kRed), |
| viz::IsSolidColorQuad(SkColors::kGray))); |
| |
| auto* quad = pass->quad_list.front(); |
| ASSERT_TRUE(quad->shared_quad_state->clip_rect); |
| // Clip is in target space. |
| EXPECT_EQ(quad->shared_quad_state->clip_rect.value(), |
| gfx::Rect(5, 5, 10, 20)); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, PresentationCallback) { |
| auto solid_color_layer = |
| CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(solid_color_layer); |
| |
| absl::optional<gfx::PresentationFeedback> feedback_opt_1; |
| absl::optional<gfx::PresentationFeedback> feedback_opt_2; |
| layer_tree_->RequestPresentationTimeForNextFrame(base::BindLambdaForTesting( |
| [&](const gfx::PresentationFeedback& feedback) { |
| feedback_opt_1 = feedback; |
| })); |
| layer_tree_->RequestPresentationTimeForNextFrame(base::BindLambdaForTesting( |
| [&](const gfx::PresentationFeedback& feedback) { |
| feedback_opt_2 = feedback; |
| })); |
| viz::CompositorFrame frame1 = ProduceFrame(); |
| |
| viz::FrameTimingDetailsMap timing_map; |
| viz::FrameTimingDetails details = BuildFrameTimingDetails(); |
| timing_map[frame1.metadata.frame_token] = details; |
| SetNextFrameTimingDetailsMap(std::move(timing_map)); |
| viz::CompositorFrame frame2 = ProduceFrame(); |
| |
| ASSERT_TRUE(feedback_opt_1); |
| ASSERT_TRUE(feedback_opt_2); |
| EXPECT_EQ(feedback_opt_1.value(), details.presentation_feedback); |
| EXPECT_EQ(feedback_opt_2.value(), details.presentation_feedback); |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, PresentationCallbackMissedFrame) { |
| auto solid_color_layer = |
| CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(solid_color_layer); |
| |
| absl::optional<gfx::PresentationFeedback> feedback_opt_1; |
| layer_tree_->RequestPresentationTimeForNextFrame(base::BindLambdaForTesting( |
| [&](const gfx::PresentationFeedback& feedback) { |
| feedback_opt_1 = feedback; |
| })); |
| viz::CompositorFrame frame1 = ProduceFrame(); |
| |
| absl::optional<gfx::PresentationFeedback> feedback_opt_2; |
| layer_tree_->RequestPresentationTimeForNextFrame(base::BindLambdaForTesting( |
| [&](const gfx::PresentationFeedback& feedback) { |
| feedback_opt_2 = feedback; |
| })); |
| viz::CompositorFrame frame2 = ProduceFrame(); |
| viz::CompositorFrame frame3 = ProduceFrame(); |
| EXPECT_FALSE(feedback_opt_1); |
| EXPECT_FALSE(feedback_opt_2); |
| |
| { |
| // Ack frame 1 which should only run the first callback. |
| viz::FrameTimingDetailsMap timing_map; |
| viz::FrameTimingDetails details = BuildFrameTimingDetails(); |
| timing_map[frame1.metadata.frame_token] = details; |
| SetNextFrameTimingDetailsMap(std::move(timing_map)); |
| viz::CompositorFrame frame4 = ProduceFrame(); |
| |
| EXPECT_TRUE(feedback_opt_1); |
| EXPECT_EQ(feedback_opt_1.value(), details.presentation_feedback); |
| EXPECT_FALSE(feedback_opt_2); |
| } |
| |
| { |
| // Ack frame 3, skipping frame 2, which should only run the second callback. |
| viz::FrameTimingDetailsMap timing_map; |
| viz::FrameTimingDetails details = BuildFrameTimingDetails(); |
| timing_map[frame3.metadata.frame_token] = details; |
| SetNextFrameTimingDetailsMap(std::move(timing_map)); |
| viz::CompositorFrame frame4 = ProduceFrame(); |
| |
| ASSERT_TRUE(feedback_opt_2); |
| EXPECT_EQ(feedback_opt_2.value(), details.presentation_feedback); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, SuccessPresentationCallback) { |
| auto solid_color_layer = |
| CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(solid_color_layer); |
| |
| absl::optional<base::TimeTicks> feedback_time_opt_1; |
| absl::optional<base::TimeTicks> feedback_time_opt_2; |
| layer_tree_->RequestSuccessfulPresentationTimeForNextFrame( |
| base::BindLambdaForTesting( |
| [&](base::TimeTicks timeticks) { feedback_time_opt_1 = timeticks; })); |
| layer_tree_->RequestSuccessfulPresentationTimeForNextFrame( |
| base::BindLambdaForTesting( |
| [&](base::TimeTicks timeticks) { feedback_time_opt_2 = timeticks; })); |
| viz::CompositorFrame frame1 = ProduceFrame(); |
| |
| viz::FrameTimingDetailsMap timing_map; |
| viz::FrameTimingDetails details = BuildFrameTimingDetails(); |
| timing_map[frame1.metadata.frame_token] = details; |
| SetNextFrameTimingDetailsMap(std::move(timing_map)); |
| viz::CompositorFrame frame2 = ProduceFrame(); |
| |
| ASSERT_TRUE(feedback_time_opt_1); |
| ASSERT_TRUE(feedback_time_opt_2); |
| EXPECT_EQ(feedback_time_opt_1.value(), |
| details.presentation_feedback.timestamp); |
| EXPECT_EQ(feedback_time_opt_2.value(), |
| details.presentation_feedback.timestamp); |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, |
| SuccessPresentationCallbackNotCalledForFailedFrame) { |
| auto solid_color_layer = |
| CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(solid_color_layer); |
| |
| absl::optional<base::TimeTicks> feedback_time_opt_1; |
| layer_tree_->RequestSuccessfulPresentationTimeForNextFrame( |
| base::BindLambdaForTesting( |
| [&](base::TimeTicks timeticks) { feedback_time_opt_1 = timeticks; })); |
| viz::CompositorFrame frame1 = ProduceFrame(); |
| viz::CompositorFrame frame2 = ProduceFrame(); |
| |
| absl::optional<base::TimeTicks> feedback_time_opt_2; |
| layer_tree_->RequestSuccessfulPresentationTimeForNextFrame( |
| base::BindLambdaForTesting( |
| [&](base::TimeTicks timeticks) { feedback_time_opt_2 = timeticks; })); |
| viz::CompositorFrame frame3 = ProduceFrame(); |
| |
| // Frame 1 failed. Should not run either callback. |
| { |
| viz::FrameTimingDetailsMap timing_map; |
| viz::FrameTimingDetails details = |
| BuildFrameTimingDetails(gfx::PresentationFeedback::kFailure); |
| timing_map[frame1.metadata.frame_token] = details; |
| SetNextFrameTimingDetailsMap(std::move(timing_map)); |
| viz::CompositorFrame frame4 = ProduceFrame(); |
| EXPECT_FALSE(feedback_time_opt_1); |
| EXPECT_FALSE(feedback_time_opt_2); |
| } |
| |
| // Successful feedback for frame 2. Should run callback 1 but not 2. |
| { |
| viz::FrameTimingDetailsMap timing_map; |
| viz::FrameTimingDetails details = BuildFrameTimingDetails(); |
| timing_map[frame2.metadata.frame_token] = details; |
| SetNextFrameTimingDetailsMap(std::move(timing_map)); |
| viz::CompositorFrame frame5 = ProduceFrame(); |
| ASSERT_TRUE(feedback_time_opt_1); |
| EXPECT_EQ(feedback_time_opt_1.value(), |
| details.presentation_feedback.timestamp); |
| ASSERT_FALSE(feedback_time_opt_2); |
| } |
| |
| // Successful feedback for frame 3. Should run 2. |
| { |
| viz::FrameTimingDetailsMap timing_map; |
| viz::FrameTimingDetails details = BuildFrameTimingDetails(); |
| timing_map[frame3.metadata.frame_token] = details; |
| SetNextFrameTimingDetailsMap(std::move(timing_map)); |
| viz::CompositorFrame frame5 = ProduceFrame(); |
| ASSERT_TRUE(feedback_time_opt_2); |
| EXPECT_EQ(feedback_time_opt_2.value(), |
| details.presentation_feedback.timestamp); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, CopyOutputRequest) { |
| auto solid_color_layer = |
| CreateSolidColorLayer(viewport_.size(), SkColors::kGray); |
| layer_tree_->SetRoot(solid_color_layer); |
| |
| auto copy_request_no_source_1 = std::make_unique<viz::CopyOutputRequest>( |
| viz::CopyOutputRequest::ResultFormat::RGBA, |
| viz::CopyOutputRequest::ResultDestination::kSystemMemory, |
| base::DoNothing()); |
| auto copy_request_no_source_2 = std::make_unique<viz::CopyOutputRequest>( |
| viz::CopyOutputRequest::ResultFormat::RGBA, |
| viz::CopyOutputRequest::ResultDestination::kSystemMemory, |
| base::DoNothing()); |
| |
| base::UnguessableToken token = base::UnguessableToken::Create(); |
| auto copy_request_with_source = std::make_unique<viz::CopyOutputRequest>( |
| viz::CopyOutputRequest::ResultFormat::RGBA, |
| viz::CopyOutputRequest::ResultDestination::kSystemMemory, |
| base::DoNothing()); |
| copy_request_with_source->set_source(token); |
| auto copy_request_with_same_source = std::make_unique<viz::CopyOutputRequest>( |
| viz::CopyOutputRequest::ResultFormat::RGBA, |
| viz::CopyOutputRequest::ResultDestination::kSystemMemory, |
| base::DoNothing()); |
| copy_request_with_same_source->set_source(token); |
| |
| base::UnguessableToken token2 = base::UnguessableToken::Create(); |
| auto copy_request_with_difference_source = |
| std::make_unique<viz::CopyOutputRequest>( |
| viz::CopyOutputRequest::ResultFormat::RGBA, |
| viz::CopyOutputRequest::ResultDestination::kSystemMemory, |
| base::DoNothing()); |
| copy_request_with_difference_source->set_source(token2); |
| |
| layer_tree_->RequestCopyOfOutput(std::move(copy_request_no_source_1)); |
| layer_tree_->RequestCopyOfOutput(std::move(copy_request_no_source_2)); |
| layer_tree_->RequestCopyOfOutput(std::move(copy_request_with_source)); |
| layer_tree_->RequestCopyOfOutput(std::move(copy_request_with_same_source)); |
| layer_tree_->RequestCopyOfOutput( |
| std::move(copy_request_with_difference_source)); |
| |
| { |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_EQ(pass->copy_requests.size(), 4u); |
| EXPECT_TRUE(pass->copy_requests[0]); |
| EXPECT_TRUE(pass->copy_requests[1]); |
| EXPECT_TRUE(pass->copy_requests[2]); |
| EXPECT_TRUE(pass->copy_requests[3]); |
| } |
| |
| { |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_EQ(pass->copy_requests.size(), 0u); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, UIResourceLayerAppendQuads) { |
| auto ui_resource_layer = UIResourceLayer::Create(); |
| ui_resource_layer->SetBounds(viewport_.size()); |
| ui_resource_layer->SetIsDrawable(true); |
| layer_tree_->SetRoot(ui_resource_layer); |
| |
| viz::ResourceId first_resource_id = viz::kInvalidResourceId; |
| { |
| auto image_info = |
| SkImageInfo::Make(1, 1, kN32_SkColorType, kPremul_SkAlphaType); |
| SkBitmap bitmap; |
| bitmap.allocPixels(image_info); |
| bitmap.setImmutable(); |
| ui_resource_layer->SetBitmap(bitmap); |
| |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT(pass->quad_list, |
| ElementsAre(AllOf(viz::IsTextureQuad(), viz::HasRect(viewport_), |
| viz::HasVisibleRect(viewport_), |
| viz::HasTransform(gfx::Transform())))); |
| const viz::TextureDrawQuad* texture_quad = |
| viz::TextureDrawQuad::MaterialCast(pass->quad_list.front()); |
| EXPECT_TRUE(texture_quad->needs_blending); |
| EXPECT_NE(viz::kInvalidResourceId, texture_quad->resource_id()); |
| EXPECT_EQ(gfx::PointF(0.0f, 0.0f), texture_quad->uv_top_left); |
| EXPECT_EQ(gfx::PointF(1.0f, 1.0f), texture_quad->uv_bottom_right); |
| EXPECT_EQ(1.0f, texture_quad->vertex_opacity[0]); |
| EXPECT_EQ(1.0f, texture_quad->vertex_opacity[1]); |
| EXPECT_EQ(1.0f, texture_quad->vertex_opacity[2]); |
| EXPECT_EQ(1.0f, texture_quad->vertex_opacity[3]); |
| |
| ASSERT_EQ(frame.resource_list.size(), 1u); |
| EXPECT_EQ(frame.resource_list[0].id, texture_quad->resource_id()); |
| EXPECT_EQ(frame.resource_list[0].size, gfx::Size(1, 1)); |
| first_resource_id = texture_quad->resource_id(); |
| |
| ASSERT_EQ(frame_sink_->uploaded_resources().size(), 1u); |
| EXPECT_EQ(frame_sink_->uploaded_resources().begin()->second.viz_resource_id, |
| texture_quad->resource_id()); |
| } |
| |
| ui_resource_layer->SetUV(gfx::PointF(0.25f, 0.25f), |
| gfx::PointF(0.75f, 0.75f)); |
| ui_resource_layer->SetVertexOpacity(0.1f, 0.2f, 0.3f, 0.4f); |
| { |
| auto image_info = |
| SkImageInfo::Make(2, 2, kN32_SkColorType, kPremul_SkAlphaType); |
| SkBitmap bitmap; |
| bitmap.allocPixels(image_info); |
| bitmap.setImmutable(); |
| ui_resource_layer->SetBitmap(bitmap); |
| |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT(pass->quad_list, |
| ElementsAre(AllOf(viz::IsTextureQuad(), viz::HasRect(viewport_), |
| viz::HasVisibleRect(viewport_), |
| viz::HasTransform(gfx::Transform())))); |
| const viz::TextureDrawQuad* texture_quad = |
| viz::TextureDrawQuad::MaterialCast(pass->quad_list.front()); |
| EXPECT_TRUE(texture_quad->needs_blending); |
| EXPECT_NE(viz::kInvalidResourceId, texture_quad->resource_id()); |
| EXPECT_EQ(gfx::PointF(0.25f, 0.25f), texture_quad->uv_top_left); |
| EXPECT_EQ(gfx::PointF(0.75f, 0.75f), texture_quad->uv_bottom_right); |
| EXPECT_EQ(0.1f, texture_quad->vertex_opacity[0]); |
| EXPECT_EQ(0.2f, texture_quad->vertex_opacity[1]); |
| EXPECT_EQ(0.3f, texture_quad->vertex_opacity[2]); |
| EXPECT_EQ(0.4f, texture_quad->vertex_opacity[3]); |
| |
| ASSERT_EQ(frame.resource_list.size(), 1u); |
| EXPECT_EQ(frame.resource_list[0].id, texture_quad->resource_id()); |
| EXPECT_EQ(frame.resource_list[0].size, gfx::Size(2, 2)); |
| EXPECT_NE(first_resource_id, texture_quad->resource_id()); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, ReclaimResources) { |
| constexpr size_t kNumLayers = 6; |
| std::vector<scoped_refptr<UIResourceLayer>> layers; |
| for (size_t i = 0; i < kNumLayers; ++i) { |
| layers.push_back(UIResourceLayer::Create()); |
| layers[i]->SetBounds(viewport_.size()); |
| layers[i]->SetIsDrawable(true); |
| if (i == 0u) { |
| layer_tree_->SetRoot(layers[i]); |
| } else { |
| layers[i - 1]->AddChild(layers[i]); |
| } |
| |
| auto image_info = |
| SkImageInfo::Make(1, 1, kN32_SkColorType, kPremul_SkAlphaType); |
| SkBitmap bitmap; |
| bitmap.allocPixels(image_info); |
| bitmap.setImmutable(); |
| layers[i]->SetBitmap(bitmap); |
| } |
| |
| viz::CompositorFrame frame = ProduceFrame(); |
| EXPECT_EQ(frame.resource_list.size(), kNumLayers); |
| for (size_t i = 0; i < kNumLayers; ++i) { |
| EXPECT_TRUE(frame_sink_->client_resource_provider()->InUseByConsumer( |
| frame.resource_list[i].id)); |
| } |
| |
| // Return every other resource. |
| std::vector<viz::ReturnedResource> returned_resources; |
| for (size_t i = 0; i < kNumLayers; i += 2) { |
| returned_resources.push_back(frame.resource_list[i].ToReturnedResource()); |
| } |
| frame_sink_->ReclaimResources(std::move(returned_resources)); |
| for (size_t i = 0; i < kNumLayers; i += 2) { |
| EXPECT_FALSE(frame_sink_->client_resource_provider()->InUseByConsumer( |
| frame.resource_list[i].id)); |
| } |
| for (size_t i = 1; i < kNumLayers; i += 2) { |
| EXPECT_TRUE(frame_sink_->client_resource_provider()->InUseByConsumer( |
| frame.resource_list[i].id)); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, NinePatchLayerAppendQuads) { |
| auto nine_patch_layer = NinePatchLayer::Create(); |
| nine_patch_layer->SetBounds(viewport_.size()); |
| nine_patch_layer->SetIsDrawable(true); |
| layer_tree_->SetRoot(nine_patch_layer); |
| |
| auto image_info = |
| SkImageInfo::Make(10, 10, kN32_SkColorType, kPremul_SkAlphaType); |
| SkBitmap bitmap; |
| bitmap.allocPixels(image_info); |
| bitmap.setImmutable(); |
| nine_patch_layer->SetBitmap(bitmap); |
| |
| nine_patch_layer->SetBorder(gfx::Rect(10, 10, 20, 20)); // 10 pixel border. |
| nine_patch_layer->SetAperture(gfx::Rect(2, 2, 6, 6)); |
| nine_patch_layer->SetFillCenter(true); |
| nine_patch_layer->SetNearestNeighbor(true); |
| |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.resource_list.size(), 1u); |
| EXPECT_EQ(frame.resource_list[0].size, gfx::Size(10, 10)); |
| ASSERT_EQ(frame_sink_->uploaded_resources().size(), 1u); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT( |
| pass->quad_list, |
| ElementsAre( |
| // Top left. |
| AllOf(viz::IsTextureQuad(), viz::HasRect(gfx::Rect(10, 10)), |
| viz::HasVisibleRect(gfx::Rect(10, 10))), |
| // Top right. |
| AllOf(viz::IsTextureQuad(), viz::HasRect(gfx::Rect(90, 0, 10, 10)), |
| viz::HasVisibleRect(gfx::Rect(90, 0, 10, 10))), |
| // Bottom left. |
| AllOf(viz::IsTextureQuad(), viz::HasRect(gfx::Rect(0, 90, 10, 10)), |
| viz::HasVisibleRect(gfx::Rect(0, 90, 10, 10))), |
| // Bottom right. |
| AllOf(viz::IsTextureQuad(), viz::HasRect(gfx::Rect(90, 90, 10, 10)), |
| viz::HasVisibleRect(gfx::Rect(90, 90, 10, 10))), |
| // Top. |
| AllOf(viz::IsTextureQuad(), viz::HasRect(gfx::Rect(10, 0, 80, 10)), |
| viz::HasVisibleRect(gfx::Rect(10, 0, 80, 10))), |
| // Left. |
| AllOf(viz::IsTextureQuad(), viz::HasRect(gfx::Rect(0, 10, 10, 80)), |
| viz::HasVisibleRect(gfx::Rect(0, 10, 10, 80))), |
| // Right. |
| AllOf(viz::IsTextureQuad(), viz::HasRect(gfx::Rect(90, 10, 10, 80)), |
| viz::HasVisibleRect(gfx::Rect(90, 10, 10, 80))), |
| // Bottom. |
| AllOf(viz::IsTextureQuad(), viz::HasRect(gfx::Rect(10, 90, 80, 10)), |
| viz::HasVisibleRect(gfx::Rect(10, 90, 80, 10))), |
| // Center. |
| AllOf(viz::IsTextureQuad(), |
| viz::HasRect(gfx::Rect(10, 10, 80, 80))))); |
| gfx::PointF expected_uv_top_left[] = { |
| gfx::PointF(0.0f, 0.0f), // Top left. |
| gfx::PointF(0.8f, 0.0f), // Top right. |
| gfx::PointF(0.0f, 0.8f), // Bottom left. |
| gfx::PointF(0.8f, 0.8f), // Bottom right. |
| gfx::PointF(0.2f, 0.0f), // Top. |
| gfx::PointF(0.0f, 0.2f), // Left. |
| gfx::PointF(0.8f, 0.2f), // Right. |
| gfx::PointF(0.2f, 0.8f), // Bottom. |
| gfx::PointF(0.2f, 0.2f), // Center. |
| }; |
| gfx::PointF expected_uv_bottom_right[] = { |
| gfx::PointF(0.2f, 0.2f), // Top left. |
| gfx::PointF(1.0f, 0.2f), // Top right. |
| gfx::PointF(0.2f, 1.0f), // Bottom left. |
| gfx::PointF(1.0f, 1.0f), // Bottom right. |
| gfx::PointF(0.8f, 0.2f), // Top. |
| gfx::PointF(0.2f, 0.8f), // Left. |
| gfx::PointF(1.0f, 0.8f), // Right. |
| gfx::PointF(0.8f, 1.0f), // Bottom. |
| gfx::PointF(0.8f, 0.8f), // Center. |
| }; |
| for (size_t i = 0; i < std::size(expected_uv_top_left); ++i) { |
| const viz::TextureDrawQuad* texture_quad = |
| viz::TextureDrawQuad::MaterialCast(pass->quad_list.ElementAt(i)); |
| EXPECT_NE(viz::kInvalidResourceId, texture_quad->resource_id()); |
| EXPECT_TRUE(texture_quad->nearest_neighbor); |
| EXPECT_EQ(expected_uv_top_left[i], texture_quad->uv_top_left); |
| EXPECT_EQ(expected_uv_bottom_right[i], texture_quad->uv_bottom_right); |
| EXPECT_EQ(1.0f, texture_quad->vertex_opacity[0]); |
| EXPECT_EQ(1.0f, texture_quad->vertex_opacity[1]); |
| EXPECT_EQ(1.0f, texture_quad->vertex_opacity[2]); |
| EXPECT_EQ(1.0f, texture_quad->vertex_opacity[3]); |
| |
| EXPECT_EQ(frame.resource_list[0].id, texture_quad->resource_id()); |
| EXPECT_EQ(frame_sink_->uploaded_resources().begin()->second.viz_resource_id, |
| texture_quad->resource_id()); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, SurfaceLayerAppendQuads) { |
| auto surface_layer = SurfaceLayer::Create(); |
| surface_layer->SetBounds(viewport_.size()); |
| surface_layer->SetIsDrawable(true); |
| layer_tree_->SetRoot(surface_layer); |
| |
| { |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT( |
| pass->quad_list, |
| ElementsAre(AllOf(viz::IsSolidColorQuad(), viz::HasRect(viewport_), |
| viz::HasVisibleRect(viewport_)))); |
| } |
| |
| base::UnguessableToken token = base::UnguessableToken::Create(); |
| viz::SurfaceId start(viz::FrameSinkId(1u, 2u), |
| viz::LocalSurfaceId(3u, 4u, token)); |
| { |
| viz::SurfaceId end(viz::FrameSinkId(1u, 2u), |
| viz::LocalSurfaceId(5u, 6u, token)); |
| cc::DeadlinePolicy deadline_policy = |
| cc::DeadlinePolicy::UseDefaultDeadline(); |
| surface_layer->SetOldestAcceptableFallback(start); |
| surface_layer->SetSurfaceId(end, deadline_policy); |
| |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT(pass->quad_list, |
| ElementsAre(AllOf(viz::IsSurfaceQuad(), viz::HasRect(viewport_), |
| viz::HasVisibleRect(viewport_)))); |
| |
| auto* quad = viz::SurfaceDrawQuad::MaterialCast(pass->quad_list.back()); |
| EXPECT_EQ(quad->surface_range, viz::SurfaceRange(start, end)); |
| EXPECT_FALSE(quad->stretch_content_to_fill_bounds); |
| EXPECT_FALSE(quad->is_reflection); |
| EXPECT_TRUE(quad->allow_merge); |
| |
| viz::CompositorFrameMetadata& metadata = frame.metadata; |
| EXPECT_EQ(metadata.referenced_surfaces, |
| std::vector<viz::SurfaceRange>{viz::SurfaceRange(start, end)}); |
| EXPECT_EQ(metadata.activation_dependencies, |
| std::vector<viz::SurfaceId>{end}); |
| EXPECT_FALSE(metadata.deadline.deadline_in_frames()); |
| EXPECT_TRUE(metadata.deadline.use_default_lower_bound_deadline()); |
| } |
| |
| { |
| viz::SurfaceId end(viz::FrameSinkId(1u, 2u), |
| viz::LocalSurfaceId(5u, 7u, token)); |
| cc::DeadlinePolicy deadline_policy = |
| cc::DeadlinePolicy::UseSpecifiedDeadline(2u); |
| surface_layer->SetSurfaceId(end, deadline_policy); |
| surface_layer->SetStretchContentToFillBounds(true); |
| |
| viz::CompositorFrame frame = ProduceFrame(); |
| ASSERT_EQ(frame.render_pass_list.size(), 1u); |
| auto& pass = frame.render_pass_list.back(); |
| ASSERT_THAT(pass->quad_list, |
| ElementsAre(AllOf(viz::IsSurfaceQuad(), viz::HasRect(viewport_), |
| viz::HasVisibleRect(viewport_)))); |
| |
| auto* quad = viz::SurfaceDrawQuad::MaterialCast(pass->quad_list.back()); |
| EXPECT_EQ(quad->surface_range, viz::SurfaceRange(start, end)); |
| EXPECT_TRUE(quad->stretch_content_to_fill_bounds); |
| |
| viz::CompositorFrameMetadata& metadata = frame.metadata; |
| EXPECT_EQ(metadata.referenced_surfaces, |
| std::vector<viz::SurfaceRange>{viz::SurfaceRange(start, end)}); |
| EXPECT_EQ(metadata.activation_dependencies, |
| std::vector<viz::SurfaceId>{end}); |
| EXPECT_EQ(metadata.deadline.deadline_in_frames(), 2u); |
| EXPECT_FALSE(metadata.deadline.use_default_lower_bound_deadline()); |
| } |
| } |
| |
| TEST_F(SlimLayerTreeCompositorFrameTest, SimpleHitTestRegionList) { |
| auto surface_layer = SurfaceLayer::Create(); |
| surface_layer->SetBounds(viewport_.size()); |
| surface_layer->SetIsDrawable(true); |
| layer_tree_->SetRoot(surface_layer); |
| |
| { |
| base::UnguessableToken token = base::UnguessableToken::Create(); |
| viz::SurfaceId surface_id(viz::FrameSinkId(1u, 2u), |
| viz::LocalSurfaceId(3u, 4u, token)); |
| cc::DeadlinePolicy deadline_policy = |
| cc::DeadlinePolicy::UseDefaultDeadline(); |
| surface_layer->SetSurfaceId(surface_id, deadline_policy); |
| |
| absl::optional<viz::HitTestRegionList> hit_test_region_list; |
| viz::CompositorFrame frame = ProduceFrame(&hit_test_region_list); |
| ASSERT_TRUE(hit_test_region_list); |
| EXPECT_EQ(hit_test_region_list->bounds, viewport_); |
| |
| ASSERT_EQ(hit_test_region_list->regions.size(), 1u); |
| auto& hit_test_region = hit_test_region_list->regions.front(); |
| EXPECT_EQ(hit_test_region.frame_sink_id, viz::FrameSinkId(1u, 2u)); |
| EXPECT_EQ(hit_test_region.rect, viewport_); |
| EXPECT_EQ(hit_test_region.transform, gfx::Transform()); |
| } |
| |
| auto child_surface_layer = SurfaceLayer::Create(); |
| surface_layer->AddChild(child_surface_layer); |
| child_surface_layer->SetBounds(gfx::Size(10, 10)); |
| child_surface_layer->SetIsDrawable(true); |
| child_surface_layer->SetPosition(gfx::PointF(10.0f, 10.0f)); |
| child_surface_layer->SetTransformOrigin(gfx::Point3F(5.0f, 5.0f, 0.0f)); |
| gfx::Transform transform; |
| transform.Rotate(45.0); |
| child_surface_layer->SetTransform(transform); |
| |
| base::UnguessableToken token = base::UnguessableToken::Create(); |
| viz::SurfaceId surface_id(viz::FrameSinkId(2u, 3u), |
| viz::LocalSurfaceId(4u, 5u, token)); |
| cc::DeadlinePolicy deadline_policy = cc::DeadlinePolicy::UseDefaultDeadline(); |
| child_surface_layer->SetSurfaceId(surface_id, deadline_policy); |
| |
| { |
| absl::optional<viz::HitTestRegionList> hit_test_region_list; |
| viz::CompositorFrame frame = ProduceFrame(&hit_test_region_list); |
| |
| ASSERT_TRUE(hit_test_region_list); |
| EXPECT_EQ(hit_test_region_list->bounds, viewport_); |
| |
| ASSERT_EQ(hit_test_region_list->regions.size(), 2u); |
| auto& root_region = hit_test_region_list->regions.back(); |
| EXPECT_EQ(root_region.frame_sink_id, viz::FrameSinkId(1u, 2u)); |
| EXPECT_EQ(root_region.rect, viewport_); |
| EXPECT_EQ(root_region.transform, gfx::Transform()); |
| |
| auto& child_region = hit_test_region_list->regions.front(); |
| EXPECT_EQ(child_region.frame_sink_id, viz::FrameSinkId(2u, 3u)); |
| EXPECT_EQ(child_region.rect, gfx::Rect(10, 10)); |
| |
| gfx::Transform expected_transform = |
| gfx::Transform::MakeTranslation(5.0f, 5.0f); |
| expected_transform.Rotate(-45.0); |
| expected_transform.Translate(-5.0f, -5.0f); |
| expected_transform.Translate(-10.0f, -10.0f); |
| |
| EXPECT_TRANSFORM_NEAR(child_region.transform, expected_transform, 1e-15); |
| EXPECT_TRUE(child_region.flags | viz::HitTestRegionFlags::kHitTestAsk); |
| EXPECT_TRUE(child_region.async_hit_test_reasons | |
| viz::AsyncHitTestReasons::kIrregularClip); |
| } |
| } |
| |
| } // namespace |
| |
| } // namespace cc::slim |