blob: e0cca85192e76dce0822e653fe9515a191276f5c [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 <lib/gtest/test_loop_fixture.h>
#include "garnet/lib/ui/gfx/tests/frame_scheduler_test.h"
namespace scenic_impl {
namespace gfx {
namespace test {
TEST_F(FrameSchedulerTest, PresentTimeZero_ShouldBeScheduledBeforeNextVsync) {
auto scheduler = CreateDefaultFrameScheduler();
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
// Schedule an update for as soon as possible.
scheduler->ScheduleUpdateForSession(/* presentation time*/ 0,
/* session id */ 1);
// Wait for one vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
mock_renderer_->EndFrame(/* frame index */ 0);
// Should have been scheduled and handled.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
}
TEST_F(FrameSchedulerTest,
PresentBiggerThanNextVsync_ShouldBeScheduledAfterNextVsync) {
auto scheduler = CreateDefaultFrameScheduler();
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
// Schedule an update for in between the next two vsyncs.
zx_time_t time_after_vsync = fake_display_->GetLastVsyncTime() +
(1.5 * fake_display_->GetVsyncInterval());
scheduler->ScheduleUpdateForSession(/* presentation time*/ time_after_vsync,
/* session id */ 1);
// Wait for one vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
// Nothing should have been scheduled yet.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
// Wait for one more vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
EXPECT_EQ(mock_renderer_->pending_frames(), 1u);
mock_renderer_->EndFrame(/* frame index */ 0);
// Should have been scheduled and handled now.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
}
TEST_F(FrameSchedulerTest, SinglePresent_ShouldGetSingleRenderCall) {
auto scheduler = CreateDefaultFrameScheduler();
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_updater_->ratchet_present_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 0u);
scheduler->ScheduleUpdateForSession(Now().get(), /* session id */ 1);
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
// Wait for one vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
// Present should have been scheduled and handled.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_updater_->ratchet_present_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 0u);
// End the pending frame.
EXPECT_EQ(mock_renderer_->pending_frames(), 1u);
mock_renderer_->EndFrame(/* frame index */ 0);
EXPECT_EQ(mock_renderer_->pending_frames(), 0u);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 1u);
// Wait for a very long time.
RunLoopFor(zx::sec(10));
// No further render calls should have been made.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 1u);
}
TEST_F(FrameSchedulerTest, PresentsForTheSameFrame_ShouldGetSingleRenderCall) {
auto scheduler = CreateDefaultFrameScheduler();
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
SessionId session_id1 = 1;
SessionId session_id2 = 2;
// Schedule two updates for now.
zx_time_t now = Now().get();
scheduler->ScheduleUpdateForSession(now, session_id1);
scheduler->ScheduleUpdateForSession(now, session_id2);
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
// Wait for one vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
// Both Presents should have been scheduled and handled.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
// End the pending frame.
EXPECT_EQ(mock_renderer_->pending_frames(), 1u);
mock_renderer_->EndFrame(/* frame index */ 0);
EXPECT_EQ(mock_renderer_->pending_frames(), 0u);
// Wait for a very long time.
RunLoopFor(zx::sec(10));
// No further render calls should have been made.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
}
TEST_F(FrameSchedulerTest,
PresentsForDifferentFrames_ShouldGetSeparateRenderCalls) {
auto scheduler = CreateDefaultFrameScheduler();
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
SessionId session_id = 1;
// Schedule an update for now.
zx_time_t now = Now().get();
scheduler->ScheduleUpdateForSession(now, session_id);
// Schedule an update for in between the next two vsyncs.
zx_time_t time_after_vsync = fake_display_->GetLastVsyncTime() +
(1.5 * fake_display_->GetVsyncInterval());
scheduler->ScheduleUpdateForSession(time_after_vsync, session_id);
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
// Wait for one vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
// First Present should have been scheduled and handled.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
EXPECT_EQ(mock_renderer_->pending_frames(), 1u);
mock_renderer_->EndFrame(/* frame index */ 0);
// Wait for one more vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
// Second Present should have been scheduled and handled.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 2u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 2u);
}
TEST_F(FrameSchedulerTest,
SecondPresentDuringRender_ShouldApplyUpdatesAndReschedule) {
auto scheduler = CreateDefaultFrameScheduler();
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_updater_->ratchet_present_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 0u);
SessionId session_id = 1;
// Schedule an update for now.
zx_time_t now = Now().get();
scheduler->ScheduleUpdateForSession(now, session_id);
// Wait for one vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_updater_->ratchet_present_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
EXPECT_EQ(mock_renderer_->pending_frames(), 1u);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 0u);
// Schedule another update for now.
scheduler->ScheduleUpdateForSession(now, session_id);
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
// Updates should be applied, but not rendered.
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 2u);
EXPECT_EQ(mock_updater_->ratchet_present_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 0u);
// End previous frame.
EXPECT_EQ(mock_renderer_->pending_frames(), 1u);
mock_renderer_->EndFrame(/* frame index */ 0);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 1u);
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
// Second render should have occurred.
EXPECT_EQ(mock_updater_->ratchet_present_call_count(), 2u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 2u);
mock_renderer_->EndFrame(/* frame index */ 0);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 2u);
}
TEST_F(FrameSchedulerTest, RenderCalls_ShouldNotExceed_MaxOutstandingFrames) {
auto scheduler = CreateDefaultFrameScheduler();
SessionId session_id = 1;
auto maximum_allowed_render_calls = scheduler->kMaxOutstandingFrames;
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
// Schedule more updates than the maximum, and signal them rendered but not
// presented.
zx_time_t now = Now().get();
for (size_t i = 0; i < maximum_allowed_render_calls + 1; ++i) {
scheduler->ScheduleUpdateForSession(now, session_id);
// Wait for a long time
RunLoopFor(zx::duration(5 * fake_display_->GetVsyncInterval()));
if (mock_renderer_->render_frame_call_count() <= i) {
break;
}
// Signal frame rendered.
mock_renderer_->SignalFrameRendered(i);
}
EXPECT_LE(mock_renderer_->render_frame_call_count(),
maximum_allowed_render_calls);
}
TEST_F(FrameSchedulerTest,
SignalPreviousFramesPresented_ShouldBeCalledOnlyOnFramePresented) {
auto scheduler = CreateDefaultFrameScheduler();
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
SessionId session_id = 1;
// Schedule an update for now.
zx_time_t now = Now().get();
scheduler->ScheduleUpdateForSession(now, session_id);
// Wait for one vsync period.
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
// Schedule another update.
scheduler->ScheduleUpdateForSession(now, session_id);
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
// Dropped frame should not trigger frame presented signal.
mock_renderer_->SignalFrameDropped(/* frame index */ 0);
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 2u);
// Presented frame should trigger frame presented signal.
mock_renderer_->SignalFrameRendered(/* frame index */ 0);
mock_renderer_->SignalFramePresented(/* frame index */ 0);
EXPECT_EQ(mock_updater_->signal_previous_frames_presented_call_count(), 1u);
}
TEST_F(FrameSchedulerTest, FailedUpdate_ShouldNotTriggerRenderCall) {
auto scheduler = CreateDefaultFrameScheduler();
SessionId session_id = 1;
mock_updater_->SetUpdateSessionsReturnValue({.needs_render = false});
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
scheduler->ScheduleUpdateForSession(Now().get(), session_id);
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
}
TEST_F(FrameSchedulerTest,
NoOpUpdateWithSecondPendingUpdate_ShouldBeRescheduled) {
auto scheduler = CreateDefaultFrameScheduler();
SessionId session_id = 1;
mock_updater_->SetUpdateSessionsReturnValue({.needs_render = false});
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 0u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
scheduler->ScheduleUpdateForSession(Now().get(), session_id);
scheduler->ScheduleUpdateForSession(
Now().get() + fake_display_->GetVsyncInterval(), session_id);
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 1u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 0u);
mock_updater_->SetUpdateSessionsReturnValue({.needs_render = true});
RunLoopFor(zx::duration(fake_display_->GetVsyncInterval()));
EXPECT_EQ(mock_updater_->update_sessions_call_count(), 2u);
EXPECT_EQ(mock_renderer_->render_frame_call_count(), 1u);
}
} // namespace test
} // namespace gfx
} // namespace scenic_impl