blob: 6b97166a9af4ffe609539cc0b8c8cc639753b512 [file] [log] [blame]
// Copyright 2011 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/common/frame_sinks/begin_frame_source.h"
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_mock_time_task_runner.h"
#include "components/viz/common/features.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/begin_frame_source_test.h"
#include "components/viz/test/fake_delay_based_time_source.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::NiceMock;
using testing::_;
namespace viz {
namespace {
// Returns a fake TimeTicks based on the given microsecond offset.
base::TimeTicks TicksFromMicroseconds(int64_t micros) {
return base::TimeTicks() + base::Microseconds(micros);
}
// BeginFrameSource testing ----------------------------------------------------
TEST(BeginFrameSourceTest, SourceIdsAreUnique) {
StubBeginFrameSource source1;
StubBeginFrameSource source2;
StubBeginFrameSource source3;
EXPECT_NE(source1.source_id(), source2.source_id());
EXPECT_NE(source1.source_id(), source3.source_id());
EXPECT_NE(source2.source_id(), source3.source_id());
}
class TestTaskRunner : public base::TestMockTimeTaskRunner {
public:
TestTaskRunner()
: base::TestMockTimeTaskRunner(
base::TestMockTimeTaskRunner::Type::kStandalone) {
AdvanceMockTickClock(base::Microseconds(1000));
}
TestTaskRunner(const TestTaskRunner&) = delete;
TestTaskRunner& operator=(const TestTaskRunner&) = delete;
void FastForwardTo(base::TimeTicks end_time) {
base::TimeDelta offset = end_time - NowTicks();
DCHECK_GE(offset, base::TimeDelta());
FastForwardBy(offset);
}
private:
~TestTaskRunner() override = default; // Ref-counted.
};
// BackToBackBeginFrameSource testing
// ------------------------------------------
class BackToBackBeginFrameSourceTest : public ::testing::Test {
protected:
static const int64_t kDeadline;
static const int64_t kInterval;
void SetUp() override {
task_runner_ = base::MakeRefCounted<TestTaskRunner>();
std::unique_ptr<FakeDelayBasedTimeSource> time_source =
std::make_unique<FakeDelayBasedTimeSource>(
task_runner_->GetMockTickClock(), task_runner_.get());
delay_based_time_source_ = time_source.get();
source_ =
std::make_unique<BackToBackBeginFrameSource>(std::move(time_source));
obs_ = std::make_unique<::testing::NiceMock<MockBeginFrameObserver>>();
}
void TearDown() override { obs_.reset(); }
scoped_refptr<TestTaskRunner> task_runner_;
std::unique_ptr<BackToBackBeginFrameSource> source_;
std::unique_ptr<MockBeginFrameObserver> obs_;
raw_ptr<FakeDelayBasedTimeSource>
delay_based_time_source_; // Owned by |source_|.
};
const int64_t BackToBackBeginFrameSourceTest::kDeadline =
BeginFrameArgs::DefaultInterval().InMicroseconds();
const int64_t BackToBackBeginFrameSourceTest::kInterval =
BeginFrameArgs::DefaultInterval().InMicroseconds();
TEST_F(BackToBackBeginFrameSourceTest, AddObserverSendsBeginFrame) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
EXPECT_TRUE(task_runner_->HasPendingTask());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
1100 + kDeadline, kInterval);
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(obs_.get());
task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest,
RemoveObserverThenDidFinishFrameProducesNoFrame) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
source_->RemoveObserver(obs_.get());
source_->DidFinishFrame(obs_.get());
// Verify no BeginFrame is sent to |obs_|. There is a pending task in the
// task_runner_ as a BeginFrame was posted, but it gets aborted since |obs_|
// is removed.
task_runner_->RunUntilIdle();
EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(BackToBackBeginFrameSourceTest,
DidFinishFrameThenRemoveObserverProducesNoFrame) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(obs_.get());
source_->RemoveObserver(obs_.get());
// Task gets cancelled so it doesn't count as a pending task.
EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(BackToBackBeginFrameSourceTest,
TogglingObserverThenDidFinishFrameProducesCorrectFrame) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->RemoveObserver(obs_.get());
task_runner_->AdvanceMockTickClock(base::Microseconds(10));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
task_runner_->AdvanceMockTickClock(base::Microseconds(10));
source_->DidFinishFrame(obs_.get());
task_runner_->AdvanceMockTickClock(base::Microseconds(10));
// The begin frame is posted at the time when the observer was added,
// so it ignores changes to "now" afterward.
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1110,
1110 + kDeadline, kInterval);
EXPECT_TRUE(task_runner_->HasPendingTask());
task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest,
DidFinishFrameThenTogglingObserverProducesCorrectFrame) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(obs_.get());
task_runner_->AdvanceMockTickClock(base::Microseconds(10));
source_->RemoveObserver(obs_.get());
task_runner_->AdvanceMockTickClock(base::Microseconds(10));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
task_runner_->AdvanceMockTickClock(base::Microseconds(10));
// Ticks at the time at which the observer was added, ignoring the
// last change to "now".
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1120,
1120 + kDeadline, kInterval);
EXPECT_TRUE(task_runner_->HasPendingTask());
task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameNoObserver) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
source_->RemoveObserver(obs_.get());
source_->DidFinishFrame(obs_.get());
task_runner_->RunUntilIdle();
EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_F(BackToBackBeginFrameSourceTest, DidFinishFrameMultipleCallsIdempotent) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(obs_.get());
source_->DidFinishFrame(obs_.get());
source_->DidFinishFrame(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
1100 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(obs_.get());
source_->DidFinishFrame(obs_.get());
source_->DidFinishFrame(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 1200,
1200 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest, DelayInPostedTaskProducesCorrectFrame) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(obs_.get());
task_runner_->AdvanceMockTickClock(base::Microseconds(50));
// Ticks at the time the last frame finished, so ignores the last change to
// "now".
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
1100 + kDeadline, kInterval);
EXPECT_TRUE(task_runner_->HasPendingTask());
task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversSynchronized) {
NiceMock<MockBeginFrameObserver> obs1, obs2;
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
source_->AddObserver(&obs1);
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
source_->AddObserver(&obs2);
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
kInterval);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline,
kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(&obs1);
source_->DidFinishFrame(&obs2);
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(&obs1);
source_->DidFinishFrame(&obs2);
EXPECT_TRUE(task_runner_->HasPendingTask());
source_->RemoveObserver(&obs1);
source_->RemoveObserver(&obs2);
task_runner_->RunUntilIdle();
}
TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversInterleaved) {
NiceMock<MockBeginFrameObserver> obs1, obs2;
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
source_->AddObserver(&obs1);
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
source_->AddObserver(&obs2);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(&obs1);
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 1200, 1200 + kDeadline,
kInterval);
task_runner_->RunUntilIdle();
source_->DidFinishFrame(&obs1);
source_->RemoveObserver(&obs1);
// Removing all finished observers should disable the time source.
EXPECT_FALSE(delay_based_time_source_->Active());
// Finishing the frame for |obs1| posts a begin frame task, which will be
// aborted since |obs1| is removed. Clear that from the task runner.
task_runner_->RunUntilIdle();
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(&obs2);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 1300, 1300 + kDeadline,
kInterval);
task_runner_->RunUntilIdle();
source_->DidFinishFrame(&obs2);
source_->RemoveObserver(&obs2);
}
TEST_F(BackToBackBeginFrameSourceTest, MultipleObserversAtOnce) {
NiceMock<MockBeginFrameObserver> obs1, obs2;
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
source_->AddObserver(&obs1);
source_->AddObserver(&obs2);
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 1, 1000, 1000 + kDeadline,
kInterval);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 1, 1000, 1000 + kDeadline,
kInterval);
task_runner_->RunUntilIdle();
// |obs1| finishes first.
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(&obs1);
// |obs2| finishes also, before getting to the newly posted begin frame.
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
source_->DidFinishFrame(&obs2);
// Because the begin frame source already ticked when |obs1| finished,
// we see it as the frame time for both observers.
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 2, 1100, 1100 + kDeadline,
kInterval);
task_runner_->RunUntilIdle();
source_->DidFinishFrame(&obs1);
source_->RemoveObserver(&obs1);
source_->DidFinishFrame(&obs2);
source_->RemoveObserver(&obs2);
}
// There may not be any observers available when the OnGpuNoLongerBusy()
// function is invoked on the BackToBackBeginFrameSource. For e.g.
// consider the following sequence.
// 1. An observer is added to the source.
// 2. One BeginFrame request is generated by the timer.
// 3. The clock advances and then the DidFrameFinish() callback is invoked.
// The timer is marked as active.
// 4. Any pending BeginFrame requests are generated and then the observer
// unsubscribes.
// 5. If the OnGpuNoLongerBusy() notification is invoked after this step
// before any new observer is added, a DCHECK about pending observers
// being empty fires which crashes in debug mode and also in release
// builds with DCHECK on.
// With the suggested fix of checking in OnTimerTick() whether the timer
// is active, this DCHECK should not fire.
TEST_F(BackToBackBeginFrameSourceTest, OnGpuNoLongerBusyWithNoObservers) {
// Initial setup.
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
source_->AddObserver(obs_.get());
EXPECT_TRUE(task_runner_->HasPendingTask());
// Expectations for two BeginFrames.
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 1, 1000,
1000 + kDeadline, kInterval);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 1100,
1100 + kDeadline, kInterval);
// Advance the clock to ensure that the second BeginFrame has a different
// timestamp
task_runner_->AdvanceMockTickClock(base::Microseconds(100));
task_runner_->RunUntilIdle();
// Activate the timer so the second BeginFrame can be generated.
source_->DidFinishFrame(obs_.get());
task_runner_->RunUntilIdle();
// Observer is unsubscribed from the source here.
source_->RemoveObserver(obs_.get());
// DCHECK for pending observers list being empty should not fire.
source_->OnGpuNoLongerBusy();
}
// DelayBasedBeginFrameSource testing
// ------------------------------------------
class DelayBasedBeginFrameSourceTest
: public ::testing::Test,
public testing::WithParamInterface<bool> {
public:
bool NoLateBeginFrames() const { return GetParam(); }
void SetUp() override {
if (NoLateBeginFrames()) {
scoped_feature_list_.InitAndEnableFeature(features::kNoLateBeginFrames);
} else {
scoped_feature_list_.InitAndDisableFeature(features::kNoLateBeginFrames);
}
task_runner_ = base::MakeRefCounted<TestTaskRunner>();
std::unique_ptr<FakeDelayBasedTimeSource> time_source =
std::make_unique<FakeDelayBasedTimeSource>(
task_runner_->GetMockTickClock(), task_runner_.get());
time_source->SetTimebaseAndInterval(base::TimeTicks(),
base::Microseconds(10000));
source_ = std::make_unique<DelayBasedBeginFrameSource>(
std::move(time_source), BeginFrameSource::kNotRestartableId);
obs_ = std::make_unique<MockBeginFrameObserver>();
}
void TearDown() override { obs_.reset(); }
scoped_refptr<TestTaskRunner> task_runner_;
std::unique_ptr<DelayBasedBeginFrameSource> source_;
std::unique_ptr<MockBeginFrameObserver> obs_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_P(DelayBasedBeginFrameSourceTest,
AddObserverCallsOnBeginFrameWithMissedTick) {
if (NoLateBeginFrames()) {
return;
}
task_runner_->AdvanceMockTickClock(base::Microseconds(9010));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 10000, 20000,
10000);
source_->AddObserver(obs_.get()); // Should cause the last tick to be sent
// No tasks should need to be run for this to occur.
}
TEST_P(DelayBasedBeginFrameSourceTest, AddObserverCallsCausesOnBeginFrame) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(obs_.get());
EXPECT_EQ(TicksFromMicroseconds(10000),
task_runner_->NowTicks() + task_runner_->NextPendingTaskDelay());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
task_runner_->AdvanceMockTickClock(base::Microseconds(9010));
task_runner_->RunUntilIdle();
}
TEST_P(DelayBasedBeginFrameSourceTest, BasicOperation) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
task_runner_->FastForwardTo(TicksFromMicroseconds(30001));
source_->RemoveObserver(obs_.get());
// No new frames....
task_runner_->FastForwardTo(TicksFromMicroseconds(60000));
}
TEST_P(DelayBasedBeginFrameSourceTest, VSyncChanges) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
task_runner_->FastForwardTo(TicksFromMicroseconds(30001));
// Update the vsync information
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(27500),
base::Microseconds(10001));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 47502, 10001);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 47502, 57503, 10001);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 57503, 67504, 10001);
task_runner_->FastForwardTo(TicksFromMicroseconds(60000));
}
TEST_P(DelayBasedBeginFrameSourceTest, VSyncChangeTimebaseBeforeLastTick) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
task_runner_->FastForwardTo(TicksFromMicroseconds(30000));
// Update the vsync information such that timebase is before last tick time,
// and next tick happens within less than the new interval of the following
// tick (i.e. next_tick -> 40000, following_tick -> 41000)
// Begin frame won't be used at 41000 because this is a double-tick.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(26000),
base::Microseconds(5000));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 41000, 5000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 46000, 51000, 5000);
task_runner_->FastForwardTo(TicksFromMicroseconds(46000));
// Update the vsync information such that timebase is before last tick time,
// and next tick happens exactly one interval before the following tick
// tick (i.e. next_tick -> 51000, following_tick -> 60000)
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(42000),
base::Microseconds(9000));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 8, 51000, 60000, 9000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 9, 60000, 69000, 9000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 10, 69000, 78000, 9000);
task_runner_->FastForwardTo(TicksFromMicroseconds(70000));
}
TEST_P(DelayBasedBeginFrameSourceTest, VSyncChangeTimebaseAfterNextTick) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
task_runner_->FastForwardTo(TicksFromMicroseconds(30000));
// Update the vsync information such that timebase is after next tick time,
// and next tick happens within less than one interval of the new timebase
// Begin frame won't be used at 41000 because this is a double-tick.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(41000),
base::Microseconds(5000));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 41000, 5000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 46000, 51000, 5000);
task_runner_->FastForwardTo(TicksFromMicroseconds(46000));
// Update the vsync information such that timebase is after next tick time,
// and next tick happens exactly one interval before the new timebase
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(60000),
base::Microseconds(9000));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 8, 51000, 60000, 9000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 9, 60000, 69000, 9000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 10, 69000, 78000, 9000);
task_runner_->FastForwardTo(TicksFromMicroseconds(70000));
// Update the vsync information such that timebase is after next tick time,
// and next tick happens more than one interval before the new timebase
// Begin frame won't be used at 80000 because this is a double-tick.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(100000),
base::Microseconds(5000));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 11, 78000, 80000, 5000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 13, 85000, 90000, 5000);
task_runner_->FastForwardTo(TicksFromMicroseconds(85000));
}
TEST_P(DelayBasedBeginFrameSourceTest, VSyncChangeTimebaseBetweenTicks) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
task_runner_->FastForwardTo(TicksFromMicroseconds(30000));
// Update the vsync information such that timebase is between next tick time,
// and last tick time.
// Begin frame won't be used at 41000 because this is a double-tick.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(35000),
base::Microseconds(6000));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40000, 41000, 6000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 47000, 53000, 6000);
task_runner_->FastForwardTo(TicksFromMicroseconds(47000));
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(49000),
base::Microseconds(10000));
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 8, 53000, 59000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 9, 59000, 69000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 10, 69000, 79000, 10000);
task_runner_->FastForwardTo(TicksFromMicroseconds(70000));
}
TEST_P(DelayBasedBeginFrameSourceTest, VSyncSkipped) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(obs_.get());
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10000, 20000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30000, 40000, 10000);
task_runner_->FastForwardTo(TicksFromMicroseconds(30000));
// Advancing tick time without creating begin frames.
task_runner_->AdvanceMockTickClock(base::Microseconds(40000));
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(40000),
base::Microseconds(11000));
// By advancing tick time to 40000, we would be skipping sequence_number 5 at
// 40000 and sequence_number 6 at 51000.
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 62000, 73000, 11000);
EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 8, 73000, 84000, 11000);
task_runner_->FastForwardTo(TicksFromMicroseconds(75000));
}
TEST_P(DelayBasedBeginFrameSourceTest, MultipleObservers) {
NiceMock<MockBeginFrameObserver> obs1, obs2;
// Mock tick clock starts off at 1000.
task_runner_->FastForwardBy(base::Microseconds(9010));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs1, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(obs1, source_->source_id(), 1, 10000, 20000,
10000);
}
source_->AddObserver(&obs1); // Should cause the last tick to be sent
// No tasks should need to be run for this to occur.
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 2, 20000, 30000, 10000);
task_runner_->FastForwardBy(base::Microseconds(10000));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs2, false);
// Sequence number unchanged for missed frame with time of last normal frame.
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(obs2, source_->source_id(), 2, 20000, 30000,
10000);
}
source_->AddObserver(&obs2); // Should cause the last tick to be sent
// No tasks should need to be run for this to occur.
EXPECT_BEGIN_FRAME_USED(obs1, source_->source_id(), 3, 30000, 40000, 10000);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 3, 30000, 40000, 10000);
task_runner_->FastForwardBy(base::Microseconds(10000));
source_->RemoveObserver(&obs1);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 4, 40000, 50000, 10000);
task_runner_->FastForwardBy(base::Microseconds(10000));
source_->RemoveObserver(&obs2);
task_runner_->FastForwardTo(TicksFromMicroseconds(50000));
EXPECT_FALSE(task_runner_->HasPendingTask());
}
TEST_P(DelayBasedBeginFrameSourceTest, DoubleTick) {
NiceMock<MockBeginFrameObserver> obs;
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(&obs);
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(5000),
base::Microseconds(10000));
task_runner_->AdvanceMockTickClock(base::Microseconds(4000));
// No begin frame received.
task_runner_->RunUntilIdle();
// Begin frame received.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(10000),
base::Microseconds(10000));
task_runner_->AdvanceMockTickClock(base::Microseconds(5000));
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000);
task_runner_->RunUntilIdle();
}
TEST_P(DelayBasedBeginFrameSourceTest, DoubleTickMissedFrame) {
NiceMock<MockBeginFrameObserver> obs;
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(&obs);
source_->RemoveObserver(&obs);
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(5000),
base::Microseconds(10000));
task_runner_->AdvanceMockTickClock(base::Microseconds(4000));
// No missed frame received.
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
// This does not cause a missed BeginFrame because of double ticking
// prevention. It does not produce a new sequence number.
source_->AddObserver(&obs);
source_->RemoveObserver(&obs);
// Missed frame received.
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(10000),
base::Microseconds(10000));
task_runner_->AdvanceMockTickClock(base::Microseconds(5000));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
if (!NoLateBeginFrames()) {
// Sequence number is incremented again, because sufficient time has passed.
EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 2, 10000, 20000,
10000);
}
source_->AddObserver(&obs);
if (NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 3, 20000, 30000, 10000);
task_runner_->AdvanceMockTickClock(base::Microseconds(10010));
task_runner_->RunUntilIdle();
}
source_->RemoveObserver(&obs);
}
TEST_P(DelayBasedBeginFrameSourceTest, MultipleArgsInSameInterval) {
NiceMock<MockBeginFrameObserver> obs;
NiceMock<MockBeginFrameObserver> obs2;
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(&obs);
task_runner_->RunUntilIdle();
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000);
task_runner_->AdvanceMockTickClock(base::Microseconds(9000));
task_runner_->RunUntilIdle();
if (!NoLateBeginFrames()) {
// Sequence number should stay the same within same interval.
EXPECT_BEGIN_FRAME_USED_MISSED(obs2, source_->source_id(), 2, 10000, 20000,
10000);
}
source_->AddObserver(&obs2);
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 3, 20000, 30000, 10000);
EXPECT_BEGIN_FRAME_USED(obs2, source_->source_id(), 3, 20000, 30000, 10000);
task_runner_->AdvanceMockTickClock(base::Microseconds(10000));
task_runner_->RunUntilIdle();
}
TEST_P(DelayBasedBeginFrameSourceTest, ConsecutiveArgsDelayedByMultipleVsyncs) {
NiceMock<MockBeginFrameObserver> obs;
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 10000,
10000);
}
source_->AddObserver(&obs);
task_runner_->RunUntilIdle();
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 20000, 10000);
task_runner_->AdvanceMockTickClock(base::Microseconds(9000));
task_runner_->RunUntilIdle();
source_->RemoveObserver(&obs);
// New args created 8 intervals later.
// Sequence number should increase by this much.
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 10, 90000, 100000,
10000);
}
task_runner_->AdvanceMockTickClock(base::Microseconds(80000));
source_->AddObserver(&obs);
if (NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 11, 100000, 110000,
10000);
task_runner_->AdvanceMockTickClock(base::Microseconds(10010));
task_runner_->RunUntilIdle();
}
}
TEST_P(DelayBasedBeginFrameSourceTest, WithVrrInterval) {
NiceMock<MockBeginFrameObserver> obs;
source_->SetMaxVrrInterval(base::Microseconds(25000));
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
if (!NoLateBeginFrames()) {
EXPECT_BEGIN_FRAME_USED_MISSED(obs, source_->source_id(), 1, 0, 25000,
25000);
}
source_->AddObserver(&obs);
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 2, 10000, 35000, 25000);
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 3, 20000, 45000, 25000);
task_runner_->FastForwardTo(TicksFromMicroseconds(21000));
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(21000),
base::Microseconds(10000));
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 4, 30000, 46000, 25000);
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 5, 31000, 56000, 25000);
task_runner_->FastForwardTo(TicksFromMicroseconds(32000));
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(32000),
base::Microseconds(10000));
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 6, 41000, 57000, 25000);
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 7, 42000, 67000, 25000);
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 8, 52000, 77000, 25000);
task_runner_->FastForwardTo(TicksFromMicroseconds(53000));
source_->OnUpdateVSyncParameters(TicksFromMicroseconds(53000),
base::Microseconds(10000));
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 9, 62000, 78000, 25000);
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 10, 63000, 88000, 25000);
EXPECT_BEGIN_FRAME_USED(obs, source_->source_id(), 11, 73000, 98000, 25000);
task_runner_->FastForwardTo(TicksFromMicroseconds(73000));
}
INSTANTIATE_TEST_SUITE_P(
,
DelayBasedBeginFrameSourceTest,
testing::Bool(),
[](const ::testing::TestParamInfo<bool>& info) -> std::string {
return info.param ? "NoLateBeginFrames" : "LateBeginFrames";
});
// ExternalBeginFrameSource testing
// --------------------------------------------
class MockExternalBeginFrameSourceClient
: public ExternalBeginFrameSourceClient {
public:
MockExternalBeginFrameSourceClient() = default;
virtual ~MockExternalBeginFrameSourceClient() = default;
MOCK_METHOD1(OnNeedsBeginFrames, void(bool));
};
class ExternalBeginFrameSourceTest : public ::testing::Test {
public:
ExternalBeginFrameSourceTest()
: client_(std::make_unique<MockExternalBeginFrameSourceClient>()),
source_(std::make_unique<ExternalBeginFrameSource>(
client_.get(),
BeginFrameSource::kNotRestartableId)),
obs_(std::make_unique<MockBeginFrameObserver>()) {}
~ExternalBeginFrameSourceTest() override = default;
std::unique_ptr<MockExternalBeginFrameSourceClient> client_;
std::unique_ptr<ExternalBeginFrameSource> source_;
std::unique_ptr<MockBeginFrameObserver> obs_;
};
TEST_F(ExternalBeginFrameSourceTest, OnAnimateOnlyBeginFrameOptIn) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
source_->AddObserver(obs_.get());
// By default, an observer doesn't receive animate_only BeginFrames.
BeginFrameArgs args = CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 2, TicksFromMicroseconds(10000));
args.animate_only = true;
source_->OnBeginFrame(args);
// When opting in, an observer receives animate_only BeginFrames.
args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 3,
TicksFromMicroseconds(10001));
args.animate_only = true;
EXPECT_CALL(*obs_, WantsAnimateOnlyBeginFrames())
.WillOnce(::testing::Return(true));
EXPECT_BEGIN_FRAME_ARGS_USED(*obs_, args);
source_->OnBeginFrame(args);
EXPECT_CALL((*client_), OnNeedsBeginFrames(false)).Times(1);
source_->RemoveObserver(obs_.get());
}
TEST_F(ExternalBeginFrameSourceTest, OnBeginFrameChecksBeginFrameContinuity) {
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
source_->AddObserver(obs_.get());
BeginFrameArgs args = CreateBeginFrameArgsForTesting(
BEGINFRAME_FROM_HERE, 0, 2, TicksFromMicroseconds(10000));
EXPECT_BEGIN_FRAME_ARGS_USED(*obs_, args);
source_->OnBeginFrame(args);
// Providing same args again to OnBeginFrame() should not notify observer.
source_->OnBeginFrame(args);
// Providing same args through a different ExternalBeginFrameSource also
// does not notify observer.
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
ExternalBeginFrameSource source2(client_.get());
source2.AddObserver(obs_.get());
source2.OnBeginFrame(args);
EXPECT_CALL((*client_), OnNeedsBeginFrames(false)).Times(2);
source_->RemoveObserver(obs_.get());
source2.RemoveObserver(obs_.get());
}
TEST_F(ExternalBeginFrameSourceTest, GetMissedBeginFrameArgs) {
BeginFrameArgs args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0,
2, 10000, 10100, 100);
source_->OnBeginFrame(args);
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
if (!base::FeatureList::IsEnabled(features::kNoLateBeginFrames)) {
EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, 0, 2, 10000, 10100, 100);
}
source_->AddObserver(obs_.get());
source_->RemoveObserver(obs_.get());
// Out of order frame_time. This might not be valid but still shouldn't
// cause a DCHECK in ExternalBeginFrameSource code.
args = CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 2, 9999, 10100,
101);
source_->OnBeginFrame(args);
EXPECT_CALL((*client_), OnNeedsBeginFrames(true)).Times(1);
EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
EXPECT_CALL(*obs_, OnBeginFrame(_)).Times(0);
source_->AddObserver(obs_.get());
EXPECT_CALL((*client_), OnNeedsBeginFrames(false)).Times(1);
source_->RemoveObserver(obs_.get());
}
} // namespace
} // namespace viz