mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
437 lines
13 KiB
C++
437 lines
13 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 "nsIWidget.h"
|
|
#include "mozilla/dom/TabChild.h"
|
|
#include "mozilla/Hal.h"
|
|
#include "mozilla/layers/PLayerChild.h"
|
|
#include "mozilla/layers/PLayerTransactionChild.h"
|
|
#include "mozilla/layers/PLayerTransactionParent.h"
|
|
#include "CompositorChild.h"
|
|
#include "ipc/AutoOpenSurface.h"
|
|
#include "ipc/ShadowLayerChild.h"
|
|
#include "mozilla/layers/CompositableClient.h"
|
|
#include "mozilla/layers/ContentClient.h"
|
|
|
|
#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(¤tConfig);
|
|
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;
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::MakeSnapshotIfRequired()
|
|
{
|
|
if (!mShadowTarget) {
|
|
return;
|
|
}
|
|
if (mWidget) {
|
|
if (CompositorChild* remoteRenderer = mWidget->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;
|
|
}
|
|
|
|
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 (Compositor::GetBackend()) {
|
|
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)
|
|
{
|
|
#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 / rootTransform.GetXScale(),
|
|
metrics.mDevPixelsPerCSSPixel / 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);
|
|
}
|
|
|
|
}
|
|
}
|