Bug 1085223 - Add an occlusion culling pass to compositor layers. r=BenWa

--HG--
extra : rebase_source : fc5e7007477a8d999fdd1380bf81dd4c205099df
This commit is contained in:
Matt Woodrow 2014-10-20 17:04:39 +13:00
parent 2fe2d95e6c
commit dd47002689
4 changed files with 84 additions and 104 deletions

View File

@ -66,50 +66,6 @@ LayerHasCheckerboardingAPZC(Layer* aLayer, gfxRGBA* aOutColor)
return false;
}
/**
* Returns a rectangle of content painted opaquely by aLayer. Very consertative;
* bails by returning an empty rect in any tricky situations.
*/
static nsIntRect
GetOpaqueRect(Layer* aLayer)
{
nsIntRect result;
gfx::Matrix matrix;
bool is2D = aLayer->AsLayerComposite()->GetShadowTransform().Is2D(&matrix);
// Just bail if there's anything difficult to handle.
if (!is2D || aLayer->GetMaskLayer() ||
aLayer->GetIsFixedPosition() ||
aLayer->GetIsStickyPosition() ||
aLayer->GetEffectiveOpacity() != 1.0f ||
matrix.HasNonIntegerTranslation()) {
return result;
}
if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
result = aLayer->GetEffectiveVisibleRegion().GetLargestRectangle();
} else {
// Drill down into RefLayers because that's what we particularly care about;
// layer construction for aLayer will not have known about the opaqueness
// of any RefLayer subtrees.
RefLayer* refLayer = aLayer->AsRefLayer();
if (refLayer && refLayer->GetFirstChild()) {
result = GetOpaqueRect(refLayer->GetFirstChild());
}
}
// Translate our opaque region to cover the child
gfx::Point point = matrix.GetTranslation();
result.MoveBy(static_cast<int>(point.x), static_cast<int>(point.y));
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
if (clipRect) {
result.IntersectRect(result, *clipRect);
}
return result;
}
static void DrawLayerInfo(const RenderTargetIntRect& aClipRect,
LayerManagerComposite* aManager,
Layer* aLayer)
@ -162,12 +118,10 @@ static void PrintUniformityInfo(Layer* aLayer)
/* all of the per-layer prepared data we need to maintain */
struct PreparedLayer
{
PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect, bool aRestoreVisibleRegion, nsIntRegion &aVisibleRegion) :
mLayer(aLayer), mClipRect(aClipRect), mRestoreVisibleRegion(aRestoreVisibleRegion), mSavedVisibleRegion(aVisibleRegion) {}
PreparedLayer(LayerComposite *aLayer, RenderTargetIntRect aClipRect) :
mLayer(aLayer), mClipRect(aClipRect) {}
LayerComposite* mLayer;
RenderTargetIntRect mClipRect;
bool mRestoreVisibleRegion;
nsIntRegion mSavedVisibleRegion;
};
/* all of the prepared data that we need in RenderLayer() */
@ -223,38 +177,8 @@ ContainerPrepare(ContainerT* aContainer,
CULLING_LOG("Preparing sublayer %p\n", layerToRender->GetLayer());
nsIntRegion savedVisibleRegion;
bool restoreVisibleRegion = false;
gfx::Matrix matrix;
bool is2D = layerToRender->GetLayer()->GetBaseTransform().Is2D(&matrix);
if (i + 1 < children.Length() &&
is2D && !matrix.HasNonIntegerTranslation()) {
LayerComposite* nextLayer = static_cast<LayerComposite*>(children.ElementAt(i + 1)->ImplData());
CULLING_LOG("Culling against %p\n", nextLayer->GetLayer());
nsIntRect nextLayerOpaqueRect;
if (nextLayer && nextLayer->GetLayer()) {
nextLayerOpaqueRect = GetOpaqueRect(nextLayer->GetLayer());
gfx::Point point = matrix.GetTranslation();
nextLayerOpaqueRect.MoveBy(static_cast<int>(-point.x), static_cast<int>(-point.y));
CULLING_LOG(" point %i, %i\n", static_cast<int>(-point.x), static_cast<int>(-point.y));
CULLING_LOG(" opaque rect %i, %i, %i, %i\n", nextLayerOpaqueRect.x, nextLayerOpaqueRect.y, nextLayerOpaqueRect.width, nextLayerOpaqueRect.height);
}
if (!nextLayerOpaqueRect.IsEmpty()) {
CULLING_LOG(" draw\n");
savedVisibleRegion = layerToRender->GetShadowVisibleRegion();
nsIntRegion visibleRegion;
visibleRegion.Sub(savedVisibleRegion, nextLayerOpaqueRect);
if (visibleRegion.IsEmpty()) {
continue;
}
layerToRender->SetShadowVisibleRegion(visibleRegion);
restoreVisibleRegion = true;
} else {
CULLING_LOG(" skip\n");
}
}
layerToRender->Prepare(clipRect);
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect, restoreVisibleRegion, savedVisibleRegion));
aContainer->mPrepared->mLayers.AppendElement(PreparedLayer(layerToRender, clipRect));
}
CULLING_LOG("Preparing container layer %p\n", aContainer->GetLayer());
@ -325,11 +249,6 @@ RenderLayers(ContainerT* aContainer,
layerToRender->RenderLayer(RenderTargetPixel::ToUntyped(clipRect));
}
if (preparedData.mRestoreVisibleRegion) {
// Restore the region in case it's not covered by opaque content next time
layerToRender->SetShadowVisibleRegion(preparedData.mSavedVisibleRegion);
}
if (gfxPrefs::UniformityInfo()) {
PrintUniformityInfo(layer);
}

View File

@ -197,6 +197,55 @@ LayerManagerComposite::BeginTransactionWithDrawTarget(DrawTarget* aTarget, const
mTargetBounds = aRect;
}
void
LayerManagerComposite::ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion)
{
nsIntRegion localOpaque;
Matrix transform2d;
bool isTranslation = false;
// If aLayer has a simple transform (only an integer translation) then we
// can easily convert aOpaqueRegion into pre-transform coordinates and include
// that region.
if (aLayer->GetLocalTransform().Is2D(&transform2d)) {
if (transform2d.IsIntegerTranslation()) {
isTranslation = true;
localOpaque = aOpaqueRegion;
localOpaque.MoveBy(-transform2d._31, -transform2d._32);
}
}
// Subtract any areas that we know to be opaque from our
// visible region.
LayerComposite *composite = aLayer->AsLayerComposite();
if (!localOpaque.IsEmpty()) {
nsIntRegion visible = composite->GetShadowVisibleRegion();
visible.Sub(visible, localOpaque);
composite->SetShadowVisibleRegion(visible);
}
// Compute occlusions for our descendants (in front-to-back order) and allow them to
// contribute to localOpaque.
for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
ApplyOcclusionCulling(child, localOpaque);
}
// If we have a simple transform, then we can add our opaque area into
// aOpaqueRegion.
if (isTranslation &&
!aLayer->GetMaskLayer() &&
aLayer->GetLocalOpacity() == 1.0f) {
if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
localOpaque.Or(localOpaque, composite->GetShadowVisibleRegion());
}
localOpaque.MoveBy(transform2d._31, transform2d._32);
const nsIntRect* clip = aLayer->GetEffectiveClipRect();
if (clip) {
localOpaque.And(localOpaque, *clip);
}
aOpaqueRegion.Or(aOpaqueRegion, localOpaque);
}
}
bool
LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags)
{
@ -257,6 +306,9 @@ LayerManagerComposite::EndTransaction(DrawPaintedLayerCallback aCallback,
// so we don't need to pass any global transform here.
mRoot->ComputeEffectiveTransforms(gfx::Matrix4x4());
nsIntRegion opaque;
ApplyOcclusionCulling(mRoot, opaque);
Render();
mGeometryChanged = false;
} else {

View File

@ -165,6 +165,13 @@ public:
virtual const char* Name() const MOZ_OVERRIDE { return ""; }
/**
* Restricts the shadow visible region of layers that are covered with
* opaque content. aOpaqueRegion is the region already known to be covered
* with opaque content, in the post-transform coordinate space of aLayer.
*/
void ApplyOcclusionCulling(Layer* aLayer, nsIntRegion& aOpaqueRegion);
/**
* RAII helper class to add a mask effect with the compositable from aMaskLayer
* to the EffectChain aEffect and notify the compositable when we are done.

View File

@ -792,6 +792,26 @@ CompositorParent::CompositeCallback(TimeStamp aScheduleTime)
CompositeToTarget(nullptr);
}
// Go down the composite layer tree, setting properties to match their
// content-side counterparts.
static void
SetShadowProperties(Layer* aLayer)
{
// FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
LayerComposite* layerComposite = aLayer->AsLayerComposite();
// Set the layerComposite's base transform to the layer's base transform.
layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
layerComposite->SetShadowTransformSetByAnimation(false);
layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
layerComposite->SetShadowClipRect(aLayer->GetClipRect());
layerComposite->SetShadowOpacity(aLayer->GetOpacity());
for (Layer* child = aLayer->GetFirstChild();
child; child = child->GetNextSibling()) {
SetShadowProperties(child);
}
}
void
CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
{
@ -824,6 +844,8 @@ CompositorParent::CompositeToTarget(DrawTarget* aTarget, const nsIntRect* aRect)
mLayerManager->BeginTransaction();
}
SetShadowProperties(mLayerManager->GetRoot());
if (mForceCompositionTask && !mOverrideComposeReadiness) {
if (mCompositionManager->ReadyForCompose()) {
mForceCompositionTask->Cancel();
@ -904,26 +926,6 @@ CompositorParent::CanComposite()
!mPaused;
}
// Go down the composite layer tree, setting properties to match their
// content-side counterparts.
static void
SetShadowProperties(Layer* aLayer)
{
// FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
LayerComposite* layerComposite = aLayer->AsLayerComposite();
// Set the layerComposite's base transform to the layer's base transform.
layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
layerComposite->SetShadowTransformSetByAnimation(false);
layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
layerComposite->SetShadowClipRect(aLayer->GetClipRect());
layerComposite->SetShadowOpacity(aLayer->GetOpacity());
for (Layer* child = aLayer->GetFirstChild();
child; child = child->GetNextSibling()) {
SetShadowProperties(child);
}
}
void
CompositorParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
bool aIsFirstPaint)