910e62b5创建于 1月15日历史提交
// 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/frame_sink/ui_resource_manager.h"

#include <memory>
#include <vector>

#include "ash/frame_sink/ui_resource.h"
#include "base/test/gtest_util.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "gpu/command_buffer/client/test_shared_image_interface.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "ui/gfx/geometry/size.h"

namespace ash {
namespace {

constexpr UiSourceId kTestUiSourceId_1 = 1u;
constexpr UiSourceId kTestUiSourceId_2 = 2u;
constexpr gfx::Size kDefaultSize(20, 20);

class UiResourceManagerTest : public testing::Test {
 public:
  UiResourceManagerTest() = default;
  UiResourceManagerTest(const UiResourceManagerTest&) = delete;
  UiResourceManagerTest& operator=(const UiResourceManagerTest&) = delete;

 protected:
  std::unique_ptr<UiResource> MakeResource(
      const gfx::Size& resource_size = kDefaultSize,
      viz::SharedImageFormat format = viz::SinglePlaneFormat::kBGRA_8888,
      UiSourceId ui_source_id = kTestUiSourceId_1) {
    auto shared_image = sii_->CreateSharedImage(
        {format, resource_size, gfx::ColorSpace(),
         gpu::SHARED_IMAGE_USAGE_DISPLAY_READ, "FastInkRootViewFrame"},
        gpu::kNullSurfaceHandle);
    auto resource = std::make_unique<UiResource>(sii_, std::move(shared_image));
    resource->ui_source_id = ui_source_id;
    return resource;
  }

  void SetUp() override {
    sii_ = base::MakeRefCounted<gpu::TestSharedImageInterface>();
    resource_manager_ = std::make_unique<UiResourceManager>();
  }

  void TearDown() override { resource_manager_->LostExportedResources(); }

  scoped_refptr<gpu::SharedImageInterface> sii_;
  std::unique_ptr<UiResourceManager> resource_manager_;
};

TEST_F(UiResourceManagerTest, ReuseResource_NoResources) {
  auto resource = resource_manager_->GetResourceToReuse(
      gfx::Size(100, 100), viz::SinglePlaneFormat::kBGRA_8888,
      kTestUiSourceId_1);

  EXPECT_FALSE(resource);
}

TEST_F(UiResourceManagerTest, ReuseResource) {
  resource_manager_->OfferResourceForTesting(
      MakeResource(gfx::Size(10, 10), viz::SinglePlaneFormat::kBGRA_8888,
                   kTestUiSourceId_1));

  resource_manager_->OfferResourceForTesting(MakeResource(
      kDefaultSize, viz::SinglePlaneFormat::kBGRA_8888, kTestUiSourceId_1));

  resource_manager_->OfferResourceForTesting(
      MakeResource(gfx::Size(10, 20), viz::SinglePlaneFormat::kBGRA_8888,
                   kTestUiSourceId_2));

  resource_manager_->OfferResourceForTesting(
      MakeResource(gfx::Size(10, 20), viz::SinglePlaneFormat::kBGRA_8888,
                   kTestUiSourceId_2));

  EXPECT_EQ(resource_manager_->available_resources_count(), 4u);

  // When we have no match in the currently available resources.
  auto resource = resource_manager_->GetResourceToReuse(
      gfx::Size(100, 100), viz::SinglePlaneFormat::kBGRA_8888,
      kTestUiSourceId_1);

  EXPECT_FALSE(resource);

  // When we have the requested resource.
  resource = resource_manager_->GetResourceToReuse(
      gfx::Size(10, 10), viz::SinglePlaneFormat::kBGRA_8888, kTestUiSourceId_1);

  EXPECT_TRUE(resource);

  EXPECT_EQ(resource->ui_source_id, kTestUiSourceId_1);
  EXPECT_EQ(resource->client_shared_image()->format(),
            viz::SinglePlaneFormat::kBGRA_8888);
  EXPECT_EQ(resource->client_shared_image()->size(), gfx::Size(10, 10));

  // When we have multiple matching resources, return any matching resource.
  resource = resource_manager_->GetResourceToReuse(
      gfx::Size(10, 20), viz::SinglePlaneFormat::kBGRA_8888, kTestUiSourceId_2);

  EXPECT_TRUE(resource);

  EXPECT_EQ(resource->ui_source_id, kTestUiSourceId_2);
  EXPECT_EQ(resource->client_shared_image()->format(),
            viz::SinglePlaneFormat::kBGRA_8888);
  EXPECT_EQ(resource->client_shared_image()->size(), gfx::Size(10, 20));
}

TEST_F(UiResourceManagerTest, OfferResource) {
  resource_manager_->OfferResourceForTesting(MakeResource());
  resource_manager_->OfferResourceForTesting(MakeResource());

  // As soon as we offer a resource, it is available to be used.
  EXPECT_EQ(resource_manager_->available_resources_count(), 2u);
}

using UiResourceManagerDeathTest = UiResourceManagerTest;
TEST_F(UiResourceManagerDeathTest,
       NeedToClearAllExportedResourceBeforeDeletingManager) {
  resource_manager_->OfferResourceForTesting(MakeResource());
  resource_manager_->OfferAndPrepareResourceForExport(
      MakeResource(kDefaultSize));

  // The manager cannot be deleted as we still have a exported resource.
  EXPECT_DCHECK_DEATH({ resource_manager_.reset(); });
}

TEST_F(UiResourceManagerTest, PrepareResourceForExporting) {
  resource_manager_->OfferResourceForTesting(MakeResource());
  resource_manager_->OfferResourceForTesting(MakeResource());

  EXPECT_EQ(resource_manager_->exported_resources_count(), 0u);
  EXPECT_EQ(resource_manager_->available_resources_count(), 2u);

  viz::TransferableResource transferable_resource =
      resource_manager_->OfferAndPrepareResourceForExport(
          MakeResource(kDefaultSize));

  // We exported one resources leaving two resources as available.
  EXPECT_EQ(resource_manager_->exported_resources_count(), 1u);
  EXPECT_EQ(resource_manager_->available_resources_count(), 2u);
}

TEST_F(UiResourceManagerTest, CannotReuseExportedResourcesTillReclaimed) {
  const gfx::Size kDefaultSize2(10, 10);
  resource_manager_->OfferResourceForTesting(MakeResource(kDefaultSize2));
  EXPECT_EQ(resource_manager_->available_resources_count(), 1u);

  viz::TransferableResource transferable_resource =
      resource_manager_->OfferAndPrepareResourceForExport(
          MakeResource(kDefaultSize));

  // We cannot release an exported resource until the resource is reclaimed.
  auto released_resource = resource_manager_->GetResourceToReuse(
      kDefaultSize, viz::SinglePlaneFormat::kBGRA_8888, kTestUiSourceId_1);

  EXPECT_FALSE(released_resource);
  EXPECT_EQ(resource_manager_->exported_resources_count(), 1u);

  std::vector<viz::ReturnedResource> returned;
  returned.emplace_back();
  returned.back().id = transferable_resource.id;
  returned.back().count = 1;
  returned.back().lost = false;

  resource_manager_->ReclaimResources(returned);

  EXPECT_EQ(resource_manager_->exported_resources_count(), 0u);
  EXPECT_EQ(resource_manager_->available_resources_count(), 2u);

  // Now that we reclaimed the exported resource, we can now reuse it.
  released_resource = resource_manager_->GetResourceToReuse(
      kDefaultSize, viz::SinglePlaneFormat::kBGRA_8888, kTestUiSourceId_1);
  EXPECT_TRUE(released_resource);
  EXPECT_EQ(resource_manager_->available_resources_count(), 1u);
}

TEST_F(UiResourceManagerTest, ExportedResourcesAreLost) {
  resource_manager_->OfferResourceForTesting(MakeResource());
  resource_manager_->OfferResourceForTesting(MakeResource());
  resource_manager_->OfferResourceForTesting(MakeResource());

  resource_manager_->OfferAndPrepareResourceForExport(
      MakeResource(kDefaultSize));
  resource_manager_->OfferAndPrepareResourceForExport(
      MakeResource(kDefaultSize));

  EXPECT_EQ(resource_manager_->available_resources_count(), 3u);
  EXPECT_EQ(resource_manager_->exported_resources_count(), 2u);

  // If there is no chance to reclaim back the resources we need to clear the
  // exported pool.
  resource_manager_->LostExportedResources();

  EXPECT_EQ(resource_manager_->available_resources_count(), 3u);
  EXPECT_EQ(resource_manager_->exported_resources_count(), 0u);
}

TEST_F(UiResourceManagerTest, ReclaimResources) {
  auto resource1 = resource_manager_->OfferAndPrepareResourceForExport(
      MakeResource(kDefaultSize));
  auto resource2 = resource_manager_->OfferAndPrepareResourceForExport(
      MakeResource(kDefaultSize));
  {
    // Returning a non-lost resource.
    std::vector<viz::ReturnedResource> returned;
    returned.emplace_back();
    returned.back().id = resource2.id;
    returned.back().count = 1;
    returned.back().lost = false;

    EXPECT_EQ(resource_manager_->exported_resources_count(), 2u);
    resource_manager_->ReclaimResources(returned);
    EXPECT_EQ(resource_manager_->exported_resources_count(), 1u);

    // Reclaimed resource is now available to be reused.
    EXPECT_EQ(resource_manager_->available_resources_count(), 1u);
  }

  {
    // Returning a lost resource.
    std::vector<viz::ReturnedResource> returned;
    returned.emplace_back();
    returned.back().id = resource1.id;
    returned.back().count = 1;
    returned.back().lost = true;

    EXPECT_EQ(resource_manager_->exported_resources_count(), 1u);

    // We have one available resource already.
    EXPECT_EQ(resource_manager_->available_resources_count(), 1u);
    resource_manager_->ReclaimResources(returned);

    // We have received the resource so it is no more exported.
    EXPECT_EQ(resource_manager_->exported_resources_count(), 0u);

    // This reclaimed resource is lost so it cannot be reused again.
    EXPECT_EQ(resource_manager_->available_resources_count(), 1u);
  }

  auto resource3 = resource_manager_->OfferAndPrepareResourceForExport(
      MakeResource(kDefaultSize));
  auto resource4 = resource_manager_->OfferAndPrepareResourceForExport(
      MakeResource(kDefaultSize));

  {
    // Returning multiple resources.
    std::vector<viz::ReturnedResource> returned;
    returned.emplace_back();
    returned.back().id = resource3.id;
    returned.back().count = 1;
    returned.back().lost = true;

    returned.emplace_back();
    returned.back().id = resource4.id;
    returned.back().count = 1;
    returned.back().lost = false;

    EXPECT_EQ(resource_manager_->exported_resources_count(), 2u);

    // We have one available resource that was reclaimed before in the test.
    EXPECT_EQ(resource_manager_->available_resources_count(), 1u);
    resource_manager_->ReclaimResources(returned);

    // We have reclaimed all the resources.
    EXPECT_EQ(resource_manager_->exported_resources_count(), 0u);

    // One of the two exported resources was lost.
    EXPECT_EQ(resource_manager_->available_resources_count(), 2u);
  }
}

}  // namespace
}  // namespace ash