Changeset 54344 in webkit
- Timestamp:
- Feb 4, 2010 8:56:18 AM (14 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r54343 r54344 1 2010-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 1 19 2010-02-04 Pavel Feldman <pfeldman@chromium.org> 2 20 -
trunk/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp
r54284 r54344 100 100 void flushChanges(bool recursive = true); 101 101 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 102 110 public slots: 103 111 // we need to notify the client (aka the layer compositor) when the animation actually starts 104 112 void notifyAnimationStarted(); 105 113 114 signals: 115 // optimization: we don't want to use QTimer::singleShot 116 void notifyAnimationStartedAsync(); 117 106 118 public: 107 119 GraphicsLayerQt* m_layer; 108 120 109 QTransform m_baseTransfo m;121 QTransform m_baseTransform; 110 122 bool m_transformAnimationRunning; 111 123 bool m_opacityAnimationRunning; … … 158 170 bool distributeOpacity: 1; 159 171 bool align: 2; 160 State(): maskLayer(0), opacity(1 ), preserves3D(false), masksToBounds(false),172 State(): maskLayer(0), opacity(1.f), preserves3D(false), masksToBounds(false), 161 173 drawsContent(false), contentsOpaque(false), backfaceVisibility(false), 162 174 distributeOpacity(false) … … 164 176 } 165 177 } m_state; 178 179 friend class AnimationQtBase; 166 180 }; 167 181 … … 172 186 , m_changeMask(NoChanges) 173 187 { 174 // better to calculate the exposed rect in QGraphicsView than over-render in WebCore175 // FIXME: test different approaches176 setFlag(QGraphicsItem::ItemUsesExtendedStyleOption, true);177 178 188 // we use graphics-view for compositing, not for interactivity 179 189 setAcceptedMouseButtons(Qt::NoButton); … … 182 192 // we'll set the cache when we know what's going on 183 193 setCacheMode(NoCache); 194 195 connect(this, SIGNAL(notifyAnimationStartedAsync()), this, SLOT(notifyAnimationStarted()), Qt::QueuedConnection); 184 196 } 185 197 … … 204 216 } 205 217 218 void 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 206 233 void GraphicsLayerQtImpl::setBaseTransform(const QTransform& transform) 207 234 { 208 235 if (!m_layer) 209 236 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)); 219 242 setTransform(transform, true); 220 translate(-originTranslate.x(), -originTranslate.y()); 221 m_baseTransfom = transform; 243 translate(-x, -y); 244 m_baseTransform = transform; 245 } 246 247 bool 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; 222 254 } 223 255 … … 271 303 } 272 304 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 305 void GraphicsLayerQtImpl::drawContents(QPainter* painter, const QRectF& exposedRect, bool mask) 306 { 280 307 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; 302 324 } 303 325 } … … 305 327 void GraphicsLayerQtImpl::notifyChange(ChangeMask changeMask) 306 328 { 307 if (!this) 308 return; 329 Q_ASSERT(this); 309 330 310 331 m_changeMask |= changeMask; … … 343 364 const QSet<QGraphicsItem*> childrenToAdd = newChildren - currentChildren; 344 365 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) 346 368 if (QGraphicsItem* w = *it) 347 369 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) 350 372 if (QGraphicsItem* w = *it) 351 373 w->setParentItem(0); 352 }353 374 354 375 // children are ordered by z-value, let graphics-view know. … … 384 405 // that we give Qt 385 406 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()); 387 408 } 388 409 … … 390 411 switch (m_pendingContent.contentType) { 391 412 case PixmapContentType: 392 // we need cache even for images, because they need to be resized393 // to the contents rect. maybe this can be optimized though394 setCacheMode(m_transformAnimationRunning ? ItemCoordinateCache : DeviceCoordinateCache);395 413 update(); 396 414 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); 397 418 break; 398 419 … … 404 425 m_state.drawsContent = false; 405 426 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); 406 430 break; 407 431 … … 411 435 if (!m_state.drawsContent && m_layer->drawsContent()) 412 436 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 } 415 447 else 416 448 setCacheMode(NoCache); … … 449 481 450 482 // 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 452 484 453 485 m_state.maskLayer = m_layer->maskLayer(); … … 524 556 525 557 // reimp from GraphicsLayer.h 526 void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& r )527 { 528 m_impl->m_pendingContent.regionToUpdate|= QRectF(r ).toAlignedRect();558 void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect) 559 { 560 m_impl->m_pendingContent.regionToUpdate|= QRectF(rect).toAlignedRect(); 529 561 m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange); 530 562 } … … 808 840 static inline qreal applyTimingFunction(const TimingFunction& timingFunction, qreal progress, int duration) 809 841 { 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 } 811 851 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;820 852 } 821 853 … … 827 859 return; 828 860 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()) 832 862 transformOperations = *ops; 833 863 } … … 857 887 858 888 // 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(); 861 891 } 862 892 … … 933 963 // now we have a source keyframe, origin keyframe and a timing function 934 964 // 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); 938 969 } 939 970 … … 944 975 public: 945 976 TransformAnimationQt(GraphicsLayerQtImpl* layer, const KeyframeValueList& values, const IntSize& boxSize, const Animation* anim, const QString & name) 946 977 : AnimationQt<TransformOperations>(layer, values, boxSize, anim, name) 947 978 { 948 979 } … … 953 984 // when the animation dies, the transform has to go back to default 954 985 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()); 956 987 } 957 988 … … 964 995 TransformationMatrix transformMatrix; 965 996 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); 971 1013 } 972 1014 … … 981 1023 // this is a UX choice that should probably be customizable 982 1024 if (newState == QAbstractAnimation::Running) { 1025 m_sourceMatrix = m_layer.data()->m_layer->transform(); 983 1026 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); 986 1028 } else { 987 1029 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; 992 1035 }; 993 1036 … … 1007 1050 { 1008 1051 QAbstractAnimation::updateState(newState, oldState); 1052 1009 1053 if (m_layer) 1010 1054 m_layer.data()->m_opacityAnimationRunning = (newState == QAbstractAnimation::Running); … … 1080 1124 { 1081 1125 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())); 1087 1132 } 1088 1133 } -
trunk/WebKit/qt/Api/qgraphicswebview.cpp
r54284 r54344 28 28 #include "QWebPageClient.h" 29 29 #include <FrameView.h> 30 #include <QtCore/qmetaobject.h> 30 31 #include <QtCore/qsharedpointer.h> 31 32 #include <QtCore/qtimer.h> … … 79 80 #if USE(ACCELERATED_COMPOSITING) 80 81 , rootGraphicsLayer(0) 81 , shouldSync( true)82 , shouldSync(false) 82 83 #endif 83 84 { … … 86 87 // this QGraphicsWebView as the scrollbars are needed when there's no compositing 87 88 q->setFlag(QGraphicsItem::ItemUsesExtendedStyleOption); 89 syncMetaMethod = q->metaObject()->method(q->metaObject()->indexOfMethod("syncLayers()")); 88 90 #endif 89 91 } … … 131 133 // compositor telling us to do so. We'll get that call from ChromeClientQt 132 134 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; 133 138 134 139 // we need to put the root graphics layer behind the overlay (which contains the scrollbar) … … 179 184 shouldSync = true; 180 185 if (scheduleSync) 181 QTimer::singleShot(0, q, SLOT(syncLayers()));186 syncMetaMethod.invoke(q, Qt::QueuedConnection); 182 187 } 183 188 … … 225 230 if (overlay) 226 231 overlay->update(QRectF(dirtyRect)); 232 syncLayers(); 227 233 #endif 228 234 } … … 443 449 #if USE(ACCELERATED_COMPOSITING) 444 450 page()->mainFrame()->render(painter, d->overlay ? QWebFrame::ContentsLayer : QWebFrame::AllLayers, option->exposedRect.toAlignedRect()); 445 d->syncLayers();446 451 #else 447 452 page()->mainFrame()->render(painter, QWebFrame::AllLayers, option->exposedRect.toRect()); -
trunk/WebKit/qt/ChangeLog
r54284 r54344 1 2010-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 1 13 2010-02-03 Andras Becsi <abecsi@webkit.org> 2 14
Note: See TracChangeset
for help on using the changeset viewer.