blob: 6ca6a6bfb3a90ff0bfd52fde6a2ff6dd1ba330cc [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/lib/ui/gfx/engine/frame_predictor.h"
#include <lib/gtest/test_loop_fixture.h>
#include "garnet/lib/ui/gfx/tests/error_reporting_test.h"
namespace scenic_impl {
namespace gfx {
namespace test {
class FramePredictorTest : public ErrorReportingTest {
protected:
// | ::testing::Test |
void SetUp() override {
predictor_ = std::make_unique<FramePredictor>(kInitialRenderTimePrediction,
kInitialUpdateTimePrediction);
}
// | ::testing::Test |
void TearDown() override { predictor_.reset(); }
zx::time ms_to_time(uint64_t ms) {
return zx::time(0) + zx::msec(ms);
}
static constexpr zx::duration kInitialRenderTimePrediction =
zx::msec(4);
static constexpr zx::duration kInitialUpdateTimePrediction =
zx::msec(2);
std::unique_ptr<FramePredictor> predictor_;
};
TEST_F(FramePredictorTest, BasicPredictions_ShouldBeReasonable) {
PredictionRequest request = {
.now = ms_to_time(5),
.requested_presentation_time = ms_to_time(10),
.last_vsync_time = ms_to_time(0),
.vsync_interval = zx::msec(10)};
auto prediction = predictor_->GetPrediction(request);
EXPECT_GT(prediction.presentation_time, request.now);
EXPECT_GE(prediction.latch_point_time, request.now);
EXPECT_LT(prediction.latch_point_time, prediction.presentation_time);
}
TEST_F(FramePredictorTest, PredictionsAfterUpdating_ShouldBeMoreReasonable) {
const zx::duration update_duration = zx::msec(2);
const zx::duration render_duration = zx::msec(5);
const size_t kBiggerThanAllPredictionWindows = 5;
for (size_t i = 0; i < kBiggerThanAllPredictionWindows; ++i) {
predictor_->ReportRenderDuration(render_duration);
predictor_->ReportUpdateDuration(update_duration);
}
PredictionRequest request = {.now = ms_to_time(5),
.requested_presentation_time = ms_to_time(0),
.last_vsync_time = ms_to_time(0),
.vsync_interval = zx::msec(10)};
auto prediction = predictor_->GetPrediction(request);
EXPECT_GT(prediction.presentation_time, request.now);
EXPECT_GE(prediction.latch_point_time, request.now);
EXPECT_GE(prediction.presentation_time - prediction.latch_point_time,
update_duration + render_duration);
}
TEST_F(FramePredictorTest,
OneExpensiveTime_ShouldNotPredictForFutureVsyncIntervals) {
const zx::duration update_duration = zx::msec(4);
const zx::duration render_duration = zx::msec(10);
const zx::duration vsync_interval = zx::msec(10);
predictor_->ReportRenderDuration(render_duration);
predictor_->ReportUpdateDuration(update_duration);
PredictionRequest request = {.now = ms_to_time(0),
.requested_presentation_time = ms_to_time(0),
.last_vsync_time = ms_to_time(0),
.vsync_interval = vsync_interval};
auto prediction = predictor_->GetPrediction(request);
EXPECT_GE(prediction.latch_point_time, request.now);
EXPECT_LE(prediction.presentation_time,
request.last_vsync_time + request.vsync_interval);
}
TEST_F(FramePredictorTest,
ManyExpensiveTimes_ShouldPredictForFutureVsyncIntervals) {
const zx::duration update_duration = zx::msec(4);
const zx::duration render_duration = zx::msec(10);
const zx::duration vsync_interval = zx::msec(10);
for (size_t i = 0; i < 10; i++) {
predictor_->ReportRenderDuration(render_duration);
predictor_->ReportUpdateDuration(update_duration);
}
PredictionRequest request = {.now = ms_to_time(3),
.requested_presentation_time = ms_to_time(0),
.last_vsync_time = ms_to_time(0),
.vsync_interval = vsync_interval};
auto prediction = predictor_->GetPrediction(request);
EXPECT_GE(prediction.latch_point_time, request.now);
EXPECT_GE(prediction.presentation_time,
request.last_vsync_time + request.vsync_interval);
EXPECT_LE(prediction.presentation_time,
request.last_vsync_time + request.vsync_interval * 2);
EXPECT_LE(prediction.latch_point_time,
prediction.presentation_time - request.vsync_interval);
}
TEST_F(FramePredictorTest, ManyFramesOfPredictions_ShouldBeReasonable) {
const zx::duration vsync_interval = zx::msec(10);
zx::time now = ms_to_time(0);
zx::time requested_present = ms_to_time(8);
zx::time last_vsync_time = ms_to_time(0);
for (uint64_t i = 0; i < 50; ++i) {
zx::duration update_duration = zx::msec(i % 5);
zx::duration render_duration = zx::msec(5);
predictor_->ReportUpdateDuration(update_duration);
predictor_->ReportRenderDuration(render_duration);
EXPECT_GE(vsync_interval, update_duration + render_duration);
PredictionRequest request = {.now = now,
.requested_presentation_time = requested_present,
.last_vsync_time = last_vsync_time,
.vsync_interval = vsync_interval};
auto prediction = predictor_->GetPrediction(request);
EXPECT_GE(prediction.latch_point_time, request.now);
EXPECT_GE(prediction.presentation_time, requested_present);
EXPECT_LE(prediction.presentation_time,
requested_present + vsync_interval * 2);
// For the next frame, increase time to be after the predicted present to
// emulate a client that is regularly scheduling frames.
now = prediction.presentation_time + zx::msec(1);
requested_present = prediction.presentation_time + vsync_interval;
last_vsync_time = prediction.presentation_time;
}
}
TEST_F(FramePredictorTest, MissedLastVsync_ShouldPredictWithInterval) {
const zx::duration update_duration = zx::msec(4);
const zx::duration render_duration = zx::msec(5);
predictor_->ReportRenderDuration(render_duration);
predictor_->ReportUpdateDuration(update_duration);
const zx::duration vsync_interval = zx::msec(16);
zx::time last_vsync_time = ms_to_time(16);
// Make now be more than a vsync_interval beyond the last_vsync_time
zx::time now = last_vsync_time + (vsync_interval * 2) + zx::msec(3);
zx::time requested_present = now + zx::msec(9);
PredictionRequest request = {.now = now,
.requested_presentation_time = requested_present,
.last_vsync_time = last_vsync_time,
.vsync_interval = vsync_interval};
auto prediction = predictor_->GetPrediction(request);
// The predicted presentation and wakeup times should be greater than one
// vsync interval since the last reported vsync time.
EXPECT_GE(prediction.presentation_time, last_vsync_time + vsync_interval);
EXPECT_LE(prediction.presentation_time, now + (request.vsync_interval * 2));
EXPECT_LE(prediction.presentation_time - prediction.latch_point_time,
vsync_interval);
}
TEST_F(FramePredictorTest, MissedPresentRequest_ShouldTargetNextVsync) {
const zx::duration update_duration = zx::msec(2);
const zx::duration render_duration = zx::msec(4);
predictor_->ReportRenderDuration(render_duration);
predictor_->ReportUpdateDuration(update_duration);
const zx::duration vsync_interval = zx::msec(10);
zx::time last_vsync_time = ms_to_time(10);
zx::time now = ms_to_time(12);
// Request a present time in the past.
zx::time requested_present = now - zx::msec(1);
PredictionRequest request = {.now = now,
.requested_presentation_time = requested_present,
.last_vsync_time = last_vsync_time,
.vsync_interval = vsync_interval};
auto prediction = predictor_->GetPrediction(request);
EXPECT_GE(prediction.presentation_time, last_vsync_time + vsync_interval);
EXPECT_LE(prediction.presentation_time, last_vsync_time + (vsync_interval * 2));
EXPECT_GE(prediction.latch_point_time,
prediction.presentation_time - vsync_interval);
}
TEST_F(FramePredictorTest, AttemptsToBeLowLatent_ShouldBePossible) {
const zx::duration update_duration = zx::msec(1);
const zx::duration render_duration = zx::msec(3);
predictor_->ReportRenderDuration(render_duration);
predictor_->ReportUpdateDuration(update_duration);
const zx::duration vsync_interval = zx::msec(10);
zx::time last_vsync_time = ms_to_time(10);
zx::time requested_present = last_vsync_time + vsync_interval;
zx::time now =
requested_present - update_duration - render_duration - zx::msec(1);
EXPECT_GT(now, last_vsync_time);
PredictionRequest request = {.now = now,
.requested_presentation_time = requested_present,
.last_vsync_time = last_vsync_time,
.vsync_interval = vsync_interval};
auto prediction = predictor_->GetPrediction(request);
// The prediction should be for the next vsync.
EXPECT_LE(prediction.presentation_time, last_vsync_time + vsync_interval);
EXPECT_GE(prediction.latch_point_time, now);
}
} // namespace test
} // namespace gfx
} // namespace scenic_impl