From 4ffc18b13424d21c4199a9aec12a3fa2916786b7 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos Date: Fri, 6 Mar 2015 07:33:11 +0000 Subject: [PATCH] Timers might never be fired during animations =================================================================== Gbp-Pq: Name fix-timers-animations.patch --- .../WebPage/gtk/LayerTreeHostGtk.cpp | 43 +++++++++++++++---- .../WebProcess/WebPage/gtk/LayerTreeHostGtk.h | 2 +- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp b/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp index 4074bf0a06..08da299fcc 100644 --- a/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp +++ b/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.cpp @@ -72,7 +72,7 @@ LayerTreeHostGtk::LayerTreeHostGtk(WebPage* webPage) : LayerTreeHost(webPage) , m_isValid(true) , m_notifyAfterScheduledLayerFlush(false) - , m_lastFlushTime(0) + , m_lastImmediateFlushTime(0) , m_layerFlushSchedulingEnabled(true) { } @@ -273,16 +273,44 @@ void LayerTreeHostGtk::paintContents(const GraphicsLayer* graphicsLayer, Graphic // FIXME: Draw page overlays. https://bugs.webkit.org/show_bug.cgi?id=131433. } +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() { + double fireTime = monotonicallyIncreasingTime(); flushAndRenderLayers(); + if (m_layerFlushTimerCallback.isScheduled() || !toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations()) + return; - if (!m_layerFlushTimerCallback.isScheduled() && toTextureMapperLayer(m_rootLayer.get())->descendantsOrSelfHaveRunningAnimations()) { - const double targetFPS = 60; - double nextFlush = std::max((1 / targetFPS) - (currentTime() - m_lastFlushTime), 0.0); - m_layerFlushTimerCallback.scheduleAfterDelay("[WebKit] layerFlushTimer", std::bind(&LayerTreeHostGtk::layerFlushTimerFired, this), - std::chrono::duration_cast(std::chrono::duration(nextFlush)), GDK_PRIORITY_EVENTS); + 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::duration(timeToNextFlush)), layerFlushTimerPriority); } bool LayerTreeHostGtk::flushPendingLayerChanges() @@ -335,7 +363,6 @@ void LayerTreeHostGtk::flushAndRenderLayers() if (!context || !context->makeContextCurrent()) return; - m_lastFlushTime = currentTime(); if (!flushPendingLayerChanges()) return; @@ -381,7 +408,7 @@ void LayerTreeHostGtk::scheduleLayerFlush() // 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), GDK_PRIORITY_EVENTS); + m_layerFlushTimerCallback.schedule("[WebKit] layerFlushTimer", std::bind(&LayerTreeHostGtk::layerFlushTimerFired, this), layerFlushTimerPriority); } void LayerTreeHostGtk::setLayerFlushSchedulingEnabled(bool layerFlushingEnabled) diff --git a/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h b/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h index 65b255e2b7..b4e9ab6809 100644 --- a/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h +++ b/Source/WebKit2/WebProcess/WebPage/gtk/LayerTreeHostGtk.h @@ -103,7 +103,7 @@ private: PageOverlayLayerMap m_pageOverlayLayers; std::unique_ptr m_textureMapper; OwnPtr m_context; - double m_lastFlushTime; + double m_lastImmediateFlushTime; bool m_layerFlushSchedulingEnabled; GMainLoopSource m_layerFlushTimerCallback; }; -- 2.30.2