| // Copyright 2016 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "base/time.h" |
| #include "cobalt/render_tree/composition_node.h" |
| #include "cobalt/renderer/submission.h" |
| #include "cobalt/renderer/submission_queue.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using cobalt::renderer::Submission; |
| using cobalt::renderer::SubmissionQueue; |
| |
| namespace { |
| const int kMaxQueueSize = 4; |
| |
| Submission MakeSubmissionWithUniqueRenderTree( |
| const base::TimeDelta& time_offset) { |
| // Create a dummy, but unique, render tree. |
| cobalt::render_tree::CompositionNode::Builder builder; |
| Submission submission(new cobalt::render_tree::CompositionNode(builder)); |
| submission.time_offset = time_offset; |
| |
| return submission; |
| } |
| |
| Submission MakeSubmissionWithUniqueRenderTree(double time_offset_in_seconds) { |
| return MakeSubmissionWithUniqueRenderTree( |
| base::TimeDelta::FromSecondsD(time_offset_in_seconds)); |
| } |
| |
| base::TimeTicks SecondsToTime(double seconds) { |
| return base::TimeTicks() + base::TimeDelta::FromSecondsD(seconds); |
| } |
| } // namespace |
| |
| // Simple "make sure we get the single item out what we put in" test. |
| TEST(SubmissionQueueTest, SimpleSubmit) { |
| SubmissionQueue queue(4, base::TimeDelta::FromSeconds(1)); |
| |
| // Make a submission with submission time of 1.0 seconds. |
| Submission first = MakeSubmissionWithUniqueRenderTree(1.0); |
| // Submit this to the queue at renderer time 2.0 seconds. |
| queue.PushSubmission(first, SecondsToTime(2.0)); |
| |
| // Get the current submission at the "same" time that the only queued |
| // submission was pushed into it. |
| Submission current = queue.GetCurrentSubmission(SecondsToTime(2.0)); |
| |
| // Verify that it is the render tree we expect it to be and also that its |
| // time is exactly equal to the submission's original time. |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(1.0, current.time_offset.InSecondsF()); |
| } |
| |
| // Test that the returned current submission has a time offset that increases |
| // as renderer time increases. |
| TEST(SubmissionQueueTest, SubmissionTimeOffsetIncreases) { |
| SubmissionQueue queue(4, base::TimeDelta::FromSeconds(1)); |
| |
| Submission first = MakeSubmissionWithUniqueRenderTree(1.0); |
| queue.PushSubmission(first, SecondsToTime(2.0)); |
| |
| // Check that as we increase the renderer time, the current submission time |
| // increases at the same pace. |
| Submission current = queue.GetCurrentSubmission(SecondsToTime(2.0)); |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(1.0, current.time_offset.InSecondsF()); |
| |
| current = queue.GetCurrentSubmission(SecondsToTime(2.5)); |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(1.5, current.time_offset.InSecondsF()); |
| |
| current = queue.GetCurrentSubmission(SecondsToTime(6.0)); |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(5.0, current.time_offset.InSecondsF()); |
| } |
| |
| // This test makes sure that if we queue multiple submissions, we will see each |
| // of them become the current submission as time moves forward. |
| TEST(SubmissionQueueTest, MultipleSubmissionsBecomeCurrentAsTimeAdvances) { |
| // We choose a very long time for the convergence time so that the renderer |
| // time will not skew much during this test. |
| SubmissionQueue queue(4, base::TimeDelta::FromSeconds(1000)); |
| |
| Submission first = MakeSubmissionWithUniqueRenderTree(1.0); |
| Submission second = MakeSubmissionWithUniqueRenderTree(2.0); |
| Submission third = MakeSubmissionWithUniqueRenderTree(3.0); |
| Submission fourth = MakeSubmissionWithUniqueRenderTree(4.0); |
| |
| queue.PushSubmission(first, SecondsToTime(2.0)); |
| queue.PushSubmission(second, SecondsToTime(2.1)); |
| queue.PushSubmission(third, SecondsToTime(2.2)); |
| queue.PushSubmission(fourth, SecondsToTime(2.3)); |
| |
| // Check that as we increase the renderer time, the current submission time |
| // increases at the same pace. |
| Submission current = queue.GetCurrentSubmission(SecondsToTime(2.5)); |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| |
| current = queue.GetCurrentSubmission(SecondsToTime(3.5)); |
| EXPECT_EQ(second.render_tree, current.render_tree); |
| |
| current = queue.GetCurrentSubmission(SecondsToTime(4.5)); |
| EXPECT_EQ(third.render_tree, current.render_tree); |
| |
| current = queue.GetCurrentSubmission(SecondsToTime(5.5)); |
| EXPECT_EQ(fourth.render_tree, current.render_tree); |
| } |
| |
| TEST(SubmissionQueueTest, TimeSkewsTowardsFasterOffsets) { |
| SubmissionQueue queue(4, base::TimeDelta::FromSeconds(1)); |
| |
| Submission first = MakeSubmissionWithUniqueRenderTree(1.0); |
| Submission second = MakeSubmissionWithUniqueRenderTree(5.0); |
| |
| // Offset of 1.0 from render tree time. |
| queue.PushSubmission(first, SecondsToTime(2.0)); |
| // Offset of 0.5 from render tree time. |
| queue.PushSubmission(second, SecondsToTime(5.5)); |
| |
| // Check that the first submission has up until this point had its time |
| // advanced at the same rate as the renderer. |
| Submission current = queue.GetCurrentSubmission(SecondsToTime(5.5)); |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(4.5, current.time_offset.InSecondsF()); |
| |
| // At this point we are half-way through the transition, where the renderer |
| // moves from being offset 1.0 from the submission time to 0.5 from the |
| // transition time. At the half-way point, we should also be exactly half |
| // way through the transition (assuming a cubic bezier is used), so that |
| // means our offset should be 0.75 at this point, so we have moved 0.25 |
| // seconds forward in time. |
| current = queue.GetCurrentSubmission(SecondsToTime(6.0)); |
| EXPECT_EQ(second.render_tree, current.render_tree); |
| EXPECT_NEAR(5.25, current.time_offset.InSecondsF(), 0.001); |
| |
| // After 1 second later, we should always be at offset 0.5 from the submission |
| // time. |
| current = queue.GetCurrentSubmission(SecondsToTime(8.0)); |
| EXPECT_EQ(second.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(7.5, current.time_offset.InSecondsF()); |
| } |
| |
| TEST(SubmissionQueueTest, |
| TimeDoesNotSkewTowardsFasterOffsetsWhenLatencyReductionIsDisabled) { |
| SubmissionQueue queue(4, base::TimeDelta::FromSeconds(1), false); |
| |
| Submission first = MakeSubmissionWithUniqueRenderTree(1.0); |
| Submission second = MakeSubmissionWithUniqueRenderTree(5.0); |
| |
| // Offset of 1.0 from render tree time. |
| queue.PushSubmission(first, SecondsToTime(2.0)); |
| // Offset of 0.5 from render tree time. |
| queue.PushSubmission(second, SecondsToTime(5.5)); |
| |
| // Check that the first submission has up until this point had its time |
| // advanced at the same rate as the renderer. |
| Submission current = queue.GetCurrentSubmission(SecondsToTime(5.5)); |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(4.5, current.time_offset.InSecondsF()); |
| |
| // At this point we are half-way through the transition. Ordinarily the |
| // submission queue would try to transition from the initial offset of 1.0 |
| // to the new offset of 0.5, but because latency reduction is disabled, the |
| // offset should never increase and it should stay at 1.0. |
| current = queue.GetCurrentSubmission(SecondsToTime(6.0)); |
| EXPECT_EQ(second.render_tree, current.render_tree); |
| EXPECT_NEAR(5, current.time_offset.InSecondsF(), 0.001); |
| |
| // After 1 second later, we should still be unchanged from offset 1.0 |
| current = queue.GetCurrentSubmission(SecondsToTime(8.0)); |
| EXPECT_EQ(second.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(7, current.time_offset.InSecondsF()); |
| |
| // A third submission that increases the latency should indeed still affect |
| // the submission queue transitioned time. |
| Submission third = MakeSubmissionWithUniqueRenderTree(9.5); |
| queue.PushSubmission(third, SecondsToTime(11.0)); |
| |
| // Nothing should have changed yet... |
| current = queue.GetCurrentSubmission(SecondsToTime(11.0)); |
| EXPECT_EQ(third.render_tree, current.render_tree); |
| EXPECT_NEAR(10, current.time_offset.InSecondsF(), 0.001); |
| |
| // We should be transitioning to a larger offset now... |
| current = queue.GetCurrentSubmission(SecondsToTime(11.5)); |
| EXPECT_EQ(third.render_tree, current.render_tree); |
| EXPECT_NEAR(10.25, current.time_offset.InSecondsF(), 0.001); |
| } |
| |
| // Check that inserting a submission older than what the renderer thinks the |
| // submission time is will cause the SubmissionQueue to jump back in time to |
| // make to accomodate the newly pushed old submission. |
| TEST(SubmissionQueueTest, PushingOldTimeResetsQueue) { |
| SubmissionQueue queue(4, base::TimeDelta::FromSeconds(1)); |
| |
| Submission first = MakeSubmissionWithUniqueRenderTree(1.0); |
| Submission second = MakeSubmissionWithUniqueRenderTree(2.0); |
| Submission third = MakeSubmissionWithUniqueRenderTree(3.0); |
| |
| queue.PushSubmission(first, SecondsToTime(2.0)); |
| queue.PushSubmission(second, SecondsToTime(2.5)); |
| |
| // Check that we're still returning the first tree at this point. |
| Submission current = queue.GetCurrentSubmission(SecondsToTime(2.5)); |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(1.5, current.time_offset.InSecondsF()); |
| |
| queue.PushSubmission(third, SecondsToTime(5.0)); |
| |
| // We expect the submission queue to not do anything rash at this point, even |
| // though it was inserted late. |
| current = queue.GetCurrentSubmission(SecondsToTime(5.0)); |
| EXPECT_EQ(third.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(4.5, current.time_offset.InSecondsF()); |
| |
| // Make sure that we never go backwards in time, even if the difference |
| // between the render time and submission time is shrinking quickly. |
| current = queue.GetCurrentSubmission(SecondsToTime(5.5)); |
| EXPECT_EQ(third.render_tree, current.render_tree); |
| EXPECT_DOUBLE_EQ(4.874633, current.time_offset.InSecondsF()); |
| } |
| |
| // This tests that pushing a submission into a full queue will result in the |
| // oldest submission to be forcefully removed, and time artifically advanced |
| // up to the next oldest submission. |
| TEST(SubmissionQueueTest, FullQueueWillEjectOldestSubmission) { |
| // Loop over this with many different values to test that numerical precision |
| // does not cause any problems here. |
| for (int64 i = 0; i < 1000; ++i) { |
| // We choose a very long time for the convergence time so that the renderer |
| // time will not skew much during this test. |
| SubmissionQueue queue(4, base::TimeDelta::FromSeconds(1000)); |
| |
| base::TimeDelta start_time = |
| base::TimeDelta::FromMicroseconds(i * i * i * i); |
| |
| Submission first = MakeSubmissionWithUniqueRenderTree( |
| base::TimeDelta::FromSeconds(1) + start_time); |
| Submission second = MakeSubmissionWithUniqueRenderTree( |
| base::TimeDelta::FromSeconds(2) + start_time); |
| Submission third = MakeSubmissionWithUniqueRenderTree( |
| base::TimeDelta::FromSeconds(3) + start_time); |
| Submission fourth = MakeSubmissionWithUniqueRenderTree( |
| base::TimeDelta::FromSeconds(4) + start_time); |
| |
| queue.PushSubmission(first, SecondsToTime(2.0)); |
| queue.PushSubmission(second, SecondsToTime(2.1)); |
| queue.PushSubmission(third, SecondsToTime(2.2)); |
| queue.PushSubmission(fourth, SecondsToTime(2.3)); |
| |
| Submission current = queue.GetCurrentSubmission(SecondsToTime(2.3)); |
| EXPECT_EQ(first.render_tree, current.render_tree); |
| |
| // Since the queue has a maximum size of 4 submissions, pushing this 5th |
| // submission should result in the first submission being ejected, and time |
| // being advanced to that of the second submission. |
| Submission fifth = MakeSubmissionWithUniqueRenderTree( |
| base::TimeDelta::FromSeconds(5) + start_time); |
| queue.PushSubmission(fifth, SecondsToTime(2.4)); |
| |
| current = queue.GetCurrentSubmission(SecondsToTime(2.4)); |
| EXPECT_EQ(second.render_tree, current.render_tree); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(2) + start_time, |
| current.time_offset); |
| } |
| } |