Changeset 54344 in webkit


Ignore:
Timestamp:
Feb 4, 2010 8:56:18 AM (14 years ago)
Author:
eric@webkit.org
Message:

2010-02-04 No'am Rosenthal <noam.rosenthal@nokia.com>

Reviewed by Ariya Hidayat.

[Qt] Tuning and optimizations to GraphicsLayerQt. Reduce unnecessary
recaching, remove QTimer::singleShot and QPixmap::scaled, more
accurate strategy of handling transform operation blends. Rotating a
bordered-table, for example, now runs at 50FPS instead of 40FPS on Maemo5.

https://bugs.webkit.org/show_bug.cgi?id=34062

This is tested by https://bugs.webkit.org/show_bug.cgi?id=34450, fps measurements.

  • platform/graphics/qt/GraphicsLayerQt.cpp: (WebCore::GraphicsLayerQtImpl::flushChanges): Fine-tune caching (WebCore::TransformAnimationQt::TransformAnimationQt): transform bugs (WebCore::OpacityAnimationQt::updateState): style change

2010-02-04 No'am Rosenthal <noam.rosenthal@nokia.com>

Reviewed by Ariya Hidayat.

[Qt] Tuning and optimizations to GraphicsLayerQt. Mainly reduced usage
of QTimer::singleShot, and moved syncLayers() from paint() to update()
https://bugs.webkit.org/show_bug.cgi?id=34062

  • Api/qgraphicswebview.cpp: (QGraphicsWebViewPrivate::update): Moved the sync operation to update (QGraphicsWebView::paint): Moved the sync operation to update
Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r54343 r54344  
     12010-02-04  No'am Rosenthal  <noam.rosenthal@nokia.com>
     2
     3        Reviewed by Ariya Hidayat.
     4
     5        [Qt] Tuning and optimizations to GraphicsLayerQt. Reduce unnecessary
     6        recaching, remove QTimer::singleShot and QPixmap::scaled, more
     7        accurate strategy of handling transform operation blends. Rotating a
     8        bordered-table, for example, now runs at 50FPS instead of 40FPS on Maemo5.
     9
     10        https://bugs.webkit.org/show_bug.cgi?id=34062
     11
     12        This is tested by https://bugs.webkit.org/show_bug.cgi?id=34450, fps measurements.
     13
     14        * platform/graphics/qt/GraphicsLayerQt.cpp:
     15        (WebCore::GraphicsLayerQtImpl::flushChanges): Fine-tune caching
     16        (WebCore::TransformAnimationQt::TransformAnimationQt): transform bugs
     17        (WebCore::OpacityAnimationQt::updateState): style change
     18
    1192010-02-04  Pavel Feldman  <pfeldman@chromium.org>
    220
  • trunk/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp

    r54284 r54344  
    100100    void flushChanges(bool recursive = true);
    101101
     102    // optimization: when we have an animation running on an element with no contents, that has child-elements with contents,
     103    // ALL of them have to have ItemCoordinateCache and not DeviceCoordinateCache
     104    void adjustCachingRecursively(bool animationIsRunning);
     105
     106    // optimization: returns true if this or an ancestor has a transform animation running.
     107    // this enables us to use ItemCoordinatesCache while the animation is running, otherwise we have to recache for every frame
     108    bool isTransformAnimationRunning() const;
     109
    102110public slots:
    103111    // we need to notify the client (aka the layer compositor) when the animation actually starts
    104112    void notifyAnimationStarted();
    105113
     114signals:
     115    // optimization: we don't want to use QTimer::singleShot
     116    void notifyAnimationStartedAsync();
     117
    106118public:
    107119    GraphicsLayerQt* m_layer;
    108120
    109     QTransform m_baseTransfom;
     121    QTransform m_baseTransform;
    110122    bool m_transformAnimationRunning;
    111123    bool m_opacityAnimationRunning;
     
    158170        bool distributeOpacity: 1;
    159171        bool align: 2;
    160         State(): maskLayer(0), opacity(1), preserves3D(false), masksToBounds(false),
     172        State(): maskLayer(0), opacity(1.f), preserves3D(false), masksToBounds(false),
    161173                  drawsContent(false), contentsOpaque(false), backfaceVisibility(false),
    162174                  distributeOpacity(false)
     
    164176        }
    165177    } m_state;
     178
     179    friend class AnimationQtBase;
    166180};
    167181
     
    172186    , m_changeMask(NoChanges)
    173187{
    174     // better to calculate the exposed rect in QGraphicsView than over-render in WebCore
    175     // FIXME: test different approaches
    176     setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
    177 
    178188    // we use graphics-view for compositing, not for interactivity
    179189    setAcceptedMouseButtons(Qt::NoButton);
     
    182192    // we'll set the cache when we know what's going on
    183193    setCacheMode(NoCache);
     194
     195    connect(this, SIGNAL(notifyAnimationStartedAsync()), this, SLOT(notifyAnimationStarted()), Qt::QueuedConnection);
    184196}
    185197
     
    204216}
    205217
     218void GraphicsLayerQtImpl::adjustCachingRecursively(bool animationIsRunning)
     219{
     220    // optimization: we make sure all our children have ItemCoordinateCache -
     221    // otherwise we end up re-rendering them during the animation
     222    const QList<QGraphicsItem*> children = childItems();
     223
     224    for (QList<QGraphicsItem*>::const_iterator it = children.begin(); it != children.end(); ++it) {
     225        if (QGraphicsItem* item = *it)
     226            if (GraphicsLayerQtImpl* layer = qobject_cast<GraphicsLayerQtImpl*>(item->toGraphicsObject())) {
     227                if (layer->m_layer->drawsContent() && layer->m_currentContent.contentType == HTMLContentType)
     228                    layer->setCacheMode(animationIsRunning ? QGraphicsItem::ItemCoordinateCache : QGraphicsItem::DeviceCoordinateCache);
     229            }
     230    }   
     231}
     232
    206233void GraphicsLayerQtImpl::setBaseTransform(const QTransform& transform)
    207234{
    208235    if (!m_layer)
    209236        return;
    210     // webkit has relative-to-size originPoint, graphics-view has a pixel originPoint
    211     // here we convert
    212     QPointF originTranslate(
    213             m_layer->anchorPoint().x() * m_layer->size().width(), m_layer->anchorPoint().y() * m_layer->size().height());
    214 
    215     resetTransform();
    216 
    217     // we have to manage this ourselves because QGraphicsView's transformOrigin is incomplete
    218     translate(originTranslate.x(), originTranslate.y());
     237    // webkit has relative-to-size originPoint, graphics-view has a pixel originPoint, here we convert
     238    // we have to manage this ourselves because QGraphicsView's transformOrigin is incompatible
     239    const qreal x = m_layer->anchorPoint().x() * m_layer->size().width();
     240    const qreal y = m_layer->anchorPoint().y() * m_layer->size().height();
     241    setTransform(QTransform::fromTranslate(x, y));
    219242    setTransform(transform, true);
    220     translate(-originTranslate.x(), -originTranslate.y());
    221     m_baseTransfom = transform;
     243    translate(-x, -y);
     244    m_baseTransform = transform;
     245}
     246
     247bool GraphicsLayerQtImpl::isTransformAnimationRunning() const
     248{
     249    if (m_transformAnimationRunning)
     250        return true;
     251    if (GraphicsLayerQtImpl* parent = qobject_cast<GraphicsLayerQtImpl*>(parentObject()))
     252        return parent->isTransformAnimationRunning();
     253    return false;
    222254}
    223255
     
    271303}
    272304
    273 void GraphicsLayerQtImpl::drawContents(QPainter* painter, const QRectF& r, bool mask)
    274 {
    275     QRect rect = r.toAlignedRect();
    276    
    277     if (m_currentContent.contentType != HTMLContentType && !m_state.contentsRect.isEmpty())
    278         rect = rect.intersected(m_state.contentsRect);
    279 
     305void GraphicsLayerQtImpl::drawContents(QPainter* painter, const QRectF& exposedRect, bool mask)
     306{
    280307    if (m_currentContent.backgroundColor.isValid())
    281         painter->fillRect(r, QColor(m_currentContent.backgroundColor));
    282 
    283     if (!rect.isEmpty()) {
    284         switch (m_currentContent.contentType) {
    285         case PixmapContentType:
    286             // we have to scale the image to the contentsRect
    287             // FIXME: a better way would probably be drawPixmap with a src/target rect
    288             painter->drawPixmap(rect.topLeft(), m_currentContent.pixmap.scaled(m_state.contentsRect.size()), r);
    289             break;
    290         case ColorContentType:
    291             painter->fillRect(rect, m_currentContent.contentsBackgroundColor);
    292             break;
    293         default:
    294             if (m_state.drawsContent) {
    295                 // this is the "expensive" bit. we try to minimize calls to this
    296                 // neck of the woods by proper caching
    297                 GraphicsContext gc(painter);
    298                 m_layer->paintGraphicsLayerContents(gc, rect);
    299             }
    300             break;
    301         }
     308        painter->fillRect(exposedRect, QColor(m_currentContent.backgroundColor));
     309
     310    switch (m_currentContent.contentType) {
     311    case PixmapContentType:
     312        painter->drawPixmap(m_state.contentsRect, m_currentContent.pixmap);
     313        break;
     314    case ColorContentType:
     315        painter->fillRect(m_state.contentsRect, m_currentContent.contentsBackgroundColor);
     316        break;
     317    default:
     318        if (m_state.drawsContent) {
     319            // this is the "expensive" bit. we try to minimize calls to this neck of the woods by proper caching
     320            GraphicsContext gc(painter);
     321            m_layer->paintGraphicsLayerContents(gc, exposedRect.toAlignedRect());
     322        }
     323        break;
    302324    }
    303325}
     
    305327void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask)
    306328{
    307     if (!this)
    308         return;
     329    Q_ASSERT(this);
    309330
    310331    m_changeMask |= changeMask;
     
    343364        const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren;
    344365        const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren;
    345         for (QSet<QGraphicsItem*>::const_iterator it = childrenToAdd.begin(); it != childrenToAdd.end(); ++it) {
     366
     367        for (QSet<QGraphicsItem*>::const_iterator it = childrenToAdd.begin(); it != childrenToAdd.end(); ++it)
    346368             if (QGraphicsItem* w = *it)
    347369                w->setParentItem(this);
    348         }
    349         for (QSet<QGraphicsItem*>::const_iterator it = childrenToRemove.begin(); it != childrenToRemove.end(); ++it) {
     370
     371        for (QSet<QGraphicsItem*>::const_iterator it = childrenToRemove.begin(); it != childrenToRemove.end(); ++it)
    350372             if (QGraphicsItem* w = *it)
    351373                w->setParentItem(0);
    352         }
    353374
    354375        // children are ordered by z-value, let graphics-view know.
     
    384405        // that we give Qt
    385406        if (m_state.transform != m_layer->transform() || m_state.anchorPoint != m_layer->anchorPoint() || m_state.size != m_layer->size())
    386             setBaseTransform(QTransform(m_layer->transform()));
     407            setBaseTransform(m_layer->transform());
    387408    }
    388409
     
    390411        switch (m_pendingContent.contentType) {
    391412        case PixmapContentType:
    392             // we need cache even for images, because they need to be resized
    393             // to the contents rect. maybe this can be optimized though
    394             setCacheMode(m_transformAnimationRunning ? ItemCoordinateCache : DeviceCoordinateCache);
    395413            update();
    396414            setFlag(ItemHasNoContents, false);
     415
     416            // we only use ItemUsesExtendedStyleOption for HTML content - pixmap can be handled better with regular clipping
     417            setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false);
    397418            break;
    398419
     
    404425            m_state.drawsContent = false;
    405426            setFlag(ItemHasNoContents, false);
     427
     428            // we only use ItemUsesExtendedStyleOption for HTML content - colors don't gain much from that anyway
     429            setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, false);
    406430            break;
    407431
     
    411435            if (!m_state.drawsContent && m_layer->drawsContent())
    412436                update();
    413             if (m_layer->drawsContent())
    414                 setCacheMode(m_transformAnimationRunning ? ItemCoordinateCache : DeviceCoordinateCache);
     437                if (m_layer->drawsContent()) {
     438                    const QGraphicsItem::CacheMode mewCacheMode = isTransformAnimationRunning() ? ItemCoordinateCache : DeviceCoordinateCache;
     439
     440                    // optimization: QGraphicsItem doesn't always perform this test
     441                    if (mewCacheMode != cacheMode())
     442                        setCacheMode(mewCacheMode);
     443
     444                    // HTML content: we want to use exposedRect so we don't use WebCore rendering if we don't have to
     445                    setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);
     446                }
    415447            else
    416448                setCacheMode(NoCache);
     
    449481
    450482    // FIXME: the following flags are currently not handled, as they don't have a clear test or are in low priority
    451     // GeometryOrientationChange, ContentsOrientationChange, BackfaceVisibilityChange, ChildrenTransformChange
     483    // GeometryOrientationChange, ContentsOrientationChange, BackfaceVisibilityChange, ChildrenTransformChange, Preserves3DChange
    452484
    453485    m_state.maskLayer = m_layer->maskLayer();
     
    524556
    525557// reimp from GraphicsLayer.h
    526 void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& r)
    527 {
    528     m_impl->m_pendingContent.regionToUpdate|= QRectF(r).toAlignedRect();
     558void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect)
     559{
     560    m_impl->m_pendingContent.regionToUpdate|= QRectF(rect).toAlignedRect();
    529561    m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange);
    530562}
     
    808840static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, int duration)
    809841{
    810     if (timingFunction.type() == LinearTimingFunction)
     842        if (timingFunction.type() == LinearTimingFunction)
     843            return progress;
     844        if (timingFunction.type() == CubicBezierTimingFunction) {
     845            return solveCubicBezierFunction(timingFunction.x1(),
     846                                            timingFunction.y1(),
     847                                            timingFunction.x2(),
     848                                            timingFunction.y2(),
     849                                            double(progress), double(duration) / 1000);
     850        }
    811851        return progress;
    812     if (timingFunction.type() == CubicBezierTimingFunction) {
    813         return solveCubicBezierFunction(timingFunction.x1(),
    814                                         timingFunction.y1(),
    815                                         timingFunction.x2(),
    816                                         timingFunction.y2(),
    817                                         double(progress), double(duration) / 1000);
    818     }
    819     return progress;
    820852}
    821853
     
    827859        return;
    828860
    829     const TransformOperations* ops = static_cast<const TransformAnimationValue*>(animationValue)->value();
    830 
    831     if (ops)
     861    if (const TransformOperations* ops = static_cast<const TransformAnimationValue*>(animationValue)->value())
    832862        transformOperations = *ops;
    833863}
     
    857887
    858888        // for some reason I have do this asynchronously - or the animation won't work
    859         if (newState == Running && oldState == Stopped)
    860             QTimer::singleShot(0, m_layer.data(), SLOT(notifyAnimationStarted()));
     889        if (newState == Running && oldState == Stopped && m_layer.data())
     890            m_layer.data()->notifyAnimationStartedAsync();
    861891    }
    862892
     
    933963        // now we have a source keyframe, origin keyframe and a timing function
    934964        // we can now process the progress and apply the frame
    935         qreal normalizedProgress = (it.key() == it2.key()) ? 0 : (progress - it.key()) / (it2.key() - it.key());
    936         normalizedProgress = applyTimingFunction(timingFunc, normalizedProgress, duration() / 1000);
    937         applyFrame(fromValue, toValue, normalizedProgress);
     965        progress = (!progress || progress == 1 || it.key() == it2.key())
     966                                         ? progress
     967                                         : applyTimingFunction(timingFunc, (progress - it.key()) / (it2.key() - it.key()), duration() / 1000);
     968        applyFrame(fromValue, toValue, progress);
    938969    }
    939970
     
    944975public:
    945976    TransformAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name)
    946                 : AnimationQt<TransformOperations>(layer, values, boxSize, anim, name)
     977        : AnimationQt<TransformOperations>(layer, values, boxSize, anim, name)
    947978    {
    948979    }
     
    953984        // when the animation dies, the transform has to go back to default
    954985        if (m_layer)
    955             m_layer.data()->setBaseTransform(QTransform(m_layer.data()->m_layer->transform()));
     986            m_layer.data()->setBaseTransform(m_layer.data()->m_layer->transform());
    956987    }
    957988
     
    964995        TransformationMatrix transformMatrix;
    965996
    966         // this looks simple but is really tricky to get right. Use caution.
    967         for (size_t i = 0; i < targetOperations.size(); ++i)
    968             targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize);
    969 
    970         m_layer.data()->setBaseTransform(QTransform(transformMatrix));
     997        // sometimes the animation values from WebCore are misleading and we have to use the actual matrix as source
     998        // The Mac implementation simply doesn't try to accelerate those (e.g. 360deg rotation), but we do.
     999        if (progress == 1 || !targetOperations.size() || sourceOperations == targetOperations) {
     1000            TransformationMatrix sourceMatrix;
     1001            sourceOperations.apply(m_boxSize, sourceMatrix);
     1002            transformMatrix = m_sourceMatrix;
     1003            transformMatrix.blend(sourceMatrix, 1 - progress);
     1004        } else if (targetOperations.size() != sourceOperations.size()) {
     1005            transformMatrix = m_sourceMatrix;
     1006            targetOperations.apply(m_boxSize, transformMatrix);
     1007            transformMatrix.blend(m_sourceMatrix, progress);
     1008        } else {
     1009            for (size_t i = 0; i < targetOperations.size(); ++i)
     1010                targetOperations.operations()[i]->blend(sourceOperations.at(i), progress)->apply(transformMatrix, m_boxSize);
     1011        }
     1012        m_layer.data()->setBaseTransform(transformMatrix);
    9711013    }
    9721014
     
    9811023        // this is a UX choice that should probably be customizable
    9821024        if (newState == QAbstractAnimation::Running) {
     1025            m_sourceMatrix = m_layer.data()->m_layer->transform();
    9831026            m_layer.data()->m_transformAnimationRunning = true;
    984             if (m_layer.data()->cacheMode() == QGraphicsItem::DeviceCoordinateCache)
    985                 m_layer.data()->setCacheMode(QGraphicsItem::ItemCoordinateCache);
     1027            m_layer.data()->adjustCachingRecursively(true);
    9861028        } else {
    9871029            m_layer.data()->m_transformAnimationRunning = false;
    988             if (m_layer.data()->cacheMode() == QGraphicsItem::ItemCoordinateCache)
    989                 m_layer.data()->setCacheMode(QGraphicsItem::DeviceCoordinateCache);
    990         }
    991     }
     1030            m_layer.data()->adjustCachingRecursively(false);
     1031        }
     1032    }
     1033
     1034    TransformationMatrix m_sourceMatrix;
    9921035};
    9931036
     
    10071050    {
    10081051        QAbstractAnimation::updateState(newState, oldState);
     1052
    10091053        if (m_layer)
    10101054            m_layer.data()->m_opacityAnimationRunning = (newState == QAbstractAnimation::Running);
     
    10801124{
    10811125    for (QList<QWeakPointer<QAbstractAnimation> >::iterator it = m_impl->m_animations.begin(); it != m_impl->m_animations.end(); ++it) {
    1082         if (*it) {
    1083             AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data());
    1084             if (anim && anim->m_keyframesName == QString(name))
    1085                 QTimer::singleShot(timeOffset * 1000, anim, SLOT(pause()));
    1086         }
     1126        if (!(*it))
     1127            continue;
     1128
     1129        AnimationQtBase* anim = static_cast<AnimationQtBase*>((*it).data());
     1130        if (anim && anim->m_keyframesName == QString(name))
     1131            QTimer::singleShot(timeOffset * 1000, anim, SLOT(pause()));
    10871132    }
    10881133}
  • trunk/WebKit/qt/Api/qgraphicswebview.cpp

    r54284 r54344  
    2828#include "QWebPageClient.h"
    2929#include <FrameView.h>
     30#include <QtCore/qmetaobject.h>
    3031#include <QtCore/qsharedpointer.h>
    3132#include <QtCore/qtimer.h>
     
    7980#if USE(ACCELERATED_COMPOSITING)
    8081        , rootGraphicsLayer(0)
    81         , shouldSync(true)
     82        , shouldSync(false)
    8283#endif
    8384    {
     
    8687        // this QGraphicsWebView as the scrollbars are needed when there's no compositing
    8788        q->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption);
     89        syncMetaMethod = q->metaObject()->method(q->metaObject()->indexOfMethod("syncLayers()"));
    8890#endif
    8991    }
     
    131133    // compositor telling us to do so. We'll get that call from ChromeClientQt
    132134    bool shouldSync;
     135
     136    // we have to flush quite often, so we use a meta-method instead of QTimer::singleShot for putting the event in the queue
     137    QMetaMethod syncMetaMethod;
    133138
    134139    // we need to put the root graphics layer behind the overlay (which contains the scrollbar)
     
    179184    shouldSync = true;
    180185    if (scheduleSync)
    181         QTimer::singleShot(0, q, SLOT(syncLayers()));
     186        syncMetaMethod.invoke(q, Qt::QueuedConnection);
    182187}
    183188
     
    225230    if (overlay)
    226231        overlay->update(QRectF(dirtyRect));
     232    syncLayers();
    227233#endif
    228234}
     
    443449#if USE(ACCELERATED_COMPOSITING)
    444450    page()->mainFrame()->render(painter, d->overlay ? QWebFrame::ContentsLayer : QWebFrame::AllLayers, option->exposedRect.toAlignedRect());
    445     d->syncLayers();
    446451#else
    447452    page()->mainFrame()->render(painter, QWebFrame::AllLayers, option->exposedRect.toRect());
  • trunk/WebKit/qt/ChangeLog

    r54284 r54344  
     12010-02-04  No'am Rosenthal  <noam.rosenthal@nokia.com>
     2
     3        Reviewed by Ariya Hidayat.
     4
     5        [Qt] Tuning and optimizations to GraphicsLayerQt. Mainly reduced usage
     6        of QTimer::singleShot, and moved syncLayers() from paint() to update()
     7        https://bugs.webkit.org/show_bug.cgi?id=34062
     8
     9        * Api/qgraphicswebview.cpp:
     10        (QGraphicsWebViewPrivate::update): Moved the sync operation to update
     11        (QGraphicsWebView::paint): Moved the sync operation to update
     12
    1132010-02-03  Andras Becsi  <abecsi@webkit.org>
    214
Note: See TracChangeset for help on using the changeset viewer.