author | Chris Lord <chrislord.net@gmail.com> |
Thu, 29 Nov 2012 13:08:40 +0000 | |
changeset 123582 | 13ee7ebc101a00c4b9b7a7f9abeff8bf972a3061 |
parent 123581 | b2175d0f4770574f63d1d9345cd8887508643240 |
child 123583 | a281a71fc4e6ae51a318545c0148f9ea5404e219 |
push id | 2151 |
push user | lsblakk@mozilla.com |
push date | Tue, 19 Feb 2013 18:06:57 +0000 |
treeherder | mozilla-beta@4952e88741ec [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bgirard |
bugs | 814864 |
milestone | 20.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
gfx/layers/basic/BasicTiledThebesLayer.cpp | file | annotate | diff | comparison | revisions | |
gfx/layers/basic/BasicTiledThebesLayer.h | file | annotate | diff | comparison | revisions |
--- a/gfx/layers/basic/BasicTiledThebesLayer.cpp +++ b/gfx/layers/basic/BasicTiledThebesLayer.cpp @@ -363,34 +363,38 @@ BasicTiledThebesLayer::ComputeProgressiv } if (NS_ABS(scrollDiffY) >= NS_ABS(scrollDiffX)) { tileBounds.x += incX; } else { tileBounds.y += incY; } } - bool repeatImmediately = false; if (!aRegionToPaint.Contains(aInvalidRegion)) { // The region needed to paint is larger then our progressive chunk size // therefore update what we want to paint and ask for a new paint transaction. // If we need to draw more than one tile to maintain coherency, make // sure it happens in the same transaction by requesting this work be // repeated immediately. // If this is unnecessary, the remaining work will be done tile-by-tile in // subsequent transactions. if (!drawingLowPrecision && paintInSingleTransaction) { - repeatImmediately = true; - } else { - BasicManager()->SetRepeatTransaction(); + return true; } + + BasicManager()->SetRepeatTransaction(); + return false; } - return repeatImmediately; + // We're not repeating painting and we've not requested a repeat transaction, + // so the paint is finished. If there's still a separate low precision + // paint to do, it will get marked as unfinished later. + mPaintData.mPaintFinished = true; + return false; } bool BasicTiledThebesLayer::ProgressiveUpdate(BasicTiledLayerBuffer& aTiledBuffer, nsIntRegion& aValidRegion, nsIntRegion& aInvalidRegion, const nsIntRegion& aOldValidRegion, const gfx3DMatrix& aTransform, @@ -438,16 +442,90 @@ BasicTiledThebesLayer::ProgressiveUpdate aTiledBuffer.PaintThebes(this, validOrStale, regionToPaint, aCallback, aCallbackData); aInvalidRegion.Sub(aInvalidRegion, regionToPaint); } while (repeat); return true; } void +BasicTiledThebesLayer::BeginPaint() +{ + if (BasicManager()->IsRepeatTransaction()) { + return; + } + + mPaintData.mLowPrecisionPaintCount = 0; + mPaintData.mPaintFinished = false; + + // Calculate the transform required to convert screen space into layer space + mPaintData.mTransformScreenToLayer = GetEffectiveTransform(); + // XXX Not sure if this code for intermediate surfaces is correct. + // It rarely gets hit though, and shouldn't have terrible consequences + // even if it is wrong. + for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { + if (parent->UseIntermediateSurface()) { + mPaintData.mTransformScreenToLayer.PreMultiply(parent->GetEffectiveTransform()); + } + } + mPaintData.mTransformScreenToLayer.Invert(); + + // Compute the critical display port in layer space. + mPaintData.mLayerCriticalDisplayPort.SetEmpty(); + const gfx::Rect& criticalDisplayPort = GetParent()->GetFrameMetrics().mCriticalDisplayPort; + if (!criticalDisplayPort.IsEmpty()) { + gfxRect transformedCriticalDisplayPort = + mPaintData.mTransformScreenToLayer.TransformBounds( + gfxRect(criticalDisplayPort.x, criticalDisplayPort.y, + criticalDisplayPort.width, criticalDisplayPort.height)); + transformedCriticalDisplayPort.RoundOut(); + mPaintData.mLayerCriticalDisplayPort = nsIntRect(transformedCriticalDisplayPort.x, + transformedCriticalDisplayPort.y, + transformedCriticalDisplayPort.width, + transformedCriticalDisplayPort.height); + } + + // Calculate the frame resolution. + mPaintData.mResolution.SizeTo(1, 1); + for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { + const FrameMetrics& metrics = parent->GetFrameMetrics(); + mPaintData.mResolution.width *= metrics.mResolution.width; + mPaintData.mResolution.height *= metrics.mResolution.height; + } + + // Calculate the scroll offset since the last transaction, and the + // composition bounds. + mPaintData.mCompositionBounds.SetEmpty(); + mPaintData.mScrollOffset.MoveTo(0, 0); + Layer* primaryScrollable = BasicManager()->GetPrimaryScrollableLayer(); + if (primaryScrollable) { + const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); + mPaintData.mScrollOffset = metrics.mScrollOffset; + gfxRect transformedViewport = mPaintData.mTransformScreenToLayer.TransformBounds( + gfxRect(metrics.mCompositionBounds.x, metrics.mCompositionBounds.y, + metrics.mCompositionBounds.width, metrics.mCompositionBounds.height)); + transformedViewport.RoundOut(); + mPaintData.mCompositionBounds = + nsIntRect(transformedViewport.x, transformedViewport.y, + transformedViewport.width, transformedViewport.height); + } +} + +void +BasicTiledThebesLayer::EndPaint(bool aFinish) +{ + if (!aFinish && !mPaintData.mPaintFinished) { + return; + } + + mLastScrollOffset = mPaintData.mScrollOffset; + mPaintData.mPaintFinished = true; +} + +void BasicTiledThebesLayer::PaintThebes(gfxContext* aContext, Layer* aMaskLayer, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData, ReadbackProcessor* aReadback) { if (!aCallback) { BasicManager()->SetTransactionIncomplete(); @@ -463,200 +541,197 @@ BasicTiledThebesLayer::PaintThebes(gfxCo mValidRegion = nsIntRegion(); } if (mLowPrecisionTiledBuffer.HasFormatChanged(this)) { mLowPrecisionValidRegion = nsIntRegion(); } nsIntRegion invalidRegion = mVisibleRegion; invalidRegion.Sub(invalidRegion, mValidRegion); - if (invalidRegion.IsEmpty()) + if (invalidRegion.IsEmpty()) { + EndPaint(true); return; + } - // Calculate the transform required to convert screen space into layer space - gfx3DMatrix transform = GetEffectiveTransform(); - // XXX Not sure if this code for intermediate surfaces is correct. - // It rarely gets hit though, and shouldn't have terrible consequences - // even if it is wrong. - for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { - if (parent->UseIntermediateSurface()) { - transform.PreMultiply(parent->GetEffectiveTransform()); - } + // Fast path for no progressive updates, no low-precision updates and no + // critical display-port set. + if (!gfxPlatform::UseProgressiveTilePainting() && + !gfxPlatform::UseLowPrecisionBuffer() && + GetParent()->GetFrameMetrics().mCriticalDisplayPort.IsEmpty()) { + mValidRegion = mVisibleRegion; + mTiledBuffer.PaintThebes(this, mValidRegion, invalidRegion, aCallback, aCallbackData); + mTiledBuffer.ReadLock(); + + static_cast<BasicImplData*>(aMaskLayer->ImplData())->Paint(aContext, nullptr); + + // Create a heap copy owned and released by the compositor. This is needed + // since we're sending this over an async message and content needs to be + // be able to modify the tiled buffer in the next transaction. + // TODO: Remove me once Bug 747811 lands. + BasicTiledLayerBuffer *heapCopy = new BasicTiledLayerBuffer(mTiledBuffer); + BasicManager()->PaintedTiledLayerBuffer(BasicManager()->Hold(this), heapCopy); + mTiledBuffer.ClearPaintedRegion(); + + return; } - transform.Invert(); - nsIntRect layerDisplayPort; - nsIntRegion lowPrecisionInvalidRegion; - const gfx::Rect& criticalDisplayPort = GetParent()->GetFrameMetrics().mCriticalDisplayPort; - if (!criticalDisplayPort.IsEmpty()) { - // Calculate the invalid region for the low precision buffer - lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion); + // Calculate everything we need to perform the paint. + BeginPaint(); + if (mPaintData.mPaintFinished) { + return; + } + + // Make sure that tiles that fall outside of the visible region are + // discarded on the first update. + if (!BasicManager()->IsRepeatTransaction()) { + mValidRegion.And(mValidRegion, mVisibleRegion); + } - // Find the critical display port in layer space. - gfxRect transformedCriticalDisplayPort = transform.TransformBounds( - gfxRect(criticalDisplayPort.x, criticalDisplayPort.y, - criticalDisplayPort.width, criticalDisplayPort.height)); - transformedCriticalDisplayPort.RoundOut(); - layerDisplayPort = nsIntRect(transformedCriticalDisplayPort.x, - transformedCriticalDisplayPort.y, - transformedCriticalDisplayPort.width, - transformedCriticalDisplayPort.height); + nsIntRegion lowPrecisionInvalidRegion; + if (!mPaintData.mLayerCriticalDisplayPort.IsEmpty()) { + // Make sure that tiles that fall outside of the critical displayport are + // discarded on the first update. + if (!BasicManager()->IsRepeatTransaction()) { + mValidRegion.And(mValidRegion, mPaintData.mLayerCriticalDisplayPort); + } + + if (gfxPlatform::UseLowPrecisionBuffer()) { + // Calculate the invalid region for the low precision buffer + lowPrecisionInvalidRegion.Sub(mVisibleRegion, mLowPrecisionValidRegion); + + // Remove the valid region from the low precision valid region (we don't + // validate this part of the low precision buffer). + lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); + } // Clip the invalid region to the critical display-port - invalidRegion.And(invalidRegion, layerDisplayPort); + invalidRegion.And(invalidRegion, mPaintData.mLayerCriticalDisplayPort); if (invalidRegion.IsEmpty() && lowPrecisionInvalidRegion.IsEmpty()) { + EndPaint(true); return; } } - gfxSize resolution(1, 1); - for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) { - const FrameMetrics& metrics = parent->GetFrameMetrics(); - resolution.width *= metrics.mResolution.width; - resolution.height *= metrics.mResolution.height; - } - - // Calculate the scroll offset since the last transaction, and the - // composition bounds. - nsIntRect compositionBounds; - gfx::Point scrollOffset(0, 0); - Layer* primaryScrollable = BasicManager()->GetPrimaryScrollableLayer(); - if (primaryScrollable) { - const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics(); - scrollOffset = metrics.mScrollOffset; - gfxRect transformedViewport = transform.TransformBounds( - gfxRect(metrics.mCompositionBounds.x, metrics.mCompositionBounds.y, - metrics.mCompositionBounds.width, metrics.mCompositionBounds.height)); - transformedViewport.RoundOut(); - compositionBounds = nsIntRect(transformedViewport.x, transformedViewport.y, - transformedViewport.width, transformedViewport.height); - } - - if (!invalidRegion.IsEmpty()) { + if (!invalidRegion.IsEmpty() && mPaintData.mLowPrecisionPaintCount == 0) { bool updatedBuffer = false; // Only draw progressively when the resolution is unchanged. if (gfxPlatform::UseProgressiveTilePainting() && !BasicManager()->HasShadowTarget() && - mTiledBuffer.GetFrameResolution() == resolution) { + mTiledBuffer.GetFrameResolution() == mPaintData.mResolution) { // Store the old valid region, then clear it before painting. // We clip the old valid region to the visible region, as it only gets // used to decide stale content (currently valid and previously visible) nsIntRegion oldValidRegion = mTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, mVisibleRegion); - if (!layerDisplayPort.IsEmpty()) { - oldValidRegion.And(oldValidRegion, layerDisplayPort); - } - - // Make sure that tiles that fall outside of the visible region are - // discarded on the first update. - if (!BasicManager()->IsRepeatTransaction()) { - mValidRegion.And(mValidRegion, mVisibleRegion); - if (!layerDisplayPort.IsEmpty()) { - mValidRegion.And(mValidRegion, layerDisplayPort); - } + if (!mPaintData.mLayerCriticalDisplayPort.IsEmpty()) { + oldValidRegion.And(oldValidRegion, mPaintData.mLayerCriticalDisplayPort); } updatedBuffer = ProgressiveUpdate(mTiledBuffer, mValidRegion, invalidRegion, - oldValidRegion, transform, compositionBounds, - scrollOffset, resolution, aCallback, aCallbackData); + oldValidRegion, mPaintData.mTransformScreenToLayer, + mPaintData.mCompositionBounds, mPaintData.mScrollOffset, + mPaintData.mResolution, aCallback, aCallbackData); } else { updatedBuffer = true; - mTiledBuffer.SetFrameResolution(resolution); + mTiledBuffer.SetFrameResolution(mPaintData.mResolution); mValidRegion = mVisibleRegion; - if (!layerDisplayPort.IsEmpty()) { - mValidRegion.And(mValidRegion, layerDisplayPort); + if (!mPaintData.mLayerCriticalDisplayPort.IsEmpty()) { + mValidRegion.And(mValidRegion, mPaintData.mLayerCriticalDisplayPort); } mTiledBuffer.PaintThebes(this, mValidRegion, invalidRegion, aCallback, aCallbackData); } if (updatedBuffer) { + mFirstPaint = false; mTiledBuffer.ReadLock(); // Only paint the mask layer on the first transaction. if (aMaskLayer && !BasicManager()->IsRepeatTransaction()) { static_cast<BasicImplData*>(aMaskLayer->ImplData()) ->Paint(aContext, nullptr); } - // Create a heap copy owned and released by the compositor. This is needed - // since we're sending this over an async message and content needs to be - // be able to modify the tiled buffer in the next transaction. // TODO: Remove me once Bug 747811 lands. BasicTiledLayerBuffer *heapCopy = new BasicTiledLayerBuffer(mTiledBuffer); BasicManager()->PaintedTiledLayerBuffer(BasicManager()->Hold(this), heapCopy); mTiledBuffer.ClearPaintedRegion(); + + // If there are low precision updates, mark the paint as unfinished and + // request a repeat transaction. + if (!lowPrecisionInvalidRegion.IsEmpty() && mPaintData.mPaintFinished) { + BasicManager()->SetRepeatTransaction(); + mPaintData.mLowPrecisionPaintCount = 1; + mPaintData.mPaintFinished = false; + } + + // Return so that low precision updates aren't performed in the same + // transaction as high-precision updates. + EndPaint(false); + return; } } - // If we have a critical display-port defined, render the full display-port - // progressively in the low-precision tiled buffer. + // Render the low precision buffer, if there's area to invalidate and the + // visible region is larger than the critical display port. bool updatedLowPrecision = false; - if (gfxPlatform::UseLowPrecisionBuffer() && - !criticalDisplayPort.IsEmpty() && - !nsIntRegion(layerDisplayPort).Contains(mVisibleRegion)) { + if (!lowPrecisionInvalidRegion.IsEmpty() && + !nsIntRegion(mPaintData.mLayerCriticalDisplayPort).Contains(mVisibleRegion)) { nsIntRegion oldValidRegion = mLowPrecisionTiledBuffer.GetValidRegion(); oldValidRegion.And(oldValidRegion, mVisibleRegion); // If the frame resolution has changed, invalidate the buffer - if (mLowPrecisionTiledBuffer.GetFrameResolution() != resolution) { + if (mLowPrecisionTiledBuffer.GetFrameResolution() != mPaintData.mResolution) { if (!mLowPrecisionValidRegion.IsEmpty()) { updatedLowPrecision = true; } oldValidRegion.SetEmpty(); mLowPrecisionValidRegion.SetEmpty(); - mLowPrecisionTiledBuffer.SetFrameResolution(resolution); + mLowPrecisionTiledBuffer.SetFrameResolution(mPaintData.mResolution); } // Invalidate previously valid content that is no longer visible - if (!BasicManager()->IsRepeatTransaction()) { + if (mPaintData.mLowPrecisionPaintCount == 1) { mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, mVisibleRegion); } + mPaintData.mLowPrecisionPaintCount++; // Remove the valid high-precision region from the invalid low-precision // region. We don't want to spend time drawing things twice. - nsIntRegion invalidHighPrecisionIntersect; - invalidHighPrecisionIntersect.And(lowPrecisionInvalidRegion, mValidRegion); - lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, invalidHighPrecisionIntersect); + lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion); if (!lowPrecisionInvalidRegion.IsEmpty()) { updatedLowPrecision = ProgressiveUpdate(mLowPrecisionTiledBuffer, mLowPrecisionValidRegion, - lowPrecisionInvalidRegion, oldValidRegion, transform, - compositionBounds, scrollOffset, resolution, aCallback, - aCallbackData); + lowPrecisionInvalidRegion, oldValidRegion, + mPaintData.mTransformScreenToLayer, + mPaintData.mCompositionBounds, mPaintData.mScrollOffset, + mPaintData.mResolution, aCallback, aCallbackData); } - - // Re-add the high-precision valid region intersection so that we can - // maintain coherency when the valid region changes. - lowPrecisionInvalidRegion.Or(lowPrecisionInvalidRegion, invalidHighPrecisionIntersect); } else if (!mLowPrecisionValidRegion.IsEmpty()) { // Clear the low precision tiled buffer updatedLowPrecision = true; mLowPrecisionValidRegion.SetEmpty(); mLowPrecisionTiledBuffer.PaintThebes(this, mLowPrecisionValidRegion, mLowPrecisionValidRegion, aCallback, aCallbackData); } // We send a Painted callback if we clear the valid region of the low // precision buffer, so that the shadow buffer's valid region can be updated // and the associated resources can be freed. if (updatedLowPrecision) { mLowPrecisionTiledBuffer.ReadLock(); + // TODO: Remove me once Bug 747811 lands. BasicTiledLayerBuffer *heapCopy = new BasicTiledLayerBuffer(mLowPrecisionTiledBuffer); // The GL layer manager uses the buffer resolution to distinguish calls // to PaintedTiledLayerBuffer. BasicManager()->PaintedTiledLayerBuffer(BasicManager()->Hold(this), heapCopy); mLowPrecisionTiledBuffer.ClearPaintedRegion(); } - // The transaction is completed, store the last scroll offset. - if (!BasicManager()->GetRepeatTransaction()) { - mLastScrollOffset = scrollOffset; - } - mFirstPaint = false; + EndPaint(false); } } // mozilla } // layers
--- a/gfx/layers/basic/BasicTiledThebesLayer.h +++ b/gfx/layers/basic/BasicTiledThebesLayer.h @@ -62,16 +62,30 @@ struct BasicTiledLayerTile { void ReadUnlock() { mSurface->ReadUnlock(); } void ReadLock() { mSurface->ReadLock(); } }; +/** + * This struct stores all the data necessary to perform a paint so that it + * doesn't need to be recalculated on every repeated transaction. + */ +struct BasicTiledLayerPaintData { + gfx::Point mScrollOffset; + gfx3DMatrix mTransformScreenToLayer; + nsIntRect mLayerCriticalDisplayPort; + gfxSize mResolution; + nsIntRect mCompositionBounds; + uint16_t mLowPrecisionPaintCount; + bool mPaintFinished : 1; +}; + class BasicTiledThebesLayer; /** * Provide an instance of TiledLayerBuffer backed by image surfaces. * This buffer provides an implementation to ValidateTile using a * thebes callback and can support painting using a single paint buffer * which is much faster then painting directly into the tiles. */ @@ -260,20 +274,34 @@ private: const nsIntRegion& aOldValidRegion, const gfx3DMatrix& aTransform, const nsIntRect& aCompositionBounds, const gfx::Point& aScrollOffset, const gfxSize& aResolution, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData); + /** + * For the initial PaintThebes of a transaction, calculates all the data + * needed for that paint and any repeated transactions. + */ + void BeginPaint(); + + /** + * When a paint ends, updates any data necessary to persist until the next + * paint. If aFinish is true, this will cause the paint to be marked as + * finished. + */ + void EndPaint(bool aFinish); + // Members BasicTiledLayerBuffer mTiledBuffer; BasicTiledLayerBuffer mLowPrecisionTiledBuffer; nsIntRegion mLowPrecisionValidRegion; gfx::Point mLastScrollOffset; bool mFirstPaint; + BasicTiledLayerPaintData mPaintData; }; } // layers } // mozilla #endif