[GTK] Use a RunLoop::Timer to schedule rendering frames in accelerated compositing...
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 1 Nov 2015 08:31:16 +0000 (08:31 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 1 Nov 2015 08:31:16 +0000 (08:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=150756

Reviewed by Darin Adler.

Use a RunLoop::Timer instead of a GMainLoopSource for the
accelerated compositing render loop to improve the performance,
since RunLoop::Timer uses a persistent source.
All the logic to schedule rames has been moved to a helper
internal class RenderFrameScheduler.

* WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp:
(WebKit::LayerTreeHostGtk::RenderFrameScheduler::RenderFrameScheduler):
(WebKit::LayerTreeHostGtk::RenderFrameScheduler::~RenderFrameScheduler):
(WebKit::LayerTreeHostGtk::RenderFrameScheduler::start):
(WebKit::LayerTreeHostGtk::RenderFrameScheduler::stop):
(WebKit::shouldSkipNextFrameBecauseOfContinousImmediateFlushes):
(WebKit::LayerTreeHostGtk::RenderFrameScheduler::nextFrame):
(WebKit::LayerTreeHostGtk::RenderFrameScheduler::renderFrame):
(WebKit::LayerTreeHostGtk::LayerTreeHostGtk):
(WebKit::LayerTreeHostGtk::renderFrame):
(WebKit::LayerTreeHostGtk::scheduleLayerFlush):
(WebKit::LayerTreeHostGtk::cancelPendingLayerFlush):
(WebKit::LayerTreeHostGtk::layerFlushTimerFired): Deleted.
* WebProcess/WebPage/gtk/LayerTreeHostGtk.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@191854 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebKit2/ChangeLog
Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp
Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h

index 15f87d62e99ecf508d1e90e6ed65b5b18af31374..9020b08fb45ef2c9050a0b256aed1bf3101add54 100644 (file)
@@ -1,3 +1,31 @@
+2015-11-01  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK] Use a RunLoop::Timer to schedule rendering frames in accelerated compositing mode
+        https://bugs.webkit.org/show_bug.cgi?id=150756
+
+        Reviewed by Darin Adler.
+
+        Use a RunLoop::Timer instead of a GMainLoopSource for the
+        accelerated compositing render loop to improve the performance,
+        since RunLoop::Timer uses a persistent source.
+        All the logic to schedule rames has been moved to a helper
+        internal class RenderFrameScheduler.
+
+        * WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp:
+        (WebKit::LayerTreeHostGtk::RenderFrameScheduler::RenderFrameScheduler):
+        (WebKit::LayerTreeHostGtk::RenderFrameScheduler::~RenderFrameScheduler):
+        (WebKit::LayerTreeHostGtk::RenderFrameScheduler::start):
+        (WebKit::LayerTreeHostGtk::RenderFrameScheduler::stop):
+        (WebKit::shouldSkipNextFrameBecauseOfContinousImmediateFlushes):
+        (WebKit::LayerTreeHostGtk::RenderFrameScheduler::nextFrame):
+        (WebKit::LayerTreeHostGtk::RenderFrameScheduler::renderFrame):
+        (WebKit::LayerTreeHostGtk::LayerTreeHostGtk):
+        (WebKit::LayerTreeHostGtk::renderFrame):
+        (WebKit::LayerTreeHostGtk::scheduleLayerFlush):
+        (WebKit::LayerTreeHostGtk::cancelPendingLayerFlush):
+        (WebKit::LayerTreeHostGtk::layerFlushTimerFired): Deleted.
+        * WebProcess/WebPage/gtk/LayerTreeHostGtk.h:
+
 2015-10-31  Andreas Kling  <akling@apple.com>
 
         Add a debug overlay with information about web process resource usage.
index 69ef6abaa6b6e56ce6418ba1c04bf1be7e97d391..94a1174a34e3ad79ea9c2018acb420ff0a49a42c 100644 (file)
@@ -61,6 +61,71 @@ using namespace WebCore;
 
 namespace WebKit {
 
+LayerTreeHostGtk::RenderFrameScheduler::RenderFrameScheduler(std::function<bool()> renderer)
+    : m_renderer(WTF::move(renderer))
+    , m_timer(RunLoop::main(), this, &LayerTreeHostGtk::RenderFrameScheduler::renderFrame)
+{
+    // We use a RunLoop timer because otherwise GTK+ event handling during dragging can starve WebCore timers, which have a lower priority.
+    // Use a higher priority than WebCore timers.
+    m_timer.setPriority(GDK_PRIORITY_REDRAW - 1);
+}
+
+LayerTreeHostGtk::RenderFrameScheduler::~RenderFrameScheduler()
+{
+}
+
+void LayerTreeHostGtk::RenderFrameScheduler::start()
+{
+    if (m_timer.isActive())
+        return;
+    m_fireTime = 0;
+    nextFrame();
+}
+
+void LayerTreeHostGtk::RenderFrameScheduler::stop()
+{
+    m_timer.stop();
+}
+
+static inline bool shouldSkipNextFrameBecauseOfContinousImmediateFlushes(double current, double lastImmediateFlushTime)
+{
+    // 100ms is about a perceptable delay in UI, so when scheduling layer flushes immediately for more than 100ms,
+    // we skip the next frame to ensure pending timers have a change to be fired.
+    static const double maxDurationOfImmediateFlushes = 0.100;
+    if (!lastImmediateFlushTime)
+        return false;
+    return lastImmediateFlushTime + maxDurationOfImmediateFlushes < current;
+}
+
+void LayerTreeHostGtk::RenderFrameScheduler::nextFrame()
+{
+    static const double targetFramerate = 1 / 60.0;
+    // When rendering layers takes more time than the target delay (0.016), we end up scheduling layer flushes
+    // immediately. Since the layer flush timer has a higher priority than WebCore timers, these are never
+    // fired while we keep scheduling layer flushes immediately.
+    double current = monotonicallyIncreasingTime();
+    double timeToNextFlush = std::max(targetFramerate - (current - m_fireTime), 0.0);
+    if (timeToNextFlush)
+        m_lastImmediateFlushTime = 0;
+    else if (!m_lastImmediateFlushTime)
+        m_lastImmediateFlushTime = current;
+
+    if (shouldSkipNextFrameBecauseOfContinousImmediateFlushes(current, m_lastImmediateFlushTime)) {
+        timeToNextFlush = targetFramerate;
+        m_lastImmediateFlushTime = 0;
+    }
+
+    m_timer.startOneShot(timeToNextFlush);
+}
+
+void LayerTreeHostGtk::RenderFrameScheduler::renderFrame()
+{
+    m_fireTime = monotonicallyIncreasingTime();
+    if (!m_renderer() || m_timer.isActive())
+        return;
+    nextFrame();
+}
+
 PassRefPtr<LayerTreeHostGtk> LayerTreeHostGtk::create(WebPage* webPage)
 {
     RefPtr<LayerTreeHostGtk> host = adoptRef(new LayerTreeHostGtk(webPage));
@@ -72,9 +137,9 @@ LayerTreeHostGtk::LayerTreeHostGtk(WebPage* webPage)
     : LayerTreeHost(webPage)
     , m_isValid(true)
     , m_notifyAfterScheduledLayerFlush(false)
-    , m_lastImmediateFlushTime(0)
     , m_layerFlushSchedulingEnabled(true)
     , m_viewOverlayRootLayer(nullptr)
+    , m_renderFrameScheduler(std::bind(&LayerTreeHostGtk::renderFrame, this))
 {
 }
 
@@ -237,44 +302,10 @@ float LayerTreeHostGtk::pageScaleFactor() const
     return m_webPage->pageScaleFactor();
 }
 
-static inline bool shouldSkipNextFrameBecauseOfContinousImmediateFlushes(double current, double lastImmediateFlushTime)
-{
-    // 100ms is about a perceptable delay in UI, so when scheduling layer flushes immediately for more than 100ms,
-    // we skip the next frame to ensure pending timers have a change to be fired.
-    static const double maxDurationOfImmediateFlushes = 0.100;
-    if (!lastImmediateFlushTime)
-        return false;
-    return lastImmediateFlushTime + maxDurationOfImmediateFlushes < current;
-}
-
-// Use a higher priority than WebCore timers.
-static const int layerFlushTimerPriority = GDK_PRIORITY_REDRAW - 1;
-
-void LayerTreeHostGtk::layerFlushTimerFired()
+bool LayerTreeHostGtk::renderFrame()
 {
-    double fireTime = monotonicallyIncreasingTime();
     flushAndRenderLayers();
-    if (m_layerFlushTimerCallback.isScheduled() || !downcast<GraphicsLayerTextureMapper>(*m_rootLayer).layer().descendantsOrSelfHaveRunningAnimations())
-        return;
-
-    static const double targetFramerate = 1 / 60.0;
-    // When rendering layers takes more time than the target delay (0.016), we end up scheduling layer flushes
-    // immediately. Since the layer flush timer has a higher priority than WebCore timers, these are never
-    // fired while we keep scheduling layer flushes immediately.
-    double current = monotonicallyIncreasingTime();
-    double timeToNextFlush = std::max(targetFramerate - (current - fireTime), 0.0);
-    if (timeToNextFlush)
-        m_lastImmediateFlushTime = 0;
-    else if (!m_lastImmediateFlushTime)
-        m_lastImmediateFlushTime = current;
-
-    if (shouldSkipNextFrameBecauseOfContinousImmediateFlushes(current, m_lastImmediateFlushTime)) {
-        timeToNextFlush = targetFramerate;
-        m_lastImmediateFlushTime = 0;
-    }
-
-    m_layerFlushTimerCallback.scheduleAfterDelay("[WebKit] layerFlushTimer", std::bind(&LayerTreeHostGtk::layerFlushTimerFired, this),
-        std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::duration<double>(timeToNextFlush)), layerFlushTimerPriority);
+    return downcast<GraphicsLayerTextureMapper>(*m_rootLayer).layer().descendantsOrSelfHaveRunningAnimations();
 }
 
 bool LayerTreeHostGtk::flushPendingLayerChanges()
@@ -348,9 +379,7 @@ void LayerTreeHostGtk::scheduleLayerFlush()
     if (!m_layerFlushSchedulingEnabled || !m_textureMapper)
         return;
 
-    // We use a GLib timer because otherwise GTK+ event handling during dragging can starve WebCore timers, which have a lower priority.
-    if (!m_layerFlushTimerCallback.isScheduled())
-        m_layerFlushTimerCallback.schedule("[WebKit] layerFlushTimer", std::bind(&LayerTreeHostGtk::layerFlushTimerFired, this), layerFlushTimerPriority);
+    m_renderFrameScheduler.start();
 }
 
 void LayerTreeHostGtk::setLayerFlushSchedulingEnabled(bool layerFlushingEnabled)
@@ -375,7 +404,7 @@ void LayerTreeHostGtk::pageBackgroundTransparencyChanged()
 
 void LayerTreeHostGtk::cancelPendingLayerFlush()
 {
-    m_layerFlushTimerCallback.cancel();
+    m_renderFrameScheduler.stop();
 }
 
 void LayerTreeHostGtk::setViewOverlayRootLayer(WebCore::GraphicsLayer* viewOverlayRootLayer)
index 9c67a67dac87d74b7151f5f573307b65058a0efc..07a22476022bb806c04b1029c257bb4cf61d7222 100644 (file)
@@ -35,7 +35,7 @@
 #include <WebCore/GLContext.h>
 #include <WebCore/GraphicsLayerClient.h>
 #include <WebCore/TransformationMatrix.h>
-#include <wtf/glib/GMainLoopSource.h>
+#include <wtf/RunLoop.h>
 
 namespace WebKit {
 
@@ -65,6 +65,25 @@ protected:
     virtual void setNativeSurfaceHandleForCompositing(uint64_t) override;
 
 private:
+
+    class RenderFrameScheduler {
+    public:
+        RenderFrameScheduler(std::function<bool()>);
+        ~RenderFrameScheduler();
+
+        void start();
+        void stop();
+
+    private:
+        void renderFrame();
+        void nextFrame();
+
+        std::function<bool()> m_renderer;
+        RunLoop::Timer<RenderFrameScheduler> m_timer;
+        double m_fireTime { 0 };
+        double m_lastImmediateFlushTime { 0 };
+    };
+
     // LayerTreeHost
     virtual const LayerTreeContext& layerTreeContext() override;
     virtual void setShouldNotifyAfterNextScheduledLayerFlush(bool) override;
@@ -87,7 +106,7 @@ private:
     void flushAndRenderLayers();
     void cancelPendingLayerFlush();
 
-    void layerFlushTimerFired();
+    bool renderFrame();
 
     bool makeContextCurrent();
 
@@ -100,9 +119,9 @@ private:
     std::unique_ptr<WebCore::GLContext> m_context;
     double m_lastImmediateFlushTime;
     bool m_layerFlushSchedulingEnabled;
-    GMainLoopSource m_layerFlushTimerCallback;
     WebCore::GraphicsLayer* m_viewOverlayRootLayer;
     WebCore::TransformationMatrix m_scaleMatrix;
+    RenderFrameScheduler m_renderFrameScheduler;
 };
 
 } // namespace WebKit