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 COMPUTER, 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 33namespace WebCore { 34 35DisplayRefreshMonitorClient::DisplayRefreshMonitorClient() 36 : m_scheduled(false) 37 , m_displayIDIsSet(false) 38{ 39} 40 41DisplayRefreshMonitorClient::~DisplayRefreshMonitorClient() 42{ 43 DisplayRefreshMonitorManager::sharedManager()->unregisterClient(this); 44} 45 46void DisplayRefreshMonitorClient::fireDisplayRefreshIfNeeded(double timestamp) 47{ 48 if (m_scheduled) { 49 m_scheduled = false; 50 displayRefreshFired(timestamp); 51 } 52} 53 54DisplayRefreshMonitor::DisplayRefreshMonitor(PlatformDisplayID displayID) 55 : m_monotonicAnimationStartTime(0) 56 , m_active(true) 57 , m_scheduled(false) 58 , m_previousFrameDone(true) 59 , m_unscheduledFireCount(0) 60 , m_displayID(displayID) 61 , m_clientsToBeNotified(nullptr) 62#if PLATFORM(MAC) 63 , m_displayLink(0) 64#endif 65#if PLATFORM(BLACKBERRY) 66 , m_animationClient(0) 67#endif 68{ 69} 70 71void DisplayRefreshMonitor::handleDisplayRefreshedNotificationOnMainThread(void* data) 72{ 73 DisplayRefreshMonitor* monitor = static_cast<DisplayRefreshMonitor*>(data); 74 monitor->displayDidRefresh(); 75} 76 77void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient* client) 78{ 79 m_clients.add(client); 80} 81 82bool DisplayRefreshMonitor::removeClient(DisplayRefreshMonitorClient* client) 83{ 84 HashSet<DisplayRefreshMonitorClient*>::iterator it = m_clients.find(client); 85 if (it != m_clients.end()) { 86 if (m_clientsToBeNotified) 87 m_clientsToBeNotified->remove(client); 88 m_clients.remove(it); 89 return true; 90 } 91 return false; 92} 93 94void DisplayRefreshMonitor::displayDidRefresh() 95{ 96 double monotonicAnimationStartTime; 97 { 98 MutexLocker lock(m_mutex); 99 if (!m_scheduled) 100 ++m_unscheduledFireCount; 101 else 102 m_unscheduledFireCount = 0; 103 104 m_scheduled = false; 105 monotonicAnimationStartTime = m_monotonicAnimationStartTime; 106 } 107 108 // The call back can cause all our clients to be unregistered, so we need to protect 109 // against deletion until the end of the method. 110 RefPtr<DisplayRefreshMonitor> protect(this); 111 112 // Copy the hash table and remove clients from it one by one so we don't notify 113 // any client twice, but can respond to removal of clients during the delivery process. 114 HashSet<DisplayRefreshMonitorClient*> clientsToBeNotified = m_clients; 115 m_clientsToBeNotified = &clientsToBeNotified; 116 while (!clientsToBeNotified.isEmpty()) { 117 // Take a random client out of the set. Ordering doesn't matter. 118 // FIXME: Would read more cleanly if HashSet had a take function. 119 auto it = clientsToBeNotified.begin(); 120 DisplayRefreshMonitorClient* client = *it; 121 clientsToBeNotified.remove(it); 122 123 client->fireDisplayRefreshIfNeeded(monotonicAnimationStartTime); 124 125 // This checks if this function was reentered. In that case, stop iterating 126 // since it's not safe to use the set any more. 127 if (m_clientsToBeNotified != &clientsToBeNotified) 128 break; 129 } 130 if (m_clientsToBeNotified == &clientsToBeNotified) 131 m_clientsToBeNotified = nullptr; 132 133 { 134 MutexLocker lock(m_mutex); 135 m_previousFrameDone = true; 136 } 137 138 DisplayRefreshMonitorManager::sharedManager()->displayDidRefresh(this); 139} 140 141DisplayRefreshMonitorManager* DisplayRefreshMonitorManager::sharedManager() 142{ 143 DEFINE_STATIC_LOCAL(DisplayRefreshMonitorManager, manager, ()); 144 return &manager; 145} 146 147DisplayRefreshMonitor* DisplayRefreshMonitorManager::ensureMonitorForClient(DisplayRefreshMonitorClient* client) 148{ 149 DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID); 150 if (it == m_monitors.end()) { 151 RefPtr<DisplayRefreshMonitor> monitor = DisplayRefreshMonitor::create(client->m_displayID); 152 monitor->addClient(client); 153 DisplayRefreshMonitor* result = monitor.get(); 154 m_monitors.add(client->m_displayID, monitor.release()); 155 return result; 156 } 157 it->value->addClient(client); 158 return it->value.get(); 159} 160 161void DisplayRefreshMonitorManager::registerClient(DisplayRefreshMonitorClient* client) 162{ 163 if (!client->m_displayIDIsSet) 164 return; 165 166 ensureMonitorForClient(client); 167} 168 169void DisplayRefreshMonitorManager::unregisterClient(DisplayRefreshMonitorClient* client) 170{ 171 if (!client->m_displayIDIsSet) 172 return; 173 174 DisplayRefreshMonitorMap::iterator it = m_monitors.find(client->m_displayID); 175 if (it == m_monitors.end()) 176 return; 177 178 DisplayRefreshMonitor* monitor = it->value.get(); 179 if (monitor->removeClient(client)) { 180 if (!monitor->hasClients()) 181 m_monitors.remove(it); 182 } 183} 184 185bool DisplayRefreshMonitorManager::scheduleAnimation(DisplayRefreshMonitorClient* client) 186{ 187 if (!client->m_displayIDIsSet) 188 return false; 189 190 DisplayRefreshMonitor* monitor = ensureMonitorForClient(client); 191 192 client->m_scheduled = true; 193 return monitor->requestRefreshCallback(); 194} 195 196void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor* monitor) 197{ 198 if (monitor->shouldBeTerminated()) { 199 DisplayRefreshMonitorMap::iterator it = m_monitors.find(monitor->displayID()); 200 ASSERT(it != m_monitors.end()); 201 m_monitors.remove(it); 202 } 203} 204 205void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient* client) 206{ 207 if (client->m_displayIDIsSet && client->m_displayID == displayID) 208 return; 209 210 unregisterClient(client); 211 client->setDisplayID(displayID); 212 registerClient(client); 213 if (client->m_scheduled) 214 scheduleAnimation(client); 215} 216 217} 218 219#endif // USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) 220