Bug 902525 - Part 2: Create layers for isolated groups when blending is involved. r=roc

This commit is contained in:
Rik Cabanier 2013-09-15 16:24:06 -04:00
parent 492ade5e96
commit 80eedaeab4
7 changed files with 126 additions and 3 deletions

View File

@ -182,6 +182,7 @@ Layer::Layer(LayerManager* aManager, void* aImplData) :
mPostYScale(1.0f),
mOpacity(1.0),
mMixBlendMode(gfxContext::OPERATOR_OVER),
mForceIsolatedGroup(false),
mContentFlags(0),
mUseClipRect(false),
mUseTileSourceRect(false),

View File

@ -741,6 +741,20 @@ public:
Mutated();
}
}
void SetForceIsolatedGroup(bool aForceIsolatedGroup)
{
if(mForceIsolatedGroup != aForceIsolatedGroup) {
MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ForceIsolatedGroup", this));
mForceIsolatedGroup = aForceIsolatedGroup;
Mutated();
}
}
bool GetForceIsolatedGroup() const
{
return mForceIsolatedGroup;
}
/**
* CONSTRUCTION PHASE ONLY
@ -1323,6 +1337,7 @@ protected:
InfallibleTArray<AnimData> mAnimationData;
float mOpacity;
gfxContext::GraphicsOperator mMixBlendMode;
bool mForceIsolatedGroup;
nsIntRect mClipRect;
nsIntRect mTileSourceRect;
nsIntRegion mInvalidRegion;

View File

@ -73,6 +73,7 @@ BasicContainerLayer::ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToS
*/
mUseIntermediateSurface =
GetMaskLayer() ||
GetForceIsolatedGroup() ||
(GetMixBlendMode() != gfxContext::OPERATOR_OVER && HasMultipleChildren()) ||
(GetEffectiveOpacity() != 1.0 && (HasMultipleChildren() || hasSingleBlendingChild));
}

View File

@ -3,6 +3,7 @@ DECLARE_DISPLAY_ITEM_TYPE(ALT_FEEDBACK)
DECLARE_DISPLAY_ITEM_TYPE(BACKGROUND)
DECLARE_DISPLAY_ITEM_TYPE(THEMED_BACKGROUND)
DECLARE_DISPLAY_ITEM_TYPE_FLAGS(BACKGROUND_COLOR,TYPE_RENDERS_NO_IMAGES)
DECLARE_DISPLAY_ITEM_TYPE(BLEND_CONTAINER)
DECLARE_DISPLAY_ITEM_TYPE(BORDER)
DECLARE_DISPLAY_ITEM_TYPE(BOX_SHADOW_OUTER)
DECLARE_DISPLAY_ITEM_TYPE(BOX_SHADOW_INNER)

View File

@ -486,7 +486,8 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mHasFixedItems(false),
mIsInFixedPosition(false),
mIsCompositingCheap(false),
mContainsPluginItem(false)
mContainsPluginItem(false),
mContainsBlendMode(false)
{
MOZ_COUNT_CTOR(nsDisplayListBuilder);
PL_InitArenaPool(&mPool, "displayListArena", 1024,
@ -3117,6 +3118,49 @@ bool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* a
return true;
}
nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList,
uint32_t aFlags)
: nsDisplayWrapList(aBuilder, aFrame, aList) {
MOZ_COUNT_CTOR(nsDisplayBlendContainer);
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayBlendContainer::~nsDisplayBlendContainer() {
MOZ_COUNT_DTOR(nsDisplayBlendContainer);
}
#endif
// nsDisplayBlendContainer uses layers for rendering
already_AddRefed<Layer>
nsDisplayBlendContainer::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) {
nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
aContainerParameters, nullptr);
if (!container) {
return nullptr;
}
container->SetForceIsolatedGroup(true);
return container.forget();
}
bool nsDisplayBlendContainer::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
if (aItem->GetType() != TYPE_BLEND_CONTAINER)
return false;
// items for the same content element should be merged into a single
// compositing group
// aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
if (aItem->Frame()->GetContent() != mFrame->GetContent())
return false;
if (aItem->GetClip() != GetClip())
return false;
MergeFromTrackingMergedFrames(static_cast<nsDisplayBlendContainer*>(aItem));
return true;
}
nsDisplayOwnLayer::nsDisplayOwnLayer(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList,
uint32_t aFlags)

View File

@ -622,6 +622,14 @@ public:
void SetContainsPluginItem() { mContainsPluginItem = true; }
bool ContainsPluginItem() { return mContainsPluginItem; }
/**
* mContainsBlendMode is true if we processed a display item that
* has a blend mode attached. We do this so we can insert a
* nsDisplayBlendContainer in the parent stacking context.
*/
void SetContainsBlendMode(bool aContainsBlendMode) { mContainsBlendMode = aContainsBlendMode; }
bool ContainsBlendMode() const { return mContainsBlendMode; }
DisplayListClipState& ClipState() { return mClipState; }
private:
@ -680,6 +688,7 @@ private:
bool mIsInFixedPosition;
bool mIsCompositingCheap;
bool mContainsPluginItem;
bool mContainsBlendMode;
};
class nsDisplayItem;
@ -2498,6 +2507,27 @@ public:
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
};
class nsDisplayBlendContainer : public nsDisplayWrapList {
public:
nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList, uint32_t aFlags = 0);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayBlendContainer();
#endif
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) MOZ_OVERRIDE;
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aParameters) MOZ_OVERRIDE
{
return mozilla::LAYER_INACTIVE;
}
virtual bool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) MOZ_OVERRIDE;
NS_DISPLAY_DECL_NAME("BlendContainer", TYPE_BLEND_CONTAINER)
};
/**
* A display item that has no purpose but to ensure its contents get
* their own layer.

View File

@ -1758,6 +1758,21 @@ WrapPreserve3DList(nsIFrame* aFrame, nsDisplayListBuilder* aBuilder, nsDisplayLi
return rv;
}
class AutoSaveRestoreBlendMode
{
nsDisplayListBuilder& mBuilder;
bool AutoResetContainsBlendMode;
public:
AutoSaveRestoreBlendMode(nsDisplayListBuilder& aBuilder)
: mBuilder(aBuilder),
AutoResetContainsBlendMode(aBuilder.ContainsBlendMode()) {
}
~AutoSaveRestoreBlendMode() {
mBuilder.SetContainsBlendMode(AutoResetContainsBlendMode);
}
};
void
nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
@ -1786,6 +1801,12 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
bool inTransform = aBuilder->IsInTransform();
bool isTransformed = IsTransformed();
// reset blend mode so we can keep track if this stacking context needs have
// a nsDisplayBlendContainer. Set the blend mode back when the routine exits
// so we keep track if the parent stacking context needs a container too.
AutoSaveRestoreBlendMode autoRestoreBlendMode(*aBuilder);
aBuilder->SetContainsBlendMode(false);
if (isTransformed) {
if (aBuilder->IsForPainting() &&
nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this)) {
@ -1818,6 +1839,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
}
bool useOpacity = HasOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
bool useBlendMode = disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
IsScrollFrameActive(nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
@ -1826,7 +1848,7 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
if (isTransformed || useOpacity || usingSVGEffects || useStickyPosition) {
if (isTransformed || useOpacity || useBlendMode || usingSVGEffects || useStickyPosition) {
// We don't need to pass ancestor clipping down to our children;
// everything goes inside a display item's child list, and the display
// item itself will be clipped.
@ -1975,6 +1997,11 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList));
}
}
if (aBuilder->ContainsBlendMode()) {
resultList.AppendNewToTop(
new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList));
}
aList->AppendToTop(&resultList);
}
@ -2106,11 +2133,12 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
return;
// Child is composited if it's transformed, partially transparent, or has
// SVG effects.
// SVG effects or a blend mode..
const nsStyleDisplay* disp = child->StyleDisplay();
const nsStylePosition* pos = child->StylePosition();
bool isVisuallyAtomic = child->HasOpacity()
|| child->IsTransformed()
|| disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL
|| nsSVGIntegrationUtils::UsingEffectsForFrame(child);
bool isPositioned = disp->IsPositioned(child);
@ -2165,6 +2193,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
nsDisplayList list;
nsDisplayList extraPositionedDescendants;
if (isStackingContext) {
if (disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
aBuilder->SetContainsBlendMode(true);
}
// True stacking context.
// For stacking contexts, BuildDisplayListForStackingContext handles
// clipping and MarkAbsoluteFramesForDisplayList.