JavaScriptCore:
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Mar 2009 22:01:38 +0000 (22:01 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Mar 2009 22:01:38 +0000 (22:01 +0000)
2009-03-04  Antti Koivisto  <antti@apple.com>

        Reviewed by Dave Hyatt.

        https://bugs.webkit.org/show_bug.cgi?id=24359
        Repaint throttling mechanism

        Set ENABLE_REPAINT_THROTTLING to 0 by default.

        * wtf/Platform.h:

WebCore:

2009-03-04  Antti Koivisto  <antti@apple.com>

        Reviewed by Dave Hyatt.

        https://bugs.webkit.org/show_bug.cgi?id=24359
        Repaint throttling mechanism

        Excessive repainting can slow down page loadind. This implements a timer
        based throttling mechanism. It is behind ENABLE(REPAINT_THROTTLING) and not
        enabled by default.

        * loader/FrameLoader.cpp:
        (WebCore::FrameLoader::checkCompleted):
        * page/EventHandler.cpp:
        (WebCore::EventHandler::dispatchDragEvent):
        (WebCore::EventHandler::dispatchMouseEvent):
        (WebCore::EventHandler::keyEvent):
        (WebCore::EventHandler::handleTextInputEvent):
        * page/FrameView.cpp:
        (WebCore::FrameView::FrameView):
        (WebCore::FrameView::reset):
        (WebCore::FrameView::repaintContentRectangle):
        (WebCore::FrameView::beginDeferredRepaints):
        (WebCore::FrameView::endDeferredRepaints):
        (WebCore::FrameView::checkStopDelayingDeferredRepaints):
        (WebCore::FrameView::doDeferredRepaints):
        (WebCore::FrameView::updateDeferredRepaintDelay):
        (WebCore::FrameView::resetDeferredRepaintDelay):
        (WebCore::FrameView::adjustedDeferredRepaintDelay):
        (WebCore::FrameView::deferredRepaintTimerFired):
        (WebCore::FrameView::paintContents):
        * page/FrameView.h:

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

JavaScriptCore/ChangeLog
JavaScriptCore/wtf/Platform.h
WebCore/ChangeLog
WebCore/loader/FrameLoader.cpp
WebCore/page/EventHandler.cpp
WebCore/page/FrameView.cpp
WebCore/page/FrameView.h

index 49df4ceac55f248c46058760213e0a3df3397e9b..14fa6533d0811dd923611ffa5bb7db40e02bc0d3 100644 (file)
@@ -1,3 +1,14 @@
+2009-03-04  Antti Koivisto  <antti@apple.com>
+
+        Reviewed by Dave Hyatt.
+
+        https://bugs.webkit.org/show_bug.cgi?id=24359
+        Repaint throttling mechanism
+        
+        Set ENABLE_REPAINT_THROTTLING to 0 by default.
+
+        * wtf/Platform.h:
+
 2009-03-03  David Kilzer  <ddkilzer@apple.com>
 
         <rdar://problem/6581203> WebCore and WebKit should install the same set of headers during installhdrs phase as build phase
index cec420cb41ebcb5487914df4bd6faa92b02f197a..01bce7d25b06021052865f8d3b7bcad71ad3d587 100644 (file)
 #define WTF_USE_ALTERNATE_JSIMMEDIATE 1
 #endif
 
+#if !defined(ENABLE_REPAINT_THROTTLING)
+#define ENABLE_REPAINT_THROTTLING 0
+#endif
+
 #if !defined(ENABLE_JIT)
 /* The JIT is tested & working on x86_64 Mac */
 #if PLATFORM(X86_64) && PLATFORM(MAC)
index 13d476d320b192d193204db166a56a82b90bf909..5fa5f60a9f7fcbf6082f0af3365e310dedde56d7 100644 (file)
@@ -1,3 +1,36 @@
+2009-03-04  Antti Koivisto  <antti@apple.com>
+
+        Reviewed by Dave Hyatt.
+
+        https://bugs.webkit.org/show_bug.cgi?id=24359
+        Repaint throttling mechanism
+        
+        Excessive repainting can slow down page loadind. This implements a timer
+        based throttling mechanism. It is behind ENABLE(REPAINT_THROTTLING) and not 
+        enabled by default.
+
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::checkCompleted):
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::dispatchDragEvent):
+        (WebCore::EventHandler::dispatchMouseEvent):
+        (WebCore::EventHandler::keyEvent):
+        (WebCore::EventHandler::handleTextInputEvent):
+        * page/FrameView.cpp:
+        (WebCore::FrameView::FrameView):
+        (WebCore::FrameView::reset):
+        (WebCore::FrameView::repaintContentRectangle):
+        (WebCore::FrameView::beginDeferredRepaints):
+        (WebCore::FrameView::endDeferredRepaints):
+        (WebCore::FrameView::checkStopDelayingDeferredRepaints):
+        (WebCore::FrameView::doDeferredRepaints):
+        (WebCore::FrameView::updateDeferredRepaintDelay):
+        (WebCore::FrameView::resetDeferredRepaintDelay):
+        (WebCore::FrameView::adjustedDeferredRepaintDelay):
+        (WebCore::FrameView::deferredRepaintTimerFired):
+        (WebCore::FrameView::paintContents):
+        * page/FrameView.h:
+
 2009-03-04  Sam Weinig  <sam@webkit.org>
 
         Rubber-stamped by Antti Koivisto.
index eb28102dce425724f0a773004bd0859626d975fd..20e89125b3b2270d289601a43fcbdb98b3316fd7 100644 (file)
@@ -1213,6 +1213,9 @@ void FrameLoader::loadDone()
 
 void FrameLoader::checkCompleted()
 {
+    if (m_frame->view())
+        m_frame->view()->checkStopDelayingDeferredRepaints();
+
     // Any frame that hasn't completed yet?
     for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
         if (!child->loader()->m_isComplete)
index f054b38eb5d92e7dba38b43edea64f47ab02706b..b6212514b354571fee40108a0ad781a29cd47405 100644 (file)
@@ -1333,6 +1333,8 @@ bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent)
 
 bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTarget, const PlatformMouseEvent& event, Clipboard* clipboard)
 {
+    m_frame->view()->resetDeferredRepaintDelay();
+
     IntPoint contentsPos = m_frame->view()->windowToContents(event.pos());
     
     RefPtr<MouseEvent> me = MouseEvent::create(eventType,
@@ -1540,6 +1542,8 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo
 
 bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder)
 {
+    m_frame->view()->resetDeferredRepaintDelay();
+
     updateMouseEventTargetNode(targetNode, mouseEvent, setUnder);
 
     bool swallowEvent = false;
@@ -1796,6 +1800,8 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent)
     RefPtr<Node> node = eventTargetNodeForDocument(m_frame->document());
     if (!node)
         return false;
+    
+    m_frame->view()->resetDeferredRepaintDelay();
 
     // FIXME: what is this doing here, in keyboard event handler?
     m_frame->loader()->resetMultipleFormSubmissionProtection();
@@ -2130,6 +2136,8 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve
         target = eventTargetNodeForDocument(m_frame->document());
     if (!target)
         return false;
+    
+    m_frame->view()->resetDeferredRepaintDelay();
 
     RefPtr<TextEvent> event = TextEvent::create(m_frame->domWindow(), text);
     event->setUnderlyingEvent(underlyingEvent);
index 10936c1f8f93cf59e96eb79209625488553cc5db..69fce1a0b99ed6eb70b97805baf1e374f032940b 100644 (file)
@@ -29,6 +29,7 @@
 #include "AXObjectCache.h"
 #include "CSSStyleSelector.h"
 #include "ChromeClient.h"
+#include "DocLoader.h"
 #include "EventHandler.h"
 #include "FloatRect.h"
 #include "FocusController.h"
@@ -61,6 +62,25 @@ using namespace HTMLNames;
 
 double FrameView::sCurrentPaintTimeStamp = 0.0;
 
+#if ENABLE(REPAINT_THROTTLING)
+// Normal delay
+static const double deferredRepaintDelay = 0.025;
+// Negative value would mean that first few repaints happen without a delay
+static const double initialDeferredRepaintDelayDuringLoading = 0;
+// The delay grows on each repaint to this maximum value
+static const double maxDeferredRepaintDelayDuringLoading = 2.5;
+// On each repaint the delay increses by this amount
+static const double deferredRepaintDelayIncrementDuringLoading = 0.5;
+#else
+// FIXME: Repaint throttling could be good to have on all platform.
+// The balance between CPU use and repaint frequency will need some tuning for desktop.
+// More hooks may be needed to reset the delay on things like GIF and CSS animations.
+static const double deferredRepaintDelay = 0;
+static const double initialDeferredRepaintDelayDuringLoading = 0;
+static const double maxDeferredRepaintDelayDuringLoading = 0;
+static const double deferredRepaintDelayIncrementDuringLoading = 0;
+#endif
+
 struct ScheduledEvent {
     RefPtr<Event> m_event;
     RefPtr<Node> m_eventTarget;
@@ -84,6 +104,7 @@ FrameView::FrameView(Frame* frame)
     , m_viewportRenderer(0)
     , m_wasScrolledByUser(false)
     , m_inProgrammaticScroll(false)
+    , m_deferredRepaintTimer(this, &FrameView::deferredRepaintTimerFired)
     , m_shouldUpdateWhileOffscreen(true)
 {
     init();
@@ -108,6 +129,7 @@ FrameView::FrameView(Frame* frame, const IntSize& initialSize)
     , m_viewportRenderer(0)
     , m_wasScrolledByUser(false)
     , m_inProgrammaticScroll(false)
+    , m_deferredRepaintTimer(this, &FrameView::deferredRepaintTimerFired)
     , m_shouldUpdateWhileOffscreen(true)
 {
     init();
@@ -160,8 +182,10 @@ void FrameView::reset()
     m_lastZoomFactor = 1.0f;
     m_deferringRepaints = 0;
     m_repaintCount = 0;
-    m_repaintRect = IntRect();
     m_repaintRects.clear();
+    m_deferredRepaintDelay = initialDeferredRepaintDelayDuringLoading;
+    m_deferredRepaintTimer.stop();
+    m_lastPaintTime = 0;
     m_paintRestriction = PaintRestrictionNone;
     m_isPainting = false;
     m_isVisuallyNonEmpty = false;
@@ -703,17 +727,27 @@ void FrameView::repaintContentRectangle(const IntRect& r, bool immediate)
 {
     ASSERT(!m_frame->document()->ownerElement());
 
-    if (m_deferringRepaints && !immediate) {
+    double delay = adjustedDeferredRepaintDelay();
+    if ((m_deferringRepaints || m_deferredRepaintTimer.isActive() || delay) && !immediate) {
         IntRect visibleContent = visibleContentRect();
         visibleContent.intersect(r);
-        if (!visibleContent.isEmpty()) {
-            m_repaintCount++;
-            m_repaintRect.unite(r);
-            if (m_repaintCount == cRepaintRectUnionThreshold)
-                m_repaintRects.clear();
-            else if (m_repaintCount < cRepaintRectUnionThreshold)
-                m_repaintRects.append(r);
+        if (visibleContent.isEmpty())
+            return;
+        if (m_repaintCount == cRepaintRectUnionThreshold) {
+            IntRect unionedRect;
+            for (unsigned i = 0; i < cRepaintRectUnionThreshold; ++i)
+                unionedRect.unite(m_repaintRects[i]);
+            m_repaintRects.clear();
+            m_repaintRects.append(unionedRect);
         }
+        if (m_repaintCount < cRepaintRectUnionThreshold)
+            m_repaintRects.append(r);
+        else
+            m_repaintRects[0].unite(r);
+        m_repaintCount++;
+    
+        if (!m_deferringRepaints && !m_deferredRepaintTimer.isActive())
+             m_deferredRepaintTimer.startOneShot(delay);
         return;
     }
     
@@ -730,9 +764,6 @@ void FrameView::beginDeferredRepaints()
         return page->mainFrame()->view()->beginDeferredRepaints();
 
     m_deferringRepaints++;
-    m_repaintCount = 0;
-    m_repaintRect = IntRect();
-    m_repaintRects.clear();
 }
 
 
@@ -743,18 +774,86 @@ void FrameView::endDeferredRepaints()
         return page->mainFrame()->view()->endDeferredRepaints();
 
     ASSERT(m_deferringRepaints > 0);
-    if (--m_deferringRepaints == 0) {
-        if (m_repaintCount >= cRepaintRectUnionThreshold)
-            repaintContentRectangle(m_repaintRect, false);
-        else {
-            unsigned size = m_repaintRects.size();
-            for (unsigned i = 0; i < size; i++)
-                repaintContentRectangle(m_repaintRects[i], false);
-            m_repaintRects.clear();
-        }
+
+    if (--m_deferringRepaints)
+        return;
+    
+    if (m_deferredRepaintTimer.isActive())
+        return;
+
+    if (double delay = adjustedDeferredRepaintDelay()) {
+        m_deferredRepaintTimer.startOneShot(delay);
+        return;
     }
+    
+    doDeferredRepaints();
 }
 
+void FrameView::checkStopDelayingDeferredRepaints()
+{
+    if (!m_deferredRepaintTimer.isActive())
+        return;
+
+    Document* document = m_frame->document();
+    if (document && (document->parsing() || document->docLoader()->requestCount()))
+        return;
+    
+    m_deferredRepaintTimer.stop();
+
+    doDeferredRepaints();
+}
+    
+void FrameView::doDeferredRepaints()
+{
+    ASSERT(!m_deferringRepaints);
+    if (isOffscreen() && !shouldUpdateWhileOffscreen()) {
+        m_repaintRects.clear();
+        m_repaintCount = 0;
+        return;
+    }
+    unsigned size = m_repaintRects.size();
+    for (unsigned i = 0; i < size; i++)
+        ScrollView::repaintContentRectangle(m_repaintRects[i], false);
+    m_repaintRects.clear();
+    m_repaintCount = 0;
+    
+    updateDeferredRepaintDelay();
+}
+
+void FrameView::updateDeferredRepaintDelay()
+{
+    Document* document = m_frame->document();
+    if (!document || (!document->parsing() && !document->docLoader()->requestCount())) {
+        m_deferredRepaintDelay = deferredRepaintDelay;
+        return;
+    }
+    if (m_deferredRepaintDelay < maxDeferredRepaintDelayDuringLoading) {
+        m_deferredRepaintDelay += deferredRepaintDelayIncrementDuringLoading;
+        if (m_deferredRepaintDelay > maxDeferredRepaintDelayDuringLoading)
+            m_deferredRepaintDelay = maxDeferredRepaintDelayDuringLoading;
+    }
+}
+
+void FrameView::resetDeferredRepaintDelay()
+{
+    m_deferredRepaintDelay = 0;
+    if (m_deferredRepaintTimer.isActive())
+        m_deferredRepaintTimer.startOneShot(0);
+}
+
+double FrameView::adjustedDeferredRepaintDelay() const
+{
+    if (!m_deferredRepaintDelay)
+        return 0;
+    double timeSinceLastPaint = currentTime() - m_lastPaintTime;
+    return max(0., m_deferredRepaintDelay - timeSinceLastPaint);
+}
+    
+void FrameView::deferredRepaintTimerFired(Timer<FrameView>*)
+{
+    doDeferredRepaints();
+}    
+
 void FrameView::layoutTimerFired(Timer<FrameView>*)
 {
 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
@@ -1224,8 +1323,9 @@ void FrameView::paintContents(GraphicsContext* p, const IntRect& rect)
     if (m_paintRestriction == PaintRestrictionNone)
         document->invalidateRenderedRectsForMarkersInRect(rect);
     contentRenderer->layer()->paint(p, rect, m_paintRestriction, eltRenderer);
-        
+    
     m_isPainting = false;
+    m_lastPaintTime = currentTime();
 
 #if ENABLE(DASHBOARD_SUPPORT)
     // Regions may have changed as a result of the visibility/z-index of element changing.
index 1ad0fbd166fdcd5f0d49fa46fb4bc9b1cab10bb2..42066004f92a08884e122243b55e9d4adf308571 100644 (file)
@@ -149,6 +149,8 @@ public:
 
     void beginDeferredRepaints();
     void endDeferredRepaints();
+    void checkStopDelayingDeferredRepaints();
+    void resetDeferredRepaintDelay();
 
 #if ENABLE(DASHBOARD_SUPPORT)
     void updateDashboardRegions();
@@ -203,6 +205,11 @@ private:
     virtual void repaintContentRectangle(const IntRect&, bool immediate);
     virtual void contentsResized() { setNeedsLayout(); }
     virtual void visibleContentsResized() { layout(); }
+    
+    void deferredRepaintTimerFired(Timer<FrameView>*);
+    void doDeferredRepaints();
+    void updateDeferredRepaintDelay();
+    double adjustedDeferredRepaintDelay() const;
 
     static double sCurrentPaintTimeStamp; // used for detecting decoded resource thrash in the cache
 
@@ -254,8 +261,10 @@ private:
     
     unsigned m_deferringRepaints;
     unsigned m_repaintCount;
-    IntRect m_repaintRect;
     Vector<IntRect> m_repaintRects;
+    Timer<FrameView> m_deferredRepaintTimer;
+    double m_deferredRepaintDelay;
+    double m_lastPaintTime;
 
     bool m_shouldUpdateWhileOffscreen;