1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7 #include "SoftwareVsyncSource.h"
8 #include "base/task.h"
9 #include "gfxPlatform.h"
10 #include "nsThreadUtils.h"
11
12 using namespace mozilla;
13
SoftwareVsyncSource()14 SoftwareVsyncSource::SoftwareVsyncSource()
15 {
16 MOZ_ASSERT(NS_IsMainThread());
17 mGlobalDisplay = new SoftwareDisplay();
18 }
19
~SoftwareVsyncSource()20 SoftwareVsyncSource::~SoftwareVsyncSource()
21 {
22 MOZ_ASSERT(NS_IsMainThread());
23 mGlobalDisplay = nullptr;
24 }
25
SoftwareDisplay()26 SoftwareDisplay::SoftwareDisplay()
27 : mVsyncEnabled(false)
28 {
29 // Mimic 60 fps
30 MOZ_ASSERT(NS_IsMainThread());
31 const double rate = 1000.0 / (double) gfxPlatform::GetSoftwareVsyncRate();
32 mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate);
33 mVsyncThread = new base::Thread("SoftwareVsyncThread");
34 MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "GFX: Could not start software vsync thread");
35 }
36
~SoftwareDisplay()37 SoftwareDisplay::~SoftwareDisplay() {}
38
39 void
EnableVsync()40 SoftwareDisplay::EnableVsync()
41 {
42 MOZ_ASSERT(mVsyncThread->IsRunning());
43 if (NS_IsMainThread()) {
44 if (mVsyncEnabled) {
45 return;
46 }
47 mVsyncEnabled = true;
48
49 mVsyncThread->message_loop()->PostTask(
50 NewRunnableMethod(this, &SoftwareDisplay::EnableVsync));
51 return;
52 }
53
54 MOZ_ASSERT(IsInSoftwareVsyncThread());
55 NotifyVsync(mozilla::TimeStamp::Now());
56 }
57
58 void
DisableVsync()59 SoftwareDisplay::DisableVsync()
60 {
61 MOZ_ASSERT(mVsyncThread->IsRunning());
62 if (NS_IsMainThread()) {
63 if (!mVsyncEnabled) {
64 return;
65 }
66 mVsyncEnabled = false;
67
68 mVsyncThread->message_loop()->PostTask(
69 NewRunnableMethod(this, &SoftwareDisplay::DisableVsync));
70 return;
71 }
72
73 MOZ_ASSERT(IsInSoftwareVsyncThread());
74 if (mCurrentVsyncTask) {
75 mCurrentVsyncTask->Cancel();
76 mCurrentVsyncTask = nullptr;
77 }
78 }
79
80 bool
IsVsyncEnabled()81 SoftwareDisplay::IsVsyncEnabled()
82 {
83 MOZ_ASSERT(NS_IsMainThread());
84 return mVsyncEnabled;
85 }
86
87 bool
IsInSoftwareVsyncThread()88 SoftwareDisplay::IsInSoftwareVsyncThread()
89 {
90 return mVsyncThread->thread_id() == PlatformThread::CurrentId();
91 }
92
93 void
NotifyVsync(mozilla::TimeStamp aVsyncTimestamp)94 SoftwareDisplay::NotifyVsync(mozilla::TimeStamp aVsyncTimestamp)
95 {
96 MOZ_ASSERT(IsInSoftwareVsyncThread());
97
98 mozilla::TimeStamp displayVsyncTime = aVsyncTimestamp;
99 mozilla::TimeStamp now = mozilla::TimeStamp::Now();
100 // Posted tasks can only have integer millisecond delays
101 // whereas TimeDurations can have floating point delays.
102 // Thus the vsync timestamp can be in the future, which large parts
103 // of the system can't handle, including animations. Force the timestamp to be now.
104 if (aVsyncTimestamp > now) {
105 displayVsyncTime = now;
106 }
107
108 Display::NotifyVsync(displayVsyncTime);
109
110 // Prevent skew by still scheduling based on the original
111 // vsync timestamp
112 ScheduleNextVsync(aVsyncTimestamp);
113 }
114
115 mozilla::TimeDuration
GetVsyncRate()116 SoftwareDisplay::GetVsyncRate()
117 {
118 return mVsyncRate;
119 }
120
121 void
ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp)122 SoftwareDisplay::ScheduleNextVsync(mozilla::TimeStamp aVsyncTimestamp)
123 {
124 MOZ_ASSERT(IsInSoftwareVsyncThread());
125 mozilla::TimeStamp nextVsync = aVsyncTimestamp + mVsyncRate;
126 mozilla::TimeDuration delay = nextVsync - mozilla::TimeStamp::Now();
127 if (delay.ToMilliseconds() < 0) {
128 delay = mozilla::TimeDuration::FromMilliseconds(0);
129 nextVsync = mozilla::TimeStamp::Now();
130 }
131
132 mCurrentVsyncTask =
133 NewCancelableRunnableMethod<mozilla::TimeStamp>(this,
134 &SoftwareDisplay::NotifyVsync,
135 nextVsync);
136
137 RefPtr<Runnable> addrefedTask = mCurrentVsyncTask;
138 mVsyncThread->message_loop()->PostDelayedTask(
139 addrefedTask.forget(),
140 delay.ToMilliseconds());
141 }
142
143 void
Shutdown()144 SoftwareDisplay::Shutdown()
145 {
146 MOZ_ASSERT(NS_IsMainThread());
147 DisableVsync();
148 mVsyncThread->Stop();
149 delete mVsyncThread;
150 }
151