Bug 741682 - Disable component alpha layers with BasicLayers. r=roc

This commit is contained in:
Matt Woodrow 2012-06-11 16:45:38 +12:00
parent 13e6b795f1
commit e1af0db87a
8 changed files with 174 additions and 35 deletions

View File

@ -314,6 +314,13 @@ public:
bool IsSnappingEffectiveTransforms() { return mSnapEffectiveTransforms; }
/**
* Returns true if this LayerManager can properly support layers with
* SURFACE_COMPONENT_ALPHA. This can include disabling component
* alpha if required.
*/
virtual bool AreComponentAlphaLayersEnabled() { return true; }
/**
* CONSTRUCTION PHASE ONLY
* Set the root layer. The root layer is initially null. If there is

View File

@ -95,6 +95,8 @@ public:
void* aCallbackData,
EndTransactionFlags aFlags = END_DEFAULT);
virtual bool AreComponentAlphaLayersEnabled() { return HasShadowManager(); }
virtual void SetRoot(Layer* aLayer);
virtual already_AddRefed<ThebesLayer> CreateThebesLayer();

View File

@ -89,6 +89,10 @@ public:
CollectOldLayers();
}
enum ProcessDisplayItemsFlags {
NO_COMPONENT_ALPHA = 0x01,
};
/**
* This is the method that actually walks a display list and builds
* the child layers. We invoke it recursively to process clipped sublists.
@ -96,7 +100,8 @@ public:
* if no clipping is required
*/
void ProcessDisplayItems(const nsDisplayList& aList,
FrameLayerBuilder::Clip& aClip);
FrameLayerBuilder::Clip& aClip,
PRUint32 aFlags);
/**
* This finalizes all the open ThebesLayers by popping every element off
* mThebesLayerDataStack, then sets the children of the container layer
@ -729,8 +734,11 @@ FrameLayerBuilder::UpdateDisplayItemDataForFrame(DisplayItemDataEntry* aEntry,
LayerManagerData* managerData = static_cast<LayerManagerData*>
(builder->GetRetainingLayerManager()->GetUserData(&gLayerManagerUserData));
LayerManagerData* data = static_cast<LayerManagerData*>(props.Get(LayerManagerDataProperty()));
if (!newDisplayItems) {
if (!newDisplayItems || newDisplayItems->mData.IsEmpty()) {
// This frame was visible, but isn't anymore.
if (newDisplayItems) {
builder->mNewDisplayItemData.RawRemoveEntry(newDisplayItems);
}
if (data == managerData) {
props.Remove(LayerManagerDataProperty());
}
@ -1697,7 +1705,8 @@ PaintInactiveLayer(nsDisplayListBuilder* aBuilder,
*/
void
ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
FrameLayerBuilder::Clip& aClip)
FrameLayerBuilder::Clip& aClip,
PRUint32 aFlags)
{
SAMPLE_LABEL("ContainerState", "ProcessDisplayItems");
for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
@ -1705,7 +1714,7 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
if (type == nsDisplayItem::TYPE_CLIP ||
type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
FrameLayerBuilder::Clip childClip(aClip, item);
ProcessDisplayItems(*item->GetList(), childClip);
ProcessDisplayItems(*item->GetList(), childClip, aFlags);
continue;
}
@ -1727,13 +1736,22 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
nsIFrame* activeScrolledRoot =
nsLayoutUtils::GetActiveScrolledRootFor(item, mBuilder);
nsIFrame* activeScrolledRoot;
bool forceInactive = false;
if (aFlags & NO_COMPONENT_ALPHA) {
activeScrolledRoot =
nsLayoutUtils::GetActiveScrolledRootFor(mContainerFrame,
mBuilder->ReferenceFrame());
forceInactive = true;
} else {
activeScrolledRoot = nsLayoutUtils::GetActiveScrolledRootFor(item, mBuilder);
}
// Assign the item to a layer
if (layerState == LAYER_ACTIVE_FORCE ||
layerState == LAYER_ACTIVE_EMPTY ||
layerState == LAYER_ACTIVE) {
(!forceInactive &&
(layerState == LAYER_ACTIVE_EMPTY ||
layerState == LAYER_ACTIVE))) {
// LAYER_ACTIVE_EMPTY means the layer is created just for its metadata.
// We should never see an empty layer with any visible content!
@ -1976,10 +1994,12 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer,
ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
if (entry) {
entry->mContainerLayerFrame = aContainerLayerFrame;
entry->mContainerLayerGeneration = mContainerLayerGeneration;
NS_ASSERTION(aItem->GetUnderlyingFrame(), "Must have frame");
if (tempManager) {
FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
layerBuilder->Init(mDisplayListBuilder);
layerBuilder->mMaxContainerLayerGeneration = mMaxContainerLayerGeneration;
// LayerManager user data took ownership of the FrameLayerBuilder
tempManager->SetUserData(&gLayerManagerLayerBuilder, new LayerManagerLayerBuilder(layerBuilder, true));
@ -2001,11 +2021,12 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer,
// If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been
// stored in layerBuilder. Manually add it now.
DisplayItemData data(layer, aItem->GetPerFrameKey(), LAYER_ACTIVE);
DisplayItemData data(layer, aItem->GetPerFrameKey(), LAYER_ACTIVE, mContainerLayerGeneration);
layerBuilder->StoreDataForFrame(aItem->GetUnderlyingFrame(), data);
tempManager->SetRoot(layer);
layerBuilder->WillEndTransaction();
mMaxContainerLayerGeneration = layerBuilder->mMaxContainerLayerGeneration;
nsIntRect invalid = props->ComputeDifferences(layer, nsnull);
if (aLayerState == LAYER_SVG_EFFECTS) {
@ -2015,7 +2036,8 @@ FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer,
aLayer->InvalidateRegion(invalid);
}
ClippedDisplayItem* cdi =
entry->mItems.AppendElement(ClippedDisplayItem(aItem, aClip));
entry->mItems.AppendElement(ClippedDisplayItem(aItem, aClip,
mContainerLayerGeneration));
cdi->mInactiveLayer = tempManager;
}
}
@ -2029,6 +2051,7 @@ FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame, DisplayItemData& aData)
}
entry = mNewDisplayItemData.PutEntry(aFrame);
if (entry) {
entry->mContainerLayerGeneration = mContainerLayerGeneration;
DisplayItemData *data = entry->mData.AppendElement();
*data = aData;
}
@ -2066,8 +2089,9 @@ FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
}
}
#endif
entry->mContainerLayerGeneration = mContainerLayerGeneration;
DisplayItemData* data = entry->mData.AppendElement();
DisplayItemData did(aLayer, aItem->GetPerFrameKey(), aLayerState);
DisplayItemData did(aLayer, aItem->GetPerFrameKey(), aLayerState, mContainerLayerGeneration);
*data = did;
ThebesLayer *t = aLayer->AsThebesLayer();
@ -2092,8 +2116,11 @@ nsIntPoint
FrameLayerBuilder::GetLastPaintOffset(ThebesLayer* aLayer)
{
ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
if (entry && entry->mHasExplicitLastPaintOffset)
return entry->mLastPaintOffset;
if (entry) {
entry->mContainerLayerGeneration = mContainerLayerGeneration;
if (entry->mHasExplicitLastPaintOffset)
return entry->mLastPaintOffset;
}
return GetTranslationForThebesLayer(aLayer);
}
@ -2102,6 +2129,7 @@ FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer* aLayer)
{
ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
if (entry) {
entry->mContainerLayerGeneration = mContainerLayerGeneration;
entry->mLastPaintOffset = GetTranslationForThebesLayer(aLayer);
entry->mHasExplicitLastPaintOffset = true;
}
@ -2279,6 +2307,44 @@ ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
return result;
}
/* static */ PLDHashOperator
FrameLayerBuilder::RestoreDisplayItemData(DisplayItemDataEntry* aEntry, void* aUserArg)
{
PRUint32 *generation = static_cast<PRUint32*>(aUserArg);
if (aEntry->mContainerLayerGeneration >= *generation) {
return PL_DHASH_REMOVE;
}
for (PRUint32 i = 0; i < aEntry->mData.Length(); i++) {
if (aEntry->mData[i].mContainerLayerGeneration >= *generation) {
aEntry->mData.TruncateLength(i);
return PL_DHASH_NEXT;
}
}
return PL_DHASH_NEXT;
}
/* static */ PLDHashOperator
FrameLayerBuilder::RestoreThebesLayerItemEntries(ThebesLayerItemsEntry* aEntry, void* aUserArg)
{
PRUint32 *generation = static_cast<PRUint32*>(aUserArg);
if (aEntry->mContainerLayerGeneration >= *generation) {
return PL_DHASH_REMOVE;
}
for (PRUint32 i = 0; i < aEntry->mItems.Length(); i++) {
if (aEntry->mItems[i].mContainerLayerGeneration >= *generation) {
aEntry->mItems.TruncateLength(i);
return PL_DHASH_NEXT;
}
}
return PL_DHASH_NEXT;
}
already_AddRefed<ContainerLayer>
FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
@ -2333,34 +2399,66 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
ContainerParameters scaleParameters =
ChooseScaleAndSetTransform(this, aContainerFrame, aTransform, aParameters,
containerLayer);
ContainerState state(aBuilder, aManager, GetLayerBuilderForManager(aManager),
aContainerFrame, containerLayer, scaleParameters);
PRUint32 oldGeneration = mContainerLayerGeneration;
mContainerLayerGeneration = ++mMaxContainerLayerGeneration;
LayerManagerData* data = static_cast<LayerManagerData*>
(aManager->GetUserData(&gLayerManagerUserData));
if (mRetainingManager) {
DisplayItemDataEntry* entry = mNewDisplayItemData.PutEntry(aContainerFrame);
if (entry) {
DisplayItemData *data = entry->mData.AppendElement();
DisplayItemData did(containerLayer, containerDisplayItemKey,
LAYER_ACTIVE);
LAYER_ACTIVE, mContainerLayerGeneration);
*data = did;
entry->mContainerLayerGeneration = mContainerLayerGeneration;
}
}
Clip clip;
state.ProcessDisplayItems(aChildren, clip);
LayerManagerData* data = static_cast<LayerManagerData*>
(aManager->GetUserData(&gLayerManagerUserData));
nsRect bounds;
nsIntRect pixBounds;
PRInt32 appUnitsPerDevPixel;
// 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.
PRUint32 stateFlags =
(aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) ?
ContainerState::NO_COMPONENT_ALPHA : 0;
PRUint32 flags;
state.Finish(&flags, data);
while (true) {
ContainerState state(aBuilder, aManager, GetLayerBuilderForManager(aManager),
aContainerFrame, containerLayer, scaleParameters);
Clip clip;
state.ProcessDisplayItems(aChildren, clip, stateFlags);
// 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);
bounds = state.GetChildrenBounds();
pixBounds = state.ScaleToOutsidePixels(bounds, false);
appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel();
if ((flags & Layer::CONTENT_COMPONENT_ALPHA) &&
mRetainingManager &&
!mRetainingManager->AreComponentAlphaLayersEnabled() &&
!stateFlags) {
// Since we don't want any component alpha layers on BasicLayers, we repeat
// the layer building process with this explicitely forced off.
// We restore the previous FrameLayerBuilder state since the first set
// of layer building will have changed it.
stateFlags = ContainerState::NO_COMPONENT_ALPHA;
mNewDisplayItemData.EnumerateEntries(RestoreDisplayItemData,
&mContainerLayerGeneration);
mThebesLayerItems.EnumerateEntries(RestoreThebesLayerItemEntries,
&mContainerLayerGeneration);
aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
continue;
}
break;
}
nsRect bounds = state.GetChildrenBounds();
NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds");
nsIntRect pixBounds = state.ScaleToOutsidePixels(bounds, false);
if (aContainerItem) {
nsIntRect itemVisibleRect =
aContainerItem->GetVisibleRect().ToOutsidePixels(AppUnitsPerDevPixel(aContainerItem));
@ -2372,7 +2470,7 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
// we won't paint
if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) {
bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale);
if (bounds.Contains(pixBounds.ToAppUnits(state.GetAppUnitsPerDevPixel()))) {
if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) {
// Clear CONTENT_COMPONENT_ALPHA
flags = Layer::CONTENT_OPAQUE;
}
@ -2381,6 +2479,7 @@ FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
containerLayer->SetUserData(&gNotifySubDocInvalidationData, nsnull);
mContainerLayerGeneration = oldGeneration;
return containerLayer.forget();
}

View File

@ -110,7 +110,9 @@ public:
FrameLayerBuilder() :
mRetainingManager(nsnull),
mDetectedDOMModification(false),
mInvalidateAllLayers(false)
mInvalidateAllLayers(false),
mContainerLayerGeneration(0),
mMaxContainerLayerGeneration(0)
{
MOZ_COUNT_CTOR(FrameLayerBuilder);
mNewDisplayItemData.Init();
@ -443,8 +445,13 @@ protected:
*/
class DisplayItemData {
public:
DisplayItemData(Layer* aLayer, PRUint32 aKey, LayerState aLayerState)
: mLayer(aLayer), mDisplayItemKey(aKey), mLayerState(aLayerState), mUsed(false) {}
DisplayItemData(Layer* aLayer, PRUint32 aKey, LayerState aLayerState, PRUint32 aGeneration)
: mLayer(aLayer)
, mDisplayItemKey(aKey)
, mContainerLayerGeneration(aGeneration)
, mLayerState(aLayerState)
, mUsed(false)
{}
DisplayItemData()
: mUsed(false)
@ -457,6 +464,7 @@ protected:
mInactiveManager = toCopy.mInactiveManager;
mGeometry = toCopy.mGeometry;
mDisplayItemKey = toCopy.mDisplayItemKey;
mContainerLayerGeneration = toCopy.mContainerLayerGeneration;
mLayerState = toCopy.mLayerState;
mUsed = toCopy.mUsed;
}
@ -465,6 +473,7 @@ protected:
nsRefPtr<LayerManager> mInactiveManager;
nsAutoPtr<nsDisplayItemGeometry> mGeometry;
PRUint32 mDisplayItemKey;
PRUint32 mContainerLayerGeneration;
LayerState mLayerState;
/**
@ -495,12 +504,14 @@ protected:
// This isn't actually a copy-constructor; notice that it steals toCopy's
// array. Be careful.
mData.SwapElements(toCopy.mData);
mContainerLayerGeneration = toCopy.mContainerLayerGeneration;
}
~DisplayItemDataEntry() { MOZ_COUNT_DTOR(DisplayItemDataEntry); }
bool HasNonEmptyContainerLayer();
nsAutoTArray<DisplayItemData, 1> mData;
PRUint32 mContainerLayerGeneration;
enum { ALLOW_MEMMOVE = false };
};
@ -555,8 +566,8 @@ protected:
* mItem always has an underlying frame.
*/
struct ClippedDisplayItem {
ClippedDisplayItem(nsDisplayItem* aItem, const Clip& aClip)
: mItem(aItem), mClip(aClip)
ClippedDisplayItem(nsDisplayItem* aItem, const Clip& aClip, PRUint32 aGeneration)
: mItem(aItem), mClip(aClip), mContainerLayerGeneration(aGeneration)
{
}
@ -572,6 +583,7 @@ protected:
nsRefPtr<LayerManager> mInactiveLayer;
Clip mClip;
PRUint32 mContainerLayerGeneration;
};
/**
@ -595,6 +607,7 @@ public:
// The translation set on this ThebesLayer before we started updating the
// layer tree.
nsIntPoint mLastPaintOffset;
PRUint32 mContainerLayerGeneration;
bool mHasExplicitLastPaintOffset;
/**
* The first mCommonClipCount rounded rectangle clips are identical for
@ -625,6 +638,12 @@ protected:
static PLDHashOperator StoreNewDisplayItemData(DisplayItemDataEntry* aEntry,
void* aUserArg);
static PLDHashOperator RestoreDisplayItemData(DisplayItemDataEntry* aEntry,
void *aUserArg);
static PLDHashOperator RestoreThebesLayerItemEntries(ThebesLayerItemsEntry* aEntry,
void *aUserArg);
/**
* Returns true if the DOM has been modified since we started painting,
* in which case we should bail out and not paint anymore. This should
@ -670,6 +689,9 @@ protected:
* during this paint.
*/
bool mInvalidateAllLayers;
PRUint32 mContainerLayerGeneration;
PRUint32 mMaxContainerLayerGeneration;
};
}

View File

@ -2051,7 +2051,7 @@ public:
LayerManager* aManager,
const ContainerParameters& aParameters)
{
return mozilla::LAYER_ACTIVE;
return mozilla::LAYER_ACTIVE_FORCE;
}
virtual bool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem)
{

View File

@ -1635,6 +1635,10 @@ PresShell::InitialReflow(nscoord aWidth, nscoord aHeight)
return NS_ERROR_OUT_OF_MEMORY;
}
for (nsIFrame* f = rootFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
f->RemoveStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
}
Element *root = mDocument->GetRootElement();
if (root) {

View File

@ -239,6 +239,11 @@ typedef PRUint64 nsFrameState;
// This bit acts as a loop flag for recursive paint server drawing.
#define NS_FRAME_DRAWING_AS_PAINTSERVER NS_FRAME_STATE_BIT(33)
// This bit is set on frames that create ContainerLayers with component
// alpha children. With BasicLayers we avoid creating these, so we mark
// the frames for future reference.
#define NS_FRAME_NO_COMPONENT_ALPHA NS_FRAME_STATE_BIT(34)
// Frame's overflow area was clipped by the 'clip' property.
#define NS_FRAME_HAS_CLIP NS_FRAME_STATE_BIT(35)

View File

@ -131,7 +131,7 @@ public:
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aParameters)
{ return mozilla::LAYER_ACTIVE; }
{ return mozilla::LAYER_ACTIVE_FORCE; }
NS_OVERRIDE
virtual already_AddRefed<Layer>