| // 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 "ash/rounded_display/rounded_display_frame_factory.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/frame_sink/ui_resource_manager.h" |
| #include "ash/rounded_display/rounded_display_gutter.h" |
| #include "ash/rounded_display/rounded_display_gutter_factory.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/test/ash_test_helper.h" |
| #include "components/viz/common/quads/compositor_frame.h" |
| #include "components/viz/common/quads/quad_list.h" |
| #include "components/viz/common/quads/texture_draw_quad.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/aura/window_tree_host.h" |
| #include "ui/compositor/layer_type.h" |
| #include "ui/gfx/geometry/rounded_corners_f.h" |
| |
| namespace ash { |
| namespace { |
| |
| constexpr viz::SharedImageFormat kTestSharedImageFormat = |
| SK_B32_SHIFT ? viz::SinglePlaneFormat::kRGBA_8888 |
| : viz::SinglePlaneFormat::kBGRA_8888; |
| constexpr gfx::Size kTestDisplaySize(1920, 1080); |
| constexpr gfx::RoundedCornersF kTestPanelRadii(10); |
| |
| using RoundedDisplayMasksInfo = viz::TextureDrawQuad::RoundedDisplayMasksInfo; |
| |
| class RoundedDisplayFrameFactoryTest : public AshTestBase { |
| public: |
| RoundedDisplayFrameFactoryTest() = default; |
| |
| RoundedDisplayFrameFactoryTest(const RoundedDisplayFrameFactoryTest&) = |
| delete; |
| RoundedDisplayFrameFactoryTest& operator=( |
| const RoundedDisplayFrameFactoryTest&) = delete; |
| |
| ~RoundedDisplayFrameFactoryTest() override = default; |
| |
| // AshTestBase: |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| gutter_factory_ = std::make_unique<RoundedDisplayGutterFactory>(); |
| frame_factory_ = std::make_unique<RoundedDisplayFrameFactory>(); |
| |
| host_window_ = std::make_unique<aura::Window>(/*delegate*/ nullptr); |
| host_window_->Init(ui::LayerType::LAYER_SOLID_COLOR); |
| |
| auto* root_window = ash_test_helper()->GetHost()->window(); |
| root_window->AddChild(host_window_.get()); |
| } |
| |
| // AshTestBase: |
| void TearDown() override { |
| auto* root_window = ash_test_helper()->GetHost()->window(); |
| root_window->RemoveChild(host_window_.get()); |
| resource_manager_.LostExportedResources(); |
| resource_manager_.ClearAvailableResources(); |
| AshTestBase::TearDown(); |
| } |
| |
| const std::vector<RoundedDisplayGutter*> GetGutters() { |
| std::vector<RoundedDisplayGutter*> gutters; |
| gutters.reserve(gutters_.size()); |
| |
| for (const auto& entry : gutters_) { |
| gutters.push_back(entry.get()); |
| } |
| |
| return gutters; |
| } |
| |
| // Creates vertical gutters and appends them to `gutters_`. |
| void AppendVerticalOverlayGutters(const gfx::Size& display_size_in_pixels, |
| const gfx::RoundedCornersF& panel_radii) { |
| auto overlay_gutters = gutter_factory_->CreateOverlayGutters( |
| display_size_in_pixels, panel_radii, |
| /*create_vertical_gutters=*/true); |
| |
| for (auto& gutter : overlay_gutters) { |
| gutters_.push_back(std::move(gutter)); |
| } |
| } |
| |
| // Creates horizontal gutters and appends them to `gutters_`. |
| void AppendHorizontalOverlayGutters(const gfx::Size& display_size_in_pixels, |
| const gfx::RoundedCornersF& panel_radii) { |
| auto overlay_gutters = gutter_factory_->CreateOverlayGutters( |
| display_size_in_pixels, panel_radii, |
| /*create_vertical_gutters=*/false); |
| |
| for (auto& gutter : overlay_gutters) { |
| gutters_.push_back(std::move(gutter)); |
| } |
| } |
| |
| protected: |
| std::unique_ptr<RoundedDisplayGutterFactory> gutter_factory_; |
| std::unique_ptr<RoundedDisplayFrameFactory> frame_factory_; |
| std::vector<std::unique_ptr<RoundedDisplayGutter>> gutters_; |
| UiResourceManager resource_manager_; |
| std::unique_ptr<aura::Window> host_window_; |
| }; |
| |
| // TODO(zoraiznaeem): Add more unittest coverage. |
| TEST_F(RoundedDisplayFrameFactoryTest, CompositorFrameHasCorrectStructure) { |
| AppendVerticalOverlayGutters(kTestDisplaySize, kTestPanelRadii); |
| |
| const auto& gutters = GetGutters(); |
| |
| auto frame = frame_factory_->CreateCompositorFrame( |
| viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_, |
| resource_manager_, gutters); |
| |
| // We should only have the root render pass. |
| EXPECT_EQ(frame->render_pass_list.size(), 1u); |
| |
| EXPECT_EQ(frame->size_in_pixels(), GetPrimaryDisplay().GetSizeInPixel()); |
| |
| // We should have a resource for each gutter. |
| EXPECT_EQ(frame->resource_list.size(), gutters.size()); |
| EXPECT_EQ(resource_manager_.exported_resources_count(), gutters.size()); |
| |
| auto& quad_list = frame->render_pass_list.front()->quad_list; |
| |
| // We should have created a draw quad for each gutter. |
| EXPECT_EQ(quad_list.size(), gutters.size()); |
| |
| auto& shared_quad_state_list = |
| frame->render_pass_list.front()->shared_quad_state_list; |
| |
| // We should create a shared_quad_state for each draw quad. |
| EXPECT_EQ(shared_quad_state_list.size(), gutters.size()); |
| } |
| |
| MATCHER_P(IsRoundedDisplayMasksInfoEqual, value, "") { |
| return arg.is_horizontally_positioned == value.is_horizontally_positioned && |
| arg.radii[RoundedDisplayMasksInfo::kOriginRoundedDisplayMaskIndex] == |
| value.radii |
| [RoundedDisplayMasksInfo::kOriginRoundedDisplayMaskIndex] && |
| arg.radii[RoundedDisplayMasksInfo::kOtherRoundedDisplayMaskIndex] == |
| value |
| .radii[RoundedDisplayMasksInfo::kOtherRoundedDisplayMaskIndex]; |
| } |
| |
| TEST_F(RoundedDisplayFrameFactoryTest, |
| CorrectRoundedDisplayInfo_VerticalGuttersWithTwoCorners) { |
| const auto panel_radii = gfx::RoundedCornersF(10, 0, 0, 15); |
| AppendVerticalOverlayGutters(kTestDisplaySize, panel_radii); |
| |
| // `gutter_factory_` will only create left overlay gutter. |
| EXPECT_EQ(gutters_.size(), 1u); |
| |
| auto frame = frame_factory_->CreateCompositorFrame( |
| viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_, |
| resource_manager_, GetGutters()); |
| |
| const viz::QuadList& quad_list = frame->render_pass_list.front()->quad_list; |
| ASSERT_EQ(quad_list.size(), 1u); |
| |
| EXPECT_THAT(viz::TextureDrawQuad::MaterialCast(quad_list.ElementAt(0)) |
| ->rounded_display_masks_info, |
| IsRoundedDisplayMasksInfoEqual( |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo( |
| /*origin_rounded_display_mask_radius=*/10, |
| /*other_rounded_display_mask_radius=*/15, |
| /*is_horizontally_positioned=*/false))); |
| } |
| |
| TEST_F(RoundedDisplayFrameFactoryTest, |
| CorrectRoundedDisplayInfo_HorizontalGuttersWithTwoCorners) { |
| const auto panel_radii = gfx::RoundedCornersF(15, 10, 0, 0); |
| AppendHorizontalOverlayGutters(kTestDisplaySize, panel_radii); |
| |
| // `gutter_factory_` will only create upper overlay gutter. |
| EXPECT_EQ(gutters_.size(), 1u); |
| |
| auto frame = frame_factory_->CreateCompositorFrame( |
| viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_, |
| resource_manager_, GetGutters()); |
| |
| const viz::QuadList& quad_list = frame->render_pass_list.front()->quad_list; |
| ASSERT_EQ(quad_list.size(), 1u); |
| |
| EXPECT_THAT(viz::TextureDrawQuad::MaterialCast(quad_list.ElementAt(0)) |
| ->rounded_display_masks_info, |
| IsRoundedDisplayMasksInfoEqual( |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo( |
| /*origin_rounded_display_mask_radius=*/15, |
| /*other_rounded_display_mask_radius=*/10, |
| /*is_horizontally_positioned=*/true))); |
| } |
| |
| TEST_F(RoundedDisplayFrameFactoryTest, |
| CorrectRoundedDisplayInfo_GuttersWithOneCorner) { |
| const auto panel_radii = gfx::RoundedCornersF(10, 0, 0, 0); |
| AppendHorizontalOverlayGutters(kTestDisplaySize, panel_radii); |
| |
| // `gutter_factory_` will only create upper overlay gutter. |
| EXPECT_EQ(gutters_.size(), 1u); |
| |
| auto frame = frame_factory_->CreateCompositorFrame( |
| viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_, |
| resource_manager_, GetGutters()); |
| |
| const viz::QuadList& quad_list = frame->render_pass_list.front()->quad_list; |
| ASSERT_EQ(quad_list.size(), 1u); |
| |
| EXPECT_THAT(viz::TextureDrawQuad::MaterialCast(quad_list.ElementAt(0)) |
| ->rounded_display_masks_info, |
| IsRoundedDisplayMasksInfoEqual( |
| RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo( |
| /*origin_rounded_display_mask_radius=*/10, |
| /*other_rounded_display_mask_radius=*/0, |
| /*is_horizontally_positioned=*/true))); |
| } |
| |
| TEST_F(RoundedDisplayFrameFactoryTest, OnlyCreateNewResourcesWhenNecessary) { |
| AppendVerticalOverlayGutters(kTestDisplaySize, kTestPanelRadii); |
| |
| const auto& gutters = GetGutters(); |
| |
| // Populate resources in the resource manager. |
| for (const auto* gutter : gutters) { |
| resource_manager_.OfferResource( |
| RoundedDisplayFrameFactory::CreateUiResource(gutter->bounds().size(), |
| kTestSharedImageFormat, |
| gutter->ui_source_id(), |
| /*is_overlay=*/false)); |
| } |
| |
| EXPECT_EQ(resource_manager_.available_resources_count(), 2u); |
| |
| frame_factory_->CreateCompositorFrame( |
| viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_, |
| resource_manager_, gutters); |
| |
| // Should have reused all the resources. |
| EXPECT_EQ(resource_manager_.available_resources_count(), 0u); |
| // Should have exported two resources as we have two gutters. |
| EXPECT_EQ(resource_manager_.exported_resources_count(), 2u); |
| |
| resource_manager_.LostExportedResources(); |
| |
| // Adding more resources. |
| for (int index : {0, 0}) { |
| const auto* gutter = gutters.at(index); |
| resource_manager_.OfferResource( |
| RoundedDisplayFrameFactory::CreateUiResource(gutter->bounds().size(), |
| kTestSharedImageFormat, |
| gutter->ui_source_id(), |
| /*is_overlay=*/false)); |
| } |
| |
| EXPECT_EQ(resource_manager_.available_resources_count(), 2u); |
| |
| frame_factory_->CreateCompositorFrame( |
| viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_, |
| resource_manager_, gutters); |
| |
| // We end up using the available resources and are left with the extra |
| // resource that was available. We also must have created resources for |
| // gutter for which we did not have any available resources. |
| EXPECT_EQ(resource_manager_.available_resources_count(), 1u); |
| |
| // Should have exported two resources as we have two gutters. |
| EXPECT_EQ(resource_manager_.exported_resources_count(), 2u); |
| } |
| |
| } // namespace |
| } // namespace ash |