.: Replace "Apple Computer, Inc." with "Apple Inc." in copyright headers
[WebKit-https.git] / Source / WebCore / platform / graphics / DisplayRefreshMonitor.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)
29
30 #include "DisplayRefreshMonitor.h"
31 #include <wtf/CurrentTime.h>
32 #include <wtf/Ref.h>
33
34 namespace WebCore {
35
36 DisplayRefreshMonitorClient::DisplayRefreshMonitorClient()
37     : m_scheduled(false)
38     , m_displayIDIsSet(false)
39 {
40 }
41
42 DisplayRefreshMonitorClient::~DisplayRefreshMonitorClient()
43 {
44     DisplayRefreshMonitorManager::sharedManager()->unregisterClient(this);
45 }
46
47 void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(double timestamp)
48 {
49     if (m_scheduled) {
50         m_scheduled = false;
51         displayRefreshFired(timestamp);
52     }
53 }
54
55 DisplayRefreshMonitor::DisplayRefreshMonitor(PlatformDisplayID displayID)
56     : m_monotonicAnimationStartTime(0)
57     , m_active(true)
58     , m_scheduled(false)
59     , m_previousFrameDone(true)
60     , m_unscheduledFireCount(0)
61     , m_displayID(displayID)
62     , m_clientsToBeNotified(nullptr)
63 #if PLATFORM(COCOA)
64     , m_displayLink(0)
65 #endif
66 {
67 }
68
69 void DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(void* data)
70 {
71     DisplayRefreshMonitor* monitor = static_cast<DisplayRefreshMonitor*>(data);
72     monitor->displayDidRefresh();
73 }
74
75 void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient* client)
76 {
77     m_clients.add(client);
78 }
79
80 bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient* client)
81 {
82     if (m_clientsToBeNotified)
83         m_clientsToBeNotified->remove(client);
84     return m_clients.remove(client);
85 }
86
87 void DisplayRefreshMonitor::displayDidRefresh()
88 {
89     double monotonicAnimationStartTime;
90     {
91         MutexLocker lock(m_mutex);
92         if (!m_scheduled)
93             ++m_unscheduledFireCount;
94         else
95             m_unscheduledFireCount = 0;
96
97         m_scheduled = false;
98         monotonicAnimationStartTime = m_monotonicAnimationStartTime;
99     }
100
101     // The call back can cause all our clients to be unregistered, so we need to protect
102     // against deletion until the end of the method.
103     Ref<DisplayRefreshMonitor> protect(*this);
104
105     // Copy the hash table and remove clients from it one by one so we don't notify
106     // any client twice, but can respond to removal of clients during the delivery process.
107     HashSet<DisplayRefreshMonitorClient*> clientsToBeNotified = m_clients;
108     m_clientsToBeNotified = &clientsToBeNotified;
109     while (!clientsToBeNotified.isEmpty()) {
110         // Take a random client out of the set. Ordering doesn't matter.
111         // FIXME: Would read more cleanly if HashSet had a take function.
112         auto it = clientsToBeNotified.begin();
113         DisplayRefreshMonitorClient* client = *it;
114         clientsToBeNotified.remove(it);
115
116         client->fireDisplayRefreshIfNeeded(monotonicAnimationStartTime);
117
118         // This checks if this function was reentered. In that case, stop iterating
119         // since it's not safe to use the set any more.
120         if (m_clientsToBeNotified != &clientsToBeNotified)
121             break;
122     }
123     if (m_clientsToBeNotified == &clientsToBeNotified)
124         m_clientsToBeNotified = nullptr;
125
126     {
127         MutexLocker lock(m_mutex);
128         m_previousFrameDone = true;
129     }
130     
131     DisplayRefreshMonitorManager::sharedManager()->displayDidRefresh(this);
132 }
133
134 DisplayRefreshMonitorManager* DisplayRefreshMonitorManager::sharedManager()
135 {
136     DEPRECATED_DEFINE_STATIC_LOCAL(DisplayRefreshMonitorManager, manager, ());
137     return &manager;
138 }
139
140 DisplayRefreshMonitor* DisplayRefreshMonitorManager::ensureMonitorForClient(DisplayRefreshMonitorClient* client)
141 {
142     DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID);
143     if (it == m_monitors.end()) {
144         RefPtr<DisplayRefreshMonitor> monitor = DisplayRefreshMonitor::create(client->m_displayID);
145         monitor->addClient(client);
146         DisplayRefreshMonitor* result = monitor.get();
147         m_monitors.add(client->m_displayID, monitor.release());
148         return result;
149     }
150     it->value->addClient(client);
151     return it->value.get();
152 }
153
154 void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient* client)
155 {
156     if (!client->m_displayIDIsSet)
157         return;
158         
159     ensureMonitorForClient(client);
160 }
161
162 void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient* client)
163 {
164     if (!client->m_displayIDIsSet)
165         return;
166
167     DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID);
168     if (it == m_monitors.end())
169         return;
170     
171     DisplayRefreshMonitor* monitor = it->value.get();
172     if (monitor->removeClient(client)) {
173         if (!monitor->hasClients())
174             m_monitors.remove(it);
175     }
176 }
177
178 bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient* client)
179 {
180     if (!client->m_displayIDIsSet)
181         return false;
182         
183     DisplayRefreshMonitor* monitor = ensureMonitorForClient(client);
184
185     client->m_scheduled = true;
186     return monitor->requestRefreshCallback();
187 }
188
189 void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor* monitor)
190 {
191     if (monitor->shouldBeTerminated()) {
192         ASSERT(m_monitors.contains(monitor->displayID()));
193         m_monitors.remove(monitor->displayID());
194     }
195 }
196
197 void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient* client)
198 {
199     if (client->m_displayIDIsSet && client->m_displayID == displayID)
200         return;
201     
202     unregisterClient(client);
203     client->setDisplayID(displayID);
204     registerClient(client);
205     if (client->m_scheduled)
206         scheduleAnimation(client);
207 }
208
209 }
210
211 #endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR)