Title: [275144] trunk/Source
Revision
275144
Author
[email protected]
Date
2021-03-27 19:52:37 -0700 (Sat, 27 Mar 2021)

Log Message

Allow DisplayRefreshMonitor to be more long-lived objects
https://bugs.webkit.org/show_bug.cgi?id=223844

Reviewed by Chris Dumez.

The existing behavior for DisplayRefreshMonitors was that they were created and destroyed
frequently, as their clients are registered and unregistered. In addition, some of
their subclasses had duplicated logic related to how often they fired without clients
before they were removed.

The 1:1 correspondance between DisplayRefreshMonitors and physical displays makes them
a useful place to store per-display information (like refresh rate), which should
persist whether or not they have clients, so this change prepares for that.

The main changes in this patch are:
  1. Provide virtual startNotificationMechanism()/stopNotificationMechanism() functions on
     DisplayRefreshMonitor that subclasses can use to start their CVDisplayLink or related
     functionality.

  2. Provide some shared maxUnscheduledFireCount logic that subclasses can tune to
     provide the hysteresis used to control the start/stop of the underlying
     notification mechanism.

 3. Provide a shared dispatchDisplayDidRefresh() function that most implementations
    can use.

Source/WebCore:

DisplayRefreshMonitorManager no longer destroys DisplayRefreshMonitors when they lose
all their clients. This means that DisplayRefreshMonitors are never removed, but there
should only ever be as many as there are physical displays, and they are only
active if being scheduled by clients.

* loader/EmptyClients.cpp:
* platform/graphics/DisplayRefreshMonitor.cpp:
(WebCore::DisplayRefreshMonitor::stop):
(WebCore::DisplayRefreshMonitor::removeClient):
(WebCore::DisplayRefreshMonitor::requestRefreshCallback):
(WebCore::DisplayRefreshMonitor::firedAndReachedMaxUnscheduledFireCount):
(WebCore::DisplayRefreshMonitor::displayLinkFired):
(WebCore::DisplayRefreshMonitor::dispatchDisplayDidRefresh):
(WebCore::DisplayRefreshMonitor::displayDidRefresh):
* platform/graphics/DisplayRefreshMonitor.h:
(WebCore::DisplayRefreshMonitor::setMaxUnscheduledFireCount):
(WebCore::DisplayRefreshMonitor::mutex):
(WebCore::DisplayRefreshMonitor::displayLinkFired): Deleted.
(WebCore::DisplayRefreshMonitor::stop): Deleted.
(WebCore::DisplayRefreshMonitor::shouldBeTerminated const): Deleted.
(WebCore::DisplayRefreshMonitor::isActive const): Deleted.
(WebCore::DisplayRefreshMonitor::setIsActive): Deleted.
* platform/graphics/DisplayRefreshMonitorManager.cpp:
(WebCore::DisplayRefreshMonitorManager::unregisterClient):
(WebCore::DisplayRefreshMonitorManager::displayDidRefresh):
(WebCore::DisplayRefreshMonitorManager::displayWasUpdated):
* platform/graphics/gtk/DisplayRefreshMonitorGtk.cpp:
(WebCore::DisplayRefreshMonitorGtk::~DisplayRefreshMonitorGtk):
(WebCore::onFrameClockUpdate):
(WebCore::DisplayRefreshMonitorGtk::stop):
(WebCore::DisplayRefreshMonitorGtk::startNotificationMechanism):
(WebCore::DisplayRefreshMonitorGtk::stopNotificationMechanism):
(WebCore::DisplayRefreshMonitorGtk::requestRefreshCallback): Deleted.
(WebCore::DisplayRefreshMonitorGtk::displayLinkFired): Deleted.
* platform/graphics/gtk/DisplayRefreshMonitorGtk.h:
* platform/graphics/ios/DisplayRefreshMonitorIOS.h:
* platform/graphics/ios/DisplayRefreshMonitorIOS.mm:
(-[WebDisplayLinkHandler setPaused:]):
(WebCore::DisplayRefreshMonitorIOS::DisplayRefreshMonitorIOS):
(WebCore::DisplayRefreshMonitorIOS::~DisplayRefreshMonitorIOS):
(WebCore::DisplayRefreshMonitorIOS::stop):
(WebCore::DisplayRefreshMonitorIOS::startNotificationMechanism):
(WebCore::DisplayRefreshMonitorIOS::stopNotificationMechanism):
(WebCore::DisplayRefreshMonitorIOS::requestRefreshCallback): Deleted.
(WebCore::DisplayRefreshMonitorIOS::displayLinkFired): Deleted.
* platform/graphics/mac/LegacyDisplayRefreshMonitorMac.cpp:
(WebCore::LegacyDisplayRefreshMonitorMac::LegacyDisplayRefreshMonitorMac):
(WebCore::LegacyDisplayRefreshMonitorMac::stop):
(WebCore::LegacyDisplayRefreshMonitorMac::dispatchDisplayDidRefresh):
(WebCore::LegacyDisplayRefreshMonitorMac::startNotificationMechanism):
(WebCore::LegacyDisplayRefreshMonitorMac::stopNotificationMechanism):
(WebCore::LegacyDisplayRefreshMonitorMac::requestRefreshCallback): Deleted.
(WebCore::LegacyDisplayRefreshMonitorMac::displayLinkFired): Deleted.
* platform/graphics/mac/LegacyDisplayRefreshMonitorMac.h:
* platform/graphics/win/DisplayRefreshMonitorWin.cpp:
(WebCore::DisplayRefreshMonitorWin::DisplayRefreshMonitorWin):
(WebCore::DisplayRefreshMonitorWin::startNotificationMechanism):
(WebCore::DisplayRefreshMonitorWin::stopNotificationMechanism):
(WebCore::DisplayRefreshMonitorWin::requestRefreshCallback): Deleted.
(WebCore::DisplayRefreshMonitorWin::displayLinkFired): Deleted.
* platform/graphics/win/DisplayRefreshMonitorWin.h:

Source/WebKit:

* Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.cpp:
* Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.h:
* UIProcess/mac/DisplayLink.cpp:
(WebKit::DisplayLink::DisplayLink):
(WebKit::DisplayLink::~DisplayLink):
(WebKit::DisplayLink::addObserver):
(WebKit::DisplayLink::notifyObserversDisplayWasRefreshed):
* WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h:
* WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm:
(WebKit::RemoteLayerTreeDisplayRefreshMonitor::requestRefreshCallback):
* WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp:
(WebKit::DisplayRefreshMonitorMac::DisplayRefreshMonitorMac):
(WebKit::DisplayRefreshMonitorMac::~DisplayRefreshMonitorMac):
(WebKit::DisplayRefreshMonitorMac::dispatchDisplayDidRefresh):
(WebKit::DisplayRefreshMonitorMac::startNotificationMechanism):
(WebKit::DisplayRefreshMonitorMac::stopNotificationMechanism):
(WebKit::DisplayRefreshMonitorMac::requestRefreshCallback): Deleted.
(WebKit::DisplayRefreshMonitorMac::displayLinkFired): Deleted.
* WebProcess/WebPage/mac/DisplayRefreshMonitorMac.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (275143 => 275144)


--- trunk/Source/WebCore/ChangeLog	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/ChangeLog	2021-03-28 02:52:37 UTC (rev 275144)
@@ -1,3 +1,93 @@
+2021-03-27  Simon Fraser  <[email protected]>
+
+        Allow DisplayRefreshMonitor to be more long-lived objects
+        https://bugs.webkit.org/show_bug.cgi?id=223844
+
+        Reviewed by Chris Dumez.
+
+        The existing behavior for DisplayRefreshMonitors was that they were created and destroyed
+        frequently, as their clients are registered and unregistered. In addition, some of
+        their subclasses had duplicated logic related to how often they fired without clients
+        before they were removed.
+
+        The 1:1 correspondance between DisplayRefreshMonitors and physical displays makes them
+        a useful place to store per-display information (like refresh rate), which should
+        persist whether or not they have clients, so this change prepares for that.
+        
+        The main changes in this patch are:
+          1. Provide virtual startNotificationMechanism()/stopNotificationMechanism() functions on
+             DisplayRefreshMonitor that subclasses can use to start their CVDisplayLink or related
+             functionality.
+
+          2. Provide some shared maxUnscheduledFireCount logic that subclasses can tune to
+             provide the hysteresis used to control the start/stop of the underlying
+             notification mechanism.
+
+         3. Provide a shared dispatchDisplayDidRefresh() function that most implementations
+            can use.
+
+        DisplayRefreshMonitorManager no longer destroys DisplayRefreshMonitors when they lose
+        all their clients. This means that DisplayRefreshMonitors are never removed, but there
+        should only ever be as many as there are physical displays, and they are only
+        active if being scheduled by clients.
+
+        * loader/EmptyClients.cpp:
+        * platform/graphics/DisplayRefreshMonitor.cpp:
+        (WebCore::DisplayRefreshMonitor::stop):
+        (WebCore::DisplayRefreshMonitor::removeClient):
+        (WebCore::DisplayRefreshMonitor::requestRefreshCallback):
+        (WebCore::DisplayRefreshMonitor::firedAndReachedMaxUnscheduledFireCount):
+        (WebCore::DisplayRefreshMonitor::displayLinkFired):
+        (WebCore::DisplayRefreshMonitor::dispatchDisplayDidRefresh):
+        (WebCore::DisplayRefreshMonitor::displayDidRefresh):
+        * platform/graphics/DisplayRefreshMonitor.h:
+        (WebCore::DisplayRefreshMonitor::setMaxUnscheduledFireCount):
+        (WebCore::DisplayRefreshMonitor::mutex):
+        (WebCore::DisplayRefreshMonitor::displayLinkFired): Deleted.
+        (WebCore::DisplayRefreshMonitor::stop): Deleted.
+        (WebCore::DisplayRefreshMonitor::shouldBeTerminated const): Deleted.
+        (WebCore::DisplayRefreshMonitor::isActive const): Deleted.
+        (WebCore::DisplayRefreshMonitor::setIsActive): Deleted.
+        * platform/graphics/DisplayRefreshMonitorManager.cpp:
+        (WebCore::DisplayRefreshMonitorManager::unregisterClient):
+        (WebCore::DisplayRefreshMonitorManager::displayDidRefresh):
+        (WebCore::DisplayRefreshMonitorManager::displayWasUpdated):
+        * platform/graphics/gtk/DisplayRefreshMonitorGtk.cpp:
+        (WebCore::DisplayRefreshMonitorGtk::~DisplayRefreshMonitorGtk):
+        (WebCore::onFrameClockUpdate):
+        (WebCore::DisplayRefreshMonitorGtk::stop):
+        (WebCore::DisplayRefreshMonitorGtk::startNotificationMechanism):
+        (WebCore::DisplayRefreshMonitorGtk::stopNotificationMechanism):
+        (WebCore::DisplayRefreshMonitorGtk::requestRefreshCallback): Deleted.
+        (WebCore::DisplayRefreshMonitorGtk::displayLinkFired): Deleted.
+        * platform/graphics/gtk/DisplayRefreshMonitorGtk.h:
+        * platform/graphics/ios/DisplayRefreshMonitorIOS.h:
+        * platform/graphics/ios/DisplayRefreshMonitorIOS.mm:
+        (-[WebDisplayLinkHandler setPaused:]):
+        (WebCore::DisplayRefreshMonitorIOS::DisplayRefreshMonitorIOS):
+        (WebCore::DisplayRefreshMonitorIOS::~DisplayRefreshMonitorIOS):
+        (WebCore::DisplayRefreshMonitorIOS::stop):
+        (WebCore::DisplayRefreshMonitorIOS::startNotificationMechanism):
+        (WebCore::DisplayRefreshMonitorIOS::stopNotificationMechanism):
+        (WebCore::DisplayRefreshMonitorIOS::requestRefreshCallback): Deleted.
+        (WebCore::DisplayRefreshMonitorIOS::displayLinkFired): Deleted.
+        * platform/graphics/mac/LegacyDisplayRefreshMonitorMac.cpp:
+        (WebCore::LegacyDisplayRefreshMonitorMac::LegacyDisplayRefreshMonitorMac):
+        (WebCore::LegacyDisplayRefreshMonitorMac::stop):
+        (WebCore::LegacyDisplayRefreshMonitorMac::dispatchDisplayDidRefresh):
+        (WebCore::LegacyDisplayRefreshMonitorMac::startNotificationMechanism):
+        (WebCore::LegacyDisplayRefreshMonitorMac::stopNotificationMechanism):
+        (WebCore::LegacyDisplayRefreshMonitorMac::requestRefreshCallback): Deleted.
+        (WebCore::LegacyDisplayRefreshMonitorMac::displayLinkFired): Deleted.
+        * platform/graphics/mac/LegacyDisplayRefreshMonitorMac.h:
+        * platform/graphics/win/DisplayRefreshMonitorWin.cpp:
+        (WebCore::DisplayRefreshMonitorWin::DisplayRefreshMonitorWin):
+        (WebCore::DisplayRefreshMonitorWin::startNotificationMechanism):
+        (WebCore::DisplayRefreshMonitorWin::stopNotificationMechanism):
+        (WebCore::DisplayRefreshMonitorWin::requestRefreshCallback): Deleted.
+        (WebCore::DisplayRefreshMonitorWin::displayLinkFired): Deleted.
+        * platform/graphics/win/DisplayRefreshMonitorWin.h:
+
 2021-03-27  Ryosuke Niwa  <[email protected]>
 
         Don't add Frame as an opaque root of DOMWindow

Modified: trunk/Source/WebCore/loader/EmptyClients.cpp (275143 => 275144)


--- trunk/Source/WebCore/loader/EmptyClients.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/loader/EmptyClients.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -137,6 +137,9 @@
     bool requestRefreshCallback() final { return false; }
     void stop() final { }
 
+    bool startNotificationMechanism() final { return true; }
+    void stopNotificationMechanism() final { }
+
 private:
     explicit EmptyDisplayRefreshMonitor(PlatformDisplayID displayID)
         : DisplayRefreshMonitor(displayID)

Modified: trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -44,6 +44,8 @@
 
 namespace WebCore {
 
+constexpr unsigned maxUnscheduledFireCount { 1 };
+
 RefPtr<DisplayRefreshMonitor> DisplayRefreshMonitor::createDefaultDisplayRefreshMonitor(PlatformDisplayID displayID)
 {
 #if PLATFORM(MAC)
@@ -80,6 +82,12 @@
 
 DisplayRefreshMonitor::~DisplayRefreshMonitor() = default;
 
+void DisplayRefreshMonitor::stop()
+{
+    stopNotificationMechanism();
+    setIsScheduled(false);
+}
+
 void DisplayRefreshMonitor::addClient(DisplayRefreshMonitorClient& client)
 {
     m_clients.add(&client);
@@ -89,24 +97,68 @@
 {
     if (m_clientsToBeNotified)
         m_clientsToBeNotified->remove(&client);
+
     return m_clients.remove(&client);
 }
 
-void DisplayRefreshMonitor::displayDidRefresh()
+bool DisplayRefreshMonitor::requestRefreshCallback()
 {
-    ASSERT(isMainRunLoop());
+    auto locker = holdLock(m_lock);
+    
+    if (isScheduled())
+        return true;
 
+    if (!startNotificationMechanism())
+        return false;
+
+    setIsScheduled(true);
+    return true;
+}
+
+bool DisplayRefreshMonitor::firedAndReachedMaxUnscheduledFireCount()
+{
+    ASSERT(m_lock.isLocked());
+
+    if (isScheduled()) {
+        m_unscheduledFireCount = 0;
+        return false;
+    }
+
+    ++m_unscheduledFireCount;
+    return m_unscheduledFireCount > m_maxUnscheduledFireCount;
+}
+
+void DisplayRefreshMonitor::displayLinkFired()
+{
     {
-        LockHolder lock(m_mutex);
-        LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitor " << this << " displayDidRefresh for display " << displayID() << " scheduled " << m_scheduled << " unscheduledFireCount " << m_unscheduledFireCount);
-        if (!m_scheduled)
-            ++m_unscheduledFireCount;
-        else
-            m_unscheduledFireCount = 0;
+        auto locker = holdLock(m_lock);
 
-        m_scheduled = false;
+        // This may be off the main thread.
+        if (!isPreviousFrameDone())
+            return;
+
+        LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitor::displayLinkFired for display " << displayID() << " - scheduled " << isScheduled() << " unscheduledFireCount " << m_unscheduledFireCount << " of " << m_maxUnscheduledFireCount);
+        if (firedAndReachedMaxUnscheduledFireCount()) {
+            stopNotificationMechanism();
+            return;
+        }
+
+        setIsScheduled(false);
+        setIsPreviousFrameDone(false);
     }
+    dispatchDisplayDidRefresh();
+}
 
+void DisplayRefreshMonitor::dispatchDisplayDidRefresh()
+{
+    ASSERT(isMainThread());
+    displayDidRefresh();
+}
+
+void DisplayRefreshMonitor::displayDidRefresh()
+{
+    ASSERT(isMainThread());
+
     // The call back can cause all our clients to be unregistered, so we need to protect
     // against deletion until the end of the method.
     Ref<DisplayRefreshMonitor> protectedThis(*this);
@@ -129,7 +181,7 @@
         m_clientsToBeNotified = nullptr;
 
     {
-        LockHolder lock(m_mutex);
+        auto locker = holdLock(m_lock);
         setIsPreviousFrameDone(true);
     }
 

Modified: trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitor.h	2021-03-28 02:52:37 UTC (rev 275144)
@@ -39,20 +39,19 @@
 class DisplayRefreshMonitorFactory;
 
 class DisplayRefreshMonitor : public ThreadSafeRefCounted<DisplayRefreshMonitor> {
+    friend class DisplayRefreshMonitorManager;
 public:
     static RefPtr<DisplayRefreshMonitor> create(DisplayRefreshMonitorFactory*, PlatformDisplayID);
     WEBCORE_EXPORT virtual ~DisplayRefreshMonitor();
+    
+    WEBCORE_EXPORT virtual void stop();
 
-    virtual void displayLinkFired() { }
-
     virtual void setPreferredFramesPerSecond(FramesPerSecond) { }
 
     // Return true if callback request was scheduled, false if it couldn't be
     // (e.g., hardware refresh is not available)
-    virtual bool requestRefreshCallback() = 0;
+    WEBCORE_EXPORT virtual bool requestRefreshCallback();
 
-    virtual void stop() { }
-
     void windowScreenDidChange(PlatformDisplayID);
     
     bool hasClients() const { return m_clients.size(); }
@@ -61,24 +60,21 @@
     
     PlatformDisplayID displayID() const { return m_displayID; }
 
-    bool shouldBeTerminated() const
-    {
-        const int maxInactiveFireCount = 1;
-        return !m_scheduled && m_unscheduledFireCount > maxInactiveFireCount;
-    }
-
     static RefPtr<DisplayRefreshMonitor> createDefaultDisplayRefreshMonitor(PlatformDisplayID);
+    WEBCORE_EXPORT virtual void displayLinkFired();
 
 protected:
     WEBCORE_EXPORT explicit DisplayRefreshMonitor(PlatformDisplayID);
 
-    friend class DisplayRefreshMonitorManager;
-    
-    Lock& mutex() { return m_mutex; }
+    WEBCORE_EXPORT virtual void dispatchDisplayDidRefresh();
 
-    bool isActive() const { return m_active; }
-    void setIsActive(bool active) { m_active = active; }
+    Lock& lock() { return m_lock; }
+    void setMaxUnscheduledFireCount(unsigned count) { m_maxUnscheduledFireCount = count; }
 
+    // Returns true if the start was successful.
+    WEBCORE_EXPORT virtual bool startNotificationMechanism() = 0;
+    WEBCORE_EXPORT virtual void stopNotificationMechanism() = 0;
+
     bool isScheduled() const { return m_scheduled; }
     void setIsScheduled(bool scheduled) { m_scheduled = scheduled; }
 
@@ -85,19 +81,21 @@
     bool isPreviousFrameDone() const { return m_previousFrameDone; }
     void setIsPreviousFrameDone(bool done) { m_previousFrameDone = done; }
 
-    virtual bool hasRequestedRefreshCallback() const { return false; }
     WEBCORE_EXPORT void displayDidRefresh();
 
 private:
+    bool firedAndReachedMaxUnscheduledFireCount();
 
     HashSet<DisplayRefreshMonitorClient*> m_clients;
     HashSet<DisplayRefreshMonitorClient*>* m_clientsToBeNotified { nullptr };
-    Lock m_mutex;
     PlatformDisplayID m_displayID { 0 };
-    int m_unscheduledFireCount { 0 }; // Number of times the display link has fired with no clients.
-    bool m_active { true };
+
+    Lock m_lock;
     bool m_scheduled { false };
     bool m_previousFrameDone { true };
+    
+    unsigned m_unscheduledFireCount { 0 };
+    unsigned m_maxUnscheduledFireCount { 0 };
 };
 
 }

Modified: trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/DisplayRefreshMonitorManager.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -72,11 +72,9 @@
     auto index = findMonitorForDisplayID(clientDisplayID);
     if (index == notFound)
         return;
+
     RefPtr<DisplayRefreshMonitor> monitor = m_monitors[index].monitor;
-    if (monitor->removeClient(client)) {
-        if (!monitor->hasClients())
-            m_monitors.remove(index);
-    }
+    monitor->removeClient(client);
 }
 
 void DisplayRefreshMonitorManager::setPreferredFramesPerSecond(DisplayRefreshMonitorClient& client, FramesPerSecond preferredFramesPerSecond)
@@ -94,16 +92,9 @@
     return false;
 }
 
-void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor& monitor)
+void DisplayRefreshMonitorManager::displayDidRefresh(DisplayRefreshMonitor&)
 {
-    if (!monitor.shouldBeTerminated())
-        return;
-
-    LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitorManager::displayDidRefresh() - destroying monitor " << &monitor);
-
-    m_monitors.removeFirstMatching([&](auto& monitorWrapper) {
-        return monitorWrapper.monitor == &monitor;
-    });
+    // Maybe we should remove monitors that haven't been active for some time.
 }
 
 void DisplayRefreshMonitorManager::windowScreenDidChange(PlatformDisplayID displayID, DisplayRefreshMonitorClient& client)
@@ -120,7 +111,7 @@
 void DisplayRefreshMonitorManager::displayWasUpdated(PlatformDisplayID displayID)
 {
     auto* monitor = monitorForDisplayID(displayID);
-    if (monitor && monitor->hasRequestedRefreshCallback())
+    if (monitor)
         monitor->displayLinkFired();
 }
 

Modified: trunk/Source/WebCore/platform/graphics/gtk/DisplayRefreshMonitorGtk.cpp (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/gtk/DisplayRefreshMonitorGtk.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/gtk/DisplayRefreshMonitorGtk.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -40,6 +40,16 @@
 
 DisplayRefreshMonitorGtk::~DisplayRefreshMonitorGtk()
 {
+    ASSERT(!m_window);
+}
+
+static void onFrameClockUpdate(GdkFrameClock*, DisplayRefreshMonitorGtk* monitor)
+{
+    monitor->displayLinkFired();
+}
+
+void DisplayRefreshMonitorGtk::stop()
+{
     if (!m_window)
         return;
 
@@ -46,50 +56,45 @@
     auto* frameClock = gtk_widget_get_frame_clock(m_window);
     ASSERT(frameClock);
     g_signal_handlers_disconnect_matched(frameClock, G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
-    gdk_frame_clock_end_updating(frameClock);
+
     gtk_widget_destroy(m_window);
+    m_window = nullptr;
 }
 
-static void onFrameClockUpdate(GdkFrameClock*, DisplayRefreshMonitorGtk* monitor)
+bool DisplayRefreshMonitorGtk::startNotificationMechanism()
 {
-    monitor->displayLinkFired();
-}
+    if (m_clockIsActive)
+        return true;
 
-bool DisplayRefreshMonitorGtk::requestRefreshCallback()
-{
-    if (!isActive())
-        return false;
-
+    GdkFrameClock* frameClock;
     if (!m_window) {
         // GdkFrameClockIdle is private in GDK, so we need to create a toplevel to get its frame clock.
         m_window = gtk_offscreen_window_new();
         gtk_widget_realize(m_window);
 
-        auto* frameClock = gtk_widget_get_frame_clock(m_window);
+        frameClock = gtk_widget_get_frame_clock(m_window);
         ASSERT(frameClock);
-
         g_signal_connect(frameClock, "update", G_CALLBACK(onFrameClockUpdate), this);
-        gdk_frame_clock_begin_updating(frameClock);
+    } else
+        frameClock = gtk_widget_get_frame_clock(m_window);
 
-        setIsActive(true);
-    }
+    ASSERT(frameClock);
+    gdk_frame_clock_begin_updating(frameClock);
 
-    LockHolder lock(mutex());
-    setIsScheduled(true);
+    m_clockIsActive = true;
     return true;
 }
 
-void DisplayRefreshMonitorGtk::displayLinkFired()
+void DisplayRefreshMonitorGtk::stopNotificationMechanism()
 {
-    {
-        LockHolder lock(mutex());
-        if (!isPreviousFrameDone())
-            return;
+    if (!m_clockIsActive)
+        return;
 
-        setIsPreviousFrameDone(false);
-    }
-    ASSERT(isMainThread());
-    displayDidRefresh();
+    auto* frameClock = gtk_widget_get_frame_clock(m_window);
+    ASSERT(frameClock);
+    gdk_frame_clock_end_updating(frameClock);
+
+    m_clockIsActive = false;    
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/gtk/DisplayRefreshMonitorGtk.h (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/gtk/DisplayRefreshMonitorGtk.h	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/gtk/DisplayRefreshMonitorGtk.h	2021-03-28 02:52:37 UTC (rev 275144)
@@ -42,13 +42,15 @@
 
     virtual ~DisplayRefreshMonitorGtk();
 
-    void displayLinkFired() override;
-    bool requestRefreshCallback() override;
-
 private:
     explicit DisplayRefreshMonitorGtk(PlatformDisplayID);
 
+    void stop() final;
+    bool startNotificationMechanism() final;
+    void stopNotificationMechanism() final;
+
     GtkWidget* m_window { nullptr };
+    bool m_clockIsActive { false };
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/ios/DisplayRefreshMonitorIOS.h (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/ios/DisplayRefreshMonitorIOS.h	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/ios/DisplayRefreshMonitorIOS.h	2021-03-28 02:52:37 UTC (rev 275144)
@@ -43,14 +43,17 @@
     
     virtual ~DisplayRefreshMonitorIOS();
 
-    void displayLinkFired() override;
-    bool requestRefreshCallback() override;
-
 private:
     explicit DisplayRefreshMonitorIOS(PlatformDisplayID);
+
+    void stop() final;
+    bool startNotificationMechanism() final;
+    void stopNotificationMechanism() final;
+
     RetainPtr<WebDisplayLinkHandler> m_handler;
+    bool m_displayLinkIsActive { false };
 };
 
-}
+} // namespace WebCore
 
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebCore/platform/graphics/ios/DisplayRefreshMonitorIOS.mm (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/ios/DisplayRefreshMonitorIOS.mm	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/ios/DisplayRefreshMonitorIOS.mm	2021-03-28 02:52:37 UTC (rev 275144)
@@ -28,11 +28,14 @@
 
 #if PLATFORM(IOS_FAMILY)
 
+#import "Logging.h"
 #import "WebCoreThread.h"
 #import <QuartzCore/CADisplayLink.h>
 #import <wtf/MainThread.h>
+#import <wtf/text/TextStream.h>
 
 using WebCore::DisplayRefreshMonitorIOS;
+
 @interface WebDisplayLinkHandler : NSObject
 {
     DisplayRefreshMonitorIOS* m_monitor;
@@ -42,6 +45,7 @@
 - (id)initWithMonitor:(DisplayRefreshMonitorIOS*)monitor;
 - (void)setPreferredFramesPerSecond:(NSInteger)preferredFramesPerSecond;
 - (void)handleDisplayLink:(CADisplayLink *)sender;
+- (void)setPaused:(BOOL)paused;
 - (void)invalidate;
 
 @end
@@ -78,6 +82,11 @@
     m_monitor->displayLinkFired();
 }
 
+- (void)setPaused:(BOOL)paused
+{
+    [m_displayLink setPaused:paused];
+}
+
 - (void)invalidate
 {
     [m_displayLink invalidate];
@@ -88,39 +97,51 @@
 
 namespace WebCore {
 
+constexpr unsigned maxUnscheduledFireCount { 1 };
+
 DisplayRefreshMonitorIOS::DisplayRefreshMonitorIOS(PlatformDisplayID displayID)
     : DisplayRefreshMonitor(displayID)
 {
+    setMaxUnscheduledFireCount(maxUnscheduledFireCount);
 }
 
 DisplayRefreshMonitorIOS::~DisplayRefreshMonitorIOS()
 {
+    ASSERT(!m_handler);
+}
+
+void DisplayRefreshMonitorIOS::stop()
+{
     [m_handler invalidate];
+    m_handler = nil;
 }
 
-bool DisplayRefreshMonitorIOS::requestRefreshCallback()
+bool DisplayRefreshMonitorIOS::startNotificationMechanism()
 {
-    if (!isActive())
-        return false;
+    if (m_displayLinkIsActive)
+        return true;
 
     if (!m_handler) {
+        LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitorIOS::startNotificationMechanism - creating WebDisplayLinkHandler");
         m_handler = adoptNS([[WebDisplayLinkHandler alloc] initWithMonitor:this]);
-        setIsActive(true);
     }
 
-    setIsScheduled(true);
+    LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitorIOS::startNotificationMechanism - starting WebDisplayLinkHandler");
+    [m_handler setPaused:NO];
+    m_displayLinkIsActive = true;
     return true;
 }
 
-void DisplayRefreshMonitorIOS::displayLinkFired()
+void DisplayRefreshMonitorIOS::stopNotificationMechanism()
 {
-    if (!isPreviousFrameDone())
+    if (!m_displayLinkIsActive)
         return;
 
-    setIsPreviousFrameDone(false);
-    displayDidRefresh();
+    LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitorIOS::stopNotificationMechanism - pausing WebDisplayLinkHandler");
+    [m_handler setPaused:YES];
+    m_displayLinkIsActive = false;
 }
 
-}
+} // namespace WebCore
 
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebCore/platform/graphics/mac/LegacyDisplayRefreshMonitorMac.cpp (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/mac/LegacyDisplayRefreshMonitorMac.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/mac/LegacyDisplayRefreshMonitorMac.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -28,16 +28,21 @@
 
 #if PLATFORM(MAC)
 
+#include "Logging.h"
 #include "RuntimeApplicationChecks.h"
-#include <QuartzCore/QuartzCore.h>
+#include <CoreVideo/CVDisplayLink.h>
 #include <wtf/RunLoop.h>
+#include <wtf/text/TextStream.h>
 
 namespace WebCore {
 
+constexpr unsigned maxUnscheduledFireCount { 20 };
+
 LegacyDisplayRefreshMonitorMac::LegacyDisplayRefreshMonitorMac(PlatformDisplayID displayID)
     : DisplayRefreshMonitor(displayID)
 {
     ASSERT(!isInWebProcess());
+    setMaxUnscheduledFireCount(maxUnscheduledFireCount);
 }
 
 LegacyDisplayRefreshMonitorMac::~LegacyDisplayRefreshMonitorMac()
@@ -47,10 +52,8 @@
 
 void LegacyDisplayRefreshMonitorMac::stop()
 {
-    if (!m_displayLink)
-        return;
-
-    CVDisplayLinkStop(m_displayLink);
+    DisplayRefreshMonitor::stop();
+    LOG_WITH_STREAM(DisplayLink, stream << "LegacyDisplayRefreshMonitorMac::stop for dipslay " << displayID() << " destroying display link");
     CVDisplayLinkRelease(m_displayLink);
     m_displayLink = nullptr;
 }
@@ -62,14 +65,18 @@
     return kCVReturnSuccess;
 }
 
-bool LegacyDisplayRefreshMonitorMac::requestRefreshCallback()
+void LegacyDisplayRefreshMonitorMac::dispatchDisplayDidRefresh()
 {
-    if (!isActive())
-        return false;
+    RunLoop::main().dispatch([this, protectedThis = makeRef(*this)] {
+        if (m_displayLink)
+            displayDidRefresh();
+    });
+}
 
+bool LegacyDisplayRefreshMonitorMac::startNotificationMechanism()
+{
     if (!m_displayLink) {
-        setIsActive(false);
-        CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID(), &m_displayLink);
+        auto error = CVDisplayLinkCreateWithCGDisplay(displayID(), &m_displayLink);
         if (error)
             return false;
 
@@ -76,33 +83,34 @@
         error = CVDisplayLinkSetOutputCallback(m_displayLink, displayLinkCallback, this);
         if (error)
             return false;
+    }
 
-        error = CVDisplayLinkStart(m_displayLink);
+    if (!m_displayLinkIsActive) {
+        LOG_WITH_STREAM(DisplayLink, stream << "LegacyDisplayRefreshMonitorMac::startNotificationMechanism for display " << displayID() << " starting display link");
+
+        auto error = CVDisplayLinkStart(m_displayLink);
         if (error)
             return false;
-
-        setIsActive(true);
+        
+        m_displayLinkIsActive = true;
     }
 
-    LockHolder lock(mutex());
-    setIsScheduled(true);
     return true;
 }
 
-void LegacyDisplayRefreshMonitorMac::displayLinkFired()
+void LegacyDisplayRefreshMonitorMac::stopNotificationMechanism()
 {
-    LockHolder lock(mutex());
-    if (!isPreviousFrameDone())
+    if (!m_displayLinkIsActive)
         return;
 
-    setIsPreviousFrameDone(false);
-
-    RunLoop::main().dispatch([this, protectedThis = makeRef(*this)] {
-        if (m_displayLink)
-            displayDidRefresh();
-    });
+    if (m_displayLink) {
+        LOG_WITH_STREAM(DisplayLink, stream << "LegacyDisplayRefreshMonitorMac::stopNotificationMechanism for display " << displayID() << " stopping display link");
+        CVDisplayLinkStop(m_displayLink);
+    }
+        
+    m_displayLinkIsActive = false;
 }
 
-}
+} // namespace WebCore
 
 #endif // PLATFORM(MAC)

Modified: trunk/Source/WebCore/platform/graphics/mac/LegacyDisplayRefreshMonitorMac.h (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/mac/LegacyDisplayRefreshMonitorMac.h	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/mac/LegacyDisplayRefreshMonitorMac.h	2021-03-28 02:52:37 UTC (rev 275144)
@@ -43,16 +43,20 @@
     
     virtual ~LegacyDisplayRefreshMonitorMac();
 
-    void displayLinkFired() final;
-    bool requestRefreshCallback() final;
-    void stop() final;
-
 private:
     explicit LegacyDisplayRefreshMonitorMac(PlatformDisplayID);
 
+    void dispatchDisplayDidRefresh() final;
+
+    void stop() final;
+
+    bool startNotificationMechanism() final;
+    void stopNotificationMechanism() final;
+
     CVDisplayLinkRef m_displayLink { nullptr };
+    bool m_displayLinkIsActive { false };
 };
 
-}
+} // namespace WebCore
 
 #endif // PLATFORM(MAC)

Modified: trunk/Source/WebCore/platform/graphics/win/DisplayRefreshMonitorWin.cpp (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/win/DisplayRefreshMonitorWin.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/win/DisplayRefreshMonitorWin.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -35,27 +35,21 @@
 
 DisplayRefreshMonitorWin::DisplayRefreshMonitorWin(PlatformDisplayID displayID)
     : DisplayRefreshMonitor(displayID)
-    , m_timer(RunLoop::main(), this, &DisplayRefreshMonitorWin::displayLinkFired)
+    , m_timer(RunLoop::main(), this, &DisplayRefreshMonitor::displayLinkFired)
 {
 }
 
-bool DisplayRefreshMonitorWin::requestRefreshCallback()
+bool DisplayRefreshMonitorWin::startNotificationMechanism()
 {
-    if (!isActive())
-        return false;
-    m_timer.startOneShot(16_ms);
-    setIsActive(true);
-    setIsScheduled(true);
+    if (!m_timer.isActive())
+        m_timer.startRepeating(16_ms);
+
     return true;
 }
 
-void DisplayRefreshMonitorWin::displayLinkFired()
+void DisplayRefreshMonitorWin::stopNotificationMechanism()
 {
-    if (!isPreviousFrameDone())
-        return;
-
-    setIsPreviousFrameDone(false);
-    displayDidRefresh();
+    m_timer.stop();
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/win/DisplayRefreshMonitorWin.h (275143 => 275144)


--- trunk/Source/WebCore/platform/graphics/win/DisplayRefreshMonitorWin.h	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebCore/platform/graphics/win/DisplayRefreshMonitorWin.h	2021-03-28 02:52:37 UTC (rev 275144)
@@ -34,11 +34,12 @@
 public:
     static RefPtr<DisplayRefreshMonitorWin> create(PlatformDisplayID);
 
-    void displayLinkFired() override;
-    bool requestRefreshCallback() override;
-
 private:
     explicit DisplayRefreshMonitorWin(PlatformDisplayID);
+
+    bool startNotificationMechanism() final;
+    void stopNotificationMechanism() final;
+
     RunLoop::Timer<DisplayRefreshMonitorWin> m_timer;
 };
 

Modified: trunk/Source/WebKit/ChangeLog (275143 => 275144)


--- trunk/Source/WebKit/ChangeLog	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebKit/ChangeLog	2021-03-28 02:52:37 UTC (rev 275144)
@@ -1,3 +1,51 @@
+2021-03-27  Simon Fraser  <[email protected]>
+
+        Allow DisplayRefreshMonitor to be more long-lived objects
+        https://bugs.webkit.org/show_bug.cgi?id=223844
+
+        Reviewed by Chris Dumez.
+
+        The existing behavior for DisplayRefreshMonitors was that they were created and destroyed
+        frequently, as their clients are registered and unregistered. In addition, some of
+        their subclasses had duplicated logic related to how often they fired without clients
+        before they were removed.
+
+        The 1:1 correspondance between DisplayRefreshMonitors and physical displays makes them
+        a useful place to store per-display information (like refresh rate), which should
+        persist whether or not they have clients, so this change prepares for that.
+        
+        The main changes in this patch are:
+          1. Provide virtual startNotificationMechanism()/stopNotificationMechanism() functions on
+             DisplayRefreshMonitor that subclasses can use to start their CVDisplayLink or related
+             functionality.
+
+          2. Provide some shared maxUnscheduledFireCount logic that subclasses can tune to
+             provide the hysteresis used to control the start/stop of the underlying
+             notification mechanism.
+
+         3. Provide a shared dispatchDisplayDidRefresh() function that most implementations
+            can use.
+
+        * Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.cpp:
+        * Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.h:
+        * UIProcess/mac/DisplayLink.cpp:
+        (WebKit::DisplayLink::DisplayLink):
+        (WebKit::DisplayLink::~DisplayLink):
+        (WebKit::DisplayLink::addObserver):
+        (WebKit::DisplayLink::notifyObserversDisplayWasRefreshed):
+        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h:
+        * WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm:
+        (WebKit::RemoteLayerTreeDisplayRefreshMonitor::requestRefreshCallback):
+        * WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp:
+        (WebKit::DisplayRefreshMonitorMac::DisplayRefreshMonitorMac):
+        (WebKit::DisplayRefreshMonitorMac::~DisplayRefreshMonitorMac):
+        (WebKit::DisplayRefreshMonitorMac::dispatchDisplayDidRefresh):
+        (WebKit::DisplayRefreshMonitorMac::startNotificationMechanism):
+        (WebKit::DisplayRefreshMonitorMac::stopNotificationMechanism):
+        (WebKit::DisplayRefreshMonitorMac::requestRefreshCallback): Deleted.
+        (WebKit::DisplayRefreshMonitorMac::displayLinkFired): Deleted.
+        * WebProcess/WebPage/mac/DisplayRefreshMonitorMac.h:
+
 2021-03-27  Kate Cheney  <[email protected]>
 
         PCM: Send report to both click source and attribution destination website

Modified: trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.cpp (275143 => 275144)


--- trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -55,7 +55,7 @@
 
     bool previousFrameDone { false };
     {
-        LockHolder locker(mutex());
+        auto locker = holdLock(lock());
         setIsScheduled(true);
         previousFrameDone = isPreviousFrameDone();
     }
@@ -71,7 +71,7 @@
 
 bool ThreadedDisplayRefreshMonitor::requiresDisplayRefreshCallback()
 {
-    LockHolder locker(mutex());
+    auto locker = holdLock(lock());
     return isScheduled() && isPreviousFrameDone();
 }
 
@@ -87,7 +87,7 @@
     m_displayRefreshTimer.stop();
     bool wasScheduled = false;
     {
-        LockHolder locker(mutex());
+        auto locker = holdLock(lock());
         wasScheduled = isScheduled();
     }
     if (wasScheduled)
@@ -95,11 +95,12 @@
     m_client = nullptr;
 }
 
+// FIXME: Refactor to share more code with DisplayRefreshMonitor::displayLinkFired().
 void ThreadedDisplayRefreshMonitor::displayRefreshCallback()
 {
     bool shouldHandleDisplayRefreshNotification { false };
     {
-        LockHolder locker(mutex());
+        auto locker = holdLock(lock());
         shouldHandleDisplayRefreshNotification = isScheduled() && isPreviousFrameDone();
         if (shouldHandleDisplayRefreshNotification)
             setIsPreviousFrameDone(false);
@@ -111,7 +112,7 @@
     // Retrieve the scheduled status for this DisplayRefreshMonitor.
     bool hasBeenRescheduled { false };
     {
-        LockHolder locker(mutex());
+        auto locker = holdLock(lock());
         hasBeenRescheduled = isScheduled();
     }
 

Modified: trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.h (275143 => 275144)


--- trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.h	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebKit/Shared/CoordinatedGraphics/threadedcompositor/ThreadedDisplayRefreshMonitor.h	2021-03-28 02:52:37 UTC (rev 275144)
@@ -57,6 +57,9 @@
 private:
     ThreadedDisplayRefreshMonitor(WebCore::PlatformDisplayID, Client&);
 
+    bool startNotificationMechanism() final { return true; }
+    void stopNotificationMechanism() final { }
+
     void displayRefreshCallback();
     RunLoop::Timer<ThreadedDisplayRefreshMonitor> m_displayRefreshTimer;
     Client* m_client;

Modified: trunk/Source/WebKit/UIProcess/mac/DisplayLink.cpp (275143 => 275144)


--- trunk/Source/WebKit/UIProcess/mac/DisplayLink.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebKit/UIProcess/mac/DisplayLink.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -29,8 +29,10 @@
 #if HAVE(CVDISPLAYLINK)
 
 #include "EventDispatcherMessages.h"
+#include "Logging.h"
 #include "WebProcessMessages.h"
 #include <wtf/ProcessPrivilege.h>
+#include <wtf/text/TextStream.h>
 
 namespace WebKit {
 
@@ -40,6 +42,8 @@
 DisplayLink::DisplayLink(WebCore::PlatformDisplayID displayID)
     : m_displayID(displayID)
 {
+    LOG_WITH_STREAM(DisplayLink, stream << "[UI ] Creating DisplayLink for display " << displayID);
+
     // FIXME: We can get here with displayID == 0 (webkit.org/b/212120), in which case CVDisplayLinkCreateWithCGDisplay()
     // probably defaults to the main screen.
     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
@@ -58,6 +62,8 @@
 
 DisplayLink::~DisplayLink()
 {
+    LOG_WITH_STREAM(DisplayLink, stream << "[UI ] Destroying DisplayLink for display " << m_displayID);
+
     ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
     ASSERT(m_displayLink);
     if (!m_displayLink)
@@ -85,6 +91,7 @@
     }
 
     if (!CVDisplayLinkIsRunning(m_displayLink)) {
+        LOG_WITH_STREAM(DisplayLink, stream << "[UI ] DisplayLink for display " << m_displayID << " starting CVDisplayLink");
         CVReturn error = CVDisplayLinkStart(m_displayLink);
         if (error)
             WTFLogAlways("Could not start the display link: %d", error);
@@ -134,13 +141,16 @@
 
     LockHolder locker(m_observersLock);
     if (m_observers.isEmpty()) {
-        if (++m_fireCountWithoutObservers >= maxFireCountWithoutObservers)
+        if (++m_fireCountWithoutObservers >= maxFireCountWithoutObservers) {
+            LOG_WITH_STREAM(DisplayLink, stream << "[UI ] DisplayLink for display " << m_displayID << " fired " << m_fireCountWithoutObservers << " times with no observers; stopping CVDisplayLink");
             CVDisplayLinkStop(m_displayLink);
+        }
         return;
     }
     m_fireCountWithoutObservers = 0;
 
     for (auto& connection : m_observers.keys()) {
+        LOG_WITH_STREAM(DisplayLink, stream << "[UI ] DisplayLink for display " << m_displayID << " sending for connection " << connection->uniqueID() << " on background queue " << shouldSendIPCOnBackgroundQueue);
         if (shouldSendIPCOnBackgroundQueue)
             connection->send(Messages::EventDispatcher::DisplayWasRefreshed(m_displayID), 0);
         else

Modified: trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h (275143 => 275144)


--- trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.h	2021-03-28 02:52:37 UTC (rev 275144)
@@ -41,7 +41,7 @@
     virtual ~RemoteLayerTreeDisplayRefreshMonitor();
 
     void setPreferredFramesPerSecond(WebCore::FramesPerSecond) override;
-    bool requestRefreshCallback() override;
+    bool requestRefreshCallback() final;
 
     void didUpdateLayers();
     void updateDrawingArea(RemoteLayerTreeDrawingArea&);
@@ -49,6 +49,9 @@
 private:
     explicit RemoteLayerTreeDisplayRefreshMonitor(WebCore::PlatformDisplayID, RemoteLayerTreeDrawingArea&);
 
+    bool startNotificationMechanism() final { return true; }
+    void stopNotificationMechanism() final { }
+
     WeakPtr<RemoteLayerTreeDrawingArea> m_drawingArea;
 };
 

Modified: trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm (275143 => 275144)


--- trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebKit/WebProcess/WebPage/RemoteLayerTree/RemoteLayerTreeDisplayRefreshMonitor.mm	2021-03-28 02:52:37 UTC (rev 275144)
@@ -26,6 +26,9 @@
 #import "config.h"
 #import "RemoteLayerTreeDisplayRefreshMonitor.h"
 
+#import "Logging.h"
+#include <wtf/text/TextStream.h>
+
 namespace WebKit {
 using namespace WebCore;
 
@@ -49,13 +52,14 @@
 
 bool RemoteLayerTreeDisplayRefreshMonitor::requestRefreshCallback()
 {
-    if (!m_drawingArea || !isActive())
+    if (!m_drawingArea)
         return false;
 
-    if (!isScheduled())
+    if (!isScheduled()) {
+        LOG_WITH_STREAM(DisplayLink, stream << "RemoteLayerTreeDisplayRefreshMonitor::requestRefreshCallback - triggering update");
         static_cast<DrawingArea&>(*m_drawingArea.get()).triggerRenderingUpdate();
+    }
 
-    setIsActive(true);
     setIsScheduled(true);
     return true;
 }

Modified: trunk/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp (275143 => 275144)


--- trunk/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.cpp	2021-03-28 02:52:37 UTC (rev 275144)
@@ -39,49 +39,61 @@
 namespace WebKit {
 using namespace WebCore;
 
+// Avoid repeated start/stop IPC when rescheduled inside the callback.
+constexpr unsigned maxUnscheduledFireCount { 1 };
+
 DisplayRefreshMonitorMac::DisplayRefreshMonitorMac(PlatformDisplayID displayID)
     : DisplayRefreshMonitor(displayID)
     , m_observerID(DisplayLinkObserverID::generate())
 {
     ASSERT(isMainRunLoop());
+    setMaxUnscheduledFireCount(maxUnscheduledFireCount);
 }
 
 DisplayRefreshMonitorMac::~DisplayRefreshMonitorMac()
 {
-    if (m_hasSentMessage)
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::StopDisplayLink(m_observerID, displayID()), 0);
+    // stop() should have been called.
+    ASSERT(!m_displayLinkIsActive);
 }
 
-bool DisplayRefreshMonitorMac::requestRefreshCallback()
+void DisplayRefreshMonitorMac::dispatchDisplayDidRefresh()
 {
-    if (!isActive())
-        return false;
+    if (!m_firstCallbackInCurrentRunloop)
+        return;
 
-    if (!m_hasSentMessage) {
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::StartDisplayLink(m_observerID, displayID()), 0);
-        m_hasSentMessage = true;
+    DisplayRefreshMonitor::dispatchDisplayDidRefresh();
+}
+
+bool DisplayRefreshMonitorMac::startNotificationMechanism()
+{
+    if (m_displayLinkIsActive)
+        return true;
+
+    LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitorMac::requestRefreshCallback - starting");
+    WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::StartDisplayLink(m_observerID, displayID()), 0);
+    if (!m_runLoopObserver) {
+        // The RunLoopObserver repeats.
         m_runLoopObserver = makeUnique<RunLoopObserver>(kCFRunLoopEntry, [this]() {
             m_firstCallbackInCurrentRunloop = true;
         });
-        m_runLoopObserver->schedule(CFRunLoopGetCurrent());
     }
-    
-    setIsScheduled(true);
 
+    m_runLoopObserver->schedule(CFRunLoopGetCurrent());
+    m_displayLinkIsActive = true;
+
     return true;
 }
 
-void DisplayRefreshMonitorMac::displayLinkFired()
+void DisplayRefreshMonitorMac::stopNotificationMechanism()
 {
-    ASSERT(isMainRunLoop());
-    if (!m_firstCallbackInCurrentRunloop)
+    if (!m_displayLinkIsActive)
         return;
 
-    m_firstCallbackInCurrentRunloop = false;
-
-    LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitorMac::displayLinkFired() for display " << displayID());
-
-    displayDidRefresh();
+    LOG_WITH_STREAM(DisplayLink, stream << "DisplayRefreshMonitorMac::requestRefreshCallback - stopping");
+    WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::StopDisplayLink(m_observerID, displayID()), 0);
+    m_runLoopObserver->invalidate();
+    
+    m_displayLinkIsActive = false;
 }
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.h (275143 => 275144)


--- trunk/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.h	2021-03-28 00:40:03 UTC (rev 275143)
+++ trunk/Source/WebKit/WebProcess/WebPage/mac/DisplayRefreshMonitorMac.h	2021-03-28 02:52:37 UTC (rev 275144)
@@ -39,20 +39,21 @@
     {
         return adoptRef(*new DisplayRefreshMonitorMac(displayID));
     }
-    
+
     virtual ~DisplayRefreshMonitorMac();
-    
-    void displayLinkFired() final;
-    bool requestRefreshCallback() final;
-    
+
 private:
     explicit DisplayRefreshMonitorMac(WebCore::PlatformDisplayID);
-    
-    bool hasRequestedRefreshCallback() const final { return m_hasSentMessage; }
 
+    void dispatchDisplayDidRefresh() final;
+
+    bool startNotificationMechanism() final;
+    void stopNotificationMechanism() final;
+
     DisplayLinkObserverID m_observerID;
     std::unique_ptr<WebCore::RunLoopObserver> m_runLoopObserver;
-    bool m_hasSentMessage { false };
+
+    bool m_displayLinkIsActive { false };
     bool m_firstCallbackInCurrentRunloop { false };
 };
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to