1 /* vim:set ts=2 sw=2 sts=2 et: */
2 /* Any copyright is dedicated to the Public Domain.
3 * http://creativecommons.org/publicdomain/zero/1.0/
4 */
5
6 #include "gmock/gmock.h"
7 #include "gtest/gtest.h"
8 #include "gfxPlatform.h"
9 #include "gfxPrefs.h"
10 #include "MainThreadUtils.h"
11 #include "nsIThread.h"
12 #include "mozilla/RefPtr.h"
13 #include "SoftwareVsyncSource.h"
14 #include "VsyncSource.h"
15 #include "mozilla/Monitor.h"
16 #include "mozilla/TimeStamp.h"
17 #include "mozilla/VsyncDispatcher.h"
18
19 using namespace mozilla;
20 using namespace mozilla::gfx;
21 using namespace mozilla::layers;
22 using ::testing::_;
23
24 // Timeout for vsync events to occur in milliseconds
25 // Windows 8.1 has intermittents at 50 ms. Raise limit to 5 vsync intervals.
26 const int kVsyncTimeoutMS = 80;
27
28 class TestVsyncObserver : public VsyncObserver {
29 public:
TestVsyncObserver()30 TestVsyncObserver()
31 : mDidGetVsyncNotification(false), mVsyncMonitor("VsyncMonitor") {}
32
NotifyVsync(TimeStamp aVsyncTimeStamp)33 virtual bool NotifyVsync(TimeStamp aVsyncTimeStamp) override {
34 MonitorAutoLock lock(mVsyncMonitor);
35 mDidGetVsyncNotification = true;
36 mVsyncMonitor.Notify();
37 return true;
38 }
39
WaitForVsyncNotification()40 void WaitForVsyncNotification() {
41 MOZ_ASSERT(NS_IsMainThread());
42 if (DidGetVsyncNotification()) {
43 return;
44 }
45
46 { // scope lock
47 MonitorAutoLock lock(mVsyncMonitor);
48 PRIntervalTime timeout = PR_MillisecondsToInterval(kVsyncTimeoutMS);
49 lock.Wait(timeout);
50 }
51 }
52
DidGetVsyncNotification()53 bool DidGetVsyncNotification() {
54 MonitorAutoLock lock(mVsyncMonitor);
55 return mDidGetVsyncNotification;
56 }
57
ResetVsyncNotification()58 void ResetVsyncNotification() {
59 MonitorAutoLock lock(mVsyncMonitor);
60 mDidGetVsyncNotification = false;
61 }
62
63 private:
64 bool mDidGetVsyncNotification;
65
66 private:
67 Monitor mVsyncMonitor;
68 };
69
70 class VsyncTester : public ::testing::Test {
71 protected:
VsyncTester()72 explicit VsyncTester() {
73 gfxPlatform::GetPlatform();
74 gfxPrefs::GetSingleton();
75 mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
76 MOZ_RELEASE_ASSERT(mVsyncSource, "GFX: Vsync source not found.");
77 }
78
~VsyncTester()79 virtual ~VsyncTester() { mVsyncSource = nullptr; }
80
81 RefPtr<VsyncSource> mVsyncSource;
82 };
83
FlushMainThreadLoop()84 static void FlushMainThreadLoop() {
85 // Some tasks are pushed onto the main thread when adding vsync observers
86 // This function will ensure all tasks are executed on the main thread
87 // before returning.
88 nsCOMPtr<nsIThread> mainThread;
89 nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
90 ASSERT_TRUE(NS_SUCCEEDED(rv));
91
92 rv = NS_OK;
93 bool processed = true;
94 while (processed && NS_SUCCEEDED(rv)) {
95 rv = mainThread->ProcessNextEvent(false, &processed);
96 }
97 }
98
99 // Tests that we can enable/disable vsync notifications
TEST_F(VsyncTester,EnableVsync)100 TEST_F(VsyncTester, EnableVsync) {
101 VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
102 globalDisplay.DisableVsync();
103 ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
104
105 globalDisplay.EnableVsync();
106 ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
107
108 globalDisplay.DisableVsync();
109 ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
110 }
111
112 // Test that if we have vsync enabled, the display should get vsync
113 // notifications
TEST_F(VsyncTester,CompositorGetVsyncNotifications)114 TEST_F(VsyncTester, CompositorGetVsyncNotifications) {
115 VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
116 globalDisplay.DisableVsync();
117 ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
118
119 RefPtr<CompositorVsyncDispatcher> vsyncDispatcher =
120 new CompositorVsyncDispatcher();
121 RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
122
123 vsyncDispatcher->SetCompositorVsyncObserver(testVsyncObserver);
124 FlushMainThreadLoop();
125 ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
126
127 testVsyncObserver->WaitForVsyncNotification();
128 ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
129
130 vsyncDispatcher = nullptr;
131 testVsyncObserver = nullptr;
132 }
133
134 // Test that if we have vsync enabled, the parent refresh driver should get
135 // notifications
TEST_F(VsyncTester,ParentRefreshDriverGetVsyncNotifications)136 TEST_F(VsyncTester, ParentRefreshDriverGetVsyncNotifications) {
137 VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
138 globalDisplay.DisableVsync();
139 ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
140
141 RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher =
142 globalDisplay.GetRefreshTimerVsyncDispatcher();
143 ASSERT_TRUE(vsyncDispatcher != nullptr);
144
145 RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
146 vsyncDispatcher->SetParentRefreshTimer(testVsyncObserver);
147 ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
148
149 testVsyncObserver->WaitForVsyncNotification();
150 ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
151 vsyncDispatcher->SetParentRefreshTimer(nullptr);
152
153 testVsyncObserver->ResetVsyncNotification();
154 testVsyncObserver->WaitForVsyncNotification();
155 ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
156
157 vsyncDispatcher = nullptr;
158 testVsyncObserver = nullptr;
159 }
160
161 // Test that child refresh vsync observers get vsync notifications
TEST_F(VsyncTester,ChildRefreshDriverGetVsyncNotifications)162 TEST_F(VsyncTester, ChildRefreshDriverGetVsyncNotifications) {
163 VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
164 globalDisplay.DisableVsync();
165 ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
166
167 RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher =
168 globalDisplay.GetRefreshTimerVsyncDispatcher();
169 ASSERT_TRUE(vsyncDispatcher != nullptr);
170
171 RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
172 vsyncDispatcher->AddChildRefreshTimer(testVsyncObserver);
173 ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
174
175 testVsyncObserver->WaitForVsyncNotification();
176 ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
177
178 vsyncDispatcher->RemoveChildRefreshTimer(testVsyncObserver);
179 testVsyncObserver->ResetVsyncNotification();
180 testVsyncObserver->WaitForVsyncNotification();
181 ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
182
183 vsyncDispatcher = nullptr;
184 testVsyncObserver = nullptr;
185 }
186
187 // Test that we can read the vsync rate
TEST_F(VsyncTester,VsyncSourceHasVsyncRate)188 TEST_F(VsyncTester, VsyncSourceHasVsyncRate) {
189 VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
190 TimeDuration vsyncRate = globalDisplay.GetVsyncRate();
191 ASSERT_NE(vsyncRate, TimeDuration::Forever());
192 ASSERT_GT(vsyncRate.ToMilliseconds(), 0);
193 }
194