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