mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
502 lines
15 KiB
C++
502 lines
15 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/LayersMessages.h" // for EditReply, etc
|
|
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
|
|
#include "mozilla/layers/PLayerChild.h" // for PLayerChild
|
|
#include "mozilla/layers/LayerTransactionChild.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)
|
|
, mNeedsComposite(false)
|
|
, mForwarder(new ShadowLayerForwarder)
|
|
{
|
|
MOZ_COUNT_CTOR(ClientLayerManager);
|
|
}
|
|
|
|
ClientLayerManager::~ClientLayerManager()
|
|
{
|
|
mRoot = nullptr;
|
|
|
|
MOZ_COUNT_DTOR(ClientLayerManager);
|
|
}
|
|
|
|
int32_t
|
|
ClientLayerManager::GetMaxTextureSize() const
|
|
{
|
|
return mForwarder->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);
|
|
}
|
|
mForwarder->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");
|
|
mForwarder->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;
|
|
mForwarder->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
|
|
profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_START);
|
|
|
|
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(!(aFlags & END_NO_REMOTE_COMPOSITE));
|
|
|
|
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(!(aFlags & END_NO_REMOTE_COMPOSITE));
|
|
MakeSnapshotIfRequired();
|
|
return true;
|
|
}
|
|
|
|
CompositorChild *
|
|
ClientLayerManager::GetRemoteRenderer()
|
|
{
|
|
if (!mWidget) {
|
|
return nullptr;
|
|
}
|
|
|
|
return mWidget->GetRemoteRenderer();
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::Composite()
|
|
{
|
|
if (CompositorChild* remoteRenderer = GetRemoteRenderer()) {
|
|
remoteRenderer->SendForceComposite();
|
|
}
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::MakeSnapshotIfRequired()
|
|
{
|
|
if (!mShadowTarget) {
|
|
return;
|
|
}
|
|
if (mWidget) {
|
|
if (CompositorChild* remoteRenderer = GetRemoteRenderer()) {
|
|
nsIntRect bounds;
|
|
mWidget->GetBounds(bounds);
|
|
SurfaceDescriptor inSnapshot, snapshot;
|
|
if (mForwarder->AllocSurfaceDescriptor(bounds.Size().ToIntSize(),
|
|
GFX_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)) {
|
|
mForwarder->DestroySharedSurface(&snapshot);
|
|
}
|
|
}
|
|
}
|
|
mShadowTarget = nullptr;
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::FlushRendering()
|
|
{
|
|
if (mWidget) {
|
|
if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
|
|
remoteRenderer->SendFlushRendering();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
|
|
{
|
|
if (mWidget) {
|
|
if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
|
|
remoteRenderer->SendNotifyRegionInvalidated(aRegion);
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
ClientLayerManager::StartFrameTimeRecording(int32_t aBufferSize)
|
|
{
|
|
CompositorChild* renderer = GetRemoteRenderer();
|
|
if (renderer) {
|
|
uint32_t startIndex;
|
|
renderer->SendStartFrameTimeRecording(aBufferSize, &startIndex);
|
|
return startIndex;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::StopFrameTimeRecording(uint32_t aStartIndex,
|
|
nsTArray<float>& aFrameIntervals)
|
|
{
|
|
CompositorChild* renderer = GetRemoteRenderer();
|
|
if (renderer) {
|
|
renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals);
|
|
}
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
|
|
{
|
|
mPhase = PHASE_FORWARD;
|
|
|
|
// forward this transaction's changeset to our LayerManagerComposite
|
|
bool sent;
|
|
AutoInfallibleTArray<EditReply, 10> replies;
|
|
if (HasShadowManager() && mForwarder->EndTransaction(&replies, aScheduleComposite, &sent)) {
|
|
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");
|
|
}
|
|
}
|
|
|
|
if (sent) {
|
|
mNeedsComposite = false;
|
|
}
|
|
} 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 mForwarder->mShadowManager &&
|
|
LayerManager::IsCompositingCheap(mForwarder->GetCompositorBackendType());
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::SetIsFirstPaint()
|
|
{
|
|
mForwarder->SetIsFirstPaint();
|
|
}
|
|
|
|
void
|
|
ClientLayerManager::ClearCachedResources(Layer* aSubtree)
|
|
{
|
|
MOZ_ASSERT(!HasShadowManager() || !aSubtree);
|
|
if (LayerTransactionChild* manager = mForwarder->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 (mForwarder->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,
|
|
ScreenRect& aCompositionBounds,
|
|
CSSToScreenScale& aZoom,
|
|
bool aDrawingCritical)
|
|
{
|
|
aZoom.scale = 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.
|
|
CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel();
|
|
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,
|
|
aCompositionBounds, aZoom);
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
ClientLayer::~ClientLayer()
|
|
{
|
|
if (HasShadow()) {
|
|
PLayerChild::Send__delete__(GetShadow());
|
|
}
|
|
MOZ_COUNT_DTOR(ClientLayer);
|
|
}
|
|
|
|
}
|
|
}
|