<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[177144] trunk/Source/WebKit2</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/177144">177144</a></dd>
<dt>Author</dt> <dd>carlosgc@webkit.org</dd>
<dt>Date</dt> <dd>2014-12-11 03:22:30 -0800 (Thu, 11 Dec 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>[GTK] Timers might never be fired during animations
https://bugs.webkit.org/show_bug.cgi?id=139062

Reviewed by Martin Robinson.

This can happen in old/slow machines where the time to render
layers might take more than 0.016. Since the layer flush timer is
using a higher priority than WebCore timers, when scheduling all
(or several) layer flushes immediately, no other sources with
lower priority are dispatched in the main loop. We could detect if
we are scheduling layer flushes immediately for too long (100ms)
and schedule the next flush after a delay to ensure other sources
with lower priority have a chance to be dispatched. Also use a
lower priority, GDK_PRIORITY_EVENTS is too high, so we use
GDK_PRIORITY_REDRAW - 1 to ensure it's higher than WebCore timers.

* WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp:
(WebKit::LayerTreeHostGtk::LayerTreeHostGtk): Rename
m_lastFlushTime as m_lastImmediateFlushTime.
(WebKit::LayerTreeHostGtk::layerFlushTimerFired): Save the
fireTime before calling flushAndRenderLayers() and compute the
next flush delay based on the elapsed time using monotonically
increasing time instead of current time. Use the target delay
as next flush delay if we have scheduled flushes immediately for
more than 100ms.
(WebKit::LayerTreeHostGtk::flushAndRenderLayers): Do not save the
layer flush time here, it's done in layerFlushTimerFired() so that
we don't need to keep it as a member.
(WebKit::LayerTreeHostGtk::scheduleLayerFlush): Use global
layerFlushTimerPriority.
* WebProcess/WebPage/gtk/LayerTreeHostGtk.h:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPagegtkLayerTreeHostGtkcpp">trunk/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp</a></li>
<li><a href="#trunkSourceWebKit2WebProcessWebPagegtkLayerTreeHostGtkh">trunk/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (177143 => 177144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-12-11 10:31:28 UTC (rev 177143)
+++ trunk/Source/WebKit2/ChangeLog        2014-12-11 11:22:30 UTC (rev 177144)
</span><span class="lines">@@ -1,3 +1,37 @@
</span><ins>+2014-12-11  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
+
+        [GTK] Timers might never be fired during animations
+        https://bugs.webkit.org/show_bug.cgi?id=139062
+
+        Reviewed by Martin Robinson.
+
+        This can happen in old/slow machines where the time to render
+        layers might take more than 0.016. Since the layer flush timer is
+        using a higher priority than WebCore timers, when scheduling all
+        (or several) layer flushes immediately, no other sources with
+        lower priority are dispatched in the main loop. We could detect if
+        we are scheduling layer flushes immediately for too long (100ms)
+        and schedule the next flush after a delay to ensure other sources
+        with lower priority have a chance to be dispatched. Also use a
+        lower priority, GDK_PRIORITY_EVENTS is too high, so we use
+        GDK_PRIORITY_REDRAW - 1 to ensure it's higher than WebCore timers.
+
+        * WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp:
+        (WebKit::LayerTreeHostGtk::LayerTreeHostGtk): Rename
+        m_lastFlushTime as m_lastImmediateFlushTime.
+        (WebKit::LayerTreeHostGtk::layerFlushTimerFired): Save the
+        fireTime before calling flushAndRenderLayers() and compute the
+        next flush delay based on the elapsed time using monotonically
+        increasing time instead of current time. Use the target delay
+        as next flush delay if we have scheduled flushes immediately for
+        more than 100ms.
+        (WebKit::LayerTreeHostGtk::flushAndRenderLayers): Do not save the
+        layer flush time here, it's done in layerFlushTimerFired() so that
+        we don't need to keep it as a member.
+        (WebKit::LayerTreeHostGtk::scheduleLayerFlush): Use global
+        layerFlushTimerPriority.
+        * WebProcess/WebPage/gtk/LayerTreeHostGtk.h:
+
</ins><span class="cx"> 2014-12-10  Jaehun Lim  &lt;ljaehun.lim@samsung.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [CMake] Fix build after WebStorageNamespaceProvider
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPagegtkLayerTreeHostGtkcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp (177143 => 177144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp        2014-12-11 10:31:28 UTC (rev 177143)
+++ trunk/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp        2014-12-11 11:22:30 UTC (rev 177144)
</span><span class="lines">@@ -72,7 +72,7 @@
</span><span class="cx">     : LayerTreeHost(webPage)
</span><span class="cx">     , m_isValid(true)
</span><span class="cx">     , m_notifyAfterScheduledLayerFlush(false)
</span><del>-    , m_lastFlushTime(0)
</del><ins>+    , m_lastImmediateFlushTime(0)
</ins><span class="cx">     , m_layerFlushSchedulingEnabled(true)
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="lines">@@ -251,16 +251,44 @@
</span><span class="cx">     // FIXME: Draw page overlays. https://bugs.webkit.org/show_bug.cgi?id=131433.
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+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 &lt; current;
+}
+
+// Use a higher priority than WebCore timers.
+static const int layerFlushTimerPriority = GDK_PRIORITY_REDRAW - 1;
+
</ins><span class="cx"> void LayerTreeHostGtk::layerFlushTimerFired()
</span><span class="cx"> {
</span><ins>+    double fireTime = monotonicallyIncreasingTime();
</ins><span class="cx">     flushAndRenderLayers();
</span><ins>+    if (m_layerFlushTimerCallback.isScheduled() || !downcast&lt;GraphicsLayerTextureMapper&gt;(*m_rootLayer).layer()-&gt;descendantsOrSelfHaveRunningAnimations())
+        return;
</ins><span class="cx"> 
</span><del>-    if (!m_layerFlushTimerCallback.isScheduled() &amp;&amp; downcast&lt;GraphicsLayerTextureMapper&gt;(*m_rootLayer).layer()-&gt;descendantsOrSelfHaveRunningAnimations()) {
-        const double targetFPS = 60;
-        double nextFlush = std::max((1 / targetFPS) - (currentTime() - m_lastFlushTime), 0.0);
-        m_layerFlushTimerCallback.scheduleAfterDelay(&quot;[WebKit] layerFlushTimer&quot;, std::bind(&amp;LayerTreeHostGtk::layerFlushTimerFired, this),
-            std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(std::chrono::duration&lt;double&gt;(nextFlush)), GDK_PRIORITY_EVENTS);
</del><ins>+    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;
</ins><span class="cx">     }
</span><ins>+
+    m_layerFlushTimerCallback.scheduleAfterDelay(&quot;[WebKit] layerFlushTimer&quot;, std::bind(&amp;LayerTreeHostGtk::layerFlushTimerFired, this),
+        std::chrono::duration_cast&lt;std::chrono::microseconds&gt;(std::chrono::duration&lt;double&gt;(timeToNextFlush)), layerFlushTimerPriority);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool LayerTreeHostGtk::flushPendingLayerChanges()
</span><span class="lines">@@ -313,7 +341,6 @@
</span><span class="cx">     if (!context || !context-&gt;makeContextCurrent())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_lastFlushTime = currentTime();
</del><span class="cx">     if (!flushPendingLayerChanges())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="lines">@@ -359,7 +386,7 @@
</span><span class="cx"> 
</span><span class="cx">     // We use a GLib timer because otherwise GTK+ event handling during dragging can starve WebCore timers, which have a lower priority.
</span><span class="cx">     if (!m_layerFlushTimerCallback.isScheduled())
</span><del>-        m_layerFlushTimerCallback.schedule(&quot;[WebKit] layerFlushTimer&quot;, std::bind(&amp;LayerTreeHostGtk::layerFlushTimerFired, this), GDK_PRIORITY_EVENTS);
</del><ins>+        m_layerFlushTimerCallback.schedule(&quot;[WebKit] layerFlushTimer&quot;, std::bind(&amp;LayerTreeHostGtk::layerFlushTimerFired, this), layerFlushTimerPriority);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void LayerTreeHostGtk::setLayerFlushSchedulingEnabled(bool layerFlushingEnabled)
</span></span></pre></div>
<a id="trunkSourceWebKit2WebProcessWebPagegtkLayerTreeHostGtkh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h (177143 => 177144)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h        2014-12-11 10:31:28 UTC (rev 177143)
+++ trunk/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h        2014-12-11 11:22:30 UTC (rev 177144)
</span><span class="lines">@@ -100,7 +100,7 @@
</span><span class="cx">     PageOverlayLayerMap m_pageOverlayLayers;
</span><span class="cx">     std::unique_ptr&lt;WebCore::TextureMapper&gt; m_textureMapper;
</span><span class="cx">     OwnPtr&lt;WebCore::GLContext&gt; m_context;
</span><del>-    double m_lastFlushTime;
</del><ins>+    double m_lastImmediateFlushTime;
</ins><span class="cx">     bool m_layerFlushSchedulingEnabled;
</span><span class="cx">     GMainLoopSource m_layerFlushTimerCallback;
</span><span class="cx"> };
</span></span></pre>
</div>
</div>

</body>
</html>