Bug 1022612. Part 31: Perform layer-level occlusion culling in FrameLayerBuilder. r=mattwoodrow

We need this to avoid constructing and painting unncecessarily large
ThebesLayers.

--HG--
extra : rebase_source : 38cbd0b0dd7724cfdb98f3e215bd0f220a687f3c
This commit is contained in:
Robert O'Callahan 2014-06-23 16:24:00 +12:00
parent 9a2fe1598a
commit d43195c660
2 changed files with 438 additions and 130 deletions

View File

@ -37,8 +37,6 @@ using namespace mozilla::gfx;
namespace mozilla {
class ContainerState;
FrameLayerBuilder::DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey,
Layer* aLayer, LayerState aLayerState, uint32_t aGeneration)
@ -244,8 +242,10 @@ public:
mSingleItemFixedToViewport(false),
mNeedComponentAlpha(false),
mForceTransparentSurface(false),
mHideAllLayersBelow(false),
mImage(nullptr),
mCommonClipCount(-1),
mNewChildLayersIndex(-1),
mAllDrawingAbove(false)
{}
/**
@ -424,6 +424,10 @@ public:
* part of its surface.
*/
bool mForceTransparentSurface;
/**
* Set if all layers below this ThebesLayer should be hidden.
*/
bool mHideAllLayersBelow;
/**
* Stores the pointer to the nsDisplayImage if we want to
@ -446,6 +450,10 @@ public:
* data is popped from the stack.
*/
int32_t mCommonClipCount;
/**
* Index of this layer in mNewChildLayers.
*/
int32_t mNewChildLayersIndex;
/*
* Updates mCommonClipCount by checking for rounded rect clips in common
* between the clip on a new item (aCurrentClip) and the common clips
@ -479,6 +487,39 @@ private:
bool mAllDrawingAbove;
};
struct NewLayerEntry {
NewLayerEntry()
: mAnimatedGeometryRoot(nullptr)
, mFixedPosFrameForLayerData(nullptr)
, mLayerContentsVisibleRect(0, 0, -1, -1)
, mHideAllLayersBelow(false)
, mOpaqueForAnimatedGeometryRootParent(false)
{}
// mLayer is null if the previous entry is for a ThebesLayer that hasn't
// been optimized to some other form (yet).
nsRefPtr<Layer> mLayer;
const nsIFrame* mAnimatedGeometryRoot;
const nsIFrame* mFixedPosFrameForLayerData;
// The following are only used for retained layers (for occlusion
// culling of those layers). These regions are all relative to the
// container reference frame.
nsIntRegion mVisibleRegion;
nsIntRegion mOpaqueRegion;
// This rect is in the layer's own coordinate space. The computed visible
// region for the layer cannot extend beyond this rect.
nsIntRect mLayerContentsVisibleRect;
bool mHideAllLayersBelow;
// When mOpaqueForAnimatedGeometryRootParent is true, the opaque region of
// this layer is opaque in the same position even subject to the animation of
// geometry of mAnimatedGeometryRoot. For example when mAnimatedGeometryRoot
// is a scrolled frame and the scrolled content is opaque everywhere in the
// displayport, we can set this flag.
// When this flag is set, we can treat this opaque region as covering
// content whose animated geometry root is the animated geometry root for
// mAnimatedGeometryRoot->GetParent().
bool mOpaqueForAnimatedGeometryRootParent;
};
/**
* This is a helper object used to build up the layer children for
* a ContainerLayer.
@ -509,6 +550,10 @@ public:
mContainerAnimatedGeometryRoot = aContainerItem
? nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder)
: mContainerReferenceFrame;
NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(aBuilder),
"Container items never return true for ShouldFixToViewport");
mContainerFixedPosFrame =
FindFixedPosFrameForLayerData(mContainerAnimatedGeometryRoot, false);
// When AllowResidualTranslation is false, display items will be drawn
// scaled with a translation by integer pixels, so we know how the snapping
// will work.
@ -534,7 +579,9 @@ public:
* @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA,
* set *aTextContentFlags to CONTENT_COMPONENT_ALPHA
*/
void Finish(uint32_t *aTextContentFlags, LayerManagerData* aData);
void Finish(uint32_t *aTextContentFlags, LayerManagerData* aData,
const nsIntRect& aContainerPixelBounds,
nsDisplayList* aChildItems);
nscoord GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel; }
@ -583,6 +630,18 @@ public:
mAppUnitsPerDevPixel);
}
nsIFrame* GetContainerFrame() const { return mContainerFrame; }
/**
* Sets aOuterVisibleRegion as aLayer's visible region. aOuterVisibleRegion
* is in the coordinate space of the container reference frame.
* aLayerContentsVisibleRect, if non-null, is in the layer's own
* coordinate system.
*/
void SetOuterVisibleRegionForLayer(Layer* aLayer,
const nsIntRegion& aOuterVisibleRegion,
const nsIntRect* aLayerContentsVisibleRect = nullptr) const;
protected:
friend class ThebesLayerData;
@ -655,6 +714,36 @@ protected:
*/
void SetFixedPositionLayerData(Layer* aLayer,
const nsIFrame* aFixedPosFrame);
/**
* Returns true if aItem's opaque area (in aOpaque) covers the entire
* scrollable area of its presshell.
*/
bool ItemCoversScrollableArea(nsDisplayItem* aItem, const nsRegion& aOpaque);
/**
* For each layer in mNewChildLayers, remove from its visible region the
* opaque regions of the layers at higher z-index, but only if they have
* the same animated geometry root and fixed-pos frame ancestor.
* The opaque region for the child layers that share the same animated
* geometry root as the container frame is returned in
* *aOpaqueRegionForContainer.
*/
void ApplyOcclusionCulling(nsIntRegion* aOpaqueRegionForContainer);
/**
* Computes the snapped opaque area of aItem. Sets aList's opaque flag
* if it covers the entire list bounds. Sets *aHideAllLayersBelow to true
* this item covers the entire viewport so that all layers below are
* permanently invisible.
*/
nsIntRegion ComputeOpaqueRect(nsDisplayItem* aItem,
const nsIFrame* aAnimatedGeometryRoot,
const nsIFrame* aFixedPosFrame,
const DisplayItemClip& aClip,
nsDisplayList* aList,
bool* aHideAllLayersBelow);
/**
* Indicate that we are done adding items to the ThebesLayer at the top of
* mThebesLayerDataStack. Set the final visible region and opaque-content
@ -697,11 +786,14 @@ protected:
* Builds an ImageLayer for the appropriate backend; the mask is relative to
* aLayer's visible region.
* aLayer is the layer to be clipped.
* aLayerVisibleRegion is the region that will be set as aLayer's visible region,
* relative to the container reference frame
* aRoundedRectClipCount is used when building mask layers for ThebesLayers,
* SetupMaskLayer will build a mask layer for only the first
* aRoundedRectClipCount rounded rects in aClip
*/
void SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
const nsIntRegion& aLayerVisibleRegion,
uint32_t aRoundedRectClipCount = UINT32_MAX);
bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList,
@ -713,6 +805,7 @@ protected:
nsIFrame* mContainerFrame;
nsIFrame* mContainerReferenceFrame;
const nsIFrame* mContainerAnimatedGeometryRoot;
const nsIFrame* mContainerFixedPosFrame;
ContainerLayer* mContainerLayer;
nsRect mContainerBounds;
DebugOnly<nsRect> mAccumulatedChildBounds;
@ -727,8 +820,12 @@ protected:
* We collect the list of children in here. During ProcessDisplayItems,
* the layers in this array either have mContainerLayer as their parent,
* or no parent.
* ThebesLayers have two entries in this array: the second one is used only if
* the ThebesLayer is optimized away to a ColorLayer or ImageLayer.
* It's essential that this array is only appended to, since ThebesLayerData
* records the index of its ThebesLayer in this array.
*/
typedef nsAutoTArray<nsRefPtr<Layer>,1> AutoLayersArray;
typedef nsAutoTArray<NewLayerEntry,1> AutoLayersArray;
AutoLayersArray mNewChildLayers;
nsTArray<nsRefPtr<ThebesLayer> > mRecycledThebesLayers;
nsDataHashtable<nsPtrHashKey<Layer>, nsRefPtr<ImageLayer> >
@ -1604,38 +1701,49 @@ AppUnitsPerDevPixel(nsDisplayItem* aItem)
#endif
/**
* Restrict the visible region of aLayer to the region that is actually visible.
* Because we only reduce the visible region here, we don't need to worry
* about whether CONTENT_OPAQUE is set; if layer was opaque in the old
* visible region, it will still be opaque in the new one.
* @param aLayerVisibleRect if non-null, the visible rect of the layer, in
* the layer's coordinate space
* @param aOuterVisibleRect the rect to restrict the visible region to, in the
* parent's coordinate system
* Set the visible region for aLayer.
* aOuterVisibleRegion is the visible region relative to the parent layer.
* Consumes *aOuterVisibleRegion.
*/
static void
SetVisibleRegionForLayer(Layer* aLayer, const nsIntRect* aLayerVisibleRect,
const nsIntRect& aOuterVisibleRect)
SetOuterVisibleRegion(Layer* aLayer, nsIntRegion* aOuterVisibleRegion,
const nsIntRect* aLayerContentsVisibleRect = nullptr)
{
gfx3DMatrix transform;
To3DMatrix(aLayer->GetTransform(), transform);
// if 'transform' is not invertible, then nothing will be displayed
// for the layer, so it doesn't really matter what we do here
gfxRect outerVisible(aOuterVisibleRect.x, aOuterVisibleRect.y,
aOuterVisibleRect.width, aOuterVisibleRect.height);
gfxRect layerVisible = transform.Inverse().ProjectRectBounds(outerVisible);
layerVisible.RoundOut();
nsIntRect visibleRect;
if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect)) {
aLayer->SetVisibleRegion(nsIntRegion());
gfxMatrix transform2D;
if (transform.Is2D(&transform2D) && !transform2D.HasNonIntegerTranslation()) {
aOuterVisibleRegion->MoveBy(-int(transform2D._31), -int(transform2D._32));
} else {
if (aLayerVisibleRect) {
visibleRect.IntersectRect(visibleRect, *aLayerVisibleRect);
nsIntRect outerRect = aOuterVisibleRegion->GetBounds();
// if 'transform' is not invertible, then nothing will be displayed
// for the layer, so it doesn't really matter what we do here
gfxRect outerVisible(outerRect.x, outerRect.y, outerRect.width, outerRect.height);
gfxRect layerVisible = transform.Inverse().ProjectRectBounds(outerVisible);
layerVisible.RoundOut();
nsIntRect visRect;
if (gfxUtils::GfxRectToIntRect(layerVisible, &visRect)) {
*aOuterVisibleRegion = visRect;
} else {
aOuterVisibleRegion->SetEmpty();
}
aLayer->SetVisibleRegion(nsIntRegion(visibleRect));
}
if (aLayerContentsVisibleRect && aLayerContentsVisibleRect->width >= 0) {
aOuterVisibleRegion->And(*aOuterVisibleRegion, *aLayerContentsVisibleRect);
}
aLayer->SetVisibleRegion(*aOuterVisibleRegion);
}
void
ContainerState::SetOuterVisibleRegionForLayer(Layer* aLayer,
const nsIntRegion& aOuterVisibleRegion,
const nsIntRect* aLayerContentsVisibleRect) const
{
nsIntRegion visRegion = aOuterVisibleRegion;
visRegion.MoveBy(mParameters.mOffset);
SetOuterVisibleRegion(aLayer, &visRegion, aLayerContentsVisibleRect);
}
nscolor
@ -1844,8 +1952,7 @@ static bool
CanOptimizeAwayThebesLayer(ThebesLayerData* aData,
FrameLayerBuilder* aLayerBuilder)
{
bool isRetained = aData->mLayer->Manager()->IsWidgetLayerManager();
if (!isRetained) {
if (!aLayerBuilder->IsBuildingRetainedLayers()) {
return false;
}
@ -1863,6 +1970,19 @@ CanOptimizeAwayThebesLayer(ThebesLayerData* aData,
return aLayerBuilder->CheckInLayerTreeCompressionMode();
}
#ifdef DEBUG
static int32_t FindIndexOfLayerIn(nsTArray<NewLayerEntry>& aArray,
Layer* aLayer)
{
for (uint32_t i = 0; i < aArray.Length(); ++i) {
if (aArray[i].mLayer == aLayer) {
return i;
}
}
return -1;
}
#endif
void
ContainerState::PopThebesLayerData()
{
@ -1875,9 +1995,10 @@ ContainerState::PopThebesLayerData()
data->mDrawRegion,
&data->mVisibleRegion,
&data->mIsSolidColorInVisibleRegion);
NewLayerEntry* newLayerEntry = &mNewChildLayers[data->mNewChildLayersIndex];
nsRefPtr<Layer> layer;
nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(mBuilder);
if ((data->mIsSolidColorInVisibleRegion || imageContainer) &&
CanOptimizeAwayThebesLayer(data, mLayerBuilder)) {
NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
@ -1913,10 +2034,16 @@ ContainerState::PopThebesLayerData()
layer = colorLayer;
}
NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
AutoLayersArray::index_type index = mNewChildLayers.IndexOf(data->mLayer);
NS_ASSERTION(index != AutoLayersArray::NoIndex, "Thebes layer not found?");
mNewChildLayers.InsertElementAt(index + 1, layer);
NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0,
"Layer already in list???");
NS_ASSERTION(newLayerEntry->mLayer == data->mLayer,
"Thebes layer at wrong index");
// Store optimized layer in reserved slot
newLayerEntry = &mNewChildLayers[data->mNewChildLayersIndex + 1];
NS_ASSERTION(!newLayerEntry->mLayer, "Slot already occupied?");
newLayerEntry->mLayer = layer;
newLayerEntry->mAnimatedGeometryRoot = data->mAnimatedGeometryRoot;
newLayerEntry->mFixedPosFrameForLayerData = data->mFixedPosFrameForLayerData;
// Hide the ThebesLayer. We leave it in the layer tree so that we
// can find and recycle it later.
@ -1930,20 +2057,18 @@ ContainerState::PopThebesLayerData()
layer->SetClipRect(nullptr);
}
Matrix transform;
if (!layer->GetTransform().Is2D(&transform)) {
NS_ERROR("Only 2D transformations currently supported");
}
// ImageLayers are already configured with a visible region
if (!imageContainer) {
NS_ASSERTION(!transform.HasNonIntegerTranslation(),
"Matrix not just an integer translation?");
// Convert from relative to the container to relative to the
// ThebesLayer itself.
nsIntRegion rgn = data->mVisibleRegion;
rgn.MoveBy(-GetTranslationForThebesLayer(data->mLayer));
layer->SetVisibleRegion(rgn);
if (mLayerBuilder->IsBuildingRetainedLayers()) {
newLayerEntry->mVisibleRegion = data->mVisibleRegion;
newLayerEntry->mOpaqueRegion = data->mOpaqueRegion;
newLayerEntry->mHideAllLayersBelow = data->mHideAllLayersBelow;
if (nsLayoutUtils::GetScrollableFrameFor(newLayerEntry->mAnimatedGeometryRoot) &&
!nsDisplayScrollLayer::IsConstructingScrollLayerForScrolledFrame(newLayerEntry->mAnimatedGeometryRoot)) {
// Async scrolling not currently active so we can propagate our opaque region
// up to the parent animated geometry root.
newLayerEntry->mOpaqueForAnimatedGeometryRootParent = true;
}
} else {
SetOuterVisibleRegionForLayer(layer, data->mVisibleRegion);
}
nsIntRegion transparentRegion;
@ -1985,14 +2110,14 @@ ContainerState::PopThebesLayerData()
// data->mCommonClipCount may be -1 if we haven't put any actual
// drawable items in this layer (i.e. it's only catching events).
int32_t commonClipCount = std::max(0, data->mCommonClipCount);
SetupMaskLayer(layer, data->mItemClip, commonClipCount);
SetupMaskLayer(layer, data->mItemClip, data->mVisibleRegion, commonClipCount);
// copy commonClipCount to the entry
FrameLayerBuilder::ThebesLayerItemsEntry* entry = mLayerBuilder->
GetThebesLayerItemsEntry(static_cast<ThebesLayer*>(layer.get()));
entry->mCommonClipCount = commonClipCount;
} else {
// mask layer for image and color layers
SetupMaskLayer(layer, data->mItemClip);
SetupMaskLayer(layer, data->mItemClip, data->mVisibleRegion);
}
uint32_t flags = 0;
@ -2280,8 +2405,18 @@ ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
thebesLayerData->mReferenceFrame = aItem->ReferenceFrame();
thebesLayerData->mSingleItemFixedToViewport = aShouldFixToViewport;
NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
*mNewChildLayers.AppendElement() = layer.forget();
NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, layer) < 0,
"Layer already in list???");
thebesLayerData->mNewChildLayersIndex = mNewChildLayers.Length();
NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
newLayerEntry->mLayer = layer.forget();
newLayerEntry->mAnimatedGeometryRoot = aAnimatedGeometryRoot;
newLayerEntry->mFixedPosFrameForLayerData = thebesLayerData->mFixedPosFrameForLayerData;
// newLayerEntry->mOpaqueRegion is filled in later from
// thebesLayerData->mOpaqueRegion, if necessary.
// Allocate another entry for this layer's optimization to ColorLayer/ImageLayer
mNewChildLayers.AppendElement();
} else {
thebesLayerData = mThebesLayerDataStack[lowestUsableLayerWithScrolledRoot];
}
@ -2411,6 +2546,55 @@ IsScrollLayerItemAndOverlayScrollbarForScrollFrame(
return false;
}
bool
ContainerState::ItemCoversScrollableArea(nsDisplayItem* aItem, const nsRegion& aOpaque)
{
nsIPresShell* presShell = aItem->Frame()->PresContext()->PresShell();
nsIFrame* rootFrame = presShell->GetRootFrame();
if (mContainerFrame != rootFrame) {
return false;
}
nsRect scrollableArea;
if (presShell->IsScrollPositionClampingScrollPortSizeSet()) {
scrollableArea = nsRect(nsPoint(0, 0),
presShell->GetScrollPositionClampingScrollPortSize());
} else {
scrollableArea = rootFrame->GetRectRelativeToSelf();
}
return aOpaque.Contains(scrollableArea);
}
nsIntRegion
ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
const nsIFrame* aAnimatedGeometryRoot,
const nsIFrame* aFixedPosFrame,
const DisplayItemClip& aClip,
nsDisplayList* aList,
bool* aHideAllLayersBelow)
{
bool snapOpaque;
nsRegion opaque = aItem->GetOpaqueRegion(mBuilder, &snapOpaque);
nsIntRegion opaquePixels;
if (!opaque.IsEmpty()) {
nsRegion opaqueClipped;
nsRegionRectIterator iter(opaque);
for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
opaqueClipped.Or(opaqueClipped, aClip.ApproximateIntersectInward(*r));
}
if (aAnimatedGeometryRoot == mContainerAnimatedGeometryRoot &&
aFixedPosFrame == mContainerFixedPosFrame &&
!aList->IsOpaque() &&
opaqueClipped.Contains(mContainerBounds)) {
aList->SetIsOpaque();
}
opaquePixels = ScaleRegionToInsidePixels(opaqueClipped, snapOpaque);
if (aFixedPosFrame && ItemCoversScrollableArea(aItem, opaque)) {
*aHideAllLayersBelow = true;
}
}
return opaquePixels;
}
/*
* Iterate through the non-clip items in aList and its descendants.
* For each item we compute the effective clip rect. Each item is assigned
@ -2504,7 +2688,6 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList,
((nsRect&)mAccumulatedChildBounds).UnionRect(mAccumulatedChildBounds, itemContent);
#endif
itemVisibleRect.IntersectRect(itemVisibleRect, itemDrawRect);
ThebesLayerData* accumulateIntoThebesLayerData = nullptr;
LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
if (layerState == LAYER_INACTIVE &&
@ -2569,13 +2752,9 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList,
continue;
}
if (itemType == nsDisplayItem::TYPE_TRANSFORM) {
mParameters.mAncestorClipRect = itemClip.HasClip() ? &clipRect : nullptr;
} else {
mParameters.mAncestorClipRect = nullptr;
}
// Just use its layer.
nsIntRect layerContentsVisibleRect(0, 0, -1, -1);
mParameters.mLayerContentsVisibleRect = &layerContentsVisibleRect;
nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
if (!ownLayer) {
continue;
@ -2650,25 +2829,39 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList,
data->AddDrawAboveRegion(itemDrawRect);
}
}
itemVisibleRect.MoveBy(mParameters.mOffset);
if (item->SetVisibleRegionOnLayer()) {
SetVisibleRegionForLayer(ownLayer, nullptr, itemVisibleRect);
}
// rounded rectangle clipping using mask layers
// (must be done after visible rect is set on layer)
if (itemClip.IsRectClippedByRoundedCorner(itemContent)) {
SetupMaskLayer(ownLayer, itemClip);
SetupMaskLayer(ownLayer, itemClip, itemVisibleRect);
}
ContainerLayer* oldContainer = ownLayer->GetParent();
if (oldContainer && oldContainer != mContainerLayer) {
oldContainer->RemoveChild(ownLayer);
}
NS_ASSERTION(!mNewChildLayers.Contains(ownLayer),
NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, ownLayer) < 0,
"Layer already in list???");
mNewChildLayers.AppendElement(ownLayer);
NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
newLayerEntry->mLayer = ownLayer;
newLayerEntry->mAnimatedGeometryRoot = animatedGeometryRoot;
newLayerEntry->mFixedPosFrameForLayerData = fixedPosFrame;
if (mLayerBuilder->IsBuildingRetainedLayers()) {
newLayerEntry->mLayerContentsVisibleRect = layerContentsVisibleRect;
newLayerEntry->mVisibleRegion = itemVisibleRect;
newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(item,
animatedGeometryRoot, fixedPosFrame, itemClip, aList,
&newLayerEntry->mHideAllLayersBelow);
} else {
SetOuterVisibleRegionForLayer(ownLayer, itemVisibleRect,
&layerContentsVisibleRect);
}
if (itemType == nsDisplayItem::TYPE_SCROLL_LAYER) {
nsDisplayScrollLayer* scrollItem = static_cast<nsDisplayScrollLayer*>(item);
newLayerEntry->mOpaqueForAnimatedGeometryRootParent =
scrollItem->IsDisplayPortOpaque();
}
/**
* No need to allocate geometry for items that aren't
@ -2698,33 +2891,17 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList,
nsAutoPtr<nsDisplayItemGeometry> geometry(item->AllocateGeometry(mBuilder));
InvalidateForLayerChange(item, thebesLayerData->mLayer, itemClip, topLeft, geometry);
mLayerBuilder->AddThebesDisplayItem(thebesLayerData, item, itemClip,
mContainerFrame,
layerState, topLeft,
geometry);
accumulateIntoThebesLayerData = thebesLayerData;
mLayerBuilder->AddThebesDisplayItem(thebesLayerData, item, itemClip, itemVisibleRect,
*this, layerState, topLeft, geometry);
nsIntRegion opaquePixels = ComputeOpaqueRect(item,
animatedGeometryRoot, thebesLayerData->mFixedPosFrameForLayerData,
itemClip, aList,
&thebesLayerData->mHideAllLayersBelow);
thebesLayerData->Accumulate(this, item, opaquePixels,
itemVisibleRect, itemDrawRect, itemClip);
}
}
bool snapOpaque;
nsRegion opaque = item->GetOpaqueRegion(mBuilder, &snapOpaque);
nsRegion opaqueClipped;
if (!opaque.IsEmpty()) {
nsRegionRectIterator iter(opaque);
for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
opaqueClipped.Or(opaqueClipped, itemClip.ApproximateIntersectInward(*r));
}
if (opaqueClipped.Contains(mContainerBounds)) {
aList->SetIsOpaque();
}
}
if (accumulateIntoThebesLayerData) {
nsIntRegion opaquePixels =
ScaleRegionToInsidePixels(opaqueClipped, snapOpaque);
accumulateIntoThebesLayerData->Accumulate(this, item,
opaquePixels, itemVisibleRect, itemDrawRect, itemClip);
}
if (itemSameCoordinateSystemChildren &&
itemSameCoordinateSystemChildren->NeedsTransparentSurface()) {
aList->SetNeedsTransparentSurface();
@ -2873,7 +3050,8 @@ void
FrameLayerBuilder::AddThebesDisplayItem(ThebesLayerData* aLayerData,
nsDisplayItem* aItem,
const DisplayItemClip& aClip,
nsIFrame* aContainerLayerFrame,
const nsIntRect& aItemVisibleRect,
const ContainerState& aContainerState,
LayerState aLayerState,
const nsPoint& aTopLeft,
nsAutoPtr<nsDisplayItemGeometry> aGeometry)
@ -2913,7 +3091,7 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayerData* aLayerData,
ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(layer);
if (entry) {
entry->mContainerLayerFrame = aContainerLayerFrame;
entry->mContainerLayerFrame = aContainerState.GetContainerFrame();
if (entry->mContainerLayerGeneration == 0) {
entry->mContainerLayerGeneration = mContainerLayerGeneration;
}
@ -2937,6 +3115,12 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayerData* aLayerData,
return;
}
bool snap;
nsRect visibleRect =
aItem->GetVisibleRect().Intersect(aItem->GetBounds(mDisplayListBuilder, &snap));
nsIntRegion rgn = visibleRect.ToOutsidePixels(thebesData->mAppUnitsPerDevPixel);
SetOuterVisibleRegion(tmpLayer, &rgn);
// If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been
// stored in layerBuilder. Manually add it now.
if (mRetainingManager) {
@ -3129,8 +3313,99 @@ ContainerState::CollectOldLayers()
}
}
struct OpaqueRegionEntry {
const nsIFrame* mAnimatedGeometryRoot;
const nsIFrame* mFixedPosFrameForLayerData;
nsIntRegion mOpaqueRegion;
};
static OpaqueRegionEntry*
FindOpaqueRegionEntry(nsTArray<OpaqueRegionEntry>& aEntries,
const nsIFrame* aAnimatedGeometryRoot,
const nsIFrame* aFixedPosFrameForLayerData)
{
for (uint32_t i = 0; i < aEntries.Length(); ++i) {
OpaqueRegionEntry* d = &aEntries[i];
if (d->mAnimatedGeometryRoot == aAnimatedGeometryRoot &&
d->mFixedPosFrameForLayerData == aFixedPosFrameForLayerData) {
return d;
}
}
return nullptr;
}
void
ContainerState::Finish(uint32_t* aTextContentFlags, LayerManagerData* aData)
ContainerState::ApplyOcclusionCulling(nsIntRegion* aOpaqueRegionForContainer)
{
nsAutoTArray<OpaqueRegionEntry,4> opaqueRegions;
bool hideAll = false;
int32_t opaqueRegionForContainer = -1;
for (int32_t i = mNewChildLayers.Length() - 1; i >= 0; --i) {
NewLayerEntry* e = &mNewChildLayers.ElementAt(i);
if (!e->mLayer) {
continue;
}
OpaqueRegionEntry* data = FindOpaqueRegionEntry(opaqueRegions,
e->mAnimatedGeometryRoot, e->mFixedPosFrameForLayerData);
if (hideAll) {
e->mVisibleRegion.SetEmpty();
} else if (data) {
e->mVisibleRegion.Sub(e->mVisibleRegion, data->mOpaqueRegion);
}
if (!e->mOpaqueRegion.IsEmpty()) {
const nsIFrame* animatedGeometryRootToCover = e->mAnimatedGeometryRoot;
if (e->mOpaqueForAnimatedGeometryRootParent &&
nsLayoutUtils::GetAnimatedGeometryRootForFrame(e->mAnimatedGeometryRoot->GetParent(),
mContainerAnimatedGeometryRoot)
== mContainerAnimatedGeometryRoot) {
animatedGeometryRootToCover = mContainerAnimatedGeometryRoot;
data = FindOpaqueRegionEntry(opaqueRegions,
animatedGeometryRootToCover, e->mFixedPosFrameForLayerData);
}
if (!data) {
if (animatedGeometryRootToCover == mContainerAnimatedGeometryRoot &&
!e->mFixedPosFrameForLayerData) {
NS_ASSERTION(opaqueRegionForContainer == -1, "Already found it?");
opaqueRegionForContainer = opaqueRegions.Length();
}
data = opaqueRegions.AppendElement();
data->mAnimatedGeometryRoot = animatedGeometryRootToCover;
data->mFixedPosFrameForLayerData = e->mFixedPosFrameForLayerData;
}
data->mOpaqueRegion.Or(data->mOpaqueRegion, e->mOpaqueRegion);
if (e->mHideAllLayersBelow) {
hideAll = true;
}
}
SetOuterVisibleRegionForLayer(e->mLayer, e->mVisibleRegion,
e->mLayerContentsVisibleRect.width >= 0 ? &e->mLayerContentsVisibleRect : nullptr);
if (e->mLayer->GetType() == Layer::TYPE_READBACK) {
// ReadbackLayers need to accurately read what's behind them. So,
// we don't want to do any occlusion culling of layers behind them.
// Theoretically we could just punch out the ReadbackLayer's rectangle
// from all mOpaqueRegions, but that's probably not worth doing.
opaqueRegions.Clear();
opaqueRegionForContainer = -1;
}
}
if (opaqueRegionForContainer >= 0) {
aOpaqueRegionForContainer->Or(*aOpaqueRegionForContainer,
opaqueRegions[opaqueRegionForContainer].mOpaqueRegion);
}
}
void
ContainerState::Finish(uint32_t* aTextContentFlags, LayerManagerData* aData,
const nsIntRect& aContainerPixelBounds,
nsDisplayList* aChildItems)
{
while (!mThebesLayerDataStack.IsEmpty()) {
PopThebesLayerData();
@ -3140,12 +3415,24 @@ ContainerState::Finish(uint32_t* aTextContentFlags, LayerManagerData* aData)
"Bounds computation mismatch");
uint32_t textContentFlags = 0;
if (mLayerBuilder->IsBuildingRetainedLayers()) {
nsIntRegion containerOpaqueRegion;
ApplyOcclusionCulling(&containerOpaqueRegion);
if (containerOpaqueRegion.Contains(aContainerPixelBounds)) {
aChildItems->SetIsOpaque();
}
}
// Make sure that current/existing layers are added to the parent and are
// in the correct order.
Layer* layer = nullptr;
for (uint32_t i = 0; i < mNewChildLayers.Length(); ++i) {
Layer* prevChild = i == 0 ? nullptr : mNewChildLayers[i - 1].get();
layer = mNewChildLayers[i];
Layer* prevChild = nullptr;
for (uint32_t i = 0; i < mNewChildLayers.Length(); ++i, prevChild = layer) {
if (!mNewChildLayers[i].mLayer) {
continue;
}
layer = mNewChildLayers[i].mLayer;
if (!layer->GetVisibleRegion().IsEmpty()) {
textContentFlags |= layer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA;
@ -3333,8 +3620,8 @@ ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
aOutgoingScale.mInActiveTransformedSubtree = true;
}
}
bool isRetained = aLayer->Manager()->IsWidgetLayerManager();
if (isRetained && (!canDraw2D || transform2d.HasNonIntegerTranslation())) {
if (aLayerBuilder->IsBuildingRetainedLayers() &&
(!canDraw2D || transform2d.HasNonIntegerTranslation())) {
aOutgoingScale.mDisableSubpixelAntialiasingInDescendants = true;
}
return true;
@ -3471,7 +3758,7 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
nsRect bounds = aChildren->GetBounds(aBuilder);
nsIntRect pixBounds;
int32_t appUnitsPerDevPixel;
nscoord appUnitsPerDevPixel;
uint32_t stateFlags = 0;
if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) &&
mRetainingManager && !mRetainingManager->AreComponentAlphaLayersEnabled()) {
@ -3488,10 +3775,9 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
// Set CONTENT_COMPONENT_ALPHA if any of our children have it.
// This is suboptimal ... a child could have text that's over transparent
// pixels in its own layer, but over opaque parts of previous siblings.
state.Finish(&flags, data);
pixBounds = state.ScaleToOutsidePixels(bounds, false);
appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel();
state.Finish(&flags, data, pixBounds, aChildren);
if ((flags & Layer::CONTENT_COMPONENT_ALPHA) &&
mRetainingManager &&
@ -3512,13 +3798,6 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
break;
}
pixBounds.MoveBy(nsIntPoint(scaleParameters.mOffset.x, scaleParameters.mOffset.y));
if (aParameters.mAncestorClipRect && !(aFlags & CONTAINER_NOT_CLIPPED_BY_ANCESTORS)) {
SetVisibleRegionForLayer(containerLayer, &pixBounds,
*aParameters.mAncestorClipRect);
} else {
containerLayer->SetVisibleRegion(pixBounds);
}
// Make sure that rounding the visible region out didn't add any area
// we won't paint
if (aChildren->IsOpaque() && !aChildren->NeedsTransparentSurface()) {
@ -3529,6 +3808,14 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
}
}
containerLayer->SetContentFlags(flags);
// If aContainerItem is non-null some BuildContainerLayer further up the
// call stack is responsible for setting containerLayer's visible region.
if (!aContainerItem) {
containerLayer->SetVisibleRegion(pixBounds);
}
if (aParameters.mLayerContentsVisibleRect) {
*aParameters.mLayerContentsVisibleRect = pixBounds + scaleParameters.mOffset;
}
mContainerLayerGeneration = oldGeneration;
nsPresContext::ClearNotifySubDocInvalidationData(containerLayer);
@ -4052,7 +4339,9 @@ SetClipCount(ThebesDisplayItemLayerUserData* aThebesData,
}
void
ContainerState::SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
ContainerState::SetupMaskLayer(Layer *aLayer,
const DisplayItemClip& aClip,
const nsIntRegion& aLayerVisibleRegion,
uint32_t aRoundedRectClipCount)
{
// if the number of clips we are going to mask has decreased, then aLayer might have
@ -4066,7 +4355,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
}
// don't build an unnecessary mask
nsIntRect layerBounds = aLayer->GetVisibleRegion().GetBounds();
nsIntRect layerBounds = aLayerVisibleRegion.GetBounds();
if (aClip.GetRoundedRectCount() == 0 ||
aRoundedRectClipCount == 0 ||
layerBounds.IsEmpty()) {

View File

@ -32,6 +32,7 @@ class ThebesLayer;
class FrameLayerBuilder;
class LayerManagerData;
class ThebesLayerData;
class ContainerState;
enum LayerState {
LAYER_NONE,
@ -57,34 +58,44 @@ public:
bool mIsInfinite;
};
struct NewLayerEntry;
struct ContainerLayerParameters {
ContainerLayerParameters() :
mXScale(1), mYScale(1), mAncestorClipRect(nullptr),
mInTransformedSubtree(false), mInActiveTransformedSubtree(false),
mDisableSubpixelAntialiasingInDescendants(false)
ContainerLayerParameters()
: mXScale(1)
, mYScale(1)
, mLayerContentsVisibleRect(nullptr)
, mInTransformedSubtree(false)
, mInActiveTransformedSubtree(false)
, mDisableSubpixelAntialiasingInDescendants(false)
{}
ContainerLayerParameters(float aXScale, float aYScale) :
mXScale(aXScale), mYScale(aYScale), mAncestorClipRect(nullptr),
mInTransformedSubtree(false), mInActiveTransformedSubtree(false),
mDisableSubpixelAntialiasingInDescendants(false)
ContainerLayerParameters(float aXScale, float aYScale)
: mXScale(aXScale)
, mYScale(aYScale)
, mLayerContentsVisibleRect(nullptr)
, mInTransformedSubtree(false)
, mInActiveTransformedSubtree(false)
, mDisableSubpixelAntialiasingInDescendants(false)
{}
ContainerLayerParameters(float aXScale, float aYScale,
const nsIntPoint& aOffset,
const ContainerLayerParameters& aParent) :
mXScale(aXScale), mYScale(aYScale), mAncestorClipRect(nullptr),
mOffset(aOffset),
mInTransformedSubtree(aParent.mInTransformedSubtree),
mInActiveTransformedSubtree(aParent.mInActiveTransformedSubtree),
mDisableSubpixelAntialiasingInDescendants(aParent.mDisableSubpixelAntialiasingInDescendants)
const ContainerLayerParameters& aParent)
: mXScale(aXScale)
, mYScale(aYScale)
, mLayerContentsVisibleRect(nullptr)
, mOffset(aOffset)
, mInTransformedSubtree(aParent.mInTransformedSubtree)
, mInActiveTransformedSubtree(aParent.mInActiveTransformedSubtree)
, mDisableSubpixelAntialiasingInDescendants(aParent.mDisableSubpixelAntialiasingInDescendants)
{}
float mXScale, mYScale;
/**
* An ancestor clip rect that can be applied to restrict the visibility
* of this container. Null if none available.
* If non-null, the rectangle in which BuildContainerLayerFor stores the
* visible rect of the layer, in the coordinate system of the created layer.
*/
const nsIntRect* mAncestorClipRect;
nsIntRect* mLayerContentsVisibleRect;
/**
* An offset to append to the transform set on all child layers created.
* An offset to apply to all child layers created.
*/
nsIntPoint mOffset;
@ -208,6 +219,8 @@ public:
* The container layer is transformed by aTransform (if non-null), and
* the result is transformed by the scale factors in aContainerParameters.
* aChildren is modified due to display item merging and flattening.
* The visible region of the returned layer is set only if aContainerItem
* is null.
*/
already_AddRefed<ContainerLayer>
BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
@ -300,7 +313,8 @@ public:
void AddThebesDisplayItem(ThebesLayerData* aLayer,
nsDisplayItem* aItem,
const DisplayItemClip& aClip,
nsIFrame* aContainerLayerFrame,
const nsIntRect& aItemVisibleRect,
const ContainerState& aContainerState,
LayerState aLayerState,
const nsPoint& aTopLeft,
nsAutoPtr<nsDisplayItemGeometry> aGeometry);
@ -597,6 +611,11 @@ public:
return mContainingThebesLayer;
}
bool IsBuildingRetainedLayers()
{
return !mContainingThebesLayer && mRetainingManager;
}
/**
* Attempt to build the most compressed layer tree possible, even if it means
* throwing away existing retained buffers.