| // 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/display/overlay_proposed_candidate.h" |
| |
| #include <tuple> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "components/viz/client/client_resource_provider.h" |
| #include "components/viz/common/quads/texture_draw_quad.h" |
| #include "components/viz/common/resources/shared_image_format.h" |
| #include "components/viz/service/display/display_resource_provider_null.h" |
| #include "components/viz/service/display/overlay_candidate_factory.h" |
| #include "components/viz/service/display/overlay_processor_strategy.h" |
| #include "components/viz/test/test_context_provider.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace viz { |
| namespace { |
| |
| using RoundedDisplayMasksInfo = TextureDrawQuad::RoundedDisplayMasksInfo; |
| |
| const auto kTestQuadRect = gfx::Rect(0, 0, 100, 100); |
| |
| class TestOverlayStrategy : public OverlayProcessorStrategy { |
| public: |
| TestOverlayStrategy() = default; |
| |
| TestOverlayStrategy(const TestOverlayStrategy&) = delete; |
| TestOverlayStrategy& operator=(const TestOverlayStrategy&) = delete; |
| |
| ~TestOverlayStrategy() override = default; |
| |
| void Propose( |
| const SkM44& output_color_matrix, |
| const OverlayProcessorInterface::FilterOperationsMap& render_pass_filters, |
| const OverlayProcessorInterface::FilterOperationsMap& |
| render_pass_backdrop_filters, |
| DisplayResourceProvider* resource_provider, |
| AggregatedRenderPassList* render_pass_list, |
| SurfaceDamageRectList* surface_damage_rect_list, |
| const PrimaryPlane* primary_plane, |
| std::vector<OverlayProposedCandidate>* candidates, |
| std::vector<gfx::Rect>* content_bounds) override {} |
| |
| bool Attempt( |
| const SkM44& output_color_matrix, |
| const OverlayProcessorInterface::FilterOperationsMap& render_pass_filters, |
| const OverlayProcessorInterface::FilterOperationsMap& |
| render_pass_backdrop_filters, |
| DisplayResourceProvider* resource_provider, |
| AggregatedRenderPassList* render_pass_list, |
| SurfaceDamageRectList* surface_damage_rect_list, |
| const PrimaryPlane* primary_plane, |
| OverlayCandidateList* candidates, |
| std::vector<gfx::Rect>* content_bounds, |
| const OverlayProposedCandidate& proposed_candidate) override { |
| return true; |
| } |
| |
| void CommitCandidate(const OverlayProposedCandidate& proposed_candidate, |
| AggregatedRenderPass* render_pass) override {} |
| }; |
| |
| // TODO(zoraiznaeem): Move resource creation code into OverlayTestBase class. |
| class OverlayProposedCandidateTest |
| : public testing::Test, |
| public ::testing::WithParamInterface< |
| std::tuple<RoundedDisplayMasksInfo, gfx::Rect, gfx::Rect>> { |
| public: |
| OverlayProposedCandidateTest() |
| : mask_info_(std::get<0>(GetParam())), |
| expected_origin_mask_bounds_(std::get<1>(GetParam())), |
| expected_other_mask_bounds_(std::get<2>(GetParam())) {} |
| |
| OverlayProposedCandidateTest(const OverlayProposedCandidateTest&) = delete; |
| OverlayProposedCandidateTest& operator=(const OverlayProposedCandidateTest&) = |
| delete; |
| |
| ~OverlayProposedCandidateTest() override = default; |
| |
| protected: |
| void TearDown() override { |
| child_resource_provider_.ReleaseAllExportedResources(true); |
| } |
| |
| ResourceId CreateResource(bool is_overlay_candidate) { |
| scoped_refptr<ContextProvider> child_context_provider = |
| TestContextProvider::Create(); |
| |
| child_context_provider->BindToCurrentSequence(); |
| |
| auto resource = TransferableResource::MakeGpu( |
| gpu::Mailbox::GenerateForSharedImage(), GL_TEXTURE_2D, gpu::SyncToken(), |
| gfx::Size(1, 1), SinglePlaneFormat::kRGBA_8888, is_overlay_candidate); |
| |
| ResourceId resource_id = |
| child_resource_provider_.ImportResource(resource, base::DoNothing()); |
| |
| int child_id = |
| resource_provider_.CreateChild(base::DoNothing(), SurfaceId()); |
| |
| // Transfer resource to the parent. |
| std::vector<ResourceId> resource_ids_to_transfer; |
| resource_ids_to_transfer.push_back(resource_id); |
| std::vector<TransferableResource> list; |
| child_resource_provider_.PrepareSendToParent( |
| resource_ids_to_transfer, &list, child_context_provider.get()); |
| resource_provider_.ReceiveFromChild(child_id, list); |
| |
| // Delete it in the child so it won't be leaked, and will be released once |
| // returned from the parent. |
| child_resource_provider_.RemoveImportedResource(resource_id); |
| |
| // In DisplayResourceProvider's namespace, use the mapped resource id. |
| std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map = |
| resource_provider_.GetChildToParentMap(child_id); |
| |
| return resource_map[list[0].id]; |
| } |
| |
| void AddQuadWithRoundedDisplayMasks( |
| gfx::Rect quad_rect, |
| bool is_overlay_candidate, |
| const gfx::Transform& quad_to_target_transform, |
| const RoundedDisplayMasksInfo& rounded_display_masks_info, |
| AggregatedRenderPass* render_pass) { |
| SharedQuadState* quad_state = render_pass->CreateAndAppendSharedQuadState(); |
| |
| quad_state->SetAll( |
| /*transform=*/quad_to_target_transform, quad_rect, |
| /*visible_layer_rect=*/quad_rect, |
| /*filter_info=*/gfx::MaskFilterInfo(), |
| /*clip=*/absl::nullopt, |
| /*are contents opaque=*/true, |
| /*opacity_f=*/1.f, |
| /*blend=*/SkBlendMode::kSrcOver, /*sorting_context=*/0); |
| |
| TextureDrawQuad* texture_quad = |
| render_pass->CreateAndAppendDrawQuad<TextureDrawQuad>(); |
| constexpr float kVertexOpacity[4] = {1.0f, 1.0f, 1.0f, 1.0f}; |
| |
| texture_quad->SetNew( |
| quad_state, quad_rect, quad_rect, |
| /*needs_blending=*/true, CreateResource(is_overlay_candidate), |
| /*premultiplied=*/true, gfx::PointF(), gfx::PointF(), |
| /*background=*/SkColors::kTransparent, kVertexOpacity, |
| /*flipped=*/false, |
| /*nearest=*/false, |
| /*secure_output=*/false, gfx::ProtectedVideoType::kClear); |
| |
| texture_quad->rounded_display_masks_info = rounded_display_masks_info; |
| } |
| |
| OverlayCandidateFactory CreateCandidateFactory( |
| const AggregatedRenderPass& render_pass, |
| const gfx::RectF& primary_rect, |
| bool has_clip_support = true, |
| bool has_arbitrary_transform_support = false, |
| bool supports_rounded_display_masks = true) { |
| return OverlayCandidateFactory( |
| &render_pass, &resource_provider_, &surface_damage_list_, &identity_, |
| primary_rect, &render_pass_filters_, /*is_delegated_context=*/false, |
| has_clip_support, has_arbitrary_transform_support, |
| supports_rounded_display_masks); |
| } |
| |
| ClientResourceProvider child_resource_provider_; |
| DisplayResourceProviderNull resource_provider_; |
| SurfaceDamageRectList surface_damage_list_; |
| SkM44 identity_; |
| OverlayProcessorInterface::FilterOperationsMap render_pass_filters_; |
| |
| RoundedDisplayMasksInfo mask_info_; |
| gfx::Rect expected_origin_mask_bounds_; |
| gfx::Rect expected_other_mask_bounds_; |
| }; |
| |
| TEST_P(OverlayProposedCandidateTest, CorrectRoundedDisplayMaskBounds) { |
| AggregatedRenderPass render_pass; |
| render_pass.SetNew(AggregatedRenderPassId::FromUnsafeValue(1), |
| gfx::Rect(0, 0, 1, 1), gfx::Rect(), gfx::Transform()); |
| |
| gfx::Transform identity; |
| identity.MakeIdentity(); |
| |
| AddQuadWithRoundedDisplayMasks(kTestQuadRect, |
| /*is_overlay_candidate=*/true, identity, |
| mask_info_, &render_pass); |
| |
| OverlayCandidateFactory factory = |
| CreateCandidateFactory(render_pass, gfx::RectF(render_pass.output_rect)); |
| |
| OverlayCandidate candidate; |
| OverlayCandidateFactory::CandidateStatus status = |
| factory.FromDrawQuad(*render_pass.quad_list.begin(), candidate); |
| ASSERT_EQ(status, OverlayCandidateFactory::CandidateStatus::kSuccess); |
| |
| TestOverlayStrategy strategy; |
| OverlayProposedCandidate proposed_candidate(render_pass.quad_list.begin(), |
| candidate, &strategy); |
| |
| auto mask_bounds = OverlayProposedCandidate::GetRoundedDisplayMasksBounds( |
| proposed_candidate); |
| |
| EXPECT_EQ( |
| mask_bounds[RoundedDisplayMasksInfo::kOriginRoundedDisplayMaskIndex], |
| expected_origin_mask_bounds_); |
| EXPECT_EQ(mask_bounds[RoundedDisplayMasksInfo::kOtherRoundedDisplayMaskIndex], |
| expected_other_mask_bounds_); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| /*no_prefix*/, |
| OverlayProposedCandidateTest, |
| testing::Values( |
| std::make_tuple( |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo( |
| /*origin_rounded_display_mask_radius=*/10, |
| /*other_rounded_display_mask_radius=*/15, |
| /*is_horizontally_positioned=*/true), |
| /*expected_origin_mask_bounds=*/gfx::Rect(0, 0, 10, 10), |
| /*expected_other_mask_bounds=*/gfx::Rect(85, 0, 15, 15)), |
| std::make_tuple( |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo( |
| /*origin_rounded_display_mask_radius=*/10, |
| /*other_rounded_display_mask_radius=*/15, |
| /*is_horizontally_positioned=*/false), |
| /*expected_origin_mask_bounds=*/gfx::Rect(0, 0, 10, 10), |
| /*expected_other_mask_bounds=*/gfx::Rect(0, 85, 15, 15)), |
| std::make_tuple( |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo( |
| /*origin_rounded_display_mask_radius=*/0, |
| /*other_rounded_display_mask_radius=*/15, |
| /*is_horizontally_positioned=*/false), |
| /*expected_origin_mask_bounds=*/gfx::Rect(), |
| /*expected_other_mask_bounds=*/gfx::Rect(0, 85, 15, 15)), |
| std::make_tuple(RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo( |
| /*origin_rounded_display_mask_radius=*/10, |
| /*other_rounded_display_mask_radius=*/0, |
| /*is_horizontally_positioned=*/false), |
| /*expected_origin_mask_bounds=*/gfx::Rect(0, 0, 10, 10), |
| /*expected_other_mask_bounds=*/gfx::Rect()), |
| std::make_tuple(RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo( |
| /*origin_rounded_display_mask_radius=*/0, |
| /*other_rounded_display_mask_radius=*/0, |
| /*is_horizontally_positioned=*/false), |
| /*expected_origin_mask_bounds=*/gfx::Rect(), |
| /*expected_other_mask_bounds=*/gfx::Rect()))); |
| |
| } // namespace |
| } // namespace viz |