#include "ash/frame_sink/frame_sink_holder.h"
#include <algorithm>
#include <memory>
#include <vector>
#include "ash/frame_sink/frame_sink_holder_test_api.h"
#include "ash/frame_sink/frame_sink_host.h"
#include "ash/frame_sink/test/test_begin_frame_source.h"
#include "ash/frame_sink/test/test_frame_factory.h"
#include "ash/frame_sink/test/test_layer_tree_frame_sink.h"
#include "ash/frame_sink/ui_resource_manager.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/gpu/context_provider.h"
#include "components/viz/common/quads/compositor_frame.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 "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace ash {
namespace {
class StubBeginFrameSource : public viz::BeginFrameSource {
public:
StubBeginFrameSource() : viz::BeginFrameSource(0u) {}
void DidFinishFrame(viz::BeginFrameObserver* obs) override {}
void AddObserver(viz::BeginFrameObserver* obs) override {}
void RemoveObserver(viz::BeginFrameObserver* obs) override {}
void OnGpuNoLongerBusy() override {}
};
MATCHER_P(IsBeginFrameAckEqual, value, "") {
return arg.frame_id == value.frame_id && arg.trace_id == value.trace_id &&
arg.has_damage == value.has_damage;
}
class FrameSinkHolderTest : public AshTestBase {
public:
FrameSinkHolderTest() = default;
FrameSinkHolderTest(const FrameSinkHolderTest&) = delete;
FrameSinkHolderTest& operator=(const FrameSinkHolderTest&) = delete;
void SetUp() override {
AshTestBase::SetUp();
aura::Window* root_window =
Shell::Get()->GetRootWindowForDisplayId(GetPrimaryDisplay().id());
auto host_window = std::make_unique<aura::Window>(nullptr);
host_window_ = host_window.release();
host_window_->Init(ui::LayerType::LAYER_SOLID_COLOR);
root_window->AddChild(host_window_);
frame_factory_ = std::make_unique<TestFrameFactory>();
auto layer_tree_frame_sink = std::make_unique<TestLayerTreeFrameSink>();
layer_tree_frame_sink_ = layer_tree_frame_sink.get();
frame_sink_holder_ = std::make_unique<FrameSinkHolder>(
std::move(layer_tree_frame_sink),
base::BindRepeating(&TestFrameFactory::CreateCompositorFrame,
base::Unretained(frame_factory_.get())),
base::DoNothing(),
base::DoNothing());
holder_weak_ptr_ = frame_sink_holder_->GetWeakPtr();
sii_ = base::MakeRefCounted<gpu::TestSharedImageInterface>();
}
std::unique_ptr<UiResource> MakeResource() {
const gfx::Size kSize = gfx::Size(20, 20);
auto shared_image = sii_->CreateSharedImage(
{viz::SinglePlaneFormat::kBGRA_8888, kSize, 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 = 1u;
return resource;
}
UiResourceManager& GetResourceManager() {
return frame_sink_holder_->resource_manager();
}
scoped_refptr<gpu::SharedImageInterface> sii_;
std::unique_ptr<FrameSinkHolder> frame_sink_holder_;
raw_ptr<aura::Window, DanglingUntriaged> host_window_;
base::WeakPtr<FrameSinkHolder> holder_weak_ptr_;
std::unique_ptr<TestFrameFactory> frame_factory_;
raw_ptr<TestLayerTreeFrameSink, DanglingUntriaged>
layer_tree_frame_sink_;
};
TEST_F(FrameSinkHolderTest, SubmitFrameSynchronouslyBeforeFirstFrameRequested) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
ASSERT_FALSE(test_api.IsFirstFrameRequested());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_TRUE(test_api.LastSubmittedFrameSize().IsEmpty());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 0);
EXPECT_TRUE(test_api.IsPendingFrame());
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
ASSERT_TRUE(test_api.IsFirstFrameRequested());
EXPECT_FALSE(test_api.IsPendingFrame());
EXPECT_TRUE(test_api.IsPendingFrameAck());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
EXPECT_THAT(
layer_tree_frame_sink_->GetLatestReceivedFrame().metadata.begin_frame_ack,
testing::Not(IsBeginFrameAckEqual(
viz::BeginFrameAck::CreateManualAckWithDamage())));
frame_sink_holder_->DidReceiveCompositorFrameAck();
EXPECT_FALSE(test_api.IsPendingFrameAck());
layer_tree_frame_sink_->ResetLatestFrameState();
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 2);
EXPECT_EQ(test_api.LastSubmittedFrameSize(),
layer_tree_frame_sink_->GetLatestReceivedFrame().size_in_pixels());
EXPECT_THAT(
layer_tree_frame_sink_->GetLatestReceivedFrame().metadata.begin_frame_ack,
IsBeginFrameAckEqual(viz::BeginFrameAck::CreateManualAckWithDamage()));
}
TEST_F(FrameSinkHolderTest, ObserveBeginFrameSourceOnDemand) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
StubBeginFrameSource source;
frame_sink_holder_->SetBeginFrameSource(&source);
EXPECT_TRUE(test_api.IsObservingBeginFrameSource());
for (int i = 0; i < 4; i++) {
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
}
frame_sink_holder_->SubmitCompositorFrame(true);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_TRUE(test_api.IsObservingBeginFrameSource());
for (int i = 0; i < 5; i++) {
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
}
EXPECT_FALSE(test_api.IsObservingBeginFrameSource());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_TRUE(test_api.IsObservingBeginFrameSource());
frame_sink_holder_->SetBeginFrameSource(nullptr);
}
TEST_F(FrameSinkHolderTest, ObserveBeginFrameSourceOnDemand_AutoUpdate) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
frame_sink_holder_->SetAutoUpdateMode(true);
StubBeginFrameSource source;
frame_sink_holder_->SetBeginFrameSource(&source);
EXPECT_TRUE(test_api.IsObservingBeginFrameSource());
for (int i = 0; i < 10; i++) {
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
}
EXPECT_TRUE(test_api.IsObservingBeginFrameSource());
frame_sink_holder_->SetAutoUpdateMode(false);
for (int i = 0; i < 5; i++) {
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
}
EXPECT_FALSE(test_api.IsObservingBeginFrameSource());
frame_sink_holder_->SetAutoUpdateMode(true);
EXPECT_TRUE(test_api.IsObservingBeginFrameSource());
frame_sink_holder_->SetBeginFrameSource(nullptr);
}
TEST_F(FrameSinkHolderTest, SubmitFrameSynchronouslyWhilePendingFrameAck) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_TRUE(test_api.IsPendingFrameAck());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
frame_factory_->SetFrameMetaData(gfx::Size(200, 200), 1.0);
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_EQ(layer_tree_frame_sink_->GetLatestReceivedFrame().size_in_pixels(),
gfx::Size(100, 100));
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
EXPECT_TRUE(test_api.IsPendingFrame());
}
TEST_F(FrameSinkHolderTest, HandlingAsynchronousFrameRequests_NoAutoUpdate) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
auto skipped_reason = layer_tree_frame_sink_->GetLatestFrameSkippedReason();
ASSERT_TRUE(skipped_reason.has_value());
EXPECT_EQ(skipped_reason, cc::FrameSkippedReason::kNoDamage);
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->SubmitCompositorFrame(false);
EXPECT_TRUE(test_api.IsPendingFrame());
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
EXPECT_THAT(
layer_tree_frame_sink_->GetLatestReceivedFrame().metadata.begin_frame_ack,
testing::Not(IsBeginFrameAckEqual(
viz::BeginFrameAck::CreateManualAckWithDamage())));
EXPECT_FALSE(test_api.IsPendingFrame());
EXPECT_TRUE(test_api.IsPendingFrameAck());
layer_tree_frame_sink_->ResetLatestFrameState();
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
skipped_reason = layer_tree_frame_sink_->GetLatestFrameSkippedReason();
ASSERT_TRUE(skipped_reason.has_value());
EXPECT_EQ(skipped_reason, cc::FrameSkippedReason::kWaitingOnMain);
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
frame_sink_holder_->DidReceiveCompositorFrameAck();
EXPECT_FALSE(test_api.IsPendingFrameAck());
layer_tree_frame_sink_->ResetLatestFrameState();
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
skipped_reason = layer_tree_frame_sink_->GetLatestFrameSkippedReason();
ASSERT_TRUE(skipped_reason.has_value());
EXPECT_EQ(skipped_reason, cc::FrameSkippedReason::kNoDamage);
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
frame_sink_holder_->SubmitCompositorFrame(false);
EXPECT_TRUE(test_api.IsPendingFrame());
EXPECT_FALSE(test_api.IsPendingFrameAck());
layer_tree_frame_sink_->ResetLatestFrameState();
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_THAT(
layer_tree_frame_sink_->GetLatestReceivedFrame().metadata.begin_frame_ack,
testing::Not(IsBeginFrameAckEqual(
viz::BeginFrameAck::CreateManualAckWithDamage())));
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 2);
EXPECT_FALSE(test_api.IsPendingFrame());
EXPECT_TRUE(test_api.IsPendingFrameAck());
}
TEST_F(FrameSinkHolderTest, DontSubmitNewFramesWhenWaitingToDeleteSinkHolder) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
base::RunLoop loop;
viz::TransferableResource resource_1 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
frame_factory_->SetFrameResources({resource_1});
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
EXPECT_FALSE(FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_), host_window_));
ASSERT_TRUE(holder_weak_ptr_);
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 2);
layer_tree_frame_sink_->ResetLatestFrameState();
holder_weak_ptr_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 2);
}
TEST_F(FrameSinkHolderTest,
DeleteSinkHolderImmediatelyWhenNoFramesIsSubmitted) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
EXPECT_TRUE(test_api.LastSubmittedFrameSize().IsEmpty());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 0);
EXPECT_TRUE(FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_), host_window_));
EXPECT_FALSE(holder_weak_ptr_);
}
TEST_F(FrameSinkHolderTest, ExtendLifeTimeOfHolderToRootWindow) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
viz::TransferableResource resource_1 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
viz::TransferableResource resource_2 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
viz::TransferableResource resource_3 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
frame_factory_->SetFrameResources({resource_1, resource_2, resource_3});
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_FALSE(test_api.LastSubmittedFrameSize().IsEmpty());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
EXPECT_FALSE(FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_), host_window_));
EXPECT_TRUE(holder_weak_ptr_);
}
TEST_F(FrameSinkHolderTest, KeepSubmittingFrameWhenAutoUpdateIsOn) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 0);
frame_sink_holder_->SetAutoUpdateMode(true);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
frame_sink_holder_->DidReceiveCompositorFrameAck();
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 2);
frame_sink_holder_->DidReceiveCompositorFrameAck();
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 3);
}
TEST_F(FrameSinkHolderTest, DeleteHolderAfterReclaimingAllResources) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
base::RunLoop loop;
viz::TransferableResource resource_1 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
viz::TransferableResource resource_2 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
frame_factory_->SetFrameResources({resource_1, resource_2});
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_FALSE(FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_), host_window_));
ASSERT_TRUE(holder_weak_ptr_);
std::vector<viz::ReturnedResource> to_be_returned_resources;
layer_tree_frame_sink_->GetFrameResourcesToReturn(to_be_returned_resources);
holder_weak_ptr_->ReclaimResources(std::move(to_be_returned_resources));
loop.RunUntilIdle();
ASSERT_FALSE(holder_weak_ptr_);
}
TEST_F(FrameSinkHolderTest, LayerTreeFrameSinkLost) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
viz::TransferableResource resource_1 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
frame_factory_->SetFrameResources({resource_1});
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_EQ(GetResourceManager().exported_resources_count(), 1u);
frame_sink_holder_->DidLoseLayerTreeFrameSink();
EXPECT_EQ(GetResourceManager().exported_resources_count(), 0u);
}
TEST_F(FrameSinkHolderTest,
LayerTreeFrameSinkLostWhenWaitingToDeleteSinkHolder) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
base::RunLoop loop;
viz::TransferableResource resource_1 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
frame_factory_->SetFrameResources({resource_1});
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_FALSE(FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_), host_window_));
ASSERT_TRUE(holder_weak_ptr_);
holder_weak_ptr_->DidLoseLayerTreeFrameSink();
loop.RunUntilIdle();
ASSERT_FALSE(holder_weak_ptr_);
}
TEST_F(FrameSinkHolderTest,
DeleteSinkHolderWithExportedResources_DuringShutdown) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
viz::TransferableResource resource_1 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
frame_factory_->SetFrameResources({resource_1});
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_EQ(frame_sink_holder_->resource_manager().exported_resources_count(),
1u);
aura::Window* root_window =
Shell::Get()->GetRootWindowForDisplayId(GetPrimaryDisplay().id());
root_window->RemoveChild(host_window_);
auto host_window = base::WrapUnique<aura::Window>(host_window_);
EXPECT_TRUE(FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_), host_window_));
EXPECT_FALSE(holder_weak_ptr_);
}
TEST_F(FrameSinkHolderTest,
DeleteSinkHolderImmediatelyWhenNoExportedResources) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
viz::TransferableResource resource_1 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
frame_factory_->SetFrameResources({resource_1});
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_FALSE(test_api.LastSubmittedFrameSize().IsEmpty());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
std::vector<viz::ReturnedResource> to_be_returned_resources;
layer_tree_frame_sink_->GetFrameResourcesToReturn(to_be_returned_resources);
frame_sink_holder_->ReclaimResources(std::move(to_be_returned_resources));
ASSERT_EQ(GetResourceManager().exported_resources_count(), 0u);
EXPECT_TRUE(FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_), host_window_));
EXPECT_FALSE(holder_weak_ptr_);
}
TEST_F(FrameSinkHolderTest, DeleteSinkHolderImmediatelyWhenFrameSinkIsLost) {
FrameSinkHolderTestApi test_api(frame_sink_holder_.get());
viz::TransferableResource resource_1 =
GetResourceManager().OfferAndPrepareResourceForExport(MakeResource());
frame_factory_->SetFrameResources({resource_1});
frame_factory_->SetFrameMetaData(gfx::Size(100, 100), 1.0);
frame_sink_holder_->OnBeginFrame(CreateValidBeginFrameArgsForTesting());
frame_sink_holder_->SubmitCompositorFrame(true);
EXPECT_FALSE(test_api.LastSubmittedFrameSize().IsEmpty());
EXPECT_EQ(layer_tree_frame_sink_->num_of_frames_received(), 1);
ASSERT_EQ(GetResourceManager().exported_resources_count(), 1u);
frame_sink_holder_->DidLoseLayerTreeFrameSink();
EXPECT_TRUE(FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(frame_sink_holder_), host_window_));
EXPECT_FALSE(holder_weak_ptr_);
}
}
}