summaryrefslogtreecommitdiffstats
path: root/gfx/layers/client/ContentClient.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/client/ContentClient.cpp881
1 files changed, 881 insertions, 0 deletions
diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp
new file mode 100644
index 0000000000..4e465f0a02
--- /dev/null
+++ b/gfx/layers/client/ContentClient.cpp
@@ -0,0 +1,881 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/ContentClient.h"
+#include "BasicLayers.h" // for BasicLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxEnv.h" // for gfxEnv
+
+#include "gfxPoint.h" // for IntSize, gfxPoint
+#include "gfxUtils.h" // for gfxUtils
+#include "ipc/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/ArrayUtils.h" // for ArrayLength
+#include "mozilla/gfx/2D.h" // for DrawTarget, Factory
+#include "mozilla/gfx/BasePoint.h" // for BasePoint
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersMessages.h" // for ThebesBufferData
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/PaintThread.h"
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
+#include "nsISupportsImpl.h" // for gfxContext::Release, etc
+#include "nsIWidget.h" // for nsIWidget
+#include "nsLayoutUtils.h"
+#ifdef XP_WIN
+# include "gfxWindowsPlatform.h"
+#endif
+#ifdef MOZ_WIDGET_GTK
+# include "gfxPlatformGtk.h"
+#endif
+#ifdef MOZ_WAYLAND
+# include "mozilla/widget/nsWaylandDisplay.h"
+#endif
+#include "ReadbackLayer.h"
+
+#include <utility>
+#include <vector>
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static TextureFlags TextureFlagsForContentClientFlags(uint32_t aBufferFlags) {
+ TextureFlags result = TextureFlags::NO_FLAGS;
+
+ if (aBufferFlags & ContentClient::BUFFER_COMPONENT_ALPHA) {
+ result |= TextureFlags::COMPONENT_ALPHA;
+ }
+
+ return result;
+}
+
+static IntRect ComputeBufferRect(const IntRect& aRequestedRect) {
+ IntRect rect(aRequestedRect);
+ // Set a minimum width to guarantee a minimum size of buffers we
+ // allocate (and work around problems on some platforms with smaller
+ // dimensions). 64 used to be the magic number needed to work around
+ // a rendering glitch on b2g (see bug 788411). Now that we don't support
+ // this device anymore we should be fine with 8 pixels as the minimum.
+ rect.SetWidth(std::max(aRequestedRect.Width(), 8));
+ return rect;
+}
+
+/* static */
+already_AddRefed<ContentClient> ContentClient::CreateContentClient(
+ CompositableForwarder* aForwarder) {
+ LayersBackend backend = aForwarder->GetCompositorBackendType();
+ if (backend != LayersBackend::LAYERS_OPENGL &&
+ backend != LayersBackend::LAYERS_D3D11 &&
+ backend != LayersBackend::LAYERS_WR &&
+ backend != LayersBackend::LAYERS_BASIC) {
+ return nullptr;
+ }
+
+ bool useDoubleBuffering = false;
+
+#ifdef XP_WIN
+ if (backend == LayersBackend::LAYERS_D3D11) {
+ useDoubleBuffering = gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend();
+ } else
+#endif
+ {
+#ifdef MOZ_WIDGET_GTK
+# ifdef MOZ_WAYLAND
+ if (widget::GetDMABufDevice()->IsDMABufTexturesEnabled()) {
+ useDoubleBuffering = true;
+ } else
+# endif
+ // We can't use double buffering when using image content with
+ // Xrender support on Linux, as ContentHostDoubleBuffered is not
+ // suited for direct uploads to the server.
+ if (!gfxPlatformGtk::GetPlatform()->UseImageOffscreenSurfaces() ||
+ !gfxVars::UseXRender())
+#endif
+ {
+ useDoubleBuffering = backend == LayersBackend::LAYERS_BASIC;
+ }
+ }
+
+ if (useDoubleBuffering || gfxEnv::ForceDoubleBuffering()) {
+ return MakeAndAddRef<ContentClientDoubleBuffered>(aForwarder);
+ }
+ return MakeAndAddRef<ContentClientSingleBuffered>(aForwarder);
+}
+
+void ContentClient::Clear() { mBuffer = nullptr; }
+
+ContentClient::PaintState ContentClient::BeginPaint(PaintedLayer* aLayer,
+ uint32_t aFlags) {
+ BufferDecision dest = CalculateBufferForPaint(aLayer, aFlags);
+
+ PaintState result;
+ result.mAsyncPaint = (aFlags & PAINT_ASYNC);
+ result.mContentType = dest.mBufferContentType;
+
+ if (!dest.mCanKeepBufferContents) {
+ // We're effectively clearing the valid region, so we need to draw
+ // the entire needed region now.
+ MOZ_ASSERT(!dest.mCanReuseBuffer);
+ MOZ_ASSERT(dest.mValidRegion.IsEmpty());
+
+ result.mRegionToInvalidate = aLayer->GetValidRegion();
+
+#if defined(MOZ_DUMP_PAINTING)
+ if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+ if (result.mContentType != mBuffer->GetContentType()) {
+ printf_stderr(
+ "Invalidating entire rotated buffer (layer %p): content type "
+ "changed\n",
+ aLayer);
+ } else if ((dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) !=
+ mBuffer->HaveBufferOnWhite()) {
+ printf_stderr(
+ "Invalidating entire rotated buffer (layer %p): component alpha "
+ "changed\n",
+ aLayer);
+ }
+ }
+#endif
+ Clear();
+ }
+
+ result.mRegionToDraw.Sub(dest.mNeededRegion, dest.mValidRegion);
+
+ if (result.mRegionToDraw.IsEmpty()) return result;
+
+ // We need to disable rotation if we're going to be resampled when
+ // drawing, because we might sample across the rotation boundary.
+ // Also disable buffer rotation when using webrender.
+ bool canHaveRotation =
+ gfxPlatform::BufferRotationEnabled() &&
+ !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
+ !(aLayer->Manager()->AsWebRenderLayerManager());
+ bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED;
+ OpenMode readMode =
+ result.mAsyncPaint ? OpenMode::OPEN_READ_ASYNC : OpenMode::OPEN_READ;
+ OpenMode writeMode = result.mAsyncPaint ? OpenMode::OPEN_READ_WRITE_ASYNC
+ : OpenMode::OPEN_READ_WRITE;
+
+ IntRect drawBounds = result.mRegionToDraw.GetBounds();
+
+ if (result.mAsyncPaint) {
+ result.mAsyncTask.reset(new PaintTask());
+ }
+
+ // Try to acquire the back buffer, copy over contents if we are using a new
+ // buffer, and rotate or unrotate the buffer as necessary
+ if (mBuffer && dest.mCanReuseBuffer) {
+ if (mBuffer->Lock(writeMode)) {
+ auto newParameters = mBuffer->AdjustedParameters(dest.mBufferRect);
+
+ bool needsUnrotate =
+ (!canHaveRotation && newParameters.IsRotated()) ||
+ (!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds));
+ bool canUnrotate =
+ !result.mAsyncPaint || mBuffer->BufferRotation() == IntPoint(0, 0);
+
+ // Only begin a frame and copy over the previous frame if we don't need
+ // to unrotate, or we can try to unrotate it. This is to ensure that we
+ // don't have a paint task that depends on another paint task.
+ if (!needsUnrotate || canUnrotate) {
+ // If we're async painting then begin to capture draw commands
+ if (result.mAsyncPaint) {
+ mBuffer->BeginCapture();
+ }
+
+ // Do not modify result.mRegionToDraw or result.mContentType after this
+ // call.
+ FinalizeFrame(result);
+ }
+
+ // Try to rotate the buffer or unrotate it if we cannot be rotated
+ if (needsUnrotate) {
+ if (canUnrotate && mBuffer->UnrotateBufferTo(newParameters)) {
+ newParameters.SetUnrotated();
+ mBuffer->SetParameters(newParameters);
+ } else {
+ MOZ_ASSERT(GetFrontBuffer());
+ mBuffer->Unlock();
+ dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
+ dest.mCanReuseBuffer = false;
+ }
+ } else {
+ mBuffer->SetParameters(newParameters);
+ }
+ } else {
+ result.mRegionToDraw = dest.mNeededRegion;
+ dest.mCanReuseBuffer = false;
+ Clear();
+ }
+ }
+
+ MOZ_ASSERT(dest.mBufferRect.Contains(result.mRegionToDraw.GetBounds()));
+
+ NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) ||
+ dest.mBufferRect == dest.mNeededRegion.GetBounds(),
+ "If we're resampling, we need to validate the entire buffer");
+
+ // We never had a buffer, the buffer wasn't big enough, the content changed
+ // types, or we failed to unrotate the buffer when requested. In any case,
+ // we need to allocate a new one and prepare it for drawing.
+ if (!dest.mCanReuseBuffer) {
+ uint32_t bufferFlags = 0;
+ if (dest.mBufferMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ bufferFlags |= BUFFER_COMPONENT_ALPHA;
+ }
+
+ RefPtr<RotatedBuffer> newBuffer =
+ CreateBuffer(result.mContentType, dest.mBufferRect, bufferFlags);
+
+ if (!newBuffer) {
+ if (Factory::ReasonableSurfaceSize(
+ IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
+ gfxCriticalNote << "Failed buffer for " << dest.mBufferRect.X() << ", "
+ << dest.mBufferRect.Y() << ", "
+ << dest.mBufferRect.Width() << ", "
+ << dest.mBufferRect.Height();
+ }
+ result.mAsyncTask = nullptr;
+ Clear();
+ return result;
+ }
+
+ if (!newBuffer->Lock(writeMode)) {
+ gfxCriticalNote << "Failed to lock new back buffer.";
+ result.mAsyncTask = nullptr;
+ Clear();
+ return result;
+ }
+
+ if (result.mAsyncPaint) {
+ newBuffer->BeginCapture();
+ }
+
+ // If we have an existing front buffer, copy it into the new back buffer
+ RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer();
+
+ if (frontBuffer && frontBuffer->Lock(readMode)) {
+ nsIntRegion updateRegion = newBuffer->BufferRect();
+ updateRegion.Sub(updateRegion, result.mRegionToDraw);
+
+ if (!updateRegion.IsEmpty()) {
+ newBuffer->UpdateDestinationFrom(*frontBuffer,
+ updateRegion.GetBounds());
+ }
+
+ frontBuffer->Unlock();
+ } else {
+ result.mRegionToDraw = dest.mNeededRegion;
+ }
+
+ Clear();
+ mBuffer = newBuffer;
+ }
+
+ NS_ASSERTION(canHaveRotation || mBuffer->BufferRotation() == IntPoint(0, 0),
+ "Rotation disabled, but we have nonzero rotation?");
+
+ if (result.mAsyncPaint) {
+ result.mAsyncTask->mTarget = mBuffer->GetBufferTarget();
+ result.mAsyncTask->mClients.AppendElement(mBuffer->GetClient());
+ if (mBuffer->GetClientOnWhite()) {
+ result.mAsyncTask->mClients.AppendElement(mBuffer->GetClientOnWhite());
+ }
+ }
+
+ nsIntRegion invalidate;
+ invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect);
+ result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
+
+ result.mClip = DrawRegionClip::DRAW;
+ result.mMode = dest.mBufferMode;
+
+ return result;
+}
+
+void ContentClient::EndPaint(
+ PaintState& aPaintState,
+ nsTArray<ReadbackProcessor::Update>* aReadbackUpdates) {
+ if (aPaintState.mAsyncTask) {
+ aPaintState.mAsyncTask->mCapture = mBuffer->EndCapture();
+ }
+}
+
+static nsIntRegion ExpandDrawRegion(ContentClient::PaintState& aPaintState,
+ RotatedBuffer::DrawIterator* aIter,
+ BackendType aBackendType) {
+ nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
+ if (aIter) {
+ // The iterators draw region currently only contains the bounds of the
+ // region, this makes it the precise region.
+ aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
+ drawPtr = &aIter->mDrawRegion;
+ }
+ if (aBackendType == BackendType::DIRECT2D ||
+ aBackendType == BackendType::DIRECT2D1_1) {
+ // Simplify the draw region to avoid hitting expensive drawing paths
+ // for complex regions.
+ drawPtr->SimplifyOutwardByArea(100 * 100);
+ }
+ return *drawPtr;
+}
+
+DrawTarget* ContentClient::BorrowDrawTargetForPainting(
+ ContentClient::PaintState& aPaintState,
+ RotatedBuffer::DrawIterator* aIter /* = nullptr */) {
+ if (aPaintState.mMode == SurfaceMode::SURFACE_NONE || !mBuffer) {
+ return nullptr;
+ }
+
+ DrawTarget* result = mBuffer->BorrowDrawTargetForQuadrantUpdate(
+ aPaintState.mRegionToDraw.GetBounds(), aIter);
+ if (!result || !result->IsValid()) {
+ if (result) {
+ mBuffer->ReturnDrawTarget(result);
+ }
+ return nullptr;
+ }
+
+ nsIntRegion regionToDraw =
+ ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
+
+ if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA ||
+ aPaintState.mContentType == gfxContentType::COLOR_ALPHA) {
+ // HaveBuffer() => we have an existing buffer that we must clear
+ for (auto iter = regionToDraw.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ result->ClearRect(Rect(rect.X(), rect.Y(), rect.Width(), rect.Height()));
+ }
+ }
+
+ return result;
+}
+
+void ContentClient::ReturnDrawTarget(gfx::DrawTarget*& aReturned) {
+ mBuffer->ReturnDrawTarget(aReturned);
+}
+
+ContentClient::BufferDecision ContentClient::CalculateBufferForPaint(
+ PaintedLayer* aLayer, uint32_t aFlags) {
+ gfxContentType layerContentType = aLayer->CanUseOpaqueSurface()
+ ? gfxContentType::COLOR
+ : gfxContentType::COLOR_ALPHA;
+
+ SurfaceMode mode;
+ gfxContentType contentType;
+ IntRect destBufferRect;
+ nsIntRegion neededRegion;
+ nsIntRegion validRegion = aLayer->GetValidRegion();
+
+ bool canReuseBuffer = !!mBuffer;
+ bool canKeepBufferContents = true;
+
+ while (true) {
+ mode = aLayer->GetSurfaceMode();
+ neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
+ canReuseBuffer =
+ canReuseBuffer &&
+ ValidBufferSize(mBufferSizePolicy, mBuffer->BufferRect().Size(),
+ neededRegion.GetBounds().Size());
+ contentType = layerContentType;
+
+ if (canReuseBuffer) {
+ if (mBuffer->BufferRect().Contains(neededRegion.GetBounds())) {
+ // We don't need to adjust mBufferRect.
+ destBufferRect = mBuffer->BufferRect();
+ } else if (neededRegion.GetBounds().Size() <=
+ mBuffer->BufferRect().Size()) {
+ // The buffer's big enough but doesn't contain everything that's
+ // going to be visible. We'll move it.
+ destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(),
+ mBuffer->BufferRect().Size());
+ } else {
+ destBufferRect = neededRegion.GetBounds();
+ }
+ } else {
+ destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
+ }
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+#else
+ if (!aLayer->GetParent() ||
+ !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
+ !aLayer->AsShadowableLayer() ||
+ !aLayer->AsShadowableLayer()->HasShadow()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ } else {
+ contentType = gfxContentType::COLOR;
+ }
+#endif
+ }
+
+ if ((aFlags & PAINT_WILL_RESAMPLE) &&
+ (!neededRegion.GetBounds().IsEqualInterior(destBufferRect) ||
+ neededRegion.GetNumRects() > 1)) {
+ // The area we add to neededRegion might not be painted opaquely.
+ if (mode == SurfaceMode::SURFACE_OPAQUE) {
+ contentType = gfxContentType::COLOR_ALPHA;
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ }
+
+ // We need to validate the entire buffer, to make sure that only valid
+ // pixels are sampled.
+ neededRegion = destBufferRect;
+ }
+
+ // If we have an existing buffer, but the content type has changed or we
+ // have transitioned into/out of component alpha, then we need to recreate
+ // it.
+ bool needsComponentAlpha = (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA);
+ bool backBufferChangedSurface =
+ mBuffer && (contentType != mBuffer->GetContentType() ||
+ needsComponentAlpha != mBuffer->HaveBufferOnWhite());
+ if (canKeepBufferContents && backBufferChangedSurface) {
+ // Restart the decision process; we won't re-enter since we guard on
+ // being able to keep the buffer contents.
+ canReuseBuffer = false;
+ canKeepBufferContents = false;
+ validRegion.SetEmpty();
+ continue;
+ }
+ break;
+ }
+
+ NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
+ "Destination rect doesn't contain what we need to paint");
+
+ BufferDecision dest;
+ dest.mNeededRegion = std::move(neededRegion);
+ dest.mValidRegion = std::move(validRegion);
+ dest.mBufferRect = destBufferRect;
+ dest.mBufferMode = mode;
+ dest.mBufferContentType = contentType;
+ dest.mCanReuseBuffer = canReuseBuffer;
+ dest.mCanKeepBufferContents = canKeepBufferContents;
+ return dest;
+}
+
+bool ContentClient::ValidBufferSize(BufferSizePolicy aPolicy,
+ const gfx::IntSize& aBufferSize,
+ const gfx::IntSize& aVisibleBoundsSize) {
+ return (
+ aVisibleBoundsSize == aBufferSize ||
+ (SizedToVisibleBounds != aPolicy && aVisibleBoundsSize < aBufferSize));
+}
+
+void ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix) {
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
+}
+
+// We pass a null pointer for the ContentClient Forwarder argument, which means
+// this client will not have a ContentHost on the other side.
+ContentClientBasic::ContentClientBasic(gfx::BackendType aBackend)
+ : ContentClient(nullptr, ContainsVisibleBounds), mBackend(aBackend) {}
+
+void ContentClientBasic::DrawTo(PaintedLayer* aLayer, gfx::DrawTarget* aTarget,
+ float aOpacity, gfx::CompositionOp aOp,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform) {
+ if (!mBuffer) {
+ return;
+ }
+
+ mBuffer->DrawTo(aLayer, aTarget, aOpacity, aOp, aMask, aMaskTransform);
+}
+
+RefPtr<RotatedBuffer> ContentClientBasic::CreateBuffer(gfxContentType aType,
+ const IntRect& aRect,
+ uint32_t aFlags) {
+ MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ gfxDevCrash(LogReason::AlphaWithBasicClient)
+ << "Asking basic content client for component alpha";
+ }
+
+ IntSize size(aRect.Width(), aRect.Height());
+ RefPtr<gfx::DrawTarget> drawTarget;
+
+#ifdef XP_WIN
+ if (mBackend == BackendType::CAIRO &&
+ (aType == gfxContentType::COLOR ||
+ aType == gfxContentType::COLOR_ALPHA)) {
+ RefPtr<gfxASurface> surf = new gfxWindowsSurface(
+ size, aType == gfxContentType::COLOR ? gfxImageFormat::X8R8G8B8_UINT32
+ : gfxImageFormat::A8R8G8B8_UINT32);
+ drawTarget = gfxPlatform::CreateDrawTargetForSurface(surf, size);
+ }
+#endif
+
+ if (!drawTarget) {
+ drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
+ mBackend, size,
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
+ }
+
+ if (!drawTarget) {
+ return nullptr;
+ }
+
+ return new DrawTargetRotatedBuffer(drawTarget, nullptr, aRect,
+ IntPoint(0, 0));
+}
+
+class RemoteBufferReadbackProcessor : public TextureReadbackSink {
+ public:
+ RemoteBufferReadbackProcessor(
+ nsTArray<ReadbackProcessor::Update>* aReadbackUpdates,
+ const IntRect& aBufferRect, const nsIntPoint& aBufferRotation)
+ : mReadbackUpdates(aReadbackUpdates->Clone()),
+ mBufferRect(aBufferRect),
+ mBufferRotation(aBufferRotation) {
+ for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
+ mLayerRefs.push_back(mReadbackUpdates[i].mLayer);
+ }
+ }
+
+ virtual void ProcessReadback(
+ gfx::DataSourceSurface* aSourceSurface) override {
+ SourceRotatedBuffer rotBuffer(aSourceSurface, nullptr, mBufferRect,
+ mBufferRotation);
+
+ for (uint32_t i = 0; i < mReadbackUpdates.Length(); ++i) {
+ ReadbackProcessor::Update& update = mReadbackUpdates[i];
+ nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
+
+ ReadbackSink* sink = update.mLayer->GetSink();
+
+ if (!sink) {
+ continue;
+ }
+
+ if (!aSourceSurface) {
+ sink->SetUnknown(update.mSequenceCounter);
+ continue;
+ }
+
+ RefPtr<DrawTarget> dt = sink->BeginUpdate(update.mUpdateRect + offset,
+ update.mSequenceCounter);
+ if (!dt) {
+ continue;
+ }
+
+ dt->SetTransform(Matrix::Translation(offset.x, offset.y));
+
+ rotBuffer.DrawBufferWithRotation(dt);
+
+ update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
+ }
+ }
+
+ private:
+ nsTArray<ReadbackProcessor::Update> mReadbackUpdates;
+ // This array is used to keep the layers alive until the callback.
+ std::vector<RefPtr<Layer>> mLayerRefs;
+
+ IntRect mBufferRect;
+ nsIntPoint mBufferRotation;
+};
+
+void ContentClientRemoteBuffer::EndPaint(
+ PaintState& aPaintState,
+ nsTArray<ReadbackProcessor::Update>* aReadbackUpdates) {
+ MOZ_ASSERT(!mBuffer || !mBuffer->HaveBufferOnWhite() || !aReadbackUpdates ||
+ aReadbackUpdates->Length() == 0);
+
+ RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
+
+ if (remoteBuffer && remoteBuffer->IsLocked()) {
+ if (aReadbackUpdates && aReadbackUpdates->Length() > 0) {
+ RefPtr<TextureReadbackSink> readbackSink =
+ new RemoteBufferReadbackProcessor(aReadbackUpdates,
+ remoteBuffer->BufferRect(),
+ remoteBuffer->BufferRotation());
+
+ remoteBuffer->GetClient()->SetReadbackSink(readbackSink);
+ }
+
+ remoteBuffer->Unlock();
+ remoteBuffer->SyncWithObject(mForwarder->GetSyncObject());
+ }
+
+ ContentClient::EndPaint(aPaintState, aReadbackUpdates);
+}
+
+RefPtr<RotatedBuffer> ContentClientRemoteBuffer::CreateBuffer(
+ gfxContentType aType, const IntRect& aRect, uint32_t aFlags) {
+ // If we hit this assertion, then it might be due to an empty transaction
+ // followed by a real transaction. Our buffers should be created (but not
+ // painted in the empty transaction) and then painted (but not created) in the
+ // real transaction. That is kind of fragile, and this assert will catch
+ // circumstances where we screw that up, e.g., by unnecessarily recreating our
+ // buffers.
+ MOZ_ASSERT(!mIsNewBuffer,
+ "Bad! Did we create a buffer twice without painting?");
+
+ gfx::SurfaceFormat format =
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType);
+
+ TextureFlags textureFlags = TextureFlagsForContentClientFlags(aFlags);
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ textureFlags |= TextureFlags::COMPONENT_ALPHA;
+ }
+
+ RefPtr<RotatedBuffer> buffer =
+ CreateBufferInternal(aRect, format, textureFlags);
+
+ if (!buffer) {
+ return nullptr;
+ }
+
+ mIsNewBuffer = true;
+ mTextureFlags = textureFlags;
+
+ return buffer;
+}
+
+RefPtr<RotatedBuffer> ContentClientRemoteBuffer::CreateBufferInternal(
+ const gfx::IntRect& aRect, gfx::SurfaceFormat aFormat,
+ TextureFlags aFlags) {
+ TextureAllocationFlags textureAllocFlags =
+ TextureAllocationFlags::ALLOC_DEFAULT;
+
+ RefPtr<TextureClient> textureClient = CreateTextureClientForDrawing(
+ aFormat, aRect.Size(), BackendSelector::Content,
+ aFlags | ExtraTextureFlags() | TextureFlags::BLOCKING_READ_LOCK,
+ textureAllocFlags);
+
+ if (!textureClient || !AddTextureClient(textureClient)) {
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> textureClientOnWhite;
+ if (aFlags & TextureFlags::COMPONENT_ALPHA) {
+ TextureAllocationFlags allocFlags = TextureAllocationFlags::ALLOC_DEFAULT;
+ if (mForwarder->SupportsTextureDirectMapping()) {
+ allocFlags =
+ TextureAllocationFlags(allocFlags | ALLOC_ALLOW_DIRECT_MAPPING);
+ }
+ textureClientOnWhite =
+ textureClient->CreateSimilar(mForwarder->GetCompositorBackendType(),
+ aFlags | ExtraTextureFlags(), allocFlags);
+ if (!textureClientOnWhite || !AddTextureClient(textureClientOnWhite)) {
+ return nullptr;
+ }
+ // We don't enable the readlock for the white buffer since we always
+ // use them together and waiting on the lock for the black
+ // should be sufficient.
+ }
+
+ return new RemoteRotatedBuffer(textureClient, textureClientOnWhite, aRect,
+ IntPoint(0, 0));
+}
+
+nsIntRegion ContentClientRemoteBuffer::GetUpdatedRegion(
+ const nsIntRegion& aRegionToDraw, const nsIntRegion& aVisibleRegion) {
+ nsIntRegion updatedRegion;
+ if (mIsNewBuffer || mBuffer->DidSelfCopy()) {
+ // A buffer reallocation clears both buffers. The front buffer has all the
+ // content by now, but the back buffer is still clear. Here, in effect, we
+ // are saying to copy all of the pixels of the front buffer to the back.
+ // Also when we self-copied in the buffer, the buffer space
+ // changes and some changed buffer content isn't reflected in the
+ // draw or invalidate region (on purpose!). When this happens, we
+ // need to read back the entire buffer too.
+ updatedRegion = aVisibleRegion.GetBounds();
+ mIsNewBuffer = false;
+ } else {
+ updatedRegion = aRegionToDraw;
+ }
+
+ MOZ_ASSERT(mBuffer, "should have a back buffer by now");
+ NS_ASSERTION(mBuffer->BufferRect().Contains(aRegionToDraw.GetBounds()),
+ "Update outside of buffer rect!");
+
+ return updatedRegion;
+}
+
+void ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion) {
+ nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw, aVisibleRegion);
+
+ RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
+
+ MOZ_ASSERT(remoteBuffer && remoteBuffer->GetClient());
+ if (remoteBuffer->HaveBufferOnWhite()) {
+ mForwarder->UseComponentAlphaTextures(this, remoteBuffer->GetClient(),
+ remoteBuffer->GetClientOnWhite());
+ } else {
+ AutoTArray<CompositableForwarder::TimedTextureClient, 1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = remoteBuffer->GetClient();
+ IntSize size = remoteBuffer->GetClient()->GetSize();
+ t->mPictureRect = nsIntRect(0, 0, size.width, size.height);
+
+ GetForwarder()->UseTextures(this, textures);
+ }
+
+ // This forces a synchronous transaction, so we can swap buffers now
+ // and know that we'll have sole ownership of the old front buffer
+ // by the time we paint next.
+ mForwarder->UpdateTextureRegion(
+ this,
+ ThebesBufferData(remoteBuffer->BufferRect(),
+ remoteBuffer->BufferRotation()),
+ updatedRegion);
+ SwapBuffers(updatedRegion);
+}
+
+void ContentClientRemoteBuffer::Dump(std::stringstream& aStream,
+ const char* aPrefix, bool aDumpHtml,
+ TextureDumpMode aCompress) {
+ RemoteRotatedBuffer* remoteBuffer = GetRemoteBuffer();
+
+ // TODO We should combine the OnWhite/OnBlack here an just output a single
+ // image.
+ if (!aDumpHtml) {
+ aStream << "\n" << aPrefix << "Surface: ";
+ }
+ CompositableClient::DumpTextureClient(
+ aStream, remoteBuffer ? remoteBuffer->GetClient() : nullptr, aCompress);
+}
+
+void ContentClientDoubleBuffered::Dump(std::stringstream& aStream,
+ const char* aPrefix, bool aDumpHtml,
+ TextureDumpMode aCompress) {
+ // TODO We should combine the OnWhite/OnBlack here an just output a single
+ // image.
+ if (!aDumpHtml) {
+ aStream << "\n" << aPrefix << "Surface: ";
+ }
+ CompositableClient::DumpTextureClient(
+ aStream, mFrontBuffer ? mFrontBuffer->GetClient() : nullptr, aCompress);
+}
+
+void ContentClientDoubleBuffered::Clear() {
+ ContentClient::Clear();
+ mFrontBuffer = nullptr;
+}
+
+void ContentClientDoubleBuffered::SwapBuffers(
+ const nsIntRegion& aFrontUpdatedRegion) {
+ mFrontUpdatedRegion = aFrontUpdatedRegion;
+
+ RefPtr<RemoteRotatedBuffer> frontBuffer = mFrontBuffer;
+ RefPtr<RemoteRotatedBuffer> backBuffer = GetRemoteBuffer();
+
+ std::swap(frontBuffer, backBuffer);
+
+ mFrontBuffer = frontBuffer;
+ mBuffer = backBuffer;
+
+ mFrontAndBackBufferDiffer = true;
+}
+
+ContentClient::PaintState ContentClientDoubleBuffered::BeginPaint(
+ PaintedLayer* aLayer, uint32_t aFlags) {
+ EnsureBackBufferIfFrontBuffer();
+
+ mIsNewBuffer = false;
+
+ if (!mFrontBuffer || !mBuffer) {
+ mFrontAndBackBufferDiffer = false;
+ }
+
+ if (mFrontAndBackBufferDiffer) {
+ if (mFrontBuffer->DidSelfCopy()) {
+ // We can't easily draw our front buffer into us, since we're going to be
+ // copying stuff around anyway it's easiest if we just move our situation
+ // to non-rotated while we're at it. If this situation occurs we'll have
+ // hit a self-copy path in PaintThebes before as well anyway.
+ gfx::IntRect backBufferRect = mBuffer->BufferRect();
+ backBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft());
+
+ mBuffer->SetBufferRect(backBufferRect);
+ mBuffer->SetBufferRotation(IntPoint(0, 0));
+ } else {
+ mBuffer->SetBufferRect(mFrontBuffer->BufferRect());
+ mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation());
+ }
+ }
+
+ return ContentClient::BeginPaint(aLayer, aFlags);
+}
+
+// Sync front/back buffers content
+// After executing, the new back buffer has the same (interesting) pixels as
+// the new front buffer, and mValidRegion et al. are correct wrt the new
+// back buffer (i.e. as they were for the old back buffer)
+void ContentClientDoubleBuffered::FinalizeFrame(PaintState& aPaintState) {
+ if (!mFrontAndBackBufferDiffer) {
+ MOZ_ASSERT(!mFrontBuffer || !mFrontBuffer->DidSelfCopy(),
+ "If the front buffer did a self copy then our front and back "
+ "buffer must be different.");
+ return;
+ }
+
+ MOZ_ASSERT(mFrontBuffer && mBuffer);
+ if (!mFrontBuffer || !mBuffer) {
+ return;
+ }
+
+ MOZ_LAYERS_LOG(
+ ("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>", this,
+ mFrontUpdatedRegion.GetBounds().X(), mFrontUpdatedRegion.GetBounds().Y(),
+ mFrontUpdatedRegion.GetBounds().Width(),
+ mFrontUpdatedRegion.GetBounds().Height()));
+
+ mFrontAndBackBufferDiffer = false;
+
+ nsIntRegion updateRegion = mFrontUpdatedRegion;
+ if (mFrontBuffer->DidSelfCopy()) {
+ mFrontBuffer->ClearDidSelfCopy();
+ updateRegion = mBuffer->BufferRect();
+ }
+
+ // No point in sync'ing what we are going to draw over anyway. And if there is
+ // nothing to sync at all, there is nothing to do and we can go home early.
+ updateRegion.Sub(updateRegion, aPaintState.mRegionToDraw);
+ if (updateRegion.IsEmpty()) {
+ return;
+ }
+
+ OpenMode openMode = aPaintState.mAsyncPaint ? OpenMode::OPEN_READ_ASYNC
+ : OpenMode::OPEN_READ_ONLY;
+
+ if (mFrontBuffer->Lock(openMode)) {
+ mBuffer->UpdateDestinationFrom(*mFrontBuffer, updateRegion.GetBounds());
+
+ if (aPaintState.mAsyncPaint) {
+ aPaintState.mAsyncTask->mClients.AppendElement(mFrontBuffer->GetClient());
+ if (mFrontBuffer->GetClientOnWhite()) {
+ aPaintState.mAsyncTask->mClients.AppendElement(
+ mFrontBuffer->GetClientOnWhite());
+ }
+ }
+
+ mFrontBuffer->Unlock();
+ }
+}
+
+void ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer() {
+ if (!mBuffer && mFrontBuffer) {
+ mBuffer = CreateBufferInternal(mFrontBuffer->BufferRect(),
+ mFrontBuffer->GetFormat(), mTextureFlags);
+ MOZ_ASSERT(mBuffer);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla