gecko-dev/gfx/layers/client/ClientLayerManager.cpp

464 lines
14 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 "ClientLayerManager.h"
#include "CompositorChild.h" // for CompositorChild
#include "GeckoProfiler.h" // for PROFILER_LABEL
#include "gfx3DMatrix.h" // for gfx3DMatrix
#include "gfxASurface.h" // for gfxASurface, etc
#include "ipc/AutoOpenSurface.h" // for AutoOpenSurface
#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
#include "mozilla/Hal.h"
#include "mozilla/dom/ScreenOrientation.h" // for ScreenOrientation
#include "mozilla/dom/TabChild.h" // for TabChild
#include "mozilla/hal_sandbox/PHal.h" // for ScreenConfiguration
#include "mozilla/layers/CompositableClient.h" // for CompositableChild, etc
#include "mozilla/layers/ContentClient.h" // for ContentClientRemote
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/LayerTransaction.h" // for EditReply, etc
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
#include "mozilla/layers/PLayerChild.h" // for PLayerChild
#include "mozilla/layers/PLayerTransactionChild.h"
#include "nsAString.h"
#include "nsIWidget.h" // for nsIWidget
#include "nsTArray.h" // for AutoInfallibleTArray
#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidBridge.h"
#endif
using namespace mozilla::dom;
using namespace mozilla::gfx;
namespace mozilla {
namespace layers {
ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
: mPhase(PHASE_NONE)
, mWidget(aWidget)
, mTargetRotation(ROTATION_0)
, mRepeatTransaction(false)
, mIsRepeatTransaction(false)
, mTransactionIncomplete(false)
, mCompositorMightResample(false)
{
MOZ_COUNT_CTOR(ClientLayerManager);
}
ClientLayerManager::~ClientLayerManager()
{
mRoot = nullptr;
MOZ_COUNT_DTOR(ClientLayerManager);
}
int32_t
ClientLayerManager::GetMaxTextureSize() const
{
return ShadowLayerForwarder::GetMaxTextureSize();
}
void
ClientLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering,
ScreenRotation aRotation)
{
mTargetRotation = aRotation;
if (mWidget) {
mTargetBounds = mWidget->GetNaturalBounds();
}
}
void
ClientLayerManager::SetRoot(Layer* aLayer)
{
if (mRoot != aLayer) {
// 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));
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;
}
}
void
ClientLayerManager::Mutated(Layer* aLayer)
{
LayerManager::Mutated(aLayer);
NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase");
ShadowLayerForwarder::Mutated(Hold(aLayer));
}
void
ClientLayerManager::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;
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.
ScreenOrientation orientation;
if (TabChild* window = mWidget->GetOwningTabChild()) {
orientation = window->GetOrientation();
} else {
hal::ScreenConfiguration currentConfig;
hal::GetCurrentScreenConfiguration(&currentConfig);
orientation = currentConfig.orientation();
}
nsIntRect clientBounds;
mWidget->GetClientBounds(clientBounds);
clientBounds.x = clientBounds.y = 0;
ShadowLayerForwarder::BeginTransaction(mTargetBounds, mTargetRotation, clientBounds, orientation);
// 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 && XRE_GetProcessType() == GeckoProcessType_Default) {
mShadowTarget = aTarget;
}
}
void
ClientLayerManager::BeginTransaction()
{
mInTransaction = true;
BeginTransactionWithTarget(nullptr);
}
bool
ClientLayerManager::EndTransactionInternal(DrawThebesLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags)
{
PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal");
#ifdef MOZ_LAYERS_HAVE_LOG
MOZ_LAYERS_LOG((" ----- (beginning paint)"));
Log();
#endif
NS_ASSERTION(InConstruction(), "Should be in construction phase");
mPhase = PHASE_DRAWING;
ClientLayer* root = ClientLayer::ToClientLayer(GetRoot());
mTransactionIncomplete = false;
// Apply pending tree updates before recomputing effective
// properties.
GetRoot()->ApplyPendingUpdatesToSubtree();
mThebesLayerCallback = aCallback;
mThebesLayerCallbackData = aCallbackData;
GetRoot()->ComputeEffectiveTransforms(gfx3DMatrix());
root->RenderLayer();
mThebesLayerCallback = nullptr;
mThebesLayerCallbackData = nullptr;
// 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;
NS_ASSERTION(!aCallback || !mTransactionIncomplete,
"If callback is not null, transaction must be complete");
return !mTransactionIncomplete;
}
void
ClientLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
void* aCallbackData,
EndTransactionFlags aFlags)
{
if (mWidget) {
mWidget->PrepareWindowEffects();
}
EndTransactionInternal(aCallback, aCallbackData, aFlags);
ForwardTransaction();
if (mRepeatTransaction) {
mRepeatTransaction = false;
mIsRepeatTransaction = true;
BeginTransaction();
ClientLayerManager::EndTransaction(aCallback, aCallbackData, aFlags);
mIsRepeatTransaction = false;
} else {
MakeSnapshotIfRequired();
}
}
bool
ClientLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
{
mInTransaction = false;
if (!mRoot) {
return false;
}
if (!EndTransactionInternal(nullptr, nullptr, aFlags)) {
// Return without calling ForwardTransaction. This leaves the
// ShadowLayerForwarder transaction open; the following
// EndTransaction will complete it.
return false;
}
if (mWidget) {
mWidget->PrepareWindowEffects();
}
ForwardTransaction();
MakeSnapshotIfRequired();
return true;
}
CompositorChild *
ClientLayerManager::GetRemoteRenderer()
{
if (!mWidget) {
return nullptr;
}
return mWidget->GetRemoteRenderer();
}
void
ClientLayerManager::MakeSnapshotIfRequired()
{
if (!mShadowTarget) {
return;
}
if (mWidget) {
if (CompositorChild* remoteRenderer = GetRemoteRenderer()) {
nsIntRect bounds;
mWidget->GetBounds(bounds);
SurfaceDescriptor inSnapshot, snapshot;
if (AllocSurfaceDescriptor(bounds.Size(),
gfxASurface::CONTENT_COLOR_ALPHA,
&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();
mShadowTarget->DrawSurface(source, source->GetSize());
}
if (IsSurfaceDescriptorValid(snapshot)) {
ShadowLayerForwarder::DestroySharedSurface(&snapshot);
}
}
}
mShadowTarget = nullptr;
}
void
ClientLayerManager::FlushRendering()
{
if (mWidget) {
if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
remoteRenderer->SendFlushRendering();
}
}
}
void
ClientLayerManager::ForwardTransaction()
{
mPhase = PHASE_FORWARD;
// forward this transaction's changeset to our LayerManagerComposite
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::TOpContentBufferSwap: {
MOZ_LAYERS_LOG(("[LayersForwarder] DoubleBufferSwap"));
const OpContentBufferSwap& obs = reply.get_OpContentBufferSwap();
CompositableChild* compositableChild =
static_cast<CompositableChild*>(obs.compositableChild());
ContentClientRemote* contentClient =
static_cast<ContentClientRemote*>(compositableChild->GetCompositableClient());
MOZ_ASSERT(contentClient);
contentClient->SwapBuffers(obs.frontUpdatedRegion());
break;
}
case EditReply::TOpTextureSwap: {
MOZ_LAYERS_LOG(("[LayersForwarder] TextureSwap"));
const OpTextureSwap& ots = reply.get_OpTextureSwap();
CompositableChild* compositableChild =
static_cast<CompositableChild*>(ots.compositableChild());
MOZ_ASSERT(compositableChild);
compositableChild->GetCompositableClient()
->SetDescriptorFromReply(ots.textureId(), ots.image());
break;
}
case EditReply::TReplyTextureRemoved: {
// XXX - to manage reuse of gralloc buffers, we'll need to add some
// glue code here to find the TextureClient and invoke a callback to
// let the camera know that the gralloc buffer is not used anymore on
// the compositor side and that it can reuse it.
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*
ClientLayerManager::Hold(Layer* aLayer)
{
NS_ABORT_IF_FALSE(HasShadowManager(),
"top-level tree, no shadow tree to remote to");
ShadowableLayer* shadowable = ClientLayer::ToClientLayer(aLayer);
NS_ABORT_IF_FALSE(shadowable, "trying to remote an unshadowable layer");
mKeepAlive.AppendElement(aLayer);
return shadowable;
}
bool
ClientLayerManager::IsCompositingCheap()
{
// Whether compositing is cheap depends on the parent backend.
return mShadowManager &&
LayerManager::IsCompositingCheap(GetCompositorBackendType());
}
void
ClientLayerManager::SetIsFirstPaint()
{
ShadowLayerForwarder::SetIsFirstPaint();
}
void
ClientLayerManager::ClearCachedResources(Layer* aSubtree)
{
MOZ_ASSERT(!HasShadowManager() || !aSubtree);
if (PLayerTransactionChild* manager = GetShadowManager()) {
manager->SendClearCachedResources();
}
if (aSubtree) {
ClearLayer(aSubtree);
} else if (mRoot) {
ClearLayer(mRoot);
}
}
void
ClientLayerManager::ClearLayer(Layer* aLayer)
{
ClientLayer::ToClientLayer(aLayer)->ClearCachedResources();
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
ClearLayer(child);
}
}
void
ClientLayerManager::GetBackendName(nsAString& aName)
{
switch (GetCompositorBackendType()) {
case LAYERS_BASIC: aName.AssignLiteral("Basic"); return;
case LAYERS_OPENGL: aName.AssignLiteral("OpenGL"); return;
case LAYERS_D3D9: aName.AssignLiteral("Direct3D 9"); return;
case LAYERS_D3D10: aName.AssignLiteral("Direct3D 10"); return;
case LAYERS_D3D11: aName.AssignLiteral("Direct3D 11"); return;
default: NS_RUNTIMEABORT("Invalid backend");
}
}
bool
ClientLayerManager::ProgressiveUpdateCallback(bool aHasPendingNewThebesContent,
gfx::Rect& aViewport,
float& aScaleX,
float& aScaleY,
bool aDrawingCritical)
{
aScaleX = aScaleY = 1.0;
#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();
CSSToLayerScale paintScale = metrics.mDevPixelsPerCSSPixel
/ LayerToLayoutDeviceScale(rootTransform.GetXScale(), rootTransform.GetYScale());
const CSSRect& metricsDisplayPort =
(aDrawingCritical && !metrics.mCriticalDisplayPort.IsEmpty()) ?
metrics.mCriticalDisplayPort : metrics.mDisplayPort;
LayerRect displayPort = (metricsDisplayPort + metrics.mScrollOffset) * paintScale;
return AndroidBridge::Bridge()->ProgressiveUpdateCallback(
aHasPendingNewThebesContent, displayPort, paintScale.scale, aDrawingCritical,
aViewport, aScaleX, aScaleY);
}
#endif
return false;
}
ClientLayer::~ClientLayer()
{
if (HasShadow()) {
PLayerChild::Send__delete__(GetShadow());
}
MOZ_COUNT_DTOR(ClientLayer);
}
}
}