blob: 3411cb8e540288e4ad2ab64e9c210207c710fde9 [file]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/ipc/service/image_transport_surface_overlay_mac.h"
#include <memory>
#include <utility>
#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "build/buildflag.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accelerated_widget_mac/ca_layer_tree_coordinator.h"
#include "ui/accelerated_widget_mac/ca_renderer_layer_tree.h"
using testing::_;
using testing::NiceMock;
namespace gpu {
namespace {
// Mock for CALayerTreeCoordinator to verify interactions.
class MockCALayerTreeCoordinator : public ui::CALayerTreeCoordinator {
public:
MockCALayerTreeCoordinator()
: CALayerTreeCoordinator(false,
base::DoNothing(),
base::BindRepeating([] { return true; }),
nil,
false),
ca_renderer_layer_tree_(new ui::CARendererLayerTree(true, true)) {}
~MockCALayerTreeCoordinator() override = default;
MOCK_METHOD(void,
CommitPresentedFrameToCA,
(base::TimeDelta, base::TimeTicks),
(override));
void Present(
gl::Presenter::SwapCompletionCallback completion_callback,
gl::Presenter::PresentationCallback presentation_callback) override {
num_pending_swaps_++;
}
int NumPendingSwaps() const override { return num_pending_swaps_; }
void DecreasePendingSwaps() { num_pending_swaps_--; }
private:
std::unique_ptr<ui::CARendererLayerTree> ca_renderer_layer_tree_;
int num_pending_swaps_ = 0;
};
} // namespace
} // namespace gpu
namespace gpu {
class ImageTransportSurfaceOverlayMacTest : public testing::Test {
public:
ImageTransportSurfaceOverlayMacTest() = default;
~ImageTransportSurfaceOverlayMacTest() override = default;
// testing::Test:default
void SetUp() override {
#if BUILDFLAG(IS_MAC)
std::unique_ptr<ui::VSyncCallbackMac> vsync_callback_mac = base::WrapUnique(
new ui::VSyncCallbackMac(base::DoNothing(), base::DoNothing(), false));
#endif
auto* mock = new MockCALayerTreeCoordinator();
mock_ca_layer_tree_coordinator_ = mock;
surface_overlay_mac_ =
base::MakeRefCounted<ImageTransportSurfaceOverlayMacEGL>(
#if BUILDFLAG(IS_MAC)
base::WrapUnique(mock), std::move(vsync_callback_mac));
#else
base::WrapUnique(mock));
#endif
surface_overlay_mac_->SetMaxPendingSwaps(kMaxPendingSwaps);
}
void TearDown() override {
// To prevent a dandling raw_ptr.
mock_ca_layer_tree_coordinator_ = nullptr;
}
protected:
static constexpr int kMaxPendingSwaps = 2;
raw_ptr<MockCALayerTreeCoordinator> mock_ca_layer_tree_coordinator_;
scoped_refptr<ImageTransportSurfaceOverlayMacEGL> surface_overlay_mac_;
};
TEST_F(ImageTransportSurfaceOverlayMacTest,
PresentCommitsImmediatelyWhenNotInteracting) {
// On IOS, it always commits immediately.
// On Mac, it will commit immediately when it's not handling interaction.
// The first frame is committed immediately.
gfx::FrameData data;
#if BUILDFLAG(IS_MAC)
data.is_handling_interaction = false;
#endif
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.WillOnce([this]() {
mock_ca_layer_tree_coordinator_->DecreasePendingSwaps();
});
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
// The second frame is committed immediately.
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.WillOnce([this]() {
mock_ca_layer_tree_coordinator_->DecreasePendingSwaps();
});
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
}
#if BUILDFLAG(IS_MAC)
TEST_F(ImageTransportSurfaceOverlayMacTest,
PresentDelaysCommitWhenHandlingInteraction) {
gfx::FrameData data;
data.is_handling_interaction = true;
// The first two frames will be pending.
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.Times(0);
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
// At the first VSync callback, the first frame is committed.
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.WillOnce([this]() {
mock_ca_layer_tree_coordinator_->DecreasePendingSwaps();
});
ui::VSyncParamsMac params;
surface_overlay_mac_->OnVSyncPresentation(params);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
// At the second VSync callback, the second frame is committed.
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.WillOnce([this]() {
mock_ca_layer_tree_coordinator_->DecreasePendingSwaps();
});
surface_overlay_mac_->OnVSyncPresentation(params);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
}
TEST_F(ImageTransportSurfaceOverlayMacTest,
PresentDelaysCommitWhenFramesArePending) {
// Simulate more than one pending frame.
// The first frame is pending.
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.Times(0);
gfx::FrameData data;
data.is_handling_interaction = true;
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
// With a previous pending frame, it can't commit the second frame
// immediately even if it's not handling interaction.
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.Times(0);
data.is_handling_interaction = false;
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
}
TEST_F(ImageTransportSurfaceOverlayMacTest,
PresentCommitsImmediatelyIfTooManyFramesPending) {
// Simulate that we have reached the maximum number of pending swaps.
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.Times(0);
gfx::FrameData data;
data.is_handling_interaction = true; // Even if interacting.
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
// Even if interacting.
EXPECT_CALL(*mock_ca_layer_tree_coordinator_, CommitPresentedFrameToCA(_, _))
.Times(1);
surface_overlay_mac_->Present(base::DoNothing(), base::DoNothing(), data);
testing::Mock::VerifyAndClearExpectations(mock_ca_layer_tree_coordinator_);
}
#endif
} // namespace gpu