gecko-dev/gfx/layers/composite/LayerManagerComposite.cpp
2013-04-24 11:28:05 -04:00

654 lines
20 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/layers/PLayerTransaction.h"
// This must occur *after* layers/PLayerTransaction.h to avoid
// typedefs conflicts.
#include "mozilla/Util.h"
#include "LayerManagerComposite.h"
#include "ThebesLayerComposite.h"
#include "ContainerLayerComposite.h"
#include "ImageLayerComposite.h"
#include "ColorLayerComposite.h"
#include "CanvasLayerComposite.h"
#include "CompositableHost.h"
#include "mozilla/gfx/Matrix.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Preferences.h"
#include "mozilla/layers/ImageHost.h"
#include "mozilla/layers/ContentHost.h"
#include "gfxContext.h"
#include "gfxUtils.h"
#include "gfx2DGlue.h"
#ifdef XP_MACOSX
#include "gfxPlatformMac.h"
#else
#include "gfxPlatform.h"
#endif
#include "nsIWidget.h"
#include "nsIServiceManager.h"
#include "nsIConsoleService.h"
#include "gfxCrashReporterUtils.h"
#include "GeckoProfiler.h"
#ifdef MOZ_WIDGET_ANDROID
#include <android/log.h>
#endif
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
static LayerComposite*
ToLayerComposite(Layer* aLayer)
{
return static_cast<LayerComposite*>(aLayer->ImplData());
}
static void ClearSubtree(Layer* aLayer)
{
ToLayerComposite(aLayer)->CleanupResources();
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
ClearSubtree(child);
}
}
void
LayerManagerComposite::ClearCachedResources(Layer* aSubtree)
{
MOZ_ASSERT(!aSubtree || aSubtree->Manager() == this);
Layer* subtree = aSubtree ? aSubtree : mRoot.get();
if (!subtree) {
return;
}
ClearSubtree(subtree);
// FIXME [bjacob]
// XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here.
// Do we need that?
}
/**
* LayerManagerComposite
*/
LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
{
mCompositor = aCompositor;
}
bool
LayerManagerComposite::Initialize()
{
mComposer2D = mCompositor->GetWidget()->GetComposer2D();
return mCompositor->Initialize();
}
void
LayerManagerComposite::Destroy()
{
if (!mDestroyed) {
mCompositor->GetWidget()->CleanupWindowEffects();
if (mRoot) {
RootLayer()->Destroy();
}
mRoot = nullptr;
mCompositor->Destroy();
mDestroyed = true;
}
}
void
LayerManagerComposite::UpdateRenderBounds(const nsIntRect& aRect)
{
mRenderBounds = aRect;
}
void
LayerManagerComposite::BeginTransaction()
{
mInTransaction = true;
}
void
LayerManagerComposite::BeginTransactionWithTarget(gfxContext *aTarget)
{
mInTransaction = true;
#ifdef MOZ_LAYERS_HAVE_LOG
MOZ_LAYERS_LOG(("[----- BeginTransaction"));
Log();
#endif
if (mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return;
}
mCompositor->SetTargetContext(aTarget);
}
bool
LayerManagerComposite::EndEmptyTransaction(EndTransactionFlags aFlags)
{
mInTransaction = false;
if (!mRoot)
return false;
EndTransaction(nullptr, nullptr);
return true;
}
void
LayerManagerComposite::EndTransaction(DrawThebesLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags aFlags)
{
mInTransaction = false;
#ifdef MOZ_LAYERS_HAVE_LOG
MOZ_LAYERS_LOG((" ----- (beginning paint)"));
Log();
#endif
if (mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return;
}
if (mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
if (aFlags & END_NO_COMPOSITE) {
// Apply pending tree updates before recomputing effective
// properties.
mRoot->ApplyPendingUpdatesToSubtree();
}
// The results of our drawing always go directly into a pixel buffer,
// so we don't need to pass any global transform here.
mRoot->ComputeEffectiveTransforms(gfx3DMatrix());
mThebesLayerCallback = aCallback;
mThebesLayerCallbackData = aCallbackData;
Render();
mThebesLayerCallback = nullptr;
mThebesLayerCallbackData = nullptr;
}
mCompositor->SetTargetContext(nullptr);
#ifdef MOZ_LAYERS_HAVE_LOG
Log();
MOZ_LAYERS_LOG(("]----- EndTransaction"));
#endif
}
already_AddRefed<gfxASurface>
LayerManagerComposite::CreateOptimalMaskSurface(const gfxIntSize &aSize)
{
NS_RUNTIMEABORT("Should only be called on the drawing side");
return nullptr;
}
already_AddRefed<ThebesLayer>
LayerManagerComposite::CreateThebesLayer()
{
NS_RUNTIMEABORT("Should only be called on the drawing side");
return nullptr;
}
already_AddRefed<ContainerLayer>
LayerManagerComposite::CreateContainerLayer()
{
NS_RUNTIMEABORT("Should only be called on the drawing side");
return nullptr;
}
already_AddRefed<ImageLayer>
LayerManagerComposite::CreateImageLayer()
{
NS_RUNTIMEABORT("Should only be called on the drawing side");
return nullptr;
}
already_AddRefed<ColorLayer>
LayerManagerComposite::CreateColorLayer()
{
NS_RUNTIMEABORT("Should only be called on the drawing side");
return nullptr;
}
already_AddRefed<CanvasLayer>
LayerManagerComposite::CreateCanvasLayer()
{
NS_RUNTIMEABORT("Should only be called on the drawing side");
return nullptr;
}
LayerComposite*
LayerManagerComposite::RootLayer() const
{
if (mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return nullptr;
}
return static_cast<LayerComposite*>(mRoot->ImplData());
}
void
LayerManagerComposite::Render()
{
PROFILER_LABEL("LayerManagerComposite", "Render");
if (mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return;
}
if (mComposer2D && mComposer2D->TryRender(mRoot, mWorldMatrix)) {
mCompositor->EndFrameForExternalComposition(mWorldMatrix);
return;
}
nsIntRect clipRect;
Rect bounds(mRenderBounds.x, mRenderBounds.y, mRenderBounds.width, mRenderBounds.height);
Rect actualBounds;
if (mRoot->GetClipRect()) {
clipRect = *mRoot->GetClipRect();
WorldTransformRect(clipRect);
Rect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
mCompositor->BeginFrame(&rect, mWorldMatrix, bounds, nullptr, &actualBounds);
} else {
gfx::Rect rect;
mCompositor->BeginFrame(nullptr, mWorldMatrix, bounds, &rect, &actualBounds);
clipRect = nsIntRect(rect.x, rect.y, rect.width, rect.height);
}
// Allow widget to render a custom background.
mCompositor->GetWidget()->DrawWindowUnderlay(this, nsIntRect(actualBounds.x,
actualBounds.y,
actualBounds.width,
actualBounds.height));
// Render our layers.
RootLayer()->RenderLayer(nsIntPoint(0, 0), clipRect);
// Allow widget to render a custom foreground.
mCompositor->GetWidget()->DrawWindowOverlay(this, nsIntRect(actualBounds.x,
actualBounds.y,
actualBounds.width,
actualBounds.height));
mCompositor->EndFrame();
}
void
LayerManagerComposite::SetWorldTransform(const gfxMatrix& aMatrix)
{
NS_ASSERTION(aMatrix.PreservesAxisAlignedRectangles(),
"SetWorldTransform only accepts matrices that satisfy PreservesAxisAlignedRectangles");
NS_ASSERTION(!aMatrix.HasNonIntegerScale(),
"SetWorldTransform only accepts matrices with integer scale");
mWorldMatrix = aMatrix;
}
gfxMatrix&
LayerManagerComposite::GetWorldTransform(void)
{
return mWorldMatrix;
}
void
LayerManagerComposite::WorldTransformRect(nsIntRect& aRect)
{
gfxRect grect(aRect.x, aRect.y, aRect.width, aRect.height);
grect = mWorldMatrix.TransformBounds(grect);
aRect.SetRect(grect.X(), grect.Y(), grect.Width(), grect.Height());
}
static void
SubtractTransformedRegion(nsIntRegion& aRegion,
const nsIntRegion& aRegionToSubtract,
const gfx3DMatrix& aTransform)
{
if (aRegionToSubtract.IsEmpty()) {
return;
}
// For each rect in the region, find out its bounds in screen space and
// subtract it from the screen region.
nsIntRegionRectIterator it(aRegionToSubtract);
while (const nsIntRect* rect = it.Next()) {
gfxRect incompleteRect = aTransform.TransformBounds(gfxRect(*rect));
aRegion.Sub(aRegion, nsIntRect(incompleteRect.x,
incompleteRect.y,
incompleteRect.width,
incompleteRect.height));
}
}
/* static */ void
LayerManagerComposite::ComputeRenderIntegrityInternal(Layer* aLayer,
nsIntRegion& aScreenRegion,
nsIntRegion& aLowPrecisionScreenRegion,
const gfx3DMatrix& aTransform)
{
if (aLayer->GetOpacity() <= 0.f ||
(aScreenRegion.IsEmpty() && aLowPrecisionScreenRegion.IsEmpty())) {
return;
}
// If the layer's a container, recurse into all of its children
ContainerLayer* container = aLayer->AsContainerLayer();
if (container) {
// Accumulate the transform of intermediate surfaces
gfx3DMatrix transform = aTransform;
if (container->UseIntermediateSurface()) {
transform = aLayer->GetEffectiveTransform();
transform.PreMultiply(aTransform);
}
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
ComputeRenderIntegrityInternal(child, aScreenRegion, aLowPrecisionScreenRegion, transform);
}
return;
}
// Only thebes layers can be incomplete
ThebesLayer* thebesLayer = aLayer->AsThebesLayer();
if (!thebesLayer) {
return;
}
// See if there's any incomplete rendering
nsIntRegion incompleteRegion = aLayer->GetEffectiveVisibleRegion();
incompleteRegion.Sub(incompleteRegion, thebesLayer->GetValidRegion());
if (!incompleteRegion.IsEmpty()) {
// Calculate the transform to get between screen and layer space
gfx3DMatrix transformToScreen = aLayer->GetEffectiveTransform();
transformToScreen.PreMultiply(aTransform);
SubtractTransformedRegion(aScreenRegion, incompleteRegion, transformToScreen);
// See if there's any incomplete low-precision rendering
TiledLayerComposer* composer = nullptr;
LayerComposite* shadow = aLayer->AsLayerComposite();
if (shadow) {
composer = shadow->GetTiledLayerComposer();
if (composer) {
incompleteRegion.Sub(incompleteRegion, composer->GetValidLowPrecisionRegion());
if (!incompleteRegion.IsEmpty()) {
SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen);
}
}
}
// If we can't get a valid low precision region, assume it's the same as
// the high precision region.
if (!composer) {
SubtractTransformedRegion(aLowPrecisionScreenRegion, incompleteRegion, transformToScreen);
}
}
}
static int
GetRegionArea(const nsIntRegion& aRegion)
{
int area = 0;
nsIntRegionRectIterator it(aRegion);
while (const nsIntRect* rect = it.Next()) {
area += rect->width * rect->height;
}
return area;
}
#ifdef MOZ_ANDROID_OMTC
static float
GetDisplayportCoverage(const gfx::Rect& aDisplayPort,
const gfx3DMatrix& aTransformToScreen,
const nsIntRect& aScreenRect)
{
gfxRect transformedDisplayport =
aTransformToScreen.TransformBounds(gfxRect(aDisplayPort.x,
aDisplayPort.y,
aDisplayPort.width,
aDisplayPort.height));
transformedDisplayport.RoundOut();
nsIntRect displayport = nsIntRect(transformedDisplayport.x,
transformedDisplayport.y,
transformedDisplayport.width,
transformedDisplayport.height);
if (!displayport.Contains(aScreenRect)) {
nsIntRegion coveredRegion;
coveredRegion.And(aScreenRect, displayport);
return GetRegionArea(coveredRegion) / (float)(aScreenRect.width * aScreenRect.height);
}
return 1.0f;
}
#endif // MOZ_ANDROID_OMTC
float
LayerManagerComposite::ComputeRenderIntegrity()
{
// We only ever have incomplete rendering when progressive tiles are enabled.
Layer* root = GetRoot();
if (!gfxPlatform::UseProgressiveTilePainting() || !root) {
return 1.f;
}
const FrameMetrics& rootMetrics = root->AsContainerLayer()->GetFrameMetrics();
nsIntRect screenRect(rootMetrics.mCompositionBounds.x,
rootMetrics.mCompositionBounds.y,
rootMetrics.mCompositionBounds.width,
rootMetrics.mCompositionBounds.height);
float lowPrecisionMultiplier = 1.0f;
float highPrecisionMultiplier = 1.0f;
#ifdef MOZ_ANDROID_OMTC
// Use the transform on the primary scrollable layer and its FrameMetrics
// to find out how much of the viewport the current displayport covers
Layer* primaryScrollable = GetPrimaryScrollableLayer();
if (primaryScrollable) {
// This is derived from the code in
// gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree.
const gfx3DMatrix& rootTransform = root->GetTransform();
float devPixelRatioX = 1 / rootTransform.GetXScale();
float devPixelRatioY = 1 / rootTransform.GetYScale();
gfx3DMatrix transform = primaryScrollable->GetEffectiveTransform();
transform.ScalePost(devPixelRatioX, devPixelRatioY, 1);
const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
// Clip the screen rect to the document bounds
gfxRect documentBounds =
transform.TransformBounds(gfxRect(metrics.mScrollableRect.x - metrics.mScrollOffset.x,
metrics.mScrollableRect.y - metrics.mScrollOffset.y,
metrics.mScrollableRect.width,
metrics.mScrollableRect.height));
documentBounds.RoundOut();
screenRect = screenRect.Intersect(nsIntRect(documentBounds.x, documentBounds.y,
documentBounds.width, documentBounds.height));
// If the screen rect is empty, the user has scrolled entirely into
// over-scroll and so we can be considered to have full integrity.
if (screenRect.IsEmpty()) {
return 1.0f;
}
// Work out how much of the critical display-port covers the screen
bool hasLowPrecision = false;
if (!metrics.mCriticalDisplayPort.IsEmpty()) {
hasLowPrecision = true;
highPrecisionMultiplier =
GetDisplayportCoverage(metrics.mCriticalDisplayPort, transform, screenRect);
}
// Work out how much of the display-port covers the screen
if (!metrics.mDisplayPort.IsEmpty()) {
if (hasLowPrecision) {
lowPrecisionMultiplier =
GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect);
} else {
lowPrecisionMultiplier = highPrecisionMultiplier =
GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect);
}
}
}
// If none of the screen is covered, we have zero integrity.
if (highPrecisionMultiplier <= 0.0f && lowPrecisionMultiplier <= 0.0f) {
return 0.0f;
}
#endif // MOZ_ANDROID_OMTC
nsIntRegion screenRegion(screenRect);
nsIntRegion lowPrecisionScreenRegion(screenRect);
gfx3DMatrix transform;
ComputeRenderIntegrityInternal(root, screenRegion,
lowPrecisionScreenRegion, transform);
if (!screenRegion.IsEqual(screenRect)) {
// Calculate the area of the region. All rects in an nsRegion are
// non-overlapping.
float screenArea = screenRect.width * screenRect.height;
float highPrecisionIntegrity = GetRegionArea(screenRegion) / screenArea;
float lowPrecisionIntegrity = 1.f;
if (!lowPrecisionScreenRegion.IsEqual(screenRect)) {
lowPrecisionIntegrity = GetRegionArea(lowPrecisionScreenRegion) / screenArea;
}
return ((highPrecisionIntegrity * highPrecisionMultiplier) +
(lowPrecisionIntegrity * lowPrecisionMultiplier)) / 2;
}
return 1.f;
}
already_AddRefed<ShadowThebesLayer>
LayerManagerComposite::CreateShadowThebesLayer()
{
if (LayerManagerComposite::mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return nullptr;
}
return nsRefPtr<ThebesLayerComposite>(new ThebesLayerComposite(this)).forget();
}
already_AddRefed<ShadowContainerLayer>
LayerManagerComposite::CreateShadowContainerLayer()
{
if (LayerManagerComposite::mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return nullptr;
}
return nsRefPtr<ContainerLayerComposite>(new ContainerLayerComposite(this)).forget();
}
already_AddRefed<ShadowImageLayer>
LayerManagerComposite::CreateShadowImageLayer()
{
if (LayerManagerComposite::mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return nullptr;
}
return nsRefPtr<ImageLayerComposite>(new ImageLayerComposite(this)).forget();
}
already_AddRefed<ShadowColorLayer>
LayerManagerComposite::CreateShadowColorLayer()
{
if (LayerManagerComposite::mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return nullptr;
}
return nsRefPtr<ColorLayerComposite>(new ColorLayerComposite(this)).forget();
}
already_AddRefed<ShadowCanvasLayer>
LayerManagerComposite::CreateShadowCanvasLayer()
{
if (LayerManagerComposite::mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return nullptr;
}
return nsRefPtr<CanvasLayerComposite>(new CanvasLayerComposite(this)).forget();
}
already_AddRefed<ShadowRefLayer>
LayerManagerComposite::CreateShadowRefLayer()
{
if (LayerManagerComposite::mDestroyed) {
NS_WARNING("Call on destroyed layer manager");
return nullptr;
}
return nsRefPtr<RefLayerComposite>(new RefLayerComposite(this)).forget();
}
/* static */ bool
LayerManagerComposite::AddMaskEffect(Layer* aMaskLayer, EffectChain& aEffects, bool aIs3D)
{
if (!aMaskLayer) {
return false;
}
LayerComposite* maskLayerComposite = static_cast<LayerComposite*>(aMaskLayer->ImplData());
if (!maskLayerComposite->GetCompositableHost()) {
NS_WARNING("Mask layer with no compositable host");
return false;
}
gfx::Matrix4x4 transform;
ToMatrix4x4(aMaskLayer->GetEffectiveTransform(), transform);
return maskLayerComposite->GetCompositableHost()->AddMaskEffect(aEffects, transform, aIs3D);
}
TemporaryRef<DrawTarget>
LayerManagerComposite::CreateDrawTarget(const IntSize &aSize,
SurfaceFormat aFormat)
{
#ifdef XP_MACOSX
// We don't want to accelerate if the surface is too small which indicates
// that it's likely used for an icon/static image. We also don't want to
// accelerate anything that is above the maximum texture size of weakest gpu.
// Safari uses 5000 area as the minimum for acceleration, we decided 64^2 is more logical.
bool useAcceleration = aSize.width <= 4096 && aSize.height <= 4096 &&
aSize.width > 64 && aSize.height > 64 &&
gfxPlatformMac::GetPlatform()->UseAcceleratedCanvas();
if (useAcceleration) {
return Factory::CreateDrawTarget(BACKEND_COREGRAPHICS_ACCELERATED,
aSize, aFormat);
}
#endif
return LayerManager::CreateDrawTarget(aSize, aFormat);
}
void
LayerComposite::Destroy()
{
if (!mDestroyed) {
mDestroyed = true;
CleanupResources();
}
}
} /* layers */
} /* mozilla */