mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-04-06 06:32:49 +00:00

When rendering progressively, make sure that any previously visible area that is still visible is updated at the same time. This helps maintain visual coherency on pages that invalidate previously visible areas while scrolling, and when losing layers between updates. This supersedes the previous method of only doing progressive updates while scrolling.
1346 lines
45 KiB
C++
1346 lines
45 KiB
C++
/* -*- Mode: C++; tab-width: 2; 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/dom/TabChild.h"
|
|
#include "mozilla/layers/PLayerChild.h"
|
|
#include "mozilla/layers/PLayersChild.h"
|
|
#include "mozilla/layers/PLayersParent.h"
|
|
|
|
#include "gfxSharedImageSurface.h"
|
|
#include "gfxImageSurface.h"
|
|
#include "gfxUtils.h"
|
|
#include "gfxPlatform.h"
|
|
#include "nsXULAppAPI.h"
|
|
#include "RenderTrace.h"
|
|
#include "sampler.h"
|
|
|
|
#define PIXMAN_DONT_DEFINE_STDINT
|
|
#include "pixman.h"
|
|
|
|
#include "BasicTiledThebesLayer.h"
|
|
#include "BasicLayersImpl.h"
|
|
#include "BasicThebesLayer.h"
|
|
#include "BasicContainerLayer.h"
|
|
#include "CompositorChild.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "nsIWidget.h"
|
|
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
#include "AndroidBridge.h"
|
|
#endif
|
|
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::gfx;
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
/**
|
|
* Clips to the smallest device-pixel-aligned rectangle containing aRect
|
|
* in user space.
|
|
* Returns true if the clip is "perfect", i.e. we actually clipped exactly to
|
|
* aRect.
|
|
*/
|
|
static bool
|
|
ClipToContain(gfxContext* aContext, const nsIntRect& aRect)
|
|
{
|
|
gfxRect userRect(aRect.x, aRect.y, aRect.width, aRect.height);
|
|
gfxRect deviceRect = aContext->UserToDevice(userRect);
|
|
deviceRect.RoundOut();
|
|
|
|
gfxMatrix currentMatrix = aContext->CurrentMatrix();
|
|
aContext->IdentityMatrix();
|
|
aContext->NewPath();
|
|
aContext->Rectangle(deviceRect);
|
|
aContext->Clip();
|
|
aContext->SetMatrix(currentMatrix);
|
|
|
|
return aContext->DeviceToUser(deviceRect).IsEqualInterior(userRect);
|
|
}
|
|
|
|
already_AddRefed<gfxContext>
|
|
BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer,
|
|
const nsIntRegion& aRegion,
|
|
bool* aNeedsClipToVisibleRegion)
|
|
{
|
|
// If we need to call PushGroup, we should clip to the smallest possible
|
|
// area first to minimize the size of the temporary surface.
|
|
bool didCompleteClip = ClipToContain(aContext, aRegion.GetBounds());
|
|
|
|
nsRefPtr<gfxContext> result;
|
|
if (aLayer->CanUseOpaqueSurface() &&
|
|
((didCompleteClip && aRegion.GetNumRects() == 1) ||
|
|
!aContext->CurrentMatrix().HasNonIntegerTranslation())) {
|
|
// If the layer is opaque in its visible region we can push a CONTENT_COLOR
|
|
// group. We need to make sure that only pixels inside the layer's visible
|
|
// region are copied back to the destination. Remember if we've already
|
|
// clipped precisely to the visible region.
|
|
*aNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
|
|
result = PushGroupWithCachedSurface(aContext, gfxASurface::CONTENT_COLOR);
|
|
} else {
|
|
*aNeedsClipToVisibleRegion = false;
|
|
result = aContext;
|
|
if (aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
|
|
aContext->PushGroupAndCopyBackground(gfxASurface::CONTENT_COLOR_ALPHA);
|
|
} else {
|
|
aContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
|
|
}
|
|
}
|
|
return result.forget();
|
|
}
|
|
|
|
static nsIntRect
|
|
ToOutsideIntRect(const gfxRect &aRect)
|
|
{
|
|
gfxRect r = aRect;
|
|
r.RoundOut();
|
|
return nsIntRect(r.X(), r.Y(), r.Width(), r.Height());
|
|
}
|
|
|
|
static nsIntRect
|
|
ToInsideIntRect(const gfxRect& aRect)
|
|
{
|
|
gfxRect r = aRect;
|
|
r.RoundIn();
|
|
return nsIntRect(r.X(), r.Y(), r.Width(), r.Height());
|
|
}
|
|
|
|
// A context helper for BasicLayerManager::PaintLayer() that holds all the
|
|
// painting context together in a data structure so it can be easily passed
|
|
// around. It also uses ensures that the Transform and Opaque rect are restored
|
|
// to their former state on destruction.
|
|
|
|
class PaintContext {
|
|
public:
|
|
PaintContext(gfxContext* aTarget, Layer* aLayer,
|
|
LayerManager::DrawThebesLayerCallback aCallback,
|
|
void* aCallbackData, ReadbackProcessor* aReadback)
|
|
: mTarget(aTarget)
|
|
, mTargetMatrixSR(aTarget)
|
|
, mLayer(aLayer)
|
|
, mCallback(aCallback)
|
|
, mCallbackData(aCallbackData)
|
|
, mReadback(aReadback)
|
|
, mPushedOpaqueRect(false)
|
|
{}
|
|
|
|
~PaintContext()
|
|
{
|
|
// Matrix is restored by mTargetMatrixSR
|
|
if (mPushedOpaqueRect)
|
|
{
|
|
ClearOpaqueRect();
|
|
}
|
|
}
|
|
|
|
// Gets the effective transform and returns true if it is a 2D
|
|
// transform.
|
|
bool Setup2DTransform()
|
|
{
|
|
const gfx3DMatrix& effectiveTransform = mLayer->GetEffectiveTransform();
|
|
// Will return an identity matrix for 3d transforms.
|
|
return effectiveTransform.CanDraw2D(&mTransform);
|
|
}
|
|
|
|
// Applies the effective transform if it's 2D. If it's a 3D transform then
|
|
// it applies an identity.
|
|
void Apply2DTransform()
|
|
{
|
|
mTarget->SetMatrix(mTransform);
|
|
}
|
|
|
|
// Set the opaque rect to match the bounds of the visible region.
|
|
void AnnotateOpaqueRect()
|
|
{
|
|
const nsIntRegion& visibleRegion = mLayer->GetEffectiveVisibleRegion();
|
|
const nsIntRect& bounds = visibleRegion.GetBounds();
|
|
nsRefPtr<gfxASurface> currentSurface = mTarget->CurrentSurface();
|
|
|
|
if (mTarget->IsCairo()) {
|
|
const gfxRect& targetOpaqueRect = currentSurface->GetOpaqueRect();
|
|
|
|
// Try to annotate currentSurface with a region of pixels that have been
|
|
// (or will be) painted opaque, if no such region is currently set.
|
|
if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
|
|
(mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
|
|
!mTransform.HasNonAxisAlignedTransform()) {
|
|
currentSurface->SetOpaqueRect(
|
|
mTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)));
|
|
mPushedOpaqueRect = true;
|
|
}
|
|
} else {
|
|
DrawTarget *dt = mTarget->GetDrawTarget();
|
|
const IntRect& targetOpaqueRect = dt->GetOpaqueRect();
|
|
|
|
// Try to annotate currentSurface with a region of pixels that have been
|
|
// (or will be) painted opaque, if no such region is currently set.
|
|
if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
|
|
(mLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
|
|
!mTransform.HasNonAxisAlignedTransform()) {
|
|
|
|
gfx::Rect opaqueRect = dt->GetTransform().TransformBounds(
|
|
gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height));
|
|
opaqueRect.RoundIn();
|
|
IntRect intOpaqueRect;
|
|
if (opaqueRect.ToIntRect(&intOpaqueRect)) {
|
|
mTarget->GetDrawTarget()->SetOpaqueRect(intOpaqueRect);
|
|
mPushedOpaqueRect = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the Opaque rect. Although this doesn't really restore it to it's
|
|
// previous state it will happen on the exit path of the PaintLayer() so when
|
|
// painting is complete the opaque rect qill be clear.
|
|
void ClearOpaqueRect() {
|
|
if (mTarget->IsCairo()) {
|
|
nsRefPtr<gfxASurface> currentSurface = mTarget->CurrentSurface();
|
|
currentSurface->SetOpaqueRect(gfxRect());
|
|
} else {
|
|
mTarget->GetDrawTarget()->SetOpaqueRect(IntRect());
|
|
}
|
|
}
|
|
|
|
gfxContext* mTarget;
|
|
gfxContextMatrixAutoSaveRestore mTargetMatrixSR;
|
|
Layer* mLayer;
|
|
LayerManager::DrawThebesLayerCallback mCallback;
|
|
void* mCallbackData;
|
|
ReadbackProcessor* mReadback;
|
|
gfxMatrix mTransform;
|
|
bool mPushedOpaqueRect;
|
|
};
|
|
|
|
BasicLayerManager::BasicLayerManager(nsIWidget* aWidget) :
|
|
mPhase(PHASE_NONE),
|
|
mWidget(aWidget)
|
|
, mDoubleBuffering(BUFFER_NONE), mUsingDefaultTarget(false)
|
|
, mCachedSurfaceInUse(false)
|
|
, mTransactionIncomplete(false)
|
|
, mCompositorMightResample(false)
|
|
{
|
|
MOZ_COUNT_CTOR(BasicLayerManager);
|
|
NS_ASSERTION(aWidget, "Must provide a widget");
|
|
}
|
|
|
|
BasicLayerManager::BasicLayerManager() :
|
|
mPhase(PHASE_NONE),
|
|
mWidget(nullptr)
|
|
, mDoubleBuffering(BUFFER_NONE), mUsingDefaultTarget(false)
|
|
, mCachedSurfaceInUse(false)
|
|
, mTransactionIncomplete(false)
|
|
{
|
|
MOZ_COUNT_CTOR(BasicLayerManager);
|
|
}
|
|
|
|
BasicLayerManager::~BasicLayerManager()
|
|
{
|
|
NS_ASSERTION(!InTransaction(), "Died during transaction?");
|
|
|
|
ClearCachedResources();
|
|
|
|
mRoot = nullptr;
|
|
|
|
MOZ_COUNT_DTOR(BasicLayerManager);
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::SetDefaultTarget(gfxContext* aContext)
|
|
{
|
|
NS_ASSERTION(!InTransaction(),
|
|
"Must set default target outside transaction");
|
|
mDefaultTarget = aContext;
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation)
|
|
{
|
|
mDoubleBuffering = aDoubleBuffering;
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::BeginTransaction()
|
|
{
|
|
mInTransaction = true;
|
|
mUsingDefaultTarget = true;
|
|
BeginTransactionWithTarget(mDefaultTarget);
|
|
}
|
|
|
|
already_AddRefed<gfxContext>
|
|
BasicLayerManager::PushGroupWithCachedSurface(gfxContext *aTarget,
|
|
gfxASurface::gfxContentType aContent)
|
|
{
|
|
nsRefPtr<gfxContext> ctx;
|
|
// We can't cache Azure DrawTargets at this point.
|
|
if (!mCachedSurfaceInUse && aTarget->IsCairo()) {
|
|
gfxContextMatrixAutoSaveRestore saveMatrix(aTarget);
|
|
aTarget->IdentityMatrix();
|
|
|
|
nsRefPtr<gfxASurface> currentSurf = aTarget->CurrentSurface();
|
|
gfxRect clip = aTarget->GetClipExtents();
|
|
clip.RoundOut();
|
|
|
|
ctx = mCachedSurface.Get(aContent, clip, currentSurf);
|
|
|
|
if (ctx) {
|
|
mCachedSurfaceInUse = true;
|
|
/* Align our buffer for the original surface */
|
|
ctx->SetMatrix(saveMatrix.Matrix());
|
|
return ctx.forget();
|
|
}
|
|
}
|
|
|
|
ctx = aTarget;
|
|
ctx->PushGroup(aContent);
|
|
return ctx.forget();
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::PopGroupToSourceWithCachedSurface(gfxContext *aTarget, gfxContext *aPushed)
|
|
{
|
|
if (!aTarget)
|
|
return;
|
|
nsRefPtr<gfxASurface> current = aPushed->CurrentSurface();
|
|
if (aTarget->IsCairo() && mCachedSurface.IsSurface(current)) {
|
|
gfxContextMatrixAutoSaveRestore saveMatrix(aTarget);
|
|
aTarget->IdentityMatrix();
|
|
aTarget->SetSource(current);
|
|
mCachedSurfaceInUse = false;
|
|
} else {
|
|
aTarget->PopGroupToSource();
|
|
}
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
|
|
{
|
|
mInTransaction = true;
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
MOZ_LAYERS_LOG(("[----- BeginTransaction"));
|
|
Log();
|
|
#endif
|
|
|
|
NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
|
|
mPhase = PHASE_CONSTRUCTION;
|
|
mTarget = aTarget;
|
|
}
|
|
|
|
static void
|
|
TransformIntRect(nsIntRect& aRect, const gfxMatrix& aMatrix,
|
|
nsIntRect (*aRoundMethod)(const gfxRect&))
|
|
{
|
|
gfxRect gr = gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
|
|
gr = aMatrix.TransformBounds(gr);
|
|
aRect = (*aRoundMethod)(gr);
|
|
}
|
|
|
|
/**
|
|
* This function assumes that GetEffectiveTransform transforms
|
|
* all layers to the same coordinate system (the "root coordinate system").
|
|
* It can't be used as is by accelerated layers because of intermediate surfaces.
|
|
* This must set the hidden flag to true or false on *all* layers in the subtree.
|
|
* It also sets the operator for all layers to "OVER", and call
|
|
* SetDrawAtomically(false).
|
|
* It clears mClipToVisibleRegion on all layers.
|
|
* @param aClipRect the cliprect, in the root coordinate system. We assume
|
|
* that any layer drawing is clipped to this rect. It is therefore not
|
|
* allowed to add to the opaque region outside that rect.
|
|
* @param aDirtyRect the dirty rect that will be painted, in the root
|
|
* coordinate system. Layers outside this rect should be hidden.
|
|
* @param aOpaqueRegion the opaque region covering aLayer, in the
|
|
* root coordinate system.
|
|
*/
|
|
enum {
|
|
ALLOW_OPAQUE = 0x01,
|
|
};
|
|
static void
|
|
MarkLayersHidden(Layer* aLayer, const nsIntRect& aClipRect,
|
|
const nsIntRect& aDirtyRect,
|
|
nsIntRegion& aOpaqueRegion,
|
|
uint32_t aFlags)
|
|
{
|
|
nsIntRect newClipRect(aClipRect);
|
|
uint32_t newFlags = aFlags;
|
|
|
|
// Allow aLayer or aLayer's descendants to cover underlying layers
|
|
// only if it's opaque.
|
|
if (aLayer->GetOpacity() != 1.0f) {
|
|
newFlags &= ~ALLOW_OPAQUE;
|
|
}
|
|
|
|
{
|
|
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
|
|
if (clipRect) {
|
|
nsIntRect cr = *clipRect;
|
|
// clipRect is in the container's coordinate system. Get it into the
|
|
// global coordinate system.
|
|
if (aLayer->GetParent()) {
|
|
gfxMatrix tr;
|
|
if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
|
|
// Clip rect is applied after aLayer's transform, i.e., in the coordinate
|
|
// system of aLayer's parent.
|
|
TransformIntRect(cr, tr, ToInsideIntRect);
|
|
} else {
|
|
cr.SetRect(0, 0, 0, 0);
|
|
}
|
|
}
|
|
newClipRect.IntersectRect(newClipRect, cr);
|
|
}
|
|
}
|
|
|
|
BasicImplData* data = ToData(aLayer);
|
|
data->SetOperator(gfxContext::OPERATOR_OVER);
|
|
data->SetClipToVisibleRegion(false);
|
|
data->SetDrawAtomically(false);
|
|
|
|
if (!aLayer->AsContainerLayer()) {
|
|
gfxMatrix transform;
|
|
if (!aLayer->GetEffectiveTransform().CanDraw2D(&transform)) {
|
|
data->SetHidden(false);
|
|
return;
|
|
}
|
|
|
|
nsIntRegion region = aLayer->GetEffectiveVisibleRegion();
|
|
nsIntRect r = region.GetBounds();
|
|
TransformIntRect(r, transform, ToOutsideIntRect);
|
|
r.IntersectRect(r, aDirtyRect);
|
|
data->SetHidden(aOpaqueRegion.Contains(r));
|
|
|
|
// Allow aLayer to cover underlying layers only if aLayer's
|
|
// content is opaque
|
|
if ((aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
|
|
(newFlags & ALLOW_OPAQUE)) {
|
|
nsIntRegionRectIterator it(region);
|
|
while (const nsIntRect* sr = it.Next()) {
|
|
r = *sr;
|
|
TransformIntRect(r, transform, ToInsideIntRect);
|
|
|
|
r.IntersectRect(r, newClipRect);
|
|
aOpaqueRegion.Or(aOpaqueRegion, r);
|
|
}
|
|
}
|
|
} else {
|
|
Layer* child = aLayer->GetLastChild();
|
|
bool allHidden = true;
|
|
for (; child; child = child->GetPrevSibling()) {
|
|
MarkLayersHidden(child, newClipRect, aDirtyRect, aOpaqueRegion, newFlags);
|
|
if (!ToData(child)->IsHidden()) {
|
|
allHidden = false;
|
|
}
|
|
}
|
|
data->SetHidden(allHidden);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function assumes that GetEffectiveTransform transforms
|
|
* all layers to the same coordinate system (the "root coordinate system").
|
|
* MarkLayersHidden must be called before calling this.
|
|
* @param aVisibleRect the rectangle of aLayer that is visible (i.e. not
|
|
* clipped and in the dirty rect), in the root coordinate system.
|
|
*/
|
|
static void
|
|
ApplyDoubleBuffering(Layer* aLayer, const nsIntRect& aVisibleRect)
|
|
{
|
|
BasicImplData* data = ToData(aLayer);
|
|
if (data->IsHidden())
|
|
return;
|
|
|
|
nsIntRect newVisibleRect(aVisibleRect);
|
|
|
|
{
|
|
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
|
|
if (clipRect) {
|
|
nsIntRect cr = *clipRect;
|
|
// clipRect is in the container's coordinate system. Get it into the
|
|
// global coordinate system.
|
|
if (aLayer->GetParent()) {
|
|
gfxMatrix tr;
|
|
if (aLayer->GetParent()->GetEffectiveTransform().CanDraw2D(&tr)) {
|
|
NS_ASSERTION(!tr.HasNonIntegerTranslation(),
|
|
"Parent can only have an integer translation");
|
|
cr += nsIntPoint(int32_t(tr.x0), int32_t(tr.y0));
|
|
} else {
|
|
NS_ERROR("Parent can only have an integer translation");
|
|
}
|
|
}
|
|
newVisibleRect.IntersectRect(newVisibleRect, cr);
|
|
}
|
|
}
|
|
|
|
BasicContainerLayer* container =
|
|
static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
|
|
// Layers that act as their own backbuffers should be drawn to the destination
|
|
// using OPERATOR_SOURCE to ensure that alpha values in a transparent window
|
|
// are cleared. This can also be faster than OPERATOR_OVER.
|
|
if (!container) {
|
|
data->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
data->SetDrawAtomically(true);
|
|
} else {
|
|
if (container->UseIntermediateSurface() ||
|
|
!container->ChildrenPartitionVisibleRegion(newVisibleRect)) {
|
|
// We need to double-buffer this container.
|
|
data->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
container->ForceIntermediateSurface();
|
|
} else {
|
|
// Tell the children to clip to their visible regions so our assumption
|
|
// that they don't paint outside their visible regions is valid!
|
|
for (Layer* child = aLayer->GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
ToData(child)->SetClipToVisibleRegion(true);
|
|
ApplyDoubleBuffering(child, newVisibleRect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
|
|
void* aCallbackData,
|
|
EndTransactionFlags aFlags)
|
|
{
|
|
mInTransaction = false;
|
|
|
|
EndTransactionInternal(aCallback, aCallbackData, aFlags);
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::AbortTransaction()
|
|
{
|
|
NS_ASSERTION(InConstruction(), "Should be in construction phase");
|
|
mPhase = PHASE_NONE;
|
|
mUsingDefaultTarget = false;
|
|
mInTransaction = false;
|
|
}
|
|
|
|
bool
|
|
BasicLayerManager::EndTransactionInternal(DrawThebesLayerCallback aCallback,
|
|
void* aCallbackData,
|
|
EndTransactionFlags aFlags)
|
|
{
|
|
SAMPLE_LABEL("BasicLayerManager", "EndTranscationInternal");
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
MOZ_LAYERS_LOG((" ----- (beginning paint)"));
|
|
Log();
|
|
#endif
|
|
|
|
NS_ASSERTION(InConstruction(), "Should be in construction phase");
|
|
mPhase = PHASE_DRAWING;
|
|
|
|
Layer* aLayer = GetRoot();
|
|
RenderTraceLayers(aLayer, "FF00");
|
|
|
|
mTransactionIncomplete = false;
|
|
|
|
if (aFlags & END_NO_COMPOSITE) {
|
|
// TODO: We should really just set mTarget to null and make sure we can handle that further down the call chain
|
|
nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(1, 1), gfxASurface::CONTENT_COLOR);
|
|
mTarget = new gfxContext(surf);
|
|
}
|
|
|
|
if (mTarget && mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
|
|
nsIntRect clipRect;
|
|
if (HasShadowManager()) {
|
|
// If this has a shadow manager, the clip extents of mTarget are meaningless.
|
|
// So instead just use the root layer's visible region bounds.
|
|
const nsIntRect& bounds = mRoot->GetVisibleRegion().GetBounds();
|
|
gfxRect deviceRect =
|
|
mTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height));
|
|
clipRect = ToOutsideIntRect(deviceRect);
|
|
} else {
|
|
gfxContextMatrixAutoSaveRestore save(mTarget);
|
|
mTarget->SetMatrix(gfxMatrix());
|
|
clipRect = ToOutsideIntRect(mTarget->GetClipExtents());
|
|
}
|
|
|
|
if (aFlags & END_NO_COMPOSITE) {
|
|
// Apply pending tree updates before recomputing effective
|
|
// properties.
|
|
aLayer->ApplyPendingUpdatesToSubtree();
|
|
}
|
|
|
|
// Need to do this before we call ApplyDoubleBuffering,
|
|
// which depends on correct effective transforms
|
|
mSnapEffectiveTransforms =
|
|
!(mTarget->GetFlags() & gfxContext::FLAG_DISABLE_SNAPPING);
|
|
mRoot->ComputeEffectiveTransforms(gfx3DMatrix::From2D(mTarget->CurrentMatrix()));
|
|
|
|
if (IsRetained()) {
|
|
nsIntRegion region;
|
|
MarkLayersHidden(mRoot, clipRect, clipRect, region, ALLOW_OPAQUE);
|
|
if (mUsingDefaultTarget && mDoubleBuffering != BUFFER_NONE) {
|
|
ApplyDoubleBuffering(mRoot, clipRect);
|
|
}
|
|
}
|
|
|
|
if (aFlags & END_NO_COMPOSITE) {
|
|
if (IsRetained()) {
|
|
// Clip the destination out so that we don't draw to it, and
|
|
// only end up validating ThebesLayers.
|
|
mTarget->Clip(gfxRect(0, 0, 0, 0));
|
|
PaintLayer(mTarget, mRoot, aCallback, aCallbackData, nullptr);
|
|
}
|
|
// If we're not retained, then don't composite means do nothing at all.
|
|
} else {
|
|
PaintLayer(mTarget, mRoot, aCallback, aCallbackData, nullptr);
|
|
if (mWidget) {
|
|
FlashWidgetUpdateArea(mTarget);
|
|
}
|
|
LayerManager::PostPresent();
|
|
}
|
|
|
|
if (!mTransactionIncomplete) {
|
|
// Clear out target if we have a complete transaction.
|
|
mTarget = nullptr;
|
|
}
|
|
}
|
|
|
|
#ifdef MOZ_LAYERS_HAVE_LOG
|
|
Log();
|
|
MOZ_LAYERS_LOG(("]----- EndTransaction"));
|
|
#endif
|
|
|
|
// Go back to the construction phase if the transaction isn't complete.
|
|
// Layout will update the layer tree and call EndTransaction().
|
|
mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
|
|
|
|
if (!mTransactionIncomplete) {
|
|
// This is still valid if the transaction was incomplete.
|
|
mUsingDefaultTarget = false;
|
|
}
|
|
|
|
NS_ASSERTION(!aCallback || !mTransactionIncomplete,
|
|
"If callback is not null, transaction must be complete");
|
|
|
|
// XXX - We should probably assert here that for an incomplete transaction
|
|
// out target is the default target.
|
|
|
|
return !mTransactionIncomplete;
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::FlashWidgetUpdateArea(gfxContext *aContext)
|
|
{
|
|
static bool sWidgetFlashingEnabled;
|
|
static bool sWidgetFlashingPrefCached = false;
|
|
|
|
if (!sWidgetFlashingPrefCached) {
|
|
sWidgetFlashingPrefCached = true;
|
|
mozilla::Preferences::AddBoolVarCache(&sWidgetFlashingEnabled,
|
|
"nglayout.debug.widget_update_flashing");
|
|
}
|
|
|
|
if (sWidgetFlashingEnabled) {
|
|
float r = float(rand()) / RAND_MAX;
|
|
float g = float(rand()) / RAND_MAX;
|
|
float b = float(rand()) / RAND_MAX;
|
|
aContext->SetColor(gfxRGBA(r, g, b, 0.2));
|
|
aContext->Paint();
|
|
}
|
|
}
|
|
|
|
bool
|
|
BasicLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
|
|
{
|
|
mInTransaction = false;
|
|
|
|
if (!mRoot) {
|
|
return false;
|
|
}
|
|
|
|
return EndTransactionInternal(nullptr, nullptr, aFlags);
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::SetRoot(Layer* aLayer)
|
|
{
|
|
NS_ASSERTION(aLayer, "Root can't be null");
|
|
NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
|
|
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
|
|
mRoot = aLayer;
|
|
}
|
|
|
|
static pixman_transform
|
|
Matrix3DToPixman(const gfx3DMatrix& aMatrix)
|
|
{
|
|
pixman_f_transform transform;
|
|
|
|
transform.m[0][0] = aMatrix._11;
|
|
transform.m[0][1] = aMatrix._21;
|
|
transform.m[0][2] = aMatrix._41;
|
|
transform.m[1][0] = aMatrix._12;
|
|
transform.m[1][1] = aMatrix._22;
|
|
transform.m[1][2] = aMatrix._42;
|
|
transform.m[2][0] = aMatrix._14;
|
|
transform.m[2][1] = aMatrix._24;
|
|
transform.m[2][2] = aMatrix._44;
|
|
|
|
pixman_transform result;
|
|
pixman_transform_from_pixman_f_transform(&result, &transform);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
PixmanTransform(const gfxImageSurface *aDest,
|
|
const gfxImageSurface *aSrc,
|
|
const gfx3DMatrix& aTransform,
|
|
gfxPoint aDestOffset)
|
|
{
|
|
gfxIntSize destSize = aDest->GetSize();
|
|
pixman_image_t* dest = pixman_image_create_bits(aDest->Format() == gfxASurface::ImageFormatARGB32 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
|
|
destSize.width,
|
|
destSize.height,
|
|
(uint32_t*)aDest->Data(),
|
|
aDest->Stride());
|
|
|
|
gfxIntSize srcSize = aSrc->GetSize();
|
|
pixman_image_t* src = pixman_image_create_bits(aSrc->Format() == gfxASurface::ImageFormatARGB32 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
|
|
srcSize.width,
|
|
srcSize.height,
|
|
(uint32_t*)aSrc->Data(),
|
|
aSrc->Stride());
|
|
|
|
NS_ABORT_IF_FALSE(src && dest, "Failed to create pixman images?");
|
|
|
|
pixman_transform pixTransform = Matrix3DToPixman(aTransform);
|
|
pixman_transform pixTransformInverted;
|
|
|
|
// If the transform is singular then nothing would be drawn anyway, return here
|
|
if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) {
|
|
return;
|
|
}
|
|
pixman_image_set_transform(src, &pixTransformInverted);
|
|
|
|
pixman_image_composite32(PIXMAN_OP_SRC,
|
|
src,
|
|
nullptr,
|
|
dest,
|
|
aDestOffset.x,
|
|
aDestOffset.y,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
destSize.width,
|
|
destSize.height);
|
|
|
|
pixman_image_unref(dest);
|
|
pixman_image_unref(src);
|
|
}
|
|
|
|
/**
|
|
* Transform a surface using a gfx3DMatrix and blit to the destination if
|
|
* it is efficient to do so.
|
|
*
|
|
* @param aSource Source surface.
|
|
* @param aDest Desintation context.
|
|
* @param aBounds Area represented by aSource.
|
|
* @param aTransform Transformation matrix.
|
|
* @param aDestRect Output: rectangle in which to draw returned surface on aDest
|
|
* (same size as aDest). Only filled in if this returns
|
|
* a surface.
|
|
* @param aDontBlit Never draw to aDest if this is true.
|
|
* @return Transformed surface, or nullptr if it has been drawn to aDest.
|
|
*/
|
|
static already_AddRefed<gfxASurface>
|
|
Transform3D(gfxASurface* aSource, gfxContext* aDest,
|
|
const gfxRect& aBounds, const gfx3DMatrix& aTransform,
|
|
gfxRect& aDestRect, bool aDontBlit)
|
|
{
|
|
nsRefPtr<gfxImageSurface> sourceImage = aSource->GetAsImageSurface();
|
|
if (!sourceImage) {
|
|
sourceImage = new gfxImageSurface(gfxIntSize(aBounds.width, aBounds.height), gfxPlatform::GetPlatform()->OptimalFormatForContent(aSource->GetContentType()));
|
|
nsRefPtr<gfxContext> ctx = new gfxContext(sourceImage);
|
|
|
|
aSource->SetDeviceOffset(gfxPoint(0, 0));
|
|
ctx->SetSource(aSource);
|
|
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
ctx->Paint();
|
|
}
|
|
|
|
// Find the transformed rectangle of our layer.
|
|
gfxRect offsetRect = aTransform.TransformBounds(aBounds);
|
|
|
|
// Intersect the transformed layer with the destination rectangle.
|
|
// This is in device space since we have an identity transform set on aTarget.
|
|
aDestRect = aDest->GetClipExtents();
|
|
aDestRect.IntersectRect(aDestRect, offsetRect);
|
|
aDestRect.RoundOut();
|
|
|
|
// Create a surface the size of the transformed object.
|
|
nsRefPtr<gfxASurface> dest = aDest->CurrentSurface();
|
|
nsRefPtr<gfxImageSurface> destImage;
|
|
gfxPoint offset;
|
|
bool blitComplete;
|
|
if (!destImage || aDontBlit || !aDest->ClipContainsRect(aDestRect)) {
|
|
destImage = new gfxImageSurface(gfxIntSize(aDestRect.width, aDestRect.height),
|
|
gfxASurface::ImageFormatARGB32);
|
|
offset = aDestRect.TopLeft();
|
|
blitComplete = false;
|
|
} else {
|
|
offset = -dest->GetDeviceOffset();
|
|
blitComplete = true;
|
|
}
|
|
|
|
// Include a translation to the correct origin.
|
|
gfx3DMatrix translation = gfx3DMatrix::Translation(aBounds.x, aBounds.y, 0);
|
|
|
|
// Transform the content and offset it such that the content begins at the origin.
|
|
PixmanTransform(destImage, sourceImage, translation * aTransform, offset);
|
|
|
|
if (blitComplete) {
|
|
return nullptr;
|
|
}
|
|
|
|
// If we haven't actually drawn to aDest then return our temporary image so
|
|
// that the caller can do this.
|
|
return destImage.forget();
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::PaintSelfOrChildren(PaintContext& aPaintContext,
|
|
gfxContext* aGroupTarget)
|
|
{
|
|
BasicImplData* data = ToData(aPaintContext.mLayer);
|
|
|
|
if (aPaintContext.mLayer->GetFirstChild() &&
|
|
aPaintContext.mLayer->GetMaskLayer() &&
|
|
HasShadowManager()) {
|
|
// 'paint' the mask so that it gets sent to the shadow layer tree
|
|
static_cast<BasicImplData*>(aPaintContext.mLayer->GetMaskLayer()->ImplData())
|
|
->Paint(nullptr, nullptr);
|
|
}
|
|
|
|
/* Only paint ourself, or our children - This optimization relies on this! */
|
|
Layer* child = aPaintContext.mLayer->GetFirstChild();
|
|
if (!child) {
|
|
if (aPaintContext.mLayer->AsThebesLayer()) {
|
|
data->PaintThebes(aGroupTarget, aPaintContext.mLayer->GetMaskLayer(),
|
|
aPaintContext.mCallback, aPaintContext.mCallbackData,
|
|
aPaintContext.mReadback);
|
|
} else {
|
|
data->Paint(aGroupTarget, aPaintContext.mLayer->GetMaskLayer());
|
|
}
|
|
} else {
|
|
ReadbackProcessor readback;
|
|
ContainerLayer* container =
|
|
static_cast<ContainerLayer*>(aPaintContext.mLayer);
|
|
if (IsRetained()) {
|
|
readback.BuildUpdates(container);
|
|
}
|
|
nsAutoTArray<Layer*, 12> children;
|
|
container->SortChildrenBy3DZOrder(children);
|
|
for (uint32_t i = 0; i < children.Length(); i++) {
|
|
PaintLayer(aGroupTarget, children.ElementAt(i), aPaintContext.mCallback,
|
|
aPaintContext.mCallbackData, &readback);
|
|
if (mTransactionIncomplete)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::FlushGroup(PaintContext& aPaintContext, bool aNeedsClipToVisibleRegion)
|
|
{
|
|
// If we're doing our own double-buffering, we need to avoid drawing
|
|
// the results of an incomplete transaction to the destination surface ---
|
|
// that could cause flicker. Double-buffering is implemented using a
|
|
// temporary surface for one or more container layers, so we need to stop
|
|
// those temporary surfaces from being composited to aTarget.
|
|
// ApplyDoubleBuffering guarantees that this container layer can't
|
|
// intersect any other leaf layers, so if the transaction is not yet marked
|
|
// incomplete, the contents of this container layer are the final contents
|
|
// for the window.
|
|
if (!mTransactionIncomplete) {
|
|
if (aNeedsClipToVisibleRegion) {
|
|
gfxUtils::ClipToRegion(aPaintContext.mTarget,
|
|
aPaintContext.mLayer->GetEffectiveVisibleRegion());
|
|
}
|
|
BasicContainerLayer* container = static_cast<BasicContainerLayer*>(aPaintContext.mLayer);
|
|
AutoSetOperator setOperator(aPaintContext.mTarget, container->GetOperator());
|
|
PaintWithMask(aPaintContext.mTarget, aPaintContext.mLayer->GetEffectiveOpacity(),
|
|
HasShadowManager() ? nullptr : aPaintContext.mLayer->GetMaskLayer());
|
|
}
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::PaintLayer(gfxContext* aTarget,
|
|
Layer* aLayer,
|
|
DrawThebesLayerCallback aCallback,
|
|
void* aCallbackData,
|
|
ReadbackProcessor* aReadback)
|
|
{
|
|
PaintContext paintContext(aTarget, aLayer, aCallback, aCallbackData, aReadback);
|
|
|
|
RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070");
|
|
|
|
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
|
|
// aLayer might not be a container layer, but if so we take care not to use
|
|
// the container variable
|
|
BasicContainerLayer* container = static_cast<BasicContainerLayer*>(aLayer);
|
|
bool needsGroup = aLayer->GetFirstChild() &&
|
|
container->UseIntermediateSurface();
|
|
BasicImplData* data = ToData(aLayer);
|
|
bool needsClipToVisibleRegion =
|
|
data->GetClipToVisibleRegion() && !aLayer->AsThebesLayer();
|
|
NS_ASSERTION(needsGroup || !aLayer->GetFirstChild() ||
|
|
container->GetOperator() == gfxContext::OPERATOR_OVER,
|
|
"non-OVER operator should have forced UseIntermediateSurface");
|
|
NS_ASSERTION(!aLayer->GetFirstChild() || !aLayer->GetMaskLayer() ||
|
|
container->UseIntermediateSurface(),
|
|
"ContainerLayer with mask layer should force UseIntermediateSurface");
|
|
|
|
gfxContextAutoSaveRestore contextSR;
|
|
gfxMatrix transform;
|
|
// Will return an identity matrix for 3d transforms, and is handled separately below.
|
|
bool is2D = paintContext.Setup2DTransform();
|
|
NS_ABORT_IF_FALSE(is2D || needsGroup || !aLayer->GetFirstChild(), "Must PushGroup for 3d transforms!");
|
|
|
|
bool needsSaveRestore =
|
|
needsGroup || clipRect || needsClipToVisibleRegion || !is2D;
|
|
if (needsSaveRestore) {
|
|
contextSR.SetContext(aTarget);
|
|
|
|
if (clipRect) {
|
|
aTarget->NewPath();
|
|
aTarget->Rectangle(gfxRect(clipRect->x, clipRect->y, clipRect->width, clipRect->height), true);
|
|
aTarget->Clip();
|
|
}
|
|
}
|
|
|
|
paintContext.Apply2DTransform();
|
|
|
|
const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion();
|
|
// If needsGroup is true, we'll clip to the visible region after we've popped the group
|
|
if (needsClipToVisibleRegion && !needsGroup) {
|
|
gfxUtils::ClipToRegion(aTarget, visibleRegion);
|
|
// Don't need to clip to visible region again
|
|
needsClipToVisibleRegion = false;
|
|
}
|
|
|
|
if (is2D) {
|
|
paintContext.AnnotateOpaqueRect();
|
|
}
|
|
|
|
bool clipIsEmpty = !aTarget || aTarget->GetClipExtents().IsEmpty();
|
|
if (clipIsEmpty) {
|
|
PaintSelfOrChildren(paintContext, aTarget);
|
|
return;
|
|
}
|
|
|
|
if (is2D) {
|
|
if (needsGroup) {
|
|
nsRefPtr<gfxContext> groupTarget = PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion(),
|
|
&needsClipToVisibleRegion);
|
|
PaintSelfOrChildren(paintContext, groupTarget);
|
|
PopGroupToSourceWithCachedSurface(aTarget, groupTarget);
|
|
FlushGroup(paintContext, needsClipToVisibleRegion);
|
|
} else {
|
|
PaintSelfOrChildren(paintContext, aTarget);
|
|
}
|
|
} else {
|
|
const nsIntRect& bounds = visibleRegion.GetBounds();
|
|
nsRefPtr<gfxASurface> untransformedSurface =
|
|
gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height),
|
|
gfxASurface::CONTENT_COLOR_ALPHA);
|
|
if (!untransformedSurface) {
|
|
return;
|
|
}
|
|
untransformedSurface->SetDeviceOffset(gfxPoint(-bounds.x, -bounds.y));
|
|
nsRefPtr<gfxContext> groupTarget = new gfxContext(untransformedSurface);
|
|
|
|
PaintSelfOrChildren(paintContext, groupTarget);
|
|
|
|
// Temporary fast fix for bug 725886
|
|
// Revert these changes when 725886 is ready
|
|
NS_ABORT_IF_FALSE(untransformedSurface,
|
|
"We should always allocate an untransformed surface with 3d transforms!");
|
|
gfxRect destRect;
|
|
bool dontBlit = needsClipToVisibleRegion || mTransactionIncomplete ||
|
|
aLayer->GetEffectiveOpacity() != 1.0f;
|
|
#ifdef DEBUG
|
|
if (aLayer->GetDebugColorIndex() != 0) {
|
|
gfxRGBA color((aLayer->GetDebugColorIndex() & 1) ? 1.0 : 0.0,
|
|
(aLayer->GetDebugColorIndex() & 2) ? 1.0 : 0.0,
|
|
(aLayer->GetDebugColorIndex() & 4) ? 1.0 : 0.0,
|
|
1.0);
|
|
|
|
nsRefPtr<gfxContext> temp = new gfxContext(untransformedSurface);
|
|
temp->SetColor(color);
|
|
temp->Paint();
|
|
}
|
|
#endif
|
|
const gfx3DMatrix& effectiveTransform = aLayer->GetEffectiveTransform();
|
|
nsRefPtr<gfxASurface> result =
|
|
Transform3D(untransformedSurface, aTarget, bounds,
|
|
effectiveTransform, destRect, dontBlit);
|
|
|
|
if (result) {
|
|
aTarget->SetSource(result, destRect.TopLeft());
|
|
// Azure doesn't support EXTEND_NONE, so to avoid extending the edges
|
|
// of the source surface out to the current clip region, clip to
|
|
// the rectangle of the result surface now.
|
|
aTarget->NewPath();
|
|
aTarget->Rectangle(destRect, true);
|
|
aTarget->Clip();
|
|
FlushGroup(paintContext, needsClipToVisibleRegion);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
BasicLayerManager::ClearCachedResources()
|
|
{
|
|
if (mRoot) {
|
|
ClearLayer(mRoot);
|
|
}
|
|
|
|
mCachedSurface.Expire();
|
|
}
|
|
void
|
|
BasicLayerManager::ClearLayer(Layer* aLayer)
|
|
{
|
|
ToData(aLayer)->ClearCachedResources();
|
|
for (Layer* child = aLayer->GetFirstChild(); child;
|
|
child = child->GetNextSibling()) {
|
|
ClearLayer(child);
|
|
}
|
|
}
|
|
|
|
already_AddRefed<ReadbackLayer>
|
|
BasicLayerManager::CreateReadbackLayer()
|
|
{
|
|
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
|
|
nsRefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
|
|
return layer.forget();
|
|
}
|
|
|
|
BasicShadowLayerManager::BasicShadowLayerManager(nsIWidget* aWidget) :
|
|
BasicLayerManager(aWidget), mTargetRotation(ROTATION_0),
|
|
mRepeatTransaction(false), mIsRepeatTransaction(false)
|
|
{
|
|
MOZ_COUNT_CTOR(BasicShadowLayerManager);
|
|
}
|
|
|
|
BasicShadowLayerManager::~BasicShadowLayerManager()
|
|
{
|
|
MOZ_COUNT_DTOR(BasicShadowLayerManager);
|
|
}
|
|
|
|
int32_t
|
|
BasicShadowLayerManager::GetMaxTextureSize() const
|
|
{
|
|
if (HasShadowManager()) {
|
|
return ShadowLayerForwarder::GetMaxTextureSize();
|
|
}
|
|
|
|
return INT32_MAX;
|
|
}
|
|
|
|
void
|
|
BasicShadowLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation)
|
|
{
|
|
BasicLayerManager::SetDefaultTargetConfiguration(aDoubleBuffering, aRotation);
|
|
mTargetRotation = aRotation;
|
|
if (mWidget) {
|
|
mTargetBounds = mWidget->GetNaturalBounds();
|
|
}
|
|
}
|
|
|
|
void
|
|
BasicShadowLayerManager::SetRoot(Layer* aLayer)
|
|
{
|
|
if (mRoot != aLayer) {
|
|
if (HasShadowManager()) {
|
|
// Have to hold the old root and its children in order to
|
|
// maintain the same view of the layer tree in this process as
|
|
// the parent sees. Otherwise layers can be destroyed
|
|
// mid-transaction and bad things can happen (v. bug 612573)
|
|
if (mRoot) {
|
|
Hold(mRoot);
|
|
}
|
|
ShadowLayerForwarder::SetRoot(Hold(aLayer));
|
|
}
|
|
BasicLayerManager::SetRoot(aLayer);
|
|
}
|
|
}
|
|
|
|
void
|
|
BasicShadowLayerManager::Mutated(Layer* aLayer)
|
|
{
|
|
BasicLayerManager::Mutated(aLayer);
|
|
|
|
NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase");
|
|
if (HasShadowManager() && ShouldShadow(aLayer)) {
|
|
ShadowLayerForwarder::Mutated(Hold(aLayer));
|
|
}
|
|
}
|
|
|
|
void
|
|
BasicShadowLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
|
|
{
|
|
NS_ABORT_IF_FALSE(mKeepAlive.IsEmpty(), "uncommitted txn?");
|
|
nsRefPtr<gfxContext> targetContext = aTarget;
|
|
|
|
// If the last transaction was incomplete (a failed DoEmptyTransaction),
|
|
// don't signal a new transaction to ShadowLayerForwarder. Carry on adding
|
|
// to the previous transaction.
|
|
if (HasShadowManager()) {
|
|
ShadowLayerForwarder::BeginTransaction(mTargetBounds, mTargetRotation);
|
|
|
|
// If we're drawing on behalf of a context with async pan/zoom
|
|
// enabled, then the entire buffer of thebes layers might be
|
|
// composited (including resampling) asynchronously before we get
|
|
// a chance to repaint, so we have to ensure that it's all valid
|
|
// and not rotated.
|
|
if (mWidget) {
|
|
if (TabChild* window = mWidget->GetOwningTabChild()) {
|
|
mCompositorMightResample = window->IsAsyncPanZoomEnabled();
|
|
}
|
|
}
|
|
|
|
// If we have a non-default target, we need to let our shadow manager draw
|
|
// to it. This will happen at the end of the transaction.
|
|
if (aTarget && (aTarget != mDefaultTarget) &&
|
|
XRE_GetProcessType() == GeckoProcessType_Default) {
|
|
mShadowTarget = aTarget;
|
|
// Create a temporary target for ourselves, so that
|
|
// mShadowTarget is only drawn to for the window snapshot.
|
|
nsRefPtr<gfxASurface> dummy =
|
|
gfxPlatform::GetPlatform()->CreateOffscreenSurface(
|
|
gfxIntSize(1, 1),
|
|
aTarget->OriginalSurface()->GetContentType());
|
|
mDummyTarget = new gfxContext(dummy);
|
|
aTarget = mDummyTarget;
|
|
}
|
|
}
|
|
BasicLayerManager::BeginTransactionWithTarget(aTarget);
|
|
}
|
|
|
|
void
|
|
BasicShadowLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
|
|
void* aCallbackData,
|
|
EndTransactionFlags aFlags)
|
|
{
|
|
BasicLayerManager::EndTransaction(aCallback, aCallbackData, aFlags);
|
|
ForwardTransaction();
|
|
|
|
if (mRepeatTransaction) {
|
|
mRepeatTransaction = false;
|
|
mIsRepeatTransaction = true;
|
|
BasicLayerManager::BeginTransaction();
|
|
BasicShadowLayerManager::EndTransaction(aCallback, aCallbackData, aFlags);
|
|
mIsRepeatTransaction = false;
|
|
} else if (mShadowTarget) {
|
|
if (mWidget) {
|
|
if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
|
|
nsRefPtr<gfxASurface> target = mShadowTarget->OriginalSurface();
|
|
SurfaceDescriptor inSnapshot, snapshot;
|
|
if (AllocBuffer(target->GetSize(), target->GetContentType(),
|
|
&inSnapshot) &&
|
|
// The compositor will usually reuse |snapshot| and return
|
|
// it through |outSnapshot|, but if it doesn't, it's
|
|
// responsible for freeing |snapshot|.
|
|
remoteRenderer->SendMakeSnapshot(inSnapshot, &snapshot)) {
|
|
AutoOpenSurface opener(OPEN_READ_ONLY, snapshot);
|
|
gfxASurface* source = opener.Get();
|
|
|
|
gfxContextAutoSaveRestore restore(mShadowTarget);
|
|
mShadowTarget->SetOperator(gfxContext::OPERATOR_OVER);
|
|
mShadowTarget->DrawSurface(source, source->GetSize());
|
|
}
|
|
if (IsSurfaceDescriptorValid(snapshot)) {
|
|
ShadowLayerForwarder::DestroySharedSurface(&snapshot);
|
|
}
|
|
}
|
|
}
|
|
mShadowTarget = nullptr;
|
|
mDummyTarget = nullptr;
|
|
}
|
|
}
|
|
|
|
bool
|
|
BasicShadowLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
|
|
{
|
|
if (!BasicLayerManager::EndEmptyTransaction(aFlags)) {
|
|
// Return without calling ForwardTransaction. This leaves the
|
|
// ShadowLayerForwarder transaction open; the following
|
|
// EndTransaction will complete it.
|
|
return false;
|
|
}
|
|
ForwardTransaction();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
BasicShadowLayerManager::ForwardTransaction()
|
|
{
|
|
RenderTraceScope rendertrace("Foward Transaction", "000090");
|
|
mPhase = PHASE_FORWARD;
|
|
|
|
// forward this transaction's changeset to our ShadowLayerManager
|
|
AutoInfallibleTArray<EditReply, 10> replies;
|
|
if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies)) {
|
|
for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
|
|
const EditReply& reply = replies[i];
|
|
|
|
switch (reply.type()) {
|
|
case EditReply::TOpThebesBufferSwap: {
|
|
MOZ_LAYERS_LOG(("[LayersForwarder] ThebesBufferSwap"));
|
|
|
|
const OpThebesBufferSwap& obs = reply.get_OpThebesBufferSwap();
|
|
BasicShadowableThebesLayer* thebes = GetBasicShadowable(obs)->AsThebes();
|
|
thebes->SetBackBufferAndAttrs(
|
|
obs.newBackBuffer(), obs.newValidRegion(),
|
|
obs.readOnlyFrontBuffer(), obs.frontUpdatedRegion());
|
|
break;
|
|
}
|
|
case EditReply::TOpBufferSwap: {
|
|
MOZ_LAYERS_LOG(("[LayersForwarder] BufferSwap"));
|
|
|
|
const OpBufferSwap& obs = reply.get_OpBufferSwap();
|
|
const CanvasSurface& newBack = obs.newBackBuffer();
|
|
if (newBack.type() == CanvasSurface::TSurfaceDescriptor) {
|
|
GetBasicShadowable(obs)->SetBackBuffer(newBack.get_SurfaceDescriptor());
|
|
} else if (newBack.type() == CanvasSurface::Tnull_t) {
|
|
GetBasicShadowable(obs)->SetBackBuffer(SurfaceDescriptor());
|
|
} else {
|
|
NS_RUNTIMEABORT("Unknown back image type");
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EditReply::TOpImageSwap: {
|
|
MOZ_LAYERS_LOG(("[LayersForwarder] YUVBufferSwap"));
|
|
|
|
const OpImageSwap& ois = reply.get_OpImageSwap();
|
|
BasicShadowableLayer* layer = GetBasicShadowable(ois);
|
|
const SharedImage& newBack = ois.newBackImage();
|
|
|
|
if (newBack.type() == SharedImage::TSurfaceDescriptor) {
|
|
layer->SetBackBuffer(newBack.get_SurfaceDescriptor());
|
|
} else if (newBack.type() == SharedImage::TYUVImage) {
|
|
const YUVImage& yuv = newBack.get_YUVImage();
|
|
layer->SetBackBufferYUVImage(yuv.Ydata(), yuv.Udata(), yuv.Vdata());
|
|
} else {
|
|
layer->SetBackBuffer(SurfaceDescriptor());
|
|
layer->SetBackBufferYUVImage(SurfaceDescriptor(),
|
|
SurfaceDescriptor(),
|
|
SurfaceDescriptor());
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
NS_RUNTIMEABORT("not reached");
|
|
}
|
|
}
|
|
} else if (HasShadowManager()) {
|
|
NS_WARNING("failed to forward Layers transaction");
|
|
}
|
|
|
|
mPhase = PHASE_NONE;
|
|
|
|
// this may result in Layers being deleted, which results in
|
|
// PLayer::Send__delete__() and DeallocShmem()
|
|
mKeepAlive.Clear();
|
|
}
|
|
|
|
ShadowableLayer*
|
|
BasicShadowLayerManager::Hold(Layer* aLayer)
|
|
{
|
|
NS_ABORT_IF_FALSE(HasShadowManager(),
|
|
"top-level tree, no shadow tree to remote to");
|
|
|
|
ShadowableLayer* shadowable = ToShadowable(aLayer);
|
|
NS_ABORT_IF_FALSE(shadowable, "trying to remote an unshadowable layer");
|
|
|
|
mKeepAlive.AppendElement(aLayer);
|
|
return shadowable;
|
|
}
|
|
|
|
bool
|
|
BasicShadowLayerManager::IsCompositingCheap()
|
|
{
|
|
// Whether compositing is cheap depends on the parent backend.
|
|
return mShadowManager &&
|
|
LayerManager::IsCompositingCheap(GetParentBackendType());
|
|
}
|
|
|
|
void
|
|
BasicShadowLayerManager::SetIsFirstPaint()
|
|
{
|
|
ShadowLayerForwarder::SetIsFirstPaint();
|
|
}
|
|
|
|
bool
|
|
BasicShadowLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent,
|
|
gfx::Rect& aViewport,
|
|
float& aScaleX,
|
|
float& aScaleY)
|
|
{
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
Layer* primaryScrollable = GetPrimaryScrollableLayer();
|
|
if (primaryScrollable) {
|
|
const FrameMetrics& metrics = primaryScrollable->AsContainerLayer()->GetFrameMetrics();
|
|
|
|
// This is derived from the code in
|
|
// gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree.
|
|
const gfx3DMatrix& rootTransform = GetRoot()->GetTransform();
|
|
float devPixelRatioX = 1 / rootTransform.GetXScale();
|
|
float devPixelRatioY = 1 / rootTransform.GetYScale();
|
|
gfx::Rect displayPort((metrics.mDisplayPort.x + metrics.mScrollOffset.x) * devPixelRatioX,
|
|
(metrics.mDisplayPort.y + metrics.mScrollOffset.y) * devPixelRatioY,
|
|
metrics.mDisplayPort.width * devPixelRatioX,
|
|
metrics.mDisplayPort.height * devPixelRatioY);
|
|
|
|
return AndroidBridge::Bridge()->ProgressiveUpdateCallback(
|
|
aHasPendingNewThebesContent, displayPort, devPixelRatioX,
|
|
aViewport, aScaleX, aScaleY);
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
already_AddRefed<ThebesLayer>
|
|
BasicShadowLayerManager::CreateThebesLayer()
|
|
{
|
|
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
|
|
#ifdef FORCE_BASICTILEDTHEBESLAYER
|
|
if (HasShadowManager() && GetParentBackendType() == LAYERS_OPENGL) {
|
|
// BasicTiledThebesLayer doesn't support main
|
|
// thread compositing so only return this layer
|
|
// type if we have a shadow manager.
|
|
nsRefPtr<BasicTiledThebesLayer> layer =
|
|
new BasicTiledThebesLayer(this);
|
|
MAYBE_CREATE_SHADOW(Thebes);
|
|
return layer.forget();
|
|
} else
|
|
#endif
|
|
{
|
|
nsRefPtr<BasicShadowableThebesLayer> layer =
|
|
new BasicShadowableThebesLayer(this);
|
|
MAYBE_CREATE_SHADOW(Thebes);
|
|
return layer.forget();
|
|
}
|
|
}
|
|
|
|
|
|
BasicShadowableLayer::~BasicShadowableLayer()
|
|
{
|
|
if (HasShadow()) {
|
|
PLayerChild::Send__delete__(GetShadow());
|
|
}
|
|
MOZ_COUNT_DTOR(BasicShadowableLayer);
|
|
}
|
|
|
|
}
|
|
}
|